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

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

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

View file

@ -0,0 +1,48 @@
/*
* st_approve_batch.pgc - 정산 승인 워크플로 배치 (2단 검증 + 집합 승격 + XA).
* 당일 CONFIRMED 정산을 확인한 뒤 net>0 건만 APPROVED 로 일괄 승격하고,
* 승인 대상에서 빠진 건수(net<=0)를 별도 집계한다. 승인 원장 마커 기록.
* 사용법: st_approve_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "st_dbio.h"
#include "st_approve_batch.h"
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
st_approve_rec_t rec;
memset(&rec, 0, sizeof(rec));
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16];
long h_confirmed;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL SELECT count(*) INTO :h_confirmed FROM settlement
WHERE biz_date = :h_bizdate AND status = 'CONFIRMED';
if (sqlca.sqlcode < 0) { fprintf(stderr, "CHECK FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
EXEC SQL UPDATE settlement SET status = 'APPROVED'
WHERE biz_date = :h_bizdate AND status = 'CONFIRMED' AND net > 0;
if (sqlca.sqlcode < 0) { fprintf(stderr, "APPROVE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
rec.approved = (long) sqlca.sqlerrd[2];
rec.skipped = h_confirmed - rec.approved;
EXEC SQL INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date)
VALUES (nextval('ledger_seq'), 0, 'STAPPROVE', :h_confirmed, :h_bizdate);
if (sqlca.sqlcode < 0) { fprintf(stderr, "MARKER FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> %s COMMIT: bizdate=%s approved=%ld skipped=%ld\n", ST_APPROVE_TAG, bizdate, rec.approved, rec.skipped);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,47 @@
/*
* st_deposit_batch.pgc - 보증금 차감 배치 (조건부 집합 UPDATE + 총액 조정 + XA).
* 차감 후에도 지급액이 남는(net_amount > cut) SCHEDULED 지급 전체에서 보증금
* 단위를 일괄 차감하고, 총 차감액을 음수 조정 1건으로 기록한다.
* 사용법: st_deposit_batch [YYYY-MM-DD] [cut]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "st_dbio.h"
#include "st_deposit_batch.h"
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
st_deposit_rec_t rec;
rec.cut_each = (argc > 2) ? atol(argv[2]) : ST_DEPOSIT_DEF_CUT;
rec.cut_rows = 0;
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16];
long h_cut;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
h_cut = rec.cut_each;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL UPDATE st_payment SET net_amount = net_amount - :h_cut
WHERE biz_date = :h_bizdate AND status = 'SCHEDULED' AND net_amount > :h_cut;
if (sqlca.sqlcode < 0) { fprintf(stderr, "CUT FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
rec.cut_rows = (long) sqlca.sqlerrd[2];
if (rec.cut_rows > 0) {
if (stdb_insert_adjust(0, 0, -(rec.cut_each * rec.cut_rows), "DEPOSIT", bizdate) < 0) { tpabort(0); return 1; }
}
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> %s COMMIT: bizdate=%s cut_each=%ld cut_rows=%ld total=%ld\n",
ST_DEPOSIT_TAG, bizdate, rec.cut_each, rec.cut_rows, rec.cut_each * rec.cut_rows);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,54 @@
/*
* st_grproll_batch.pgc - 가맹점그룹 통합정산 롤업 배치 (prefix 그룹 커서 + upsert + XA).
* 가맹점 ID 앞 2자리를 그룹키로 삼아 당일 정산을 그룹 단위로 롤업 집계하고,
* 그룹키 행을 st_merch_settle 에 upsert 한다 (그룹 통합정산 기초).
* 사용법: st_grproll_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "st_dbio.h"
#include "st_grproll_batch.h"
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
st_grproll_rec_t rec;
memset(&rec, 0, sizeof(rec));
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16], h_grp[8];
long h_cnt, h_net;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE grproll_bc CURSOR FOR
SELECT substr(merchant_id, 1, 2), count(*), coalesce(sum(net),0)
FROM settlement WHERE biz_date = :h_bizdate
GROUP BY substr(merchant_id, 1, 2) ORDER BY 1;
EXEC SQL OPEN grproll_bc;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH grproll_bc INTO :h_grp, :h_cnt, :h_net;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE grproll_bc; tpabort(0); return 1; }
{
char grpkey[16];
snprintf(grpkey, sizeof(grpkey), "GRP:%s", h_grp);
if (stdb_upsert_merch_settle(grpkey, bizdate, h_net, 0, h_net, h_cnt) < 0) { EXEC SQL CLOSE grproll_bc; tpabort(0); return 1; }
rec.groups++;
rec.rolled_net += h_net;
}
}
EXEC SQL CLOSE grproll_bc;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> %s COMMIT: bizdate=%s groups=%ld rolled_net=%ld\n", ST_GRPROLL_TAG, bizdate, rec.groups, rec.rolled_net);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,58 @@
/*
* st_incent_batch.pgc - 구간별 인센티브 정산 배치 (커서 + 3단 요율 사다리 + XA).
* 가맹점 일 집계(gross) 를 내림차순 커서로 순회, 1억/5천만/1천만 구간별
* 인센티브(30/20/10bps)를 산정해 양수 조정으로 지급한다. 사용법: st_incent_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "st_dbio.h"
#include "st_incent_batch.h"
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
st_incent_rec_t rec;
memset(&rec, 0, sizeof(rec));
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16], h_merch[64];
long h_gross;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE incent_bc CURSOR FOR
SELECT merchant_id, gross_amount FROM st_merch_settle
WHERE biz_date = :h_bizdate AND gross_amount >= 10000000
ORDER BY gross_amount DESC;
EXEC SQL OPEN incent_bc;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH incent_bc INTO :h_merch, :h_gross;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE incent_bc; tpabort(0); return 1; }
{
long bps;
if (h_gross >= 100000000) { bps = 30; rec.tier1++; }
else if (h_gross >= 50000000) { bps = 20; rec.tier2++; }
else { bps = 10; rec.tier3++; }
{
long inc = h_gross * bps / 10000;
if (stdb_insert_adjust(0, 0, inc, "INCENTIVE", bizdate) < 0) { EXEC SQL CLOSE incent_bc; tpabort(0); return 1; }
rec.paid_sum += inc;
}
}
}
EXEC SQL CLOSE incent_bc;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> %s COMMIT: bizdate=%s t1=%ld t2=%ld t3=%ld incentive_sum=%ld\n",
ST_INCENT_TAG, bizdate, rec.tier1, rec.tier2, rec.tier3, rec.paid_sum);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,54 @@
/*
* st_partial_batch.pgc - 부분정산 잔여 스윕 배치 (HAVING 커서 + XA).
* 부분정산으로 인해 정산 합계가 매입 net 에 못 미치는 매입을 HAVING 커서로
* 찾아, 잔여분을 PARTIAL 정산 레코드로 마저 생성한다. 사용법: st_partial_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "st_dbio.h"
#include "st_partial_batch.h"
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
st_partial_rec_t rec;
memset(&rec, 0, sizeof(rec));
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16], h_merch[64];
long h_pid, h_remain;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE partial_bc CURSOR FOR
SELECT p.purchase_id, p.merchant_id, p.net - sum(s.net)
FROM purchase p JOIN settlement s ON s.purchase_id = p.purchase_id
WHERE p.biz_date = :h_bizdate AND s.status <> 'CANCELLED'
GROUP BY p.purchase_id, p.merchant_id, p.net
HAVING sum(s.net) < p.net;
EXEC SQL OPEN partial_bc;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH partial_bc INTO :h_pid, :h_merch, :h_remain;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE partial_bc; tpabort(0); return 1; }
if (h_remain <= 0) continue;
EXEC SQL INSERT INTO settlement (settlement_id, purchase_id, merchant_id, net, biz_date, status)
VALUES (nextval('settlement_seq'), :h_pid, :h_merch, :h_remain, :h_bizdate, 'PARTIAL');
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE partial_bc; tpabort(0); return 1; }
rec.swept++;
rec.swept_amt += h_remain;
}
EXEC SQL CLOSE partial_bc;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> %s COMMIT: bizdate=%s partial_swept=%ld amt=%ld\n", ST_PARTIAL_TAG, bizdate, rec.swept, rec.swept_amt);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,43 @@
/*
* st_promo_batch.pgc - 프로모션/이벤트 정산 배치 (집합 INSERT..SELECT + XA).
* 당일 거래대금이 기준액 이상인 가맹점 집계에 대해 수수료의 10% 를 이벤트
* 환급 조정(PROMO)으로 일괄 생성한다. 사용법: st_promo_batch [YYYY-MM-DD] [threshold]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "st_dbio.h"
#include "st_promo_batch.h"
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
st_promo_rec_t rec;
rec.threshold = (argc > 2) ? atol(argv[2]) : ST_PROMO_DEF_THRESH;
rec.granted = 0;
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16];
long h_thr;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
h_thr = rec.threshold;
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 st_adjust (adj_id, settlement_id, purchase_id, delta, reason, biz_date)
SELECT nextval('st_adj_seq'), 0, 0, fee_amount / 10, 'PROMO', biz_date
FROM st_merch_settle
WHERE biz_date = :h_bizdate AND gross_amount >= :h_thr AND fee_amount > 0;
if (sqlca.sqlcode < 0) { fprintf(stderr, "%s FAIL [%d] %s\n", ST_PROMO_TAG, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
rec.granted = (long) sqlca.sqlerrd[2];
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> %s COMMIT: bizdate=%s threshold=%ld promo_granted=%ld\n", ST_PROMO_TAG, bizdate, rec.threshold, rec.granted);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,58 @@
/*
* st_ratesim_batch.pgc - 요율 시뮬레이션 배치 (XA 배치).
* 당일 매입 총액을 기준으로 요율표(st_fee_rate)의 모든 구간 요율을 커서로
* 순회하며 구간별 예상 수수료를 시뮬레이션 출력한다 (조회 전용).
* ATMI 클라이언트: tpinit/tpopen 으로 ECPG XA RM 를 열고 tpbegin 으로 ONE global tx
* 를 시작, EXEC SQL 수행 후 tpcommit 이 XA 2PC 를 구동. 사용법: st_ratesim_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "st_dbio.h"
#include "st_ratesim_batch.h"
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
st_ratesim_rec_t rec;
memset(&rec, 0, sizeof(rec));
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16], h_type[16];
long h_gross, h_min, h_bps;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_gross FROM purchase WHERE biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) { fprintf(stderr, "GROSS FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
rec.day_gross = h_gross;
EXEC SQL DECLARE ratesim_bc CURSOR FOR
SELECT fee_type, band_min, rate_bps FROM st_fee_rate ORDER BY fee_type, band_min;
EXEC SQL OPEN ratesim_bc;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH ratesim_bc INTO :h_type, :h_min, :h_bps;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ratesim_bc; tpabort(0); return 1; }
{
long sim = h_gross * h_bps / 10000; /* 이 구간 요율이 전액 적용됐을 때 */
rec.bands++;
rec.projected += sim;
printf(" SIM %-8s band_min=%ld bps=%ld -> fee=%ld\n", h_type, h_min, h_bps, sim);
}
}
EXEC SQL CLOSE ratesim_bc;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> %s COMMIT: bizdate=%s gross=%ld bands=%ld projected_total=%ld\n",
ST_RATESIM_TAG, bizdate, rec.day_gross, rec.bands, rec.projected);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,62 @@
/*
* st_recvsweep_batch.pgc - 미수금 상계 스윕 배치 (미수 가맹점 커서 + 상계 + XA).
* 매입 조인으로 미수금(음수 조정 누계)이 있는 가맹점을 커서로 찾고, 각
* 가맹점의 당일 예정 지급에서 미수금과 가용액의 min 만큼 상계 차감한다.
* 사용법: st_recvsweep_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "st_dbio.h"
#include "st_recvsweep_batch.h"
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
st_recvsweep_rec_t rec;
memset(&rec, 0, sizeof(rec));
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16], h_merch[64];
long h_recv, h_avail, h_off;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE recvsweep_bc CURSOR FOR
SELECT p.merchant_id, -sum(a.delta)
FROM st_adjust a JOIN purchase p ON p.purchase_id = a.purchase_id
WHERE a.delta < 0
GROUP BY p.merchant_id
HAVING sum(a.delta) < 0
ORDER BY p.merchant_id;
EXEC SQL OPEN recvsweep_bc;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH recvsweep_bc INTO :h_merch, :h_recv;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE recvsweep_bc; tpabort(0); return 1; }
EXEC SQL SELECT coalesce(sum(net_amount),0) INTO :h_avail FROM st_payment
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status = 'SCHEDULED';
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE recvsweep_bc; tpabort(0); return 1; }
h_off = (h_recv < h_avail) ? h_recv : h_avail;
if (h_off <= 0) continue;
EXEC SQL UPDATE st_payment SET net_amount = net_amount - :h_off
WHERE pay_id = (SELECT min(pay_id) FROM st_payment
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status = 'SCHEDULED');
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE recvsweep_bc; tpabort(0); return 1; }
rec.merchants++;
rec.offset_sum += h_off;
}
EXEC SQL CLOSE recvsweep_bc;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> %s COMMIT: bizdate=%s offset_merchants=%ld offset_sum=%ld\n",
ST_RECVSWEEP_TAG, bizdate, rec.merchants, rec.offset_sum);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,43 @@
/*
* st_rerun_batch.pgc - 정산 재실행 배치 (DELETE + INSERT..SELECT + XA).
* 당일 MDR 정산 상세를 전량 삭제한 뒤 정산-매입 조인으로 매입 fee 기준의
* MDR 상세를 집합 재생성한다 (정산 재실행). 사용법: st_rerun_batch [YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "st_dbio.h"
#include "st_rerun_batch.h"
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
st_rerun_rec_t rec;
memset(&rec, 0, sizeof(rec));
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DELETE FROM st_settle_dtl WHERE biz_date = :h_bizdate AND fee_type = 'MDR';
if (sqlca.sqlcode < 0) { fprintf(stderr, "PURGE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
rec.purged = (long) sqlca.sqlerrd[2];
EXEC SQL INSERT INTO st_settle_dtl (dtl_id, settlement_id, purchase_id, merchant_id, fee_type, amount, biz_date)
SELECT nextval('st_dtl_seq'), s.settlement_id, s.purchase_id, s.merchant_id, 'MDR', p.fee, s.biz_date
FROM settlement s JOIN purchase p ON p.purchase_id = s.purchase_id
WHERE s.biz_date = :h_bizdate AND s.status <> 'CANCELLED';
if (sqlca.sqlcode < 0) { fprintf(stderr, "REBUILD FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
rec.rebuilt = (long) sqlca.sqlerrd[2];
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> %s COMMIT: bizdate=%s purged=%ld rebuilt=%ld\n", ST_RERUN_TAG, bizdate, rec.purged, rec.rebuilt);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,57 @@
/*
* st_retrorate_batch.pgc - 요율 소급적용 배치 (조인 커서 + 행별 조정 + XA).
* 당일 MDR 정산 상세를 매입 금액과 조인 순회하며 신규 bps 로 재계산, 기계상
* 수수료와의 차액을 건별 소급 조정으로 기록한다. 사용법: st_retrorate_batch [YYYY-MM-DD] [bps]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "st_dbio.h"
#include "st_retrorate_batch.h"
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
st_retrorate_rec_t rec;
memset(&rec, 0, sizeof(rec));
rec.new_bps = (argc > 2) ? atol(argv[2]) : ST_RETRORATE_DEF_BPS;
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16];
long h_sid, h_pid, h_old, h_amount;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE retro_bc CURSOR FOR
SELECT d.settlement_id, d.purchase_id, d.amount, p.amount
FROM st_settle_dtl d JOIN purchase p ON p.purchase_id = d.purchase_id
WHERE d.biz_date = :h_bizdate AND d.fee_type = 'MDR';
EXEC SQL OPEN retro_bc;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH retro_bc INTO :h_sid, :h_pid, :h_old, :h_amount;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE retro_bc; tpabort(0); return 1; }
{
long newfee = h_amount * rec.new_bps / 10000;
long diff = newfee - h_old;
if (diff == 0) continue;
if (stdb_insert_adjust(h_sid, h_pid, diff, "RETRO_RATE", bizdate) < 0) { EXEC SQL CLOSE retro_bc; tpabort(0); return 1; }
rec.adjusted++;
rec.delta_sum += diff;
}
}
EXEC SQL CLOSE retro_bc;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> %s COMMIT: bizdate=%s new_bps=%ld adjusted=%ld delta_sum=%ld\n",
ST_RETRORATE_TAG, bizdate, rec.new_bps, rec.adjusted, rec.delta_sum);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,52 @@
/*
* st_vatreport_batch.pgc - 부가세 신고자료 생성 배치 (월 집계 + 원장 마커 + XA).
* 대상 월의 정산 상세 VAT 합계와 세금계산서 공급가/세액/건수를 집계·대사해
* 신고 기초자료를 출력하고, VATRPT 원장 마커를 남긴다.
* 사용법: st_vatreport_batch [YYYY-MM-DD (해당 월 기준일)]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "st_dbio.h"
#include "st_vatreport_batch.h"
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
st_vatreport_rec_t rec;
memset(&rec, 0, sizeof(rec));
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16];
long h_dtlvat, h_cnt, h_supply, h_invvat;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_dtlvat FROM st_settle_dtl
WHERE fee_type = 'VAT'
AND to_char(biz_date,'YYYY-MM') = to_char(:h_bizdate::date,'YYYY-MM');
if (sqlca.sqlcode < 0) { fprintf(stderr, "DTLVAT FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
rec.dtl_vat = h_dtlvat;
EXEC SQL SELECT count(*), coalesce(sum(supply),0), coalesce(sum(vat),0)
INTO :h_cnt, :h_supply, :h_invvat FROM ac_tax_invoice
WHERE to_char(biz_date,'YYYY-MM') = to_char(:h_bizdate::date,'YYYY-MM');
if (sqlca.sqlcode < 0) { fprintf(stderr, "INV FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
rec.inv_cnt = h_cnt; rec.inv_supply = h_supply; rec.inv_vat = h_invvat;
EXEC SQL INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date)
VALUES (nextval('ledger_seq'), 0, 'VATRPT', :h_dtlvat, :h_bizdate);
if (sqlca.sqlcode < 0) { fprintf(stderr, "MARKER FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> %s COMMIT: base=%s dtl_vat=%ld inv_cnt=%ld supply=%ld inv_vat=%ld diff=%ld\n",
ST_VATREPORT_TAG, bizdate, rec.dtl_vat, rec.inv_cnt, rec.inv_supply, rec.inv_vat,
rec.inv_vat - rec.dtl_vat);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,42 @@
/*
* st_weekly_batch.pgc - 주간 정산주기 배치 (7일 윈도 집합 INSERT..SELECT + XA).
* 주 종료일 기준 직전 7일 정산 net 을 가맹점별 GROUP BY/HAVING 으로 합산해
* 주 1회 지급(SCHEDULED)을 일괄 생성한다. 사용법: st_weekly_batch [주종료일 YYYY-MM-DD]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "st_dbio.h"
#include "st_weekly_batch.h"
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
st_weekly_rec_t rec;
rec.merchants = 0;
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL INSERT INTO st_payment (pay_id, merchant_id, net_amount, pay_date, status, biz_date)
SELECT nextval('st_pay_seq'), merchant_id, sum(net), :h_bizdate::date + 1, 'SCHEDULED', :h_bizdate
FROM settlement
WHERE status <> 'CANCELLED'
AND biz_date BETWEEN :h_bizdate::date - 6 AND :h_bizdate::date
GROUP BY merchant_id
HAVING sum(net) > 0;
if (sqlca.sqlcode < 0) { fprintf(stderr, "%s FAIL [%d] %s\n", ST_WEEKLY_TAG, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
rec.merchants = (long) sqlca.sqlerrd[2];
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> %s COMMIT: week_end=%s weekly_payments=%ld\n", ST_WEEKLY_TAG, bizdate, rec.merchants);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,51 @@
/*
* st_whtreport_batch.pgc - 원천세 명세 배치 (가맹점별 커서 명세 출력 + XA).
* 대상 월의 정산 상세(WHT)를 가맹점별 GROUP BY 커서로 순회하며 원천세 명세
* 라인을 한 줄씩 출력하고 총액을 집계한다 (신고 명세서 생성, 조회 전용).
* 사용법: st_whtreport_batch [YYYY-MM-DD (해당 월 기준일)]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "st_dbio.h"
#include "st_whtreport_batch.h"
int main(int argc, char **argv)
{
const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19";
st_whtreport_rec_t rec;
memset(&rec, 0, sizeof(rec));
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16], h_merch[64];
long h_cnt, h_wht;
EXEC SQL END DECLARE SECTION;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE whtrpt_bc CURSOR FOR
SELECT merchant_id, count(*), coalesce(sum(amount),0) FROM st_settle_dtl
WHERE fee_type = 'WHT'
AND to_char(biz_date,'YYYY-MM') = to_char(:h_bizdate::date,'YYYY-MM')
GROUP BY merchant_id ORDER BY merchant_id;
EXEC SQL OPEN whtrpt_bc;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH whtrpt_bc INTO :h_merch, :h_cnt, :h_wht;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE whtrpt_bc; tpabort(0); return 1; }
printf(" WHT %-16s lines=%ld tax=%ld\n", h_merch, h_cnt, h_wht);
rec.lines++;
rec.wht_total += h_wht;
}
EXEC SQL CLOSE whtrpt_bc;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> %s COMMIT: base=%s merchants=%ld wht_total=%ld\n", ST_WHTREPORT_TAG, bizdate, rec.lines, rec.wht_total);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,6 @@
/* st_approve_batch.h - st 정산 승인 워크플로 배치 copybook. */
#ifndef ST_APPROVE_BATCH_H
#define ST_APPROVE_BATCH_H
#define ST_APPROVE_TAG "st_approve_batch"
typedef struct { long approved; long skipped; } st_approve_rec_t;
#endif

View file

@ -0,0 +1,7 @@
/* st_deposit_batch.h - st 보증금 차감 배치 copybook. */
#ifndef ST_DEPOSIT_BATCH_H
#define ST_DEPOSIT_BATCH_H
#define ST_DEPOSIT_TAG "st_deposit_batch"
#define ST_DEPOSIT_DEF_CUT 50000L
typedef struct { long cut_each; long cut_rows; } st_deposit_rec_t;
#endif

View file

@ -0,0 +1,7 @@
/* st_grproll_batch.h - st 가맹점그룹 통합정산 롤업 배치 copybook. */
#ifndef ST_GRPROLL_BATCH_H
#define ST_GRPROLL_BATCH_H
#define ST_GRPROLL_TAG "st_grproll_batch"
#define ST_GRPROLL_PFX_LEN 2
typedef struct { long groups; long rolled_net; } st_grproll_rec_t;
#endif

View file

@ -0,0 +1,6 @@
/* st_incent_batch.h - st 구간별 인센티브 배치 copybook. */
#ifndef ST_INCENT_BATCH_H
#define ST_INCENT_BATCH_H
#define ST_INCENT_TAG "st_incent_batch"
typedef struct { long tier1; long tier2; long tier3; long paid_sum; } st_incent_rec_t;
#endif

View file

@ -0,0 +1,6 @@
/* st_partial_batch.h - st 부분정산 잔여 스윕 배치 copybook. */
#ifndef ST_PARTIAL_BATCH_H
#define ST_PARTIAL_BATCH_H
#define ST_PARTIAL_TAG "st_partial_batch"
typedef struct { long swept; long swept_amt; } st_partial_rec_t;
#endif

View file

@ -0,0 +1,7 @@
/* st_promo_batch.h - st 프로모션/이벤트 정산 배치 copybook. */
#ifndef ST_PROMO_BATCH_H
#define ST_PROMO_BATCH_H
#define ST_PROMO_TAG "st_promo_batch"
#define ST_PROMO_DEF_THRESH 10000000L
typedef struct { long threshold; long granted; } st_promo_rec_t;
#endif

View file

@ -0,0 +1,6 @@
/* st_ratesim_batch.h - st 요율 시뮬레이션 배치 copybook. */
#ifndef ST_RATESIM_BATCH_H
#define ST_RATESIM_BATCH_H
#define ST_RATESIM_TAG "st_ratesim_batch"
typedef struct { long day_gross; long bands; long projected; } st_ratesim_rec_t;
#endif

View file

@ -0,0 +1,6 @@
/* st_recvsweep_batch.h - st 미수금 상계 스윕 배치 copybook. */
#ifndef ST_RECVSWEEP_BATCH_H
#define ST_RECVSWEEP_BATCH_H
#define ST_RECVSWEEP_TAG "st_recvsweep_batch"
typedef struct { long merchants; long offset_sum; } st_recvsweep_rec_t;
#endif

View file

@ -0,0 +1,6 @@
/* st_rerun_batch.h - st 정산 재실행 배치 copybook. */
#ifndef ST_RERUN_BATCH_H
#define ST_RERUN_BATCH_H
#define ST_RERUN_TAG "st_rerun_batch"
typedef struct { long purged; long rebuilt; } st_rerun_rec_t;
#endif

View file

@ -0,0 +1,7 @@
/* st_retrorate_batch.h - st 요율 소급적용 배치 copybook. */
#ifndef ST_RETRORATE_BATCH_H
#define ST_RETRORATE_BATCH_H
#define ST_RETRORATE_TAG "st_retrorate_batch"
#define ST_RETRORATE_DEF_BPS 22L
typedef struct { long new_bps; long adjusted; long delta_sum; } st_retrorate_rec_t;
#endif

View file

@ -0,0 +1,6 @@
/* st_vatreport_batch.h - st 부가세 신고자료 생성 배치 copybook. */
#ifndef ST_VATREPORT_BATCH_H
#define ST_VATREPORT_BATCH_H
#define ST_VATREPORT_TAG "st_vatreport_batch"
typedef struct { long dtl_vat; long inv_cnt; long inv_supply; long inv_vat; } st_vatreport_rec_t;
#endif

View file

@ -0,0 +1,6 @@
/* st_weekly_batch.h - st 주간 정산주기 배치 copybook. */
#ifndef ST_WEEKLY_BATCH_H
#define ST_WEEKLY_BATCH_H
#define ST_WEEKLY_TAG "st_weekly_batch"
typedef struct { long merchants; } st_weekly_rec_t;
#endif

View file

@ -0,0 +1,6 @@
/* st_whtreport_batch.h - st 원천세 명세 배치 copybook. */
#ifndef ST_WHTREPORT_BATCH_H
#define ST_WHTREPORT_BATCH_H
#define ST_WHTREPORT_TAG "st_whtreport_batch"
typedef struct { long lines; long wht_total; } st_whtreport_rec_t;
#endif

View file

@ -2,14 +2,14 @@
* st_svr.pgc - st (정산/수수료) MODULE SERVER (thin dispatcher).
*
* The legacy "one module server, many services" pattern: this binary owns NO
* business logic. Each of st's 30 online services now lives in its own file
* business logic. Each of st's 75 online services now lives in its own file
* (app/src/st/svc/<SVCNAME>.pgc) with its own copybook header
* (app/src/st/svc/<SVCNAME>.h); those objects are archived into libstsvc.a and
* linked into this server. Here we only include every service copybook, tpopen()
* the ECPG XA RM, tpadvertise() all services from a {name, fn} table, and
* tpclose() at shutdown.
*
* st_svr: ONE binary that tpadvertise()s ALL 30 online st(정산/수수료) services.
* st_svr: ONE binary that tpadvertise()s ALL 75 online st(정산/수수료) services.
* SETTLE keeps the proven behavior: its own XA branch writes the settlement +
* settle ledger rows. Terminal service of the 매입 chain.
*/
@ -48,6 +48,51 @@
#include "ST_FXCONV.h"
#include "ST_CONFIRM.h"
#include "ST_STATUS.h"
#include "ST_RATESIM.h"
#include "ST_PROMOCALC.h"
#include "ST_PARTIAL.h"
#include "ST_RERUN.h"
#include "ST_CXLRECALC.h"
#include "ST_RETRORATE.h"
#include "ST_TIERINCENT.h"
#include "ST_CYCLED1.h"
#include "ST_CYCLED2.h"
#include "ST_CYCLEWEEK.h"
#include "ST_CYCLEMON.h"
#include "ST_DEPOSITCUT.h"
#include "ST_RECVOFFSET.h"
#include "ST_DEBTCOLLECT.h"
#include "ST_VATREPORT.h"
#include "ST_WHTREPORT.h"
#include "ST_APPROVE.h"
#include "ST_GRPSETTLE.h"
#include "ST_DISPUTE.h"
#include "ST_STATDETAIL.h"
#include "ST_FEEPREVIEW.h"
#include "ST_HOLDAMT.h"
#include "ST_PAYSTATUS.h"
#include "ST_ADJHIST.h"
#include "ST_DTLSUM.h"
#include "ST_MERCHRATE.h"
#include "ST_CAPCHECK.h"
#include "ST_ROUNDING.h"
#include "ST_MINFEE.h"
#include "ST_DAILYCLOSE.h"
#include "ST_REOPEN.h"
#include "ST_CARRYOVER.h"
#include "ST_NEGSWEEP.h"
#include "ST_TOPMERCH.h"
#include "ST_FEERATIO.h"
#include "ST_SPLITEVEN.h"
#include "ST_CHNLSTAT.h"
#include "ST_ISSUERSTAT.h"
#include "ST_TAXINVCHK.h"
#include "ST_FXSETTLE.h"
#include "ST_EDIRECON.h"
#include "ST_UNPAIDSUM.h"
#include "ST_PAYRETRY.h"
#include "ST_LIMITCHK.h"
#include "ST_LEDGERSNAP.h"
/* ----- advertise table: {service name, function} ---------------------- */
static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = {
@ -66,6 +111,29 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = {
{"ST_LEDGERLINK", ST_LEDGERLINK}, {"ST_BANDRATE", ST_BANDRATE},
{"ST_CAP", ST_CAP}, {"ST_FXCONV", ST_FXCONV},
{"ST_CONFIRM", ST_CONFIRM}, {"ST_STATUS", ST_STATUS},
{"ST_RATESIM", ST_RATESIM}, {"ST_PROMOCALC", ST_PROMOCALC},
{"ST_PARTIAL", ST_PARTIAL}, {"ST_RERUN", ST_RERUN},
{"ST_CXLRECALC", ST_CXLRECALC}, {"ST_RETRORATE", ST_RETRORATE},
{"ST_TIERINCENT", ST_TIERINCENT}, {"ST_CYCLED1", ST_CYCLED1},
{"ST_CYCLED2", ST_CYCLED2}, {"ST_CYCLEWEEK", ST_CYCLEWEEK},
{"ST_CYCLEMON", ST_CYCLEMON}, {"ST_DEPOSITCUT", ST_DEPOSITCUT},
{"ST_RECVOFFSET", ST_RECVOFFSET}, {"ST_DEBTCOLLECT", ST_DEBTCOLLECT},
{"ST_VATREPORT", ST_VATREPORT}, {"ST_WHTREPORT", ST_WHTREPORT},
{"ST_APPROVE", ST_APPROVE}, {"ST_GRPSETTLE", ST_GRPSETTLE},
{"ST_DISPUTE", ST_DISPUTE}, {"ST_STATDETAIL", ST_STATDETAIL},
{"ST_FEEPREVIEW", ST_FEEPREVIEW}, {"ST_HOLDAMT", ST_HOLDAMT},
{"ST_PAYSTATUS", ST_PAYSTATUS}, {"ST_ADJHIST", ST_ADJHIST},
{"ST_DTLSUM", ST_DTLSUM}, {"ST_MERCHRATE", ST_MERCHRATE},
{"ST_CAPCHECK", ST_CAPCHECK}, {"ST_ROUNDING", ST_ROUNDING},
{"ST_MINFEE", ST_MINFEE}, {"ST_DAILYCLOSE", ST_DAILYCLOSE},
{"ST_REOPEN", ST_REOPEN}, {"ST_CARRYOVER", ST_CARRYOVER},
{"ST_NEGSWEEP", ST_NEGSWEEP}, {"ST_TOPMERCH", ST_TOPMERCH},
{"ST_FEERATIO", ST_FEERATIO}, {"ST_SPLITEVEN", ST_SPLITEVEN},
{"ST_CHNLSTAT", ST_CHNLSTAT}, {"ST_ISSUERSTAT", ST_ISSUERSTAT},
{"ST_TAXINVCHK", ST_TAXINVCHK}, {"ST_FXSETTLE", ST_FXSETTLE},
{"ST_EDIRECON", ST_EDIRECON}, {"ST_UNPAIDSUM", ST_UNPAIDSUM},
{"ST_PAYRETRY", ST_PAYRETRY}, {"ST_LIMITCHK", ST_LIMITCHK},
{"ST_LEDGERSNAP", ST_LEDGERSNAP},
{NULL, NULL}
};

View file

@ -0,0 +1,19 @@
/*
* ST_ADJHIST.h - st ST_ADJHIST (copybook / record header).
* : / / .
*/
#ifndef ST_ADJHIST_H
#define ST_ADJHIST_H
#include "st_svc.h"
typedef struct {
long purchase_id;
long adj_count;
long delta_sum;
char last_reason[128];
} st_st_adjhist_rec_t;
void ST_ADJHIST(TPSVCINFO *p);
#endif /* ST_ADJHIST_H */

View file

@ -0,0 +1,39 @@
/*
* ST_ADJHIST.pgc - st 모듈 서비스 ST_ADJHIST (one service = one file).
* 조정 이력 조회: 매입에 걸린 정산 조정을 최신순 커서로 순회해 건수·증감
* 누계와 가장 최근 조정 사유를 회신한다. 이력 없으면 RC=1. 조회 전용.
*/
#include "ST_ADJHIST.h"
/* 54. ST_ADJHIST - 매입별 조정 이력 요약. */
void ST_ADJHIST(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char last[128] = "";
long cnt = 0, sum = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_delta;
char h_reason[128];
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL DECLARE adjhist_c CURSOR FOR
SELECT delta, reason FROM st_adjust WHERE purchase_id = :h_pid ORDER BY adj_id DESC;
EXEC SQL OPEN adjhist_c;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH adjhist_c INTO :h_delta, :h_reason;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE adjhist_c; FAIL(b); }
if (cnt == 0) { strncpy(last, h_reason, sizeof(last)-1); last[sizeof(last)-1] = 0; }
cnt++;
sum += h_delta;
}
EXEC SQL CLOSE adjhist_c;
if (cnt == 0) { setl(b, T_RC, 1); OK(b); }
setl(b, T_COUNT, cnt);
setl(b, T_DELTA, sum);
Bchg(b, T_REASON, 0, last, 0L);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,18 @@
/*
* ST_APPROVE.h - st ST_APPROVE (copybook / record header).
* / : CONFIRMED -> APPROVED + .
*/
#ifndef ST_APPROVE_H
#define ST_APPROVE_H
#include "st_svc.h"
typedef struct {
long purchase_id;
char approver[32];
char cur_status[16];
} st_st_approve_rec_t;
void ST_APPROVE(TPSVCINFO *p);
#endif /* ST_APPROVE_H */

View file

@ -0,0 +1,37 @@
/*
* ST_APPROVE.pgc - st 모듈 서비스 ST_APPROVE (one service = one file).
* 정산 승인 워크플로: 최신 정산 상태를 검증해 CONFIRMED 인 건만 APPROVED 로
* 승격하고 승인 원장(STAPPROVE)을 남긴다. 상태 위반 시 RC=2 로 현재 상태 회신.
*/
#include "ST_APPROVE.h"
/* 47. ST_APPROVE - 정산 승인 (CONFIRMED -> APPROVED). */
void ST_APPROVE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid;
char h_status[16], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL SELECT status, to_char(biz_date,'YYYY-MM-DD') INTO :h_status, :h_bizdate
FROM settlement WHERE purchase_id = :h_pid ORDER BY settlement_id DESC LIMIT 1;
if (sqlca.sqlcode < 0) FAIL(b);
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); Bchg(b, T_STATUS, 0, "NONE", 0L); OK(b); }
if (strcmp(h_status, "CONFIRMED") != 0) {
setl(b, T_RC, 2); /* 워크플로 순서 위반: 확정 전 승인 불가 */
Bchg(b, T_STATUS, 0, h_status, 0L);
OK(b);
}
EXEC SQL UPDATE settlement SET status = 'APPROVED'
WHERE purchase_id = :h_pid AND status = 'CONFIRMED';
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date)
VALUES (nextval('ledger_seq'), :h_pid, 'STAPPROVE', 0, :h_bizdate);
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_RC, 0);
Bchg(b, T_STATUS, 0, "A", 0L);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,18 @@
/*
* ST_CAPCHECK.h - st ST_CAPCHECK (copybook / record header).
* : / ( ).
*/
#ifndef ST_CAPCHECK_H
#define ST_CAPCHECK_H
#include "st_svc.h"
typedef struct {
long fee;
long cap;
long over;
} st_st_capcheck_rec_t;
void ST_CAPCHECK(TPSVCINFO *p);
#endif /* ST_CAPCHECK_H */

View file

@ -0,0 +1,26 @@
/*
* ST_CAPCHECK.pgc - st 모듈 서비스 ST_CAPCHECK (one service = one file).
* 수수료 상한 도달 점검: 수수료가 상한을 초과하는지 판정만 하고 초과분을
* 회신한다. ST_CAP 과 달리 조정을 기록하지 않는 순수 판정 서비스 (SQL 없음).
*/
#include "ST_CAPCHECK.h"
/* 57. ST_CAPCHECK - 상한 초과 여부 판정 (조정 없음). */
void ST_CAPCHECK(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long fee = getl(b, T_FEE), cap = getl(b, T_ARG1), over;
if (cap <= 0) cap = 100000;
over = fee - cap;
if (over > 0) {
setl(b, T_RC, 1); /* 상한 초과: 감액 대상 */
setl(b, T_DELTA, over);
} else {
setl(b, T_RC, 0);
setl(b, T_DELTA, 0);
}
setl(b, T_AMT1, cap);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,20 @@
/*
* ST_CARRYOVER.h - st ST_CARRYOVER (copybook / record header).
* : .
*/
#ifndef ST_CARRYOVER_H
#define ST_CARRYOVER_H
#include "st_svc.h"
typedef struct {
char merchant_id[64];
char biz_date[16];
long unsettled_cnt;
long unsettled_sum;
char carry_date[16];
} st_st_carryover_rec_t;
void ST_CARRYOVER(TPSVCINFO *p);
#endif /* ST_CARRYOVER_H */

View file

@ -0,0 +1,37 @@
/*
* ST_CARRYOVER.pgc - st 모듈 서비스 ST_CARRYOVER (one service = one file).
* 이월 정산: 대사완료(M) 상태이지만 정산 레코드가 없는 매입(NOT EXISTS)을
* 집계해 익영업일 지급으로 이월 예약한다. 대상 없으면 RC=1 (dbio).
*/
#include "ST_CARRYOVER.h"
/* 62. ST_CARRYOVER - 미정산분 익영업일 이월 예약. */
void ST_CARRYOVER(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16], carry[16];
long pay;
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, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_cnt, :h_sum FROM purchase p
WHERE p.merchant_id = :h_merch AND p.biz_date = :h_bizdate AND p.status = 'M'
AND NOT EXISTS (SELECT 1 FROM settlement s WHERE s.purchase_id = p.purchase_id);
if (sqlca.sqlcode < 0) FAIL(b);
if (h_cnt == 0) { setl(b, T_RC, 1); setl(b, T_COUNT, 0); OK(b); }
if (acq_next_bizday(bizdate, carry) < 0) FAIL(b);
pay = stdb_insert_payment(merch, h_sum, carry, "SCHEDULED", bizdate);
if (pay < 0) FAIL(b);
setl(b, T_ID1, pay);
setl(b, T_COUNT, h_cnt);
setl(b, T_NET, h_sum);
Bchg(b, T_KEY1, 0, carry, 0L);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,20 @@
/*
* ST_CHNLSTAT.h - st ST_CHNLSTAT (copybook / record header).
* : + .
*/
#ifndef ST_CHNLSTAT_H
#define ST_CHNLSTAT_H
#include "st_svc.h"
typedef struct {
char biz_date[16];
long channel_cnt;
long grand_net;
char top_channel[16];
long top_net;
} st_st_chnlstat_rec_t;
void ST_CHNLSTAT(TPSVCINFO *p);
#endif /* ST_CHNLSTAT_H */

View file

@ -0,0 +1,46 @@
/*
* ST_CHNLSTAT.pgc - st 모듈 서비스 ST_CHNLSTAT (one service = one file).
* 채널별 정산 통계 세분: 정산-매입 조인 커서로 채널(POS/EDC/EDI/FOREIGN)별
* 건수·net 을 분해 집계하고 최대 채널을 회신한다. 조회 전용.
*/
#include "ST_CHNLSTAT.h"
/* 67. ST_CHNLSTAT - 채널별 정산 net 분해 통계. */
void ST_CHNLSTAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16], topch[16] = "";
long channels = 0, grand = 0, topsum = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt, h_net;
char h_ch[16], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL DECLARE chnlstat_c CURSOR FOR
SELECT p.channel, count(*), coalesce(sum(s.net),0)
FROM settlement s JOIN purchase p ON p.purchase_id = s.purchase_id
WHERE s.biz_date = :h_bizdate GROUP BY p.channel ORDER BY 3 DESC;
EXEC SQL OPEN chnlstat_c;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH chnlstat_c INTO :h_ch, :h_cnt, :h_net;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE chnlstat_c; FAIL(b); }
if (channels == 0) {
strncpy(topch, h_ch, sizeof(topch)-1); topch[sizeof(topch)-1] = 0;
topsum = h_net;
}
channels++;
grand += h_net;
}
EXEC SQL CLOSE chnlstat_c;
if (channels == 0) { setl(b, T_RC, 1); OK(b); }
setl(b, T_COUNT, channels);
setl(b, T_GROSS, grand);
setl(b, T_AMT1, topsum);
Bchg(b, T_CHANNEL, 0, topch, 0L);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,18 @@
/*
* ST_CXLRECALC.h - st ST_CXLRECALC (copybook / record header).
* : net .
*/
#ifndef ST_CXLRECALC_H
#define ST_CXLRECALC_H
#include "st_svc.h"
typedef struct {
long purchase_id;
long cancel_sum;
long new_net;
} st_st_cxlrecalc_rec_t;
void ST_CXLRECALC(TPSVCINFO *p);
#endif /* ST_CXLRECALC_H */

View file

@ -0,0 +1,29 @@
/*
* ST_CXLRECALC.pgc - st 모듈 서비스 ST_CXLRECALC (one service = one file).
* 정산 취소분 재계산: ac_cancel 누계를 매입 net 에서 차감해 정산 net 을
* 재산정하고 (음수 방지 클램프) SETTLED 정산 레코드에 반영한다.
*/
#include "ST_CXLRECALC.h"
/* 35. ST_CXLRECALC - 취소 누계 반영 정산 net 재계산. */
void ST_CXLRECALC(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_cxl, h_pnet, h_new;
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_cxl FROM ac_cancel WHERE purchase_id = :h_pid;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT net INTO :h_pnet FROM purchase WHERE purchase_id = :h_pid;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
h_new = h_pnet - h_cxl;
if (h_new < 0) h_new = 0; /* 과취소 방지 클램프 */
EXEC SQL UPDATE settlement SET net = :h_new WHERE purchase_id = :h_pid AND status = 'SETTLED';
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_NET, h_new);
setl(b, T_DELTA, -h_cxl);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,19 @@
/*
* ST_CYCLED1.h - st ST_CYCLED1 (copybook / record header).
* D+1 : SETTLED .
*/
#ifndef ST_CYCLED1_H
#define ST_CYCLED1_H
#include "st_svc.h"
typedef struct {
char merchant_id[64];
char biz_date[16];
char pay_date[16];
long net_sum;
} st_st_cycled1_rec_t;
void ST_CYCLED1(TPSVCINFO *p);
#endif /* ST_CYCLED1_H */

View file

@ -0,0 +1,35 @@
/*
* ST_CYCLED1.pgc - st 모듈 서비스 ST_CYCLED1 (one service = one file).
* D+1 정산주기: 당일 SETTLED 정산 net 합계를 산출해 익영업일(pay_date=D+1)
* 지급(SCHEDULED)으로 예약한다 (dbio).
*/
#include "ST_CYCLED1.h"
/* 38. ST_CYCLED1 - D+1 주기 지급 예약. */
void ST_CYCLED1(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16], payd[16];
long pay;
EXEC SQL BEGIN DECLARE SECTION;
long h_sum;
char h_merch[64], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT coalesce(sum(net),0) INTO :h_sum FROM settlement
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status = 'SETTLED';
if (sqlca.sqlcode < 0) FAIL(b);
if (h_sum <= 0) { setl(b, T_RC, 1); OK(b); }
if (acq_next_bizday(bizdate, payd) < 0) FAIL(b);
pay = stdb_insert_payment(merch, h_sum, payd, "SCHEDULED", bizdate);
if (pay < 0) FAIL(b);
setl(b, T_ID1, pay);
setl(b, T_NET, h_sum);
Bchg(b, T_KEY1, 0, payd, 0L);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,20 @@
/*
* ST_CYCLED2.h - st ST_CYCLED2 (copybook / record header).
* D+2 : (CONFIRMED) 2 .
*/
#ifndef ST_CYCLED2_H
#define ST_CYCLED2_H
#include "st_svc.h"
typedef struct {
char merchant_id[64];
char biz_date[16];
char pay_date[16];
long confirmed_cnt;
long net_sum;
} st_st_cycled2_rec_t;
void ST_CYCLED2(TPSVCINFO *p);
#endif /* ST_CYCLED2_H */

View file

@ -0,0 +1,37 @@
/*
* ST_CYCLED2.pgc - st 모듈 서비스 ST_CYCLED2 (one service = one file).
* D+2 정산주기: 당일 CONFIRMED 확정 정산만 집계해 2영업일 뒤(D+2, 주말 스킵)
* 지급으로 예약한다. 확정 건이 없으면 RC=1 (dbio).
*/
#include "ST_CYCLED2.h"
/* 39. ST_CYCLED2 - D+2 주기 지급 예약 (확정분만). */
void ST_CYCLED2(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16], d1[16], d2[16];
long pay;
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, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_cnt, :h_sum FROM settlement
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status = 'CONFIRMED';
if (sqlca.sqlcode < 0) FAIL(b);
if (h_cnt == 0) { setl(b, T_RC, 1); setl(b, T_COUNT, 0); OK(b); }
if (acq_next_bizday(bizdate, d1) < 0) FAIL(b);
if (acq_next_bizday(d1, d2) < 0) FAIL(b); /* D+1 -> D+2, 주말 스킵 2회 */
pay = stdb_insert_payment(merch, h_sum, d2, "SCHEDULED", bizdate);
if (pay < 0) FAIL(b);
setl(b, T_ID1, pay);
setl(b, T_COUNT, h_cnt);
setl(b, T_NET, h_sum);
Bchg(b, T_KEY1, 0, d2, 0L);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,20 @@
/*
* ST_CYCLEMON.h - st ST_CYCLEMON (copybook / record header).
* : 1 .
*/
#ifndef ST_CYCLEMON_H
#define ST_CYCLEMON_H
#include "st_svc.h"
typedef struct {
char merchant_id[64];
char yyyymm[16];
long month_cnt;
long month_sum;
long pay_id;
} st_st_cyclemon_rec_t;
void ST_CYCLEMON(TPSVCINFO *p);
#endif /* ST_CYCLEMON_H */

View file

@ -0,0 +1,39 @@
/*
* ST_CYCLEMON.pgc - st 모듈 서비스 ST_CYCLEMON (one service = one file).
* 월간 정산주기: 대상 월(YYYY-MM)의 가맹점 정산 net 을 합산해 지급일 기준으로
* 월 1회 지급 레코드를 직접 INSERT 한다. 합계 0 이하이면 RC=1.
*/
#include "ST_CYCLEMON.h"
/* 41. ST_CYCLEMON - 월간 통합 지급 예약 (직접 INSERT). */
void ST_CYCLEMON(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16], ym[16], like[16];
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt, h_sum, h_pay;
char h_merch[64], h_bizdate[16], h_like[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
gets_(b, T_KEY1, ym, sizeof(ym)); if (ym[0] == 0) strcpy(ym, "2026-07");
snprintf(like, sizeof(like), "%s%%", ym);
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
strncpy(h_like, like, sizeof(h_like)-1); h_like[sizeof(h_like)-1] = 0;
EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_cnt, :h_sum FROM settlement
WHERE merchant_id = :h_merch AND to_char(biz_date,'YYYY-MM-DD') LIKE :h_like;
if (sqlca.sqlcode < 0) FAIL(b);
if (h_sum <= 0) { setl(b, T_RC, 1); setl(b, T_COUNT, h_cnt); OK(b); }
EXEC SQL SELECT nextval('st_pay_seq') INTO :h_pay;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO st_payment (pay_id, merchant_id, net_amount, pay_date, status, biz_date)
VALUES (:h_pay, :h_merch, :h_sum, :h_bizdate, 'SCHEDULED', :h_bizdate);
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_ID1, h_pay);
setl(b, T_COUNT, h_cnt);
setl(b, T_GROSS, h_sum);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,19 @@
/*
* ST_CYCLEWEEK.h - st ST_CYCLEWEEK (copybook / record header).
* : 7 1 .
*/
#ifndef ST_CYCLEWEEK_H
#define ST_CYCLEWEEK_H
#include "st_svc.h"
typedef struct {
char merchant_id[64];
char week_end[16];
long week_cnt;
long week_sum;
} st_st_cycleweek_rec_t;
void ST_CYCLEWEEK(TPSVCINFO *p);
#endif /* ST_CYCLEWEEK_H */

View file

@ -0,0 +1,36 @@
/*
* ST_CYCLEWEEK.pgc - st 모듈 서비스 ST_CYCLEWEEK (one service = one file).
* 주간 정산주기: 주 종료일 기준 직전 7일(취소 제외) 정산 net 을 합산해
* 주 1회 지급으로 예약한다. 대상 없으면 RC=1 (dbio).
*/
#include "ST_CYCLEWEEK.h"
/* 40. ST_CYCLEWEEK - 주간(7일 윈도) 통합 지급 예약. */
void ST_CYCLEWEEK(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16], payd[16];
long pay;
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, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_cnt, :h_sum FROM settlement
WHERE merchant_id = :h_merch AND status <> 'CANCELLED'
AND biz_date BETWEEN :h_bizdate::date - 6 AND :h_bizdate::date;
if (sqlca.sqlcode < 0) FAIL(b);
if (h_cnt == 0 || h_sum <= 0) { setl(b, T_RC, 1); OK(b); }
if (acq_next_bizday(bizdate, payd) < 0) FAIL(b);
pay = stdb_insert_payment(merch, h_sum, payd, "SCHEDULED", bizdate);
if (pay < 0) FAIL(b);
setl(b, T_ID1, pay);
setl(b, T_COUNT, h_cnt);
setl(b, T_GROSS, h_sum);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,20 @@
/*
* ST_DAILYCLOSE.h - st ST_DAILYCLOSE (copybook / record header).
* : // + (STCLOSE) .
*/
#ifndef ST_DAILYCLOSE_H
#define ST_DAILYCLOSE_H
#include "st_svc.h"
typedef struct {
char biz_date[16];
long settle_cnt;
long pay_cnt;
long adj_cnt;
long net_sum;
} st_st_dailyclose_rec_t;
void ST_DAILYCLOSE(TPSVCINFO *p);
#endif /* ST_DAILYCLOSE_H */

View file

@ -0,0 +1,36 @@
/*
* ST_DAILYCLOSE.pgc - st 모듈 서비스 ST_DAILYCLOSE (one service = one file).
* 정산 일마감: 당일 정산·지급·조정 건수를 각각 검증 집계하고 정산 net 총액으로
* 마감 원장(STCLOSE)을 기록해 마감을 선언한다.
*/
#include "ST_DAILYCLOSE.h"
/* 60. ST_DAILYCLOSE - 정산 일마감 선언 (3원 집계 + 마감 원장). */
void ST_DAILYCLOSE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16];
EXEC SQL BEGIN DECLARE SECTION;
long h_scnt, h_sum, h_pcnt, h_acnt;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_scnt, :h_sum
FROM settlement WHERE biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT count(*) INTO :h_pcnt FROM st_payment WHERE biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT count(*) INTO :h_acnt FROM st_adjust WHERE biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date)
VALUES (nextval('ledger_seq'), 0, 'STCLOSE', :h_sum, :h_bizdate);
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_scnt);
setl(b, T_ARG1, h_pcnt);
setl(b, T_ARG2, h_acnt);
setl(b, T_GROSS, h_sum);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,19 @@
/*
* ST_DEBTCOLLECT.h - st ST_DEBTCOLLECT (copybook / record header).
* : .
*/
#ifndef ST_DEBTCOLLECT_H
#define ST_DEBTCOLLECT_H
#include "st_svc.h"
typedef struct {
char merchant_id[64];
char biz_date[16];
long collected;
long outstanding;
} st_st_debtcollect_rec_t;
void ST_DEBTCOLLECT(TPSVCINFO *p);
#endif /* ST_DEBTCOLLECT_H */

View file

@ -0,0 +1,40 @@
/*
* ST_DEBTCOLLECT.pgc - st 모듈 서비스 ST_DEBTCOLLECT (one service = one file).
* 채권 회수 정산: 회수 입금액을 양수 조정(COLLECT)으로 기록하고 원장에 COLLECT
* 엔트리를 남긴 뒤, 회수 후 잔여 채권액을 회신한다.
*/
#include "ST_DEBTCOLLECT.h"
/* 44. ST_DEBTCOLLECT - 채권 회수 입금 정산. */
void ST_DEBTCOLLECT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16];
long adj, remain;
EXEC SQL BEGIN DECLARE SECTION;
long h_col, h_out;
char h_merch[64], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
h_col = getl(b, T_AMOUNT);
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (h_col <= 0) FAIL(b);
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT coalesce(sum(a.delta) FILTER (WHERE a.delta < 0),0) INTO :h_out
FROM st_adjust a JOIN purchase p ON p.purchase_id = a.purchase_id
WHERE p.merchant_id = :h_merch;
if (sqlca.sqlcode < 0) FAIL(b);
adj = stdb_insert_adjust(0, 0, h_col, "COLLECT", bizdate);
if (adj < 0) FAIL(b);
EXEC SQL INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date)
VALUES (nextval('ledger_seq'), 0, 'COLLECT', :h_col, :h_bizdate);
if (sqlca.sqlcode < 0) FAIL(b);
remain = (-h_out) - h_col;
if (remain < 0) remain = 0;
setl(b, T_ADJ, adj);
setl(b, T_DELTA, remain);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,19 @@
/*
* ST_DEPOSITCUT.h - st ST_DEPOSITCUT (copybook / record header).
* : .
*/
#ifndef ST_DEPOSITCUT_H
#define ST_DEPOSITCUT_H
#include "st_svc.h"
typedef struct {
char merchant_id[64];
char biz_date[16];
long deposit_due;
long cut;
} st_st_depositcut_rec_t;
void ST_DEPOSITCUT(TPSVCINFO *p);
#endif /* ST_DEPOSITCUT_H */

View file

@ -0,0 +1,39 @@
/*
* ST_DEPOSITCUT.pgc - st 모듈 서비스 ST_DEPOSITCUT (one service = one file).
* 보증금 차감: 보증금 납부 예정액과 SCHEDULED 지급 가용액 중 작은 금액을
* 최초 지급 레코드에서 차감하고 음수 조정(DEPOSIT)으로 기록한다.
*/
#include "ST_DEPOSITCUT.h"
/* 42. ST_DEPOSITCUT - 지급액 보증금 원천 차감. */
void ST_DEPOSITCUT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16];
long due = getl(b, T_AMT1), adj;
EXEC SQL BEGIN DECLARE SECTION;
long h_avail, h_cut;
char h_merch[64], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (due <= 0) due = 50000; /* 기본 보증금 납부 단위 */
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT coalesce(sum(net_amount),0) INTO :h_avail FROM st_payment
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status = 'SCHEDULED';
if (sqlca.sqlcode < 0) FAIL(b);
h_cut = (due < h_avail) ? due : h_avail;
if (h_cut <= 0) { setl(b, T_RC, 1); setl(b, T_DELTA, 0); OK(b); }
EXEC SQL UPDATE st_payment SET net_amount = net_amount - :h_cut
WHERE pay_id = (SELECT min(pay_id) FROM st_payment
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status = 'SCHEDULED');
if (sqlca.sqlcode < 0) FAIL(b);
adj = stdb_insert_adjust(0, 0, -h_cut, "DEPOSIT", bizdate);
if (adj < 0) FAIL(b);
setl(b, T_ADJ, adj);
setl(b, T_DELTA, -h_cut);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,19 @@
/*
* ST_DISPUTE.h - st ST_DISPUTE (copybook / record header).
* : + .
*/
#ifndef ST_DISPUTE_H
#define ST_DISPUTE_H
#include "st_svc.h"
typedef struct {
long purchase_id;
long claimed;
char reason[64];
char merchant_id[64];
} st_st_dispute_rec_t;
void ST_DISPUTE(TPSVCINFO *p);
#endif /* ST_DISPUTE_H */

View file

@ -0,0 +1,35 @@
/*
* ST_DISPUTE.pgc - st 모듈 서비스 ST_DISPUTE (one service = one file).
* 정산 이의신청 처리: 대상 매입의 가맹점을 역추적해 예정 지급을 보류(HELD)로
* 전환하고, 이의 금액을 DISPUTE 사유 조정으로 기록한다 (dbio).
*/
#include "ST_DISPUTE.h"
/* 49. ST_DISPUTE - 정산 이의신청 접수 (지급 보류 + 조정). */
void ST_DISPUTE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16], reason[64], rbuf[96], merch[64];
long claimed = getl(b, T_DELTA), adj;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid;
char h_merch[64];
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
gets_(b, T_REASON, reason, sizeof(reason)); if (reason[0] == 0) strcpy(reason, "UNSPEC");
EXEC SQL SELECT merchant_id INTO :h_merch FROM settlement
WHERE purchase_id = :h_pid ORDER BY settlement_id LIMIT 1;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
strncpy(merch, h_merch, sizeof(merch)-1); merch[sizeof(merch)-1] = 0;
if (stdb_update_payment_status(merch, "SCHEDULED", "HELD", bizdate) < 0) FAIL(b);
snprintf(rbuf, sizeof(rbuf), "DISPUTE:%s", reason);
adj = stdb_insert_adjust(0, h_pid, claimed, rbuf, bizdate);
if (adj < 0) FAIL(b);
setl(b, T_ADJ, adj);
Bchg(b, T_MERCHANT, 0, merch, 0L);
Bchg(b, T_STATUS, 0, "D", 0L);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,20 @@
/*
* ST_DTLSUM.h - st ST_DTLSUM (copybook / record header).
* : ////.
*/
#ifndef ST_DTLSUM_H
#define ST_DTLSUM_H
#include "st_svc.h"
typedef struct {
char merchant_id[64];
char fee_type[16];
char biz_date[16];
long cnt;
long total;
} st_st_dtlsum_rec_t;
void ST_DTLSUM(TPSVCINFO *p);
#endif /* ST_DTLSUM_H */

View file

@ -0,0 +1,37 @@
/*
* ST_DTLSUM.pgc - st 모듈 서비스 ST_DTLSUM (one service = one file).
* 정산 상세 합계: 가맹점+수수료종류+영업일 단위로 상세 라인의 건수·합계·
* 최소·최대를 집계하고 평균을 계산해 회신한다. 조회 전용.
*/
#include "ST_DTLSUM.h"
/* 55. ST_DTLSUM - 정산 상세 다중 집계 (건수/합/min/max/평균). */
void ST_DTLSUM(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], ftype[16], bizdate[16];
long avg;
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt, h_sum, h_min, h_max;
char h_merch[64], h_ftype[16], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_STR1, ftype, sizeof(ftype)); if (ftype[0] == 0) strcpy(ftype, "MDR");
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_ftype, ftype, sizeof(h_ftype)-1); h_ftype[sizeof(h_ftype)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT count(*), coalesce(sum(amount),0), coalesce(min(amount),0), coalesce(max(amount),0)
INTO :h_cnt, :h_sum, :h_min, :h_max FROM st_settle_dtl
WHERE merchant_id = :h_merch AND fee_type = :h_ftype AND biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
avg = (h_cnt > 0) ? h_sum / h_cnt : 0;
setl(b, T_COUNT, h_cnt);
setl(b, T_GROSS, h_sum);
setl(b, T_AMT1, h_min);
setl(b, T_AMT2, h_max);
setl(b, T_AMT3, avg);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,20 @@
/*
* ST_EDIRECON.h - st ST_EDIRECON (copybook / record header).
* EDI : EDI vs net .
*/
#ifndef ST_EDIRECON_H
#define ST_EDIRECON_H
#include "st_svc.h"
typedef struct {
char merchant_id[64];
char biz_date[16];
long edi_cnt;
long edi_sum;
long settle_net;
} st_st_edirecon_rec_t;
void ST_EDIRECON(TPSVCINFO *p);
#endif /* ST_EDIRECON_H */

View file

@ -0,0 +1,36 @@
/*
* ST_EDIRECON.pgc - st 모듈 서비스 ST_EDIRECON (one service = one file).
* EDI 정산 대사: 가맹점 당일 EDI 문서(ac_edi) 청구 합계와 정산 집계
* (st_merch_settle) net 을 대사한다. 집계 미존재 시 0 으로 간주. 조회 전용.
*/
#include "ST_EDIRECON.h"
/* 71. ST_EDIRECON - EDI 청구 vs 정산 집계 대사. */
void ST_EDIRECON(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16];
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt, h_edi, h_net;
char h_merch[64], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_cnt, :h_edi
FROM ac_edi WHERE merchant_id = :h_merch AND biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT net_amount INTO :h_net FROM st_merch_settle
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
if (sqlca.sqlcode == 100) h_net = 0; /* 집계 미존재: 0 대사 */
setl(b, T_COUNT, h_cnt);
setl(b, T_GROSS, h_edi);
setl(b, T_NET, h_net);
setl(b, T_DELTA, h_edi - h_net);
setl(b, T_RC, (h_edi == h_net) ? 0 : 1);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,22 @@
/*
* ST_FEEPREVIEW.h - st ST_FEEPREVIEW (copybook / record header).
* : /// .
*/
#ifndef ST_FEEPREVIEW_H
#define ST_FEEPREVIEW_H
#include "st_svc.h"
typedef struct {
char merchant_id[64];
long amount;
long mdr_bps;
long fee;
long vat;
long wht;
long take_home;
} st_st_feepreview_rec_t;
void ST_FEEPREVIEW(TPSVCINFO *p);
#endif /* ST_FEEPREVIEW_H */

View file

@ -0,0 +1,34 @@
/*
* ST_FEEPREVIEW.pgc - st 모듈 서비스 ST_FEEPREVIEW (one service = one file).
* 수수료 사전 미리보기: 가맹점 등록 요율(mdr_bps)로 수수료·부가세·원천세와
* 실수령 예상액을 계산해 회신한다. DB 쓰기 없는 견적 서비스.
*/
#include "ST_FEEPREVIEW.h"
/* 51. ST_FEEPREVIEW - 정산 실수령 예상 견적. */
void ST_FEEPREVIEW(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64];
long amount = getl(b, T_AMOUNT), fee, vat, wht, net;
EXEC SQL BEGIN DECLARE SECTION;
long h_bps;
char h_merch[64];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT mdr_bps INTO :h_bps FROM merchant WHERE merchant_id = :h_merch;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
fee = acq_fee(amount, h_bps);
vat = acq_vat(fee);
wht = acq_wht_amt(fee, 2200);
net = amount - fee - vat;
setl(b, T_FEE, fee);
setl(b, T_TAX, vat);
setl(b, T_AMT1, wht);
setl(b, T_NET, net);
setl(b, T_RC, h_bps);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,19 @@
/*
* ST_FEERATIO.h - st ST_FEERATIO (copybook / record header).
* : bps vs bps .
*/
#ifndef ST_FEERATIO_H
#define ST_FEERATIO_H
#include "st_svc.h"
typedef struct {
long purchase_id;
long eff_bps;
long contract_bps;
long diff_bps;
} st_st_feeratio_rec_t;
void ST_FEERATIO(TPSVCINFO *p);
#endif /* ST_FEERATIO_H */

View file

@ -0,0 +1,32 @@
/*
* ST_FEERATIO.pgc - st 모듈 서비스 ST_FEERATIO (one service = one file).
* 수수료율 실효 검증: 매입-가맹점 조인 한 번으로 실제 부과 수수료의 실효
* bps 를 역산하고 계약 요율과의 편차(±5bps 허용)를 판정한다. 조회 전용.
*/
#include "ST_FEERATIO.h"
/* 65. ST_FEERATIO - 실효 수수료율 vs 계약 요율 편차 검증. */
void ST_FEERATIO(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long eff, diff, adiff;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_amount, h_fee, h_bps;
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL SELECT p.amount, p.fee, m.mdr_bps INTO :h_amount, :h_fee, :h_bps
FROM purchase p JOIN merchant m ON m.merchant_id = p.merchant_id
WHERE p.purchase_id = :h_pid;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
if (h_amount <= 0) FAIL(b);
eff = h_fee * 10000 / h_amount; /* 실효 bps 역산 */
diff = eff - h_bps;
adiff = (diff < 0) ? -diff : diff;
setl(b, T_ARG1, eff);
setl(b, T_ARG2, h_bps);
setl(b, T_DELTA, diff);
setl(b, T_RC, (adiff > 5) ? 1 : 0); /* 허용 편차 5bps 초과 시 이상 */
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,20 @@
/*
* ST_FXSETTLE.h - st ST_FXSETTLE (copybook / record header).
* : ac_fx + FX .
*/
#ifndef ST_FXSETTLE_H
#define ST_FXSETTLE_H
#include "st_svc.h"
typedef struct {
long settlement_id;
long purchase_id;
char merchant_id[64];
long fx_cnt;
long krw_total;
} st_st_fxsettle_rec_t;
void ST_FXSETTLE(TPSVCINFO *p);
#endif /* ST_FXSETTLE_H */

View file

@ -0,0 +1,32 @@
/*
* ST_FXSETTLE.pgc - st 모듈 서비스 ST_FXSETTLE (one service = one file).
* 해외 매입 정산: 매입에 걸린 외화 내역(ac_fx)의 원화 환산액을 합산해 FX
* 정산 상세로 기록한다. 외화 내역 없으면 RC=1 (dbio).
*/
#include "ST_FXSETTLE.h"
/* 70. ST_FXSETTLE - 외화 환산액 정산 상세 기록. */
void ST_FXSETTLE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16];
long sid = getl(b, T_SETTLE_ID), dtl;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_cnt, h_krw;
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
EXEC SQL SELECT count(*), coalesce(sum(krw_amount),0) INTO :h_cnt, :h_krw
FROM ac_fx WHERE purchase_id = :h_pid;
if (sqlca.sqlcode < 0) FAIL(b);
if (h_cnt == 0) { setl(b, T_RC, 1); setl(b, T_AMOUNT, 0); OK(b); }
dtl = stdb_insert_fee_dtl(sid, h_pid, merch, "FX", h_krw, bizdate);
if (dtl < 0) FAIL(b);
setl(b, T_AMOUNT, h_krw);
setl(b, T_COUNT, h_cnt);
setl(b, T_RC, 0);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,20 @@
/*
* ST_GRPSETTLE.h - st ST_GRPSETTLE (copybook / record header).
* : prefix .
*/
#ifndef ST_GRPSETTLE_H
#define ST_GRPSETTLE_H
#include "st_svc.h"
typedef struct {
char group_prefix[16];
char biz_date[16];
long members;
long group_total;
long pay_id;
} st_st_grpsettle_rec_t;
void ST_GRPSETTLE(TPSVCINFO *p);
#endif /* ST_GRPSETTLE_H */

View file

@ -0,0 +1,46 @@
/*
* ST_GRPSETTLE.pgc - st 모듈 서비스 ST_GRPSETTLE (one service = one file).
* 가맹점그룹 통합정산: 그룹 prefix(LIKE) 소속 가맹점별 정산 net 을 커서로
* 합산해 그룹 대표 앞 통합 지급 1건으로 예약한다. 소속원 없으면 RC=1 (dbio).
*/
#include "ST_GRPSETTLE.h"
/* 48. ST_GRPSETTLE - 그룹 prefix 통합정산 지급. */
void ST_GRPSETTLE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char prefix[16], bizdate[16], like[24];
long members = 0, total = 0, pay;
EXEC SQL BEGIN DECLARE SECTION;
long h_msum;
char h_mid[64], h_like[24], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, prefix, sizeof(prefix)); if (prefix[0] == 0) strcpy(prefix, "M0");
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
snprintf(like, sizeof(like), "%s%%", prefix);
strncpy(h_like, like, sizeof(h_like)-1); h_like[sizeof(h_like)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL DECLARE grpset_c CURSOR FOR
SELECT merchant_id, coalesce(sum(net),0) FROM settlement
WHERE biz_date = :h_bizdate AND merchant_id LIKE :h_like
GROUP BY merchant_id ORDER BY merchant_id;
EXEC SQL OPEN grpset_c;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH grpset_c INTO :h_mid, :h_msum;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE grpset_c; FAIL(b); }
members++;
total += h_msum;
}
EXEC SQL CLOSE grpset_c;
if (members == 0) { setl(b, T_RC, 1); OK(b); }
pay = stdb_insert_payment(prefix, total, bizdate, "SCHEDULED", bizdate); /* 그룹 대표 지급 */
if (pay < 0) FAIL(b);
setl(b, T_ID1, pay);
setl(b, T_COUNT, members);
setl(b, T_GROSS, total);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,18 @@
/*
* ST_HOLDAMT.h - st ST_HOLDAMT (copybook / record header).
* : .
*/
#ifndef ST_HOLDAMT_H
#define ST_HOLDAMT_H
#include "st_svc.h"
typedef struct {
char biz_date[16];
long threshold;
long held_rows;
} st_st_holdamt_rec_t;
void ST_HOLDAMT(TPSVCINFO *p);
#endif /* ST_HOLDAMT_H */

View file

@ -0,0 +1,31 @@
/*
* ST_HOLDAMT.pgc - st 모듈 서비스 ST_HOLDAMT (one service = one file).
* 금액 조건부 보류: 임계액(threshold) 이상의 SCHEDULED 지급을 집합 UPDATE 로
* 일괄 HELD 전환하고 보류 건수를 회신한다 (고액 지급 심사용).
*/
#include "ST_HOLDAMT.h"
/* 52. ST_HOLDAMT - 임계액 이상 지급 일괄 보류. */
void ST_HOLDAMT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16];
long rows;
EXEC SQL BEGIN DECLARE SECTION;
long h_thr;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
h_thr = getl(b, T_AMT1);
if (h_thr <= 0) h_thr = 1000000; /* 기본 고액 기준 100만원 */
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL UPDATE st_payment SET status = 'HELD'
WHERE status = 'SCHEDULED' AND biz_date = :h_bizdate AND net_amount >= :h_thr;
if (sqlca.sqlcode < 0) FAIL(b);
rows = (long) sqlca.sqlerrd[2];
setl(b, T_COUNT, rows);
setl(b, T_RC, rows > 0 ? 0 : 1);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,18 @@
/*
* ST_ISSUERSTAT.h - st ST_ISSUERSTAT (copybook / record header).
* : .
*/
#ifndef ST_ISSUERSTAT_H
#define ST_ISSUERSTAT_H
#include "st_svc.h"
typedef struct {
char biz_date[16];
long issuer_cnt;
long fee_total;
} st_st_issuerstat_rec_t;
void ST_ISSUERSTAT(TPSVCINFO *p);
#endif /* ST_ISSUERSTAT_H */

View file

@ -0,0 +1,41 @@
/*
* ST_ISSUERSTAT.pgc - st 모듈 서비스 ST_ISSUERSTAT (one service = one file).
* 발급사별 수수료 통계 세분: 당일 정산완료(S) 매입을 발급사(issuer) 단위로
* 커서 순회하며 발급사 수와 수수료 총액을 집계한다. 조회 전용.
*/
#include "ST_ISSUERSTAT.h"
/* 68. ST_ISSUERSTAT - 발급사별 수수료 분해 통계. */
void ST_ISSUERSTAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16];
long issuers = 0, feesum = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_fee;
char h_issuer[32], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL DECLARE issuerstat_c CURSOR FOR
SELECT issuer, coalesce(sum(fee),0) FROM purchase
WHERE biz_date = :h_bizdate AND status = 'S'
GROUP BY issuer ORDER BY issuer;
EXEC SQL OPEN issuerstat_c;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH issuerstat_c INTO :h_issuer, :h_fee;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE issuerstat_c; FAIL(b); }
issuers++;
feesum += h_fee;
}
EXEC SQL CLOSE issuerstat_c;
if (issuers == 0) { setl(b, T_RC, 1); OK(b); }
setl(b, T_COUNT, issuers);
setl(b, T_FEE, feesum);
setl(b, T_RC, 0);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,19 @@
/*
* ST_LEDGERSNAP.h - st ST_LEDGERSNAP (copybook / record header).
* : (STSNAP) .
*/
#ifndef ST_LEDGERSNAP_H
#define ST_LEDGERSNAP_H
#include "st_svc.h"
typedef struct {
char biz_date[16];
long snap_ledger_id;
long settle_cnt;
long net_sum;
} st_st_ledgersnap_rec_t;
void ST_LEDGERSNAP(TPSVCINFO *p);
#endif /* ST_LEDGERSNAP_H */

View file

@ -0,0 +1,33 @@
/*
* ST_LEDGERSNAP.pgc - st 모듈 서비스 ST_LEDGERSNAP (one service = one file).
* 정산 스냅샷: 당일 정산 건수·net 총액을 집계해 원장에 STSNAP 스냅샷 엔트리로
* 기록하고 스냅샷 원장 ID 를 회신한다 (감사 추적용).
*/
#include "ST_LEDGERSNAP.h"
/* 75. ST_LEDGERSNAP - 당일 정산 총액 원장 스냅샷. */
void ST_LEDGERSNAP(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16];
EXEC SQL BEGIN DECLARE SECTION;
long h_cnt, h_sum, h_lid;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_cnt, :h_sum
FROM settlement WHERE biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT nextval('ledger_seq') INTO :h_lid;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date)
VALUES (:h_lid, 0, 'STSNAP', :h_sum, :h_bizdate);
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_ID1, h_lid);
setl(b, T_COUNT, h_cnt);
setl(b, T_GROSS, h_sum);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,20 @@
/*
* ST_LIMITCHK.h - st ST_LIMITCHK (copybook / record header).
* : vs daily_limit .
*/
#ifndef ST_LIMITCHK_H
#define ST_LIMITCHK_H
#include "st_svc.h"
typedef struct {
char merchant_id[64];
char biz_date[16];
long daily_limit;
long used;
long remain;
} st_st_limitchk_rec_t;
void ST_LIMITCHK(TPSVCINFO *p);
#endif /* ST_LIMITCHK_H */

View file

@ -0,0 +1,35 @@
/*
* ST_LIMITCHK.pgc - st 모듈 서비스 ST_LIMITCHK (one service = one file).
* 가맹점 한도 점검: 가맹점 일 한도(daily_limit)와 당일 매입 누계(취소 제외)를
* 비교해 잔여 한도를 회신한다. 한도 초과 시 RC=1. 조회 전용.
*/
#include "ST_LIMITCHK.h"
/* 74. ST_LIMITCHK - 일 매입 한도 잔여 점검. */
void ST_LIMITCHK(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16];
long remain;
EXEC SQL BEGIN DECLARE SECTION;
long h_limit, h_used;
char h_merch[64], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT daily_limit INTO :h_limit FROM merchant WHERE merchant_id = :h_merch;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_used FROM purchase
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status <> 'C';
if (sqlca.sqlcode < 0) FAIL(b);
remain = h_limit - h_used;
setl(b, T_AMT1, h_limit);
setl(b, T_GROSS, h_used);
setl(b, T_DELTA, remain);
setl(b, T_RC, (remain < 0) ? 1 : 0);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,21 @@
/*
* ST_MERCHRATE.h - st ST_MERCHRATE (copybook / record header).
* : merchant.mdr_bps + .
*/
#ifndef ST_MERCHRATE_H
#define ST_MERCHRATE_H
#include "st_svc.h"
typedef struct {
long settlement_id;
long purchase_id;
char merchant_id[64];
long amount;
long merch_bps;
long fee;
} st_st_merchrate_rec_t;
void ST_MERCHRATE(TPSVCINFO *p);
#endif /* ST_MERCHRATE_H */

View file

@ -0,0 +1,32 @@
/*
* ST_MERCHRATE.pgc - st 모듈 서비스 ST_MERCHRATE (one service = one file).
* 가맹점 개별 요율 적용: 구간요율표 대신 가맹점 마스터의 계약 요율(mdr_bps)로
* 수수료를 산출하고 MDR 정산 상세로 기록한다 (dbio).
*/
#include "ST_MERCHRATE.h"
/* 56. ST_MERCHRATE - 가맹점 계약 요율 수수료 산출/기록. */
void ST_MERCHRATE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16];
long sid = getl(b, T_SETTLE_ID), pid = getl(b, T_PURCHASE_ID);
long amount = getl(b, T_AMOUNT), fee, dtl;
EXEC SQL BEGIN DECLARE SECTION;
long h_bps;
char h_merch[64];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT mdr_bps INTO :h_bps FROM merchant WHERE merchant_id = :h_merch;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
fee = acq_fee(amount, h_bps);
dtl = stdb_insert_fee_dtl(sid, pid, merch, "MDR", fee, bizdate);
if (dtl < 0) FAIL(b);
setl(b, T_FEE, fee);
setl(b, T_RC, h_bps);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,20 @@
/*
* ST_MINFEE.h - st ST_MINFEE (copybook / record header).
* : .
*/
#ifndef ST_MINFEE_H
#define ST_MINFEE_H
#include "st_svc.h"
typedef struct {
long settlement_id;
long purchase_id;
char merchant_id[64];
long fee;
long min_fee;
} st_st_minfee_rec_t;
void ST_MINFEE(TPSVCINFO *p);
#endif /* ST_MINFEE_H */

View file

@ -0,0 +1,31 @@
/*
* ST_MINFEE.pgc - st 모듈 서비스 ST_MINFEE (one service = one file).
* 최저 수수료 보정: 산출 수수료가 최저 수수료에 미달하면 부족분을 ADJ 정산
* 상세로 추가 기록해 최저액을 보장한다 (dbio). 충족 시 변경 없음.
*/
#include "ST_MINFEE.h"
/* 59. ST_MINFEE - 최저 수수료 미달분 보정. */
void ST_MINFEE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16];
long sid = getl(b, T_SETTLE_ID), pid = getl(b, T_PURCHASE_ID);
long fee = getl(b, T_FEE), minfee = getl(b, T_ARG1), diff, dtl;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (minfee <= 0) minfee = 100; /* 기본 최저 수수료 100원 */
if (fee >= minfee) {
setl(b, T_FEE, fee);
setl(b, T_DELTA, 0);
OK(b);
}
diff = minfee - fee;
dtl = stdb_insert_fee_dtl(sid, pid, merch, "ADJ", diff, bizdate);
if (dtl < 0) FAIL(b);
setl(b, T_FEE, minfee);
setl(b, T_DELTA, diff);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,18 @@
/*
* ST_NEGSWEEP.h - st ST_NEGSWEEP (copybook / record header).
* : net .
*/
#ifndef ST_NEGSWEEP_H
#define ST_NEGSWEEP_H
#include "st_svc.h"
typedef struct {
char biz_date[16];
long neg_sum;
long swept;
} st_st_negsweep_rec_t;
void ST_NEGSWEEP(TPSVCINFO *p);
#endif /* ST_NEGSWEEP_H */

View file

@ -0,0 +1,33 @@
/*
* ST_NEGSWEEP.pgc - st 모듈 서비스 ST_NEGSWEEP (one service = one file).
* 음수 정산 정리: 당일 net 이 음수인 SETTLED 정산의 합계를 먼저 파악한 뒤
* 집합 UPDATE 로 일괄 CANCELLED 전환한다. 처리 건수/음수 합계를 회신.
*/
#include "ST_NEGSWEEP.h"
/* 63. ST_NEGSWEEP - 음수 net 정산 일괄 취소. */
void ST_NEGSWEEP(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16];
long rows;
EXEC SQL BEGIN DECLARE SECTION;
long h_neg;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT coalesce(sum(net),0) INTO :h_neg FROM settlement
WHERE biz_date = :h_bizdate AND net < 0 AND status = 'SETTLED';
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL UPDATE settlement SET status = 'CANCELLED'
WHERE biz_date = :h_bizdate AND net < 0 AND status = 'SETTLED';
if (sqlca.sqlcode < 0) FAIL(b);
rows = (long) sqlca.sqlerrd[2];
setl(b, T_COUNT, rows);
setl(b, T_GROSS, h_neg);
setl(b, T_RC, rows > 0 ? 0 : 1);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,20 @@
/*
* ST_PARTIAL.h - st ST_PARTIAL (copybook / record header).
* : net .
*/
#ifndef ST_PARTIAL_H
#define ST_PARTIAL_H
#include "st_svc.h"
typedef struct {
long purchase_id;
char merchant_id[64];
long part_amount;
long remain;
char biz_date[16];
} st_st_partial_rec_t;
void ST_PARTIAL(TPSVCINFO *p);
#endif /* ST_PARTIAL_H */

View file

@ -0,0 +1,44 @@
/*
* ST_PARTIAL.pgc - st 모듈 서비스 ST_PARTIAL (one service = one file).
* 부분정산: 매입 net 에서 기정산 합계를 뺀 잔여 한도 내에서 요청 금액만큼
* PARTIAL 상태의 정산 레코드를 추가 생성한다. 한도 초과/0 이하는 RC=1.
*/
#include "ST_PARTIAL.h"
/* 33. ST_PARTIAL - 부분정산 (잔여 한도 검증 후 PARTIAL 정산 생성). */
void ST_PARTIAL(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16];
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_sid, h_pnet, h_done, h_part;
char h_merch[64], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
h_part = getl(b, T_AMOUNT);
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT net INTO :h_pnet FROM purchase WHERE purchase_id = :h_pid;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
EXEC SQL SELECT coalesce(sum(net),0) INTO :h_done FROM settlement
WHERE purchase_id = :h_pid AND status <> 'CANCELLED';
if (sqlca.sqlcode < 0) FAIL(b);
if (h_part <= 0 || h_part > h_pnet - h_done) {
setl(b, T_RC, 1);
setl(b, T_NET, h_pnet - h_done); /* 잔여 한도 통지 */
OK(b);
}
EXEC SQL SELECT nextval('settlement_seq') INTO :h_sid;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO settlement (settlement_id, purchase_id, merchant_id, net, biz_date, status)
VALUES (:h_sid, :h_pid, :h_merch, :h_part, :h_bizdate, 'PARTIAL');
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_SETTLE_ID, h_sid);
setl(b, T_NET, h_pnet - h_done - h_part);
setl(b, T_RC, 0);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,19 @@
/*
* ST_PAYRETRY.h - st ST_PAYRETRY (copybook / record header).
* : .
*/
#ifndef ST_PAYRETRY_H
#define ST_PAYRETRY_H
#include "st_svc.h"
typedef struct {
char merchant_id[64];
char biz_date[16];
char retry_date[16];
long retried;
} st_st_payretry_rec_t;
void ST_PAYRETRY(TPSVCINFO *p);
#endif /* ST_PAYRETRY_H */

View file

@ -0,0 +1,34 @@
/*
* ST_PAYRETRY.pgc - st 모듈 서비스 ST_PAYRETRY (one service = one file).
* 지급 재시도: 이체 실패 등으로 CANCELLED 된 지급을 익영업일 지급일로 갱신해
* SCHEDULED 재예약한다. 대상 없으면 RC=1.
*/
#include "ST_PAYRETRY.h"
/* 73. ST_PAYRETRY - 취소 지급 익영업일 재예약. */
void ST_PAYRETRY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16], retry[16];
long rows;
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_bizdate[16], h_retry[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
if (acq_next_bizday(bizdate, retry) < 0) FAIL(b);
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
strncpy(h_retry, retry, sizeof(h_retry)-1); h_retry[sizeof(h_retry)-1] = 0;
EXEC SQL UPDATE st_payment SET status = 'SCHEDULED', pay_date = :h_retry
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status = 'CANCELLED';
if (sqlca.sqlcode < 0) FAIL(b);
rows = (long) sqlca.sqlerrd[2];
if (rows == 0) { setl(b, T_RC, 1); setl(b, T_COUNT, 0); OK(b); }
setl(b, T_COUNT, rows);
Bchg(b, T_KEY1, 0, retry, 0L);
setl(b, T_RC, 0);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,19 @@
/*
* ST_PAYSTATUS.h - st ST_PAYSTATUS (copybook / record header).
* : //.
*/
#ifndef ST_PAYSTATUS_H
#define ST_PAYSTATUS_H
#include "st_svc.h"
typedef struct {
char merchant_id[64];
char status[16];
long net_amount;
char pay_date[16];
} st_st_paystatus_rec_t;
void ST_PAYSTATUS(TPSVCINFO *p);
#endif /* ST_PAYSTATUS_H */

View file

@ -0,0 +1,35 @@
/*
* ST_PAYSTATUS.pgc - st 모듈 서비스 ST_PAYSTATUS (one service = one file).
* 지급 상태 조회: 가맹점의 가장 최근 지급 레코드에서 상태·금액·지급 예정일을
* 조회해 회신한다. 지급 이력이 없으면 NONE/RC=1.
*/
#include "ST_PAYSTATUS.h"
/* 53. ST_PAYSTATUS - 가맹점 최신 지급 상태 조회. */
void ST_PAYSTATUS(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64];
EXEC SQL BEGIN DECLARE SECTION;
long h_net;
char h_merch[64], h_status[16], h_payd[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT status, net_amount, to_char(pay_date,'YYYY-MM-DD')
INTO :h_status, :h_net, :h_payd FROM st_payment
WHERE merchant_id = :h_merch ORDER BY pay_id DESC LIMIT 1;
if (sqlca.sqlcode < 0) FAIL(b);
if (sqlca.sqlcode == 100) {
Bchg(b, T_STATUS, 0, "NONE", 0L);
setl(b, T_RC, 1);
OK(b);
}
Bchg(b, T_STATUS, 0, h_status, 0L);
setl(b, T_NET, h_net);
Bchg(b, T_KEY1, 0, h_payd, 0L);
setl(b, T_RC, 0);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,21 @@
/*
* ST_PROMOCALC.h - st ST_PROMOCALC (copybook / record header).
* / : + .
*/
#ifndef ST_PROMOCALC_H
#define ST_PROMOCALC_H
#include "st_svc.h"
typedef struct {
long settlement_id;
long purchase_id;
char event_code[32];
long fee;
long promo_pct;
long discount;
} st_st_promocalc_rec_t;
void ST_PROMOCALC(TPSVCINFO *p);
#endif /* ST_PROMOCALC_H */

View file

@ -0,0 +1,29 @@
/*
* ST_PROMOCALC.pgc - st 모듈 서비스 ST_PROMOCALC (one service = one file).
* 프로모션/이벤트 정산: 이벤트 코드와 할인율(%)을 받아 수수료 할인액을 계산하고
* 양수 조정(가맹점 환급)으로 st_adjust 에 기록한다 (dbio).
*/
#include "ST_PROMOCALC.h"
/* 32. ST_PROMOCALC - 프로모션/이벤트 수수료 할인 정산. */
void ST_PROMOCALC(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16], event[32], reason[64];
long sid = getl(b, T_SETTLE_ID), pid = getl(b, T_PURCHASE_ID);
long fee = getl(b, T_FEE), pct = getl(b, T_ARG1), disc, adj;
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
gets_(b, T_STR1, event, sizeof(event)); if (event[0] == 0) strcpy(event, "EVENT");
if (pct <= 0 || pct > 100) pct = 30; /* 기본 30% 할인 */
disc = fee * pct / 100;
if (disc <= 0) { setl(b, T_RC, 1); setl(b, T_DELTA, 0); OK(b); }
snprintf(reason, sizeof(reason), "PROMO:%s", event);
adj = stdb_insert_adjust(sid, pid, disc, reason, bizdate);
if (adj < 0) FAIL(b);
setl(b, T_ADJ, adj);
setl(b, T_DELTA, disc);
setl(b, T_FEE, fee - disc);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,20 @@
/*
* ST_RATESIM.h - st ST_RATESIM (copybook / record header).
* : + + .
*/
#ifndef ST_RATESIM_H
#define ST_RATESIM_H
#include "st_svc.h"
typedef struct {
char fee_type[16];
long amount;
long band_count;
long applied_bps;
long sim_fee;
} st_st_ratesim_rec_t;
void ST_RATESIM(TPSVCINFO *p);
#endif /* ST_RATESIM_H */

View file

@ -0,0 +1,40 @@
/*
* ST_RATESIM.pgc - st 모듈 서비스 ST_RATESIM (one service = one file).
* 요율 시뮬레이션: 요율표(st_fee_rate)의 전 구간을 커서로 순회하며 입력 금액에
* 적용될 구간(bps)을 판정하고 예상 수수료를 계산한다. 조회 전용(쓰기 없음).
*/
#include "ST_RATESIM.h"
/* 31. ST_RATESIM - 요율 시뮬레이션 (구간 판정 + 예상 수수료). */
void ST_RATESIM(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char ftype[16];
long amount = getl(b, T_AMOUNT), bands = 0, applied = 0, fee;
EXEC SQL BEGIN DECLARE SECTION;
long h_min, h_bps;
char h_ftype[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_STR1, ftype, sizeof(ftype)); if (ftype[0] == 0) strcpy(ftype, "MDR");
strncpy(h_ftype, ftype, sizeof(h_ftype)-1); h_ftype[sizeof(h_ftype)-1] = 0;
EXEC SQL DECLARE ratesim_c CURSOR FOR
SELECT band_min, rate_bps FROM st_fee_rate WHERE fee_type = :h_ftype ORDER BY band_min;
EXEC SQL OPEN ratesim_c;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH ratesim_c INTO :h_min, :h_bps;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ratesim_c; FAIL(b); }
bands++;
if (h_min <= amount) applied = h_bps; /* band_min 오름차순: 마지막 매칭 구간이 적용 요율 */
}
EXEC SQL CLOSE ratesim_c;
if (bands == 0) { setl(b, T_RC, -1); OK(b); }
fee = acq_fee(amount, applied);
setl(b, T_COUNT, bands);
setl(b, T_RC, applied);
setl(b, T_FEE, fee);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,19 @@
/*
* ST_RECVOFFSET.h - st ST_RECVOFFSET (copybook / record header).
* : ( ) .
*/
#ifndef ST_RECVOFFSET_H
#define ST_RECVOFFSET_H
#include "st_svc.h"
typedef struct {
char merchant_id[64];
char biz_date[16];
long receivable;
long offset_amt;
} st_st_recvoffset_rec_t;
void ST_RECVOFFSET(TPSVCINFO *p);
#endif /* ST_RECVOFFSET_H */

View file

@ -0,0 +1,41 @@
/*
* ST_RECVOFFSET.pgc - st 모듈 서비스 ST_RECVOFFSET (one service = one file).
* 미수금 상계: 매입 조인으로 가맹점의 누적 미수금(음수 조정 절대값)을 산출하고
* 예정 지급 가용액과의 min 만큼 최근 지급 레코드에서 상계 차감한다.
*/
#include "ST_RECVOFFSET.h"
/* 43. ST_RECVOFFSET - 미수금-지급 상계 처리. */
void ST_RECVOFFSET(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizdate[16];
EXEC SQL BEGIN DECLARE SECTION;
long h_recv, h_avail, h_off;
char h_merch[64], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL SELECT coalesce(-sum(a.delta),0) INTO :h_recv
FROM st_adjust a JOIN purchase p ON p.purchase_id = a.purchase_id
WHERE p.merchant_id = :h_merch AND a.delta < 0 AND a.biz_date <= :h_bizdate::date;
if (sqlca.sqlcode < 0) FAIL(b);
if (h_recv <= 0) { setl(b, T_RC, 1); setl(b, T_AMOUNT, 0); OK(b); }
EXEC SQL SELECT coalesce(sum(net_amount),0) INTO :h_avail FROM st_payment
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status = 'SCHEDULED';
if (sqlca.sqlcode < 0) FAIL(b);
h_off = (h_recv < h_avail) ? h_recv : h_avail;
if (h_off <= 0) { setl(b, T_RC, 2); setl(b, T_AMOUNT, 0); OK(b); } /* 상계 재원 없음 */
EXEC SQL UPDATE st_payment SET net_amount = net_amount - :h_off
WHERE pay_id = (SELECT max(pay_id) FROM st_payment
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status = 'SCHEDULED');
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_AMOUNT, h_off);
setl(b, T_AMT1, h_recv - h_off); /* 잔여 미수금 */
setl(b, T_RC, 0);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,17 @@
/*
* ST_REOPEN.h - st ST_REOPEN (copybook / record header).
* : (STCLOSE) .
*/
#ifndef ST_REOPEN_H
#define ST_REOPEN_H
#include "st_svc.h"
typedef struct {
char biz_date[16];
long removed;
} st_st_reopen_rec_t;
void ST_REOPEN(TPSVCINFO *p);
#endif /* ST_REOPEN_H */

View file

@ -0,0 +1,27 @@
/*
* ST_REOPEN.pgc - st 모듈 서비스 ST_REOPEN (one service = one file).
* 정산 마감 해제: 당일 마감 원장(STCLOSE)을 삭제해 마감을 철회한다.
* 마감 원장이 없으면(미마감) RC=1 로 회신한다.
*/
#include "ST_REOPEN.h"
/* 61. ST_REOPEN - 정산 마감 철회 (STCLOSE 삭제). */
void ST_REOPEN(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16];
long rows;
EXEC SQL BEGIN DECLARE SECTION;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0;
EXEC SQL DELETE FROM ledger WHERE entry_type = 'STCLOSE' AND biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
rows = (long) sqlca.sqlerrd[2];
setl(b, T_COUNT, rows);
setl(b, T_RC, rows > 0 ? 0 : 1);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

19
app/src/st/svc/ST_RERUN.h Normal file
View file

@ -0,0 +1,19 @@
/*
* ST_RERUN.h - st ST_RERUN (copybook / record header).
* : .
*/
#ifndef ST_RERUN_H
#define ST_RERUN_H
#include "st_svc.h"
typedef struct {
long purchase_id;
long removed;
long fee;
char merchant_id[64];
} st_st_rerun_rec_t;
void ST_RERUN(TPSVCINFO *p);
#endif /* ST_RERUN_H */

View file

@ -0,0 +1,32 @@
/*
* ST_RERUN.pgc - st 모듈 서비스 ST_RERUN (one service = one file).
* 정산 재실행: 해당 매입의 정산 상세를 전량 삭제하고 매입 원본(fee)을 기준으로
* MDR 상세 라인을 다시 생성한다. 삭제 건수를 회신한다.
*/
#include "ST_RERUN.h"
/* 34. ST_RERUN - 정산 재실행 (상세 삭제 + 원본 기준 재생성). */
void ST_RERUN(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long removed;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_amount, h_fee;
char h_merch[64], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL DELETE FROM st_settle_dtl WHERE purchase_id = :h_pid;
if (sqlca.sqlcode < 0) FAIL(b);
removed = (long) sqlca.sqlerrd[2];
EXEC SQL SELECT amount, fee, merchant_id, to_char(biz_date,'YYYY-MM-DD')
INTO :h_amount, :h_fee, :h_merch, :h_bizdate FROM purchase WHERE purchase_id = :h_pid;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
EXEC SQL INSERT INTO st_settle_dtl (dtl_id, settlement_id, purchase_id, merchant_id, fee_type, amount, biz_date)
VALUES (nextval('st_dtl_seq'), 0, :h_pid, :h_merch, 'MDR', :h_fee, :h_bizdate);
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, removed);
setl(b, T_FEE, h_fee);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,20 @@
/*
* ST_RETRORATE.h - st ST_RETRORATE (copybook / record header).
* : bps .
*/
#ifndef ST_RETRORATE_H
#define ST_RETRORATE_H
#include "st_svc.h"
typedef struct {
long purchase_id;
char fee_type[16];
long new_bps;
long old_fee;
long new_fee;
} st_st_retrorate_rec_t;
void ST_RETRORATE(TPSVCINFO *p);
#endif /* ST_RETRORATE_H */

View file

@ -0,0 +1,40 @@
/*
* ST_RETRORATE.pgc - st 모듈 서비스 ST_RETRORATE (one service = one file).
* 요율 소급적용: 신규 요율(bps)로 매입 금액의 수수료를 재계산하고, 기계상된
* 정산 상세 합계와의 차액만큼 소급 조정(st_adjust)을 기록한다 (dbio).
*/
#include "ST_RETRORATE.h"
/* 36. ST_RETRORATE - 요율 소급적용 (차액 조정 기록). */
void ST_RETRORATE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizdate[16], ftype[16];
long sid = getl(b, T_SETTLE_ID), newbps = getl(b, T_ARG1);
long newfee, diff, adj;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_amount, h_old;
char h_ftype[16];
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
gets_(b, T_BIZDATE, bizdate, sizeof(bizdate));
gets_(b, T_STR1, ftype, sizeof(ftype)); if (ftype[0] == 0) strcpy(ftype, "MDR");
if (newbps <= 0) newbps = 25;
strncpy(h_ftype, ftype, sizeof(h_ftype)-1); h_ftype[sizeof(h_ftype)-1] = 0;
EXEC SQL SELECT amount INTO :h_amount FROM purchase WHERE purchase_id = :h_pid;
if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b);
EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_old FROM st_settle_dtl
WHERE purchase_id = :h_pid AND fee_type = :h_ftype;
if (sqlca.sqlcode < 0) FAIL(b);
newfee = acq_fee(h_amount, newbps);
diff = newfee - h_old;
if (diff == 0) { setl(b, T_RC, 0); setl(b, T_DELTA, 0); OK(b); }
adj = stdb_insert_adjust(sid, h_pid, diff, "RETRO_RATE", bizdate);
if (adj < 0) FAIL(b);
setl(b, T_ADJ, adj);
setl(b, T_DELTA, diff);
setl(b, T_FEE, newfee);
OK(b);
}
/* vim: set ts=4 sw=4 et smartindent: */

View file

@ -0,0 +1,19 @@
/*
* ST_ROUNDING.h - st ST_ROUNDING (copybook / record header).
* : net 10 + .
*/
#ifndef ST_ROUNDING_H
#define ST_ROUNDING_H
#include "st_svc.h"
typedef struct {
long purchase_id;
long settlement_id;
long net_before;
long truncated;
} st_st_rounding_rec_t;
void ST_ROUNDING(TPSVCINFO *p);
#endif /* ST_ROUNDING_H */

Some files were not shown because too many files have changed in this diff Show more