diff --git a/app/src/ac/ac_svr.pgc b/app/src/ac/ac_svr.pgc index 23ccd7f..4e874f9 100644 --- a/app/src/ac/ac_svr.pgc +++ b/app/src/ac/ac_svr.pgc @@ -96,6 +96,65 @@ #include "ACQ_APPRGAP.h" #include "ACQ_FXCONVERT.h" +#include "ACQ_CHANDETAIL.h" +#include "ACQ_BRANDSUM.h" +#include "ACQ_BRANDQRY.h" +#include "ACQ_CTRYQRY.h" +#include "ACQ_CCYQRY.h" +#include "ACQ_CCYRANK.h" +#include "ACQ_CHANRANK.h" +#include "ACQ_BRANDRANK.h" +#include "ACQ_CTRYRANK.h" +#include "ACQ_ERRCLASS.h" +#include "ACQ_ERRQRY.h" +#include "ACQ_ERRRETRY.h" +#include "ACQ_ERRSTAT.h" +#include "ACQ_REBUYAPPR.h" +#include "ACQ_REBUYREJ.h" +#include "ACQ_REBUYHIST.h" +#include "ACQ_CB_RND2.h" +#include "ACQ_CB_ARB.h" +#include "ACQ_CB_FINAL.h" +#include "ACQ_CB_REVERSE.h" +#include "ACQ_CORRRSN.h" +#include "ACQ_CORRSTAT.h" +#include "ACQ_CORRAPPR.h" +#include "ACQ_INSTQRY.h" +#include "ACQ_INSTPREPAY.h" +#include "ACQ_INSTPLAN.h" +#include "ACQ_INSTCXL.h" +#include "ACQ_ESCROW.h" +#include "ACQ_ESCROWREL.h" +#include "ACQ_GUARANTEE.h" +#include "ACQ_GUARQRY.h" +#include "ACQ_TAXMODIFY.h" +#include "ACQ_TAXREVERSE.h" +#include "ACQ_TAXQRY.h" +#include "ACQ_ALERTAMT.h" +#include "ACQ_ALERTVEL.h" +#include "ACQ_ALERTDUP.h" +#include "ACQ_ALERTQRY.h" +#include "ACQ_EDIRSNDCH.h" +#include "ACQ_EDIQRY.h" +#include "ACQ_EDISTAT.h" +#include "ACQ_HOURSTAT.h" +#include "ACQ_DOWSTAT.h" +#include "ACQ_QTRSTAT.h" +#include "ACQ_GRADESTAT.h" +#include "ACQ_HALFSTAT.h" +#include "ACQ_BULKCHK.h" +#include "ACQ_BULKRPT.h" +#include "ACQ_EVENTBUY.h" +#include "ACQ_PROMOQRY.h" +#include "ACQ_PROMOSTAT.h" +#include "ACQ_LIMITEXC.h" +#include "ACQ_LIMITOVR.h" +#include "ACQ_SETTLEPRE.h" +#include "ACQ_SETTLEEXP.h" +#include "ACQ_EXCHANGE.h" +#include "ACQ_RETURNQRY.h" +#include "ACQ_RETURNSTAT.h" + /* ----- advertise table: {service name, function} (fn is extern from its file) */ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"ACQUIRE", ACQUIRE}, {"ACQ_DDC", ACQ_DDC}, @@ -136,6 +195,64 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"ACQ_VELOCITY", ACQ_VELOCITY}, {"ACQ_TOPMERCH", ACQ_TOPMERCH}, {"ACQ_NETCALC", ACQ_NETCALC}, {"ACQ_APPRGAP", ACQ_APPRGAP}, {"ACQ_FXCONVERT", ACQ_FXCONVERT}, + {"ACQ_CHANDETAIL", ACQ_CHANDETAIL}, + {"ACQ_BRANDSUM", ACQ_BRANDSUM}, + {"ACQ_BRANDQRY", ACQ_BRANDQRY}, + {"ACQ_CTRYQRY", ACQ_CTRYQRY}, + {"ACQ_CCYQRY", ACQ_CCYQRY}, + {"ACQ_CCYRANK", ACQ_CCYRANK}, + {"ACQ_CHANRANK", ACQ_CHANRANK}, + {"ACQ_BRANDRANK", ACQ_BRANDRANK}, + {"ACQ_CTRYRANK", ACQ_CTRYRANK}, + {"ACQ_ERRCLASS", ACQ_ERRCLASS}, + {"ACQ_ERRQRY", ACQ_ERRQRY}, + {"ACQ_ERRRETRY", ACQ_ERRRETRY}, + {"ACQ_ERRSTAT", ACQ_ERRSTAT}, + {"ACQ_REBUYAPPR", ACQ_REBUYAPPR}, + {"ACQ_REBUYREJ", ACQ_REBUYREJ}, + {"ACQ_REBUYHIST", ACQ_REBUYHIST}, + {"ACQ_CB_RND2", ACQ_CB_RND2}, + {"ACQ_CB_ARB", ACQ_CB_ARB}, + {"ACQ_CB_FINAL", ACQ_CB_FINAL}, + {"ACQ_CB_REVERSE", ACQ_CB_REVERSE}, + {"ACQ_CORRRSN", ACQ_CORRRSN}, + {"ACQ_CORRSTAT", ACQ_CORRSTAT}, + {"ACQ_CORRAPPR", ACQ_CORRAPPR}, + {"ACQ_INSTQRY", ACQ_INSTQRY}, + {"ACQ_INSTPREPAY", ACQ_INSTPREPAY}, + {"ACQ_INSTPLAN", ACQ_INSTPLAN}, + {"ACQ_INSTCXL", ACQ_INSTCXL}, + {"ACQ_ESCROW", ACQ_ESCROW}, + {"ACQ_ESCROWREL", ACQ_ESCROWREL}, + {"ACQ_GUARANTEE", ACQ_GUARANTEE}, + {"ACQ_GUARQRY", ACQ_GUARQRY}, + {"ACQ_TAXMODIFY", ACQ_TAXMODIFY}, + {"ACQ_TAXREVERSE", ACQ_TAXREVERSE}, + {"ACQ_TAXQRY", ACQ_TAXQRY}, + {"ACQ_ALERTAMT", ACQ_ALERTAMT}, + {"ACQ_ALERTVEL", ACQ_ALERTVEL}, + {"ACQ_ALERTDUP", ACQ_ALERTDUP}, + {"ACQ_ALERTQRY", ACQ_ALERTQRY}, + {"ACQ_EDIRSNDCH", ACQ_EDIRSNDCH}, + {"ACQ_EDIQRY", ACQ_EDIQRY}, + {"ACQ_EDISTAT", ACQ_EDISTAT}, + {"ACQ_HOURSTAT", ACQ_HOURSTAT}, + {"ACQ_DOWSTAT", ACQ_DOWSTAT}, + {"ACQ_QTRSTAT", ACQ_QTRSTAT}, + {"ACQ_GRADESTAT", ACQ_GRADESTAT}, + {"ACQ_HALFSTAT", ACQ_HALFSTAT}, + {"ACQ_BULKCHK", ACQ_BULKCHK}, + {"ACQ_BULKRPT", ACQ_BULKRPT}, + {"ACQ_EVENTBUY", ACQ_EVENTBUY}, + {"ACQ_PROMOQRY", ACQ_PROMOQRY}, + {"ACQ_PROMOSTAT", ACQ_PROMOSTAT}, + {"ACQ_LIMITEXC", ACQ_LIMITEXC}, + {"ACQ_LIMITOVR", ACQ_LIMITOVR}, + {"ACQ_SETTLEPRE", ACQ_SETTLEPRE}, + {"ACQ_SETTLEEXP", ACQ_SETTLEEXP}, + {"ACQ_EXCHANGE", ACQ_EXCHANGE}, + {"ACQ_RETURNQRY", ACQ_RETURNQRY}, + {"ACQ_RETURNSTAT", ACQ_RETURNSTAT}, {NULL, NULL} }; diff --git a/app/src/ac/batch/ac_brand_batch.h b/app/src/ac/batch/ac_brand_batch.h new file mode 100644 index 0000000..015b60d --- /dev/null +++ b/app/src/ac/batch/ac_brand_batch.h @@ -0,0 +1,19 @@ +/* + * ac_brand_batch.h - ac 배치 ac_brand_batch 카피북 (batch copybook / record header). + * ac 발급사(브랜드)별 매입 집계 (단건 집계 배치, XA). + */ +#ifndef AC_BRAND_BATCH_H +#define AC_BRAND_BATCH_H + +#include +#include +#include +#include +#include "ac_dbio.h" + +typedef struct { + long brands; + long gross; +} ac_brand_batch_rec_t; + +#endif /* AC_BRAND_BATCH_H */ diff --git a/app/src/ac/batch/ac_brand_batch.pgc b/app/src/ac/batch/ac_brand_batch.pgc new file mode 100644 index 0000000..06db0d6 --- /dev/null +++ b/app/src/ac/batch/ac_brand_batch.pgc @@ -0,0 +1,35 @@ +/* + * ac_brand_batch.pgc - ac 발급사(브랜드)별 매입 집계 (단건 집계 배치, XA). + * 영업일 매입을 발급사 기준으로 브랜드 종수와 총 매출을 집계한다. + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, aggregate(s) run on the biz_date, then tpcommit drives XA 2PC. + * Usage: ac_brand_batch [YYYY-MM-DD] + */ +EXEC SQL INCLUDE ac_brand_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_brands, 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 SELECT count(distinct issuer), coalesce(sum(amount),0) + INTO :h_brands, :h_gross FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_brand_batch 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(">>> ac_brand_batch COMMIT: bizdate=%s brands=%ld gross=%ld\n", bizdate, h_brands, h_gross); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_ccyband_batch.h b/app/src/ac/batch/ac_ccyband_batch.h new file mode 100644 index 0000000..53605df --- /dev/null +++ b/app/src/ac/batch/ac_ccyband_batch.h @@ -0,0 +1,20 @@ +/* + * ac_ccyband_batch.h - ac 배치 ac_ccyband_batch 카피북 (batch copybook / record header). + * ac 통화별 해외매입 집계 (단건 집계 배치, XA). + */ +#ifndef AC_CCYBAND_BATCH_H +#define AC_CCYBAND_BATCH_H + +#include +#include +#include +#include +#include "ac_dbio.h" + +typedef struct { + long ccys; + long fx_amount; + long krw_amount; +} ac_ccyband_batch_rec_t; + +#endif /* AC_CCYBAND_BATCH_H */ diff --git a/app/src/ac/batch/ac_ccyband_batch.pgc b/app/src/ac/batch/ac_ccyband_batch.pgc new file mode 100644 index 0000000..7ba4149 --- /dev/null +++ b/app/src/ac/batch/ac_ccyband_batch.pgc @@ -0,0 +1,35 @@ +/* + * ac_ccyband_batch.pgc - ac 통화별 해외매입 집계 (단건 집계 배치, XA). + * 해외매입 원장에서 통화 종수와 원통화/원화 합계를 집계한다. + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, aggregate(s) run on the biz_date, then tpcommit drives XA 2PC. + * Usage: ac_ccyband_batch [YYYY-MM-DD] + */ +EXEC SQL INCLUDE ac_ccyband_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_ccys, h_fx, h_krw; + 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(distinct ccy), coalesce(sum(fx_amount),0), coalesce(sum(krw_amount),0) + INTO :h_ccys, :h_fx, :h_krw FROM ac_fx WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_ccyband_batch 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(">>> ac_ccyband_batch COMMIT: bizdate=%s ccys=%ld fx=%ld krw=%ld\n", bizdate, h_ccys, h_fx, h_krw); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_country_batch.h b/app/src/ac/batch/ac_country_batch.h new file mode 100644 index 0000000..795b61d --- /dev/null +++ b/app/src/ac/batch/ac_country_batch.h @@ -0,0 +1,20 @@ +/* + * ac_country_batch.h - ac 배치 ac_country_batch 카피북 (batch copybook / record header). + * ac 국가(통화권역)별 해외매입 집계 (단건 집계 배치, XA). + */ +#ifndef AC_COUNTRY_BATCH_H +#define AC_COUNTRY_BATCH_H + +#include +#include +#include +#include +#include "ac_dbio.h" + +typedef struct { + long us; + long jp; + long etc; +} ac_country_batch_rec_t; + +#endif /* AC_COUNTRY_BATCH_H */ diff --git a/app/src/ac/batch/ac_country_batch.pgc b/app/src/ac/batch/ac_country_batch.pgc new file mode 100644 index 0000000..dee6212 --- /dev/null +++ b/app/src/ac/batch/ac_country_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_country_batch.pgc - ac 국가(통화권역)별 해외매입 집계 (단건 집계 배치, XA). + * 해외매입 원화금액을 미주/일본/기타 권역 대표통화 기준으로 분해 집계한다. + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, aggregate(s) run on the biz_date, then tpcommit drives XA 2PC. + * Usage: ac_country_batch [YYYY-MM-DD] + */ +EXEC SQL INCLUDE ac_country_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_us, h_jp, h_etc; + 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(krw_amount) FILTER (WHERE ccy IN ('USD','CAD')),0), + coalesce(sum(krw_amount) FILTER (WHERE ccy = 'JPY'),0), + coalesce(sum(krw_amount) FILTER (WHERE ccy NOT IN ('USD','CAD','JPY')),0) + INTO :h_us, :h_jp, :h_etc FROM ac_fx WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_country_batch 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(">>> ac_country_batch COMMIT: bizdate=%s us=%ld jp=%ld etc=%ld\n", bizdate, h_us, h_jp, h_etc); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_dow_batch.h b/app/src/ac/batch/ac_dow_batch.h new file mode 100644 index 0000000..d9d5230 --- /dev/null +++ b/app/src/ac/batch/ac_dow_batch.h @@ -0,0 +1,20 @@ +/* + * ac_dow_batch.h - ac 배치 ac_dow_batch 카피북 (batch copybook / record header). + * ac 요일별 매입 집계 (단건 집계 배치, XA). + */ +#ifndef AC_DOW_BATCH_H +#define AC_DOW_BATCH_H + +#include +#include +#include +#include +#include "ac_dbio.h" + +typedef struct { + long weekday; + long weekend; + long gross; +} ac_dow_batch_rec_t; + +#endif /* AC_DOW_BATCH_H */ diff --git a/app/src/ac/batch/ac_dow_batch.pgc b/app/src/ac/batch/ac_dow_batch.pgc new file mode 100644 index 0000000..ec4d1bd --- /dev/null +++ b/app/src/ac/batch/ac_dow_batch.pgc @@ -0,0 +1,36 @@ +/* + * ac_dow_batch.pgc - ac 요일별 매입 집계 (단건 집계 배치, XA). + * 영업일의 요일(dow)을 판정해 평일/주말 매입 건수와 매출을 분해 집계한다. + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, aggregate(s) run on the biz_date, then tpcommit drives XA 2PC. + * Usage: ac_dow_batch [YYYY-MM-DD] + */ +EXEC SQL INCLUDE ac_dow_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_wd, h_we, 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 SELECT count(*) FILTER (WHERE extract(dow from biz_date) BETWEEN 1 AND 5), + count(*) FILTER (WHERE extract(dow from biz_date) IN (0, 6)), coalesce(sum(amount),0) + INTO :h_wd, :h_we, :h_gross FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_dow_batch 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(">>> ac_dow_batch COMMIT: bizdate=%s weekday=%ld weekend=%ld gross=%ld\n", bizdate, h_wd, h_we, h_gross); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_errtype_batch.h b/app/src/ac/batch/ac_errtype_batch.h new file mode 100644 index 0000000..bab4a92 --- /dev/null +++ b/app/src/ac/batch/ac_errtype_batch.h @@ -0,0 +1,20 @@ +/* + * ac_errtype_batch.h - ac 배치 ac_errtype_batch 카피북 (batch copybook / record header). + * ac 오류유형별 미매입 집계 (단건 집계 배치, XA). + */ +#ifndef AC_ERRTYPE_BATCH_H +#define AC_ERRTYPE_BATCH_H + +#include +#include +#include +#include +#include "ac_dbio.h" + +typedef struct { + long dup; + long limit; + long fx; +} ac_errtype_batch_rec_t; + +#endif /* AC_ERRTYPE_BATCH_H */ diff --git a/app/src/ac/batch/ac_errtype_batch.pgc b/app/src/ac/batch/ac_errtype_batch.pgc new file mode 100644 index 0000000..ad4e90f --- /dev/null +++ b/app/src/ac/batch/ac_errtype_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_errtype_batch.pgc - ac 오류유형별 미매입 집계 (단건 집계 배치, XA). + * 미매입 원장의 사유 접두어별로 중복/한도/환율 오류 건수를 분해 집계한다. + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, aggregate(s) run on the biz_date, then tpcommit drives XA 2PC. + * Usage: ac_errtype_batch [YYYY-MM-DD] + */ +EXEC SQL INCLUDE ac_errtype_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_dup, h_lim, h_fx; + 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(*) FILTER (WHERE reason LIKE 'DUP%'), + count(*) FILTER (WHERE reason LIKE 'LIMIT%'), + count(*) FILTER (WHERE reason LIKE 'FX%') + INTO :h_dup, :h_lim, :h_fx FROM ac_unmatch WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_errtype_batch 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(">>> ac_errtype_batch COMMIT: bizdate=%s dup=%ld limit=%ld fx=%ld\n", bizdate, h_dup, h_lim, h_fx); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_escrow_batch.h b/app/src/ac/batch/ac_escrow_batch.h new file mode 100644 index 0000000..8652e39 --- /dev/null +++ b/app/src/ac/batch/ac_escrow_batch.h @@ -0,0 +1,20 @@ +/* + * ac_escrow_batch.h - ac 배치 ac_escrow_batch 카피북 (batch copybook / record header). + * ac 에스크로 잔액 집계 (단건 집계 배치, XA). + */ +#ifndef AC_ESCROW_BATCH_H +#define AC_ESCROW_BATCH_H + +#include +#include +#include +#include +#include "ac_dbio.h" + +typedef struct { + long escrow; + long release; + long balance; +} ac_escrow_batch_rec_t; + +#endif /* AC_ESCROW_BATCH_H */ diff --git a/app/src/ac/batch/ac_escrow_batch.pgc b/app/src/ac/batch/ac_escrow_batch.pgc new file mode 100644 index 0000000..db6747c --- /dev/null +++ b/app/src/ac/batch/ac_escrow_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_escrow_batch.pgc - ac 에스크로 잔액 집계 (단건 집계 배치, XA). + * 원장에서 예치(ESCROW)와 해제(ESCROW_REL) 합계를 구해 순 예치 잔액을 낸다. + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, aggregate(s) run on the biz_date, then tpcommit drives XA 2PC. + * Usage: ac_escrow_batch [YYYY-MM-DD] + */ +EXEC SQL INCLUDE ac_escrow_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_esc, h_rel, h_bal; + 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) FILTER (WHERE entry_type = 'ESCROW'),0), + coalesce(sum(amount) FILTER (WHERE entry_type = 'ESCROW_REL'),0) + INTO :h_esc, :h_rel FROM ledger WHERE biz_date = :h_bizdate; + h_bal = h_esc - h_rel; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_escrow_batch 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(">>> ac_escrow_batch COMMIT: bizdate=%s escrow=%ld release=%ld balance=%ld\n", bizdate, h_esc, h_rel, h_bal); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_exchange_batch.h b/app/src/ac/batch/ac_exchange_batch.h new file mode 100644 index 0000000..712d209 --- /dev/null +++ b/app/src/ac/batch/ac_exchange_batch.h @@ -0,0 +1,20 @@ +/* + * ac_exchange_batch.h - ac 배치 ac_exchange_batch 카피북 (batch copybook / record header). + * ac 반품/교환 집계 (단건 집계 배치, XA). + */ +#ifndef AC_EXCHANGE_BATCH_H +#define AC_EXCHANGE_BATCH_H + +#include +#include +#include +#include +#include "ac_dbio.h" + +typedef struct { + long returns; + long exchanges; + long amount; +} ac_exchange_batch_rec_t; + +#endif /* AC_EXCHANGE_BATCH_H */ diff --git a/app/src/ac/batch/ac_exchange_batch.pgc b/app/src/ac/batch/ac_exchange_batch.pgc new file mode 100644 index 0000000..0c05255 --- /dev/null +++ b/app/src/ac/batch/ac_exchange_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_exchange_batch.pgc - ac 반품/교환 집계 (단건 집계 배치, XA). + * 취소 원장에서 반품(RETURN)/교환(EXCHANGE) 건수와 금액을 분해 집계한다. + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, aggregate(s) run on the biz_date, then tpcommit drives XA 2PC. + * Usage: ac_exchange_batch [YYYY-MM-DD] + */ +EXEC SQL INCLUDE ac_exchange_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_ret, h_exc, h_amt; + 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(*) FILTER (WHERE cancel_type = 'RETURN'), + count(*) FILTER (WHERE cancel_type = 'EXCHANGE'), + coalesce(sum(amount) FILTER (WHERE cancel_type IN ('RETURN','EXCHANGE')),0) + INTO :h_ret, :h_exc, :h_amt FROM ac_cancel WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_exchange_batch 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(">>> ac_exchange_batch COMMIT: bizdate=%s return=%ld exchange=%ld amount=%ld\n", bizdate, h_ret, h_exc, h_amt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_grade_batch.h b/app/src/ac/batch/ac_grade_batch.h new file mode 100644 index 0000000..0b30616 --- /dev/null +++ b/app/src/ac/batch/ac_grade_batch.h @@ -0,0 +1,20 @@ +/* + * ac_grade_batch.h - ac 배치 ac_grade_batch 카피북 (batch copybook / record header). + * ac 가맹점등급별 매입 집계 (단건 집계 배치, XA). + */ +#ifndef AC_GRADE_BATCH_H +#define AC_GRADE_BATCH_H + +#include +#include +#include +#include +#include "ac_dbio.h" + +typedef struct { + long premium; + long std; + long basic; +} ac_grade_batch_rec_t; + +#endif /* AC_GRADE_BATCH_H */ diff --git a/app/src/ac/batch/ac_grade_batch.pgc b/app/src/ac/batch/ac_grade_batch.pgc new file mode 100644 index 0000000..16f4ac6 --- /dev/null +++ b/app/src/ac/batch/ac_grade_batch.pgc @@ -0,0 +1,38 @@ +/* + * ac_grade_batch.pgc - ac 가맹점등급별 매입 집계 (단건 집계 배치, XA). + * 가맹점 MDR(bps)을 프리미엄(<=20)/일반/기본 등급으로 나눠 매출을 집계한다. + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, aggregate(s) run on the biz_date, then tpcommit drives XA 2PC. + * Usage: ac_grade_batch [YYYY-MM-DD] + */ +EXEC SQL INCLUDE ac_grade_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_prem, h_std, h_basic; + 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(p.amount) FILTER (WHERE m.mdr_bps <= 20),0), + coalesce(sum(p.amount) FILTER (WHERE m.mdr_bps BETWEEN 21 AND 28),0), + coalesce(sum(p.amount) FILTER (WHERE m.mdr_bps > 28),0) + INTO :h_prem, :h_std, :h_basic FROM purchase p + JOIN merchant m ON m.merchant_id = p.merchant_id WHERE p.biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_grade_batch 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(">>> ac_grade_batch COMMIT: bizdate=%s premium=%ld std=%ld basic=%ld\n", bizdate, h_prem, h_std, h_basic); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_hourly_batch.h b/app/src/ac/batch/ac_hourly_batch.h new file mode 100644 index 0000000..2775537 --- /dev/null +++ b/app/src/ac/batch/ac_hourly_batch.h @@ -0,0 +1,19 @@ +/* + * ac_hourly_batch.h - ac 배치 ac_hourly_batch 카피북 (batch copybook / record header). + * ac 시간대별 매입 집계 (단건 집계 배치, XA). + */ +#ifndef AC_HOURLY_BATCH_H +#define AC_HOURLY_BATCH_H + +#include +#include +#include +#include +#include "ac_dbio.h" + +typedef struct { + long day; + long night; +} ac_hourly_batch_rec_t; + +#endif /* AC_HOURLY_BATCH_H */ diff --git a/app/src/ac/batch/ac_hourly_batch.pgc b/app/src/ac/batch/ac_hourly_batch.pgc new file mode 100644 index 0000000..f7bb7ce --- /dev/null +++ b/app/src/ac/batch/ac_hourly_batch.pgc @@ -0,0 +1,36 @@ +/* + * ac_hourly_batch.pgc - ac 시간대별 매입 집계 (단건 집계 배치, XA). + * 접수시각을 주간(6~17시)/야간으로 나눠 시간대별 매입 건수를 집계한다. + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, aggregate(s) run on the biz_date, then tpcommit drives XA 2PC. + * Usage: ac_hourly_batch [YYYY-MM-DD] + */ +EXEC SQL INCLUDE ac_hourly_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_day, h_night; + 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(*) FILTER (WHERE extract(hour from created_at) BETWEEN 6 AND 17), + count(*) FILTER (WHERE extract(hour from created_at) NOT BETWEEN 6 AND 17) + INTO :h_day, :h_night FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_hourly_batch 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(">>> ac_hourly_batch COMMIT: bizdate=%s day=%ld night=%ld\n", bizdate, h_day, h_night); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_quarter_batch.h b/app/src/ac/batch/ac_quarter_batch.h new file mode 100644 index 0000000..33ff633 --- /dev/null +++ b/app/src/ac/batch/ac_quarter_batch.h @@ -0,0 +1,21 @@ +/* + * ac_quarter_batch.h - ac 배치 ac_quarter_batch 카피북 (batch copybook / record header). + * ac 분기별 매입 집계 (단건 집계 배치, XA). + */ +#ifndef AC_QUARTER_BATCH_H +#define AC_QUARTER_BATCH_H + +#include +#include +#include +#include +#include "ac_dbio.h" + +typedef struct { + long q1; + long q2; + long q3; + long q4; +} ac_quarter_batch_rec_t; + +#endif /* AC_QUARTER_BATCH_H */ diff --git a/app/src/ac/batch/ac_quarter_batch.pgc b/app/src/ac/batch/ac_quarter_batch.pgc new file mode 100644 index 0000000..0a1e8e1 --- /dev/null +++ b/app/src/ac/batch/ac_quarter_batch.pgc @@ -0,0 +1,40 @@ +/* + * ac_quarter_batch.pgc - ac 분기별 매입 집계 (단건 집계 배치, XA). + * 영업일이 속한 연도의 분기별 매출 합계를 한 번의 집계로 산출한다. + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, aggregate(s) run on the biz_date, then tpcommit drives XA 2PC. + * Usage: ac_quarter_batch [YYYY-MM-DD] + */ +EXEC SQL INCLUDE ac_quarter_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_q1, h_q2, h_q3, h_q4; + int h_yr; + EXEC SQL END DECLARE SECTION; + + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + h_yr = atoi(bizdate); + + 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) FILTER (WHERE extract(quarter from biz_date) = 1),0), + coalesce(sum(amount) FILTER (WHERE extract(quarter from biz_date) = 2),0), + coalesce(sum(amount) FILTER (WHERE extract(quarter from biz_date) = 3),0), + coalesce(sum(amount) FILTER (WHERE extract(quarter from biz_date) = 4),0) + INTO :h_q1, :h_q2, :h_q3, :h_q4 FROM purchase WHERE extract(year from biz_date) = :h_yr; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_quarter_batch 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(">>> ac_quarter_batch COMMIT: year=%d q1=%ld q2=%ld q3=%ld q4=%ld\n", h_yr, h_q1, h_q2, h_q3, h_q4); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_rebuy_batch.h b/app/src/ac/batch/ac_rebuy_batch.h new file mode 100644 index 0000000..12665e3 --- /dev/null +++ b/app/src/ac/batch/ac_rebuy_batch.h @@ -0,0 +1,20 @@ +/* + * ac_rebuy_batch.h - ac 배치 ac_rebuy_batch 카피북 (batch copybook / record header). + * ac 재매입/취소 유형 집계 (단건 집계 배치, XA). + */ +#ifndef AC_REBUY_BATCH_H +#define AC_REBUY_BATCH_H + +#include +#include +#include +#include +#include "ac_dbio.h" + +typedef struct { + long cb; + long cb2; + long refund; +} ac_rebuy_batch_rec_t; + +#endif /* AC_REBUY_BATCH_H */ diff --git a/app/src/ac/batch/ac_rebuy_batch.pgc b/app/src/ac/batch/ac_rebuy_batch.pgc new file mode 100644 index 0000000..b3916e7 --- /dev/null +++ b/app/src/ac/batch/ac_rebuy_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_rebuy_batch.pgc - ac 재매입/취소 유형 집계 (단건 집계 배치, XA). + * 취소 원장에서 지불거절(CB/CB2)과 환불(REFUND) 건수를 분해 집계한다. + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, aggregate(s) run on the biz_date, then tpcommit drives XA 2PC. + * Usage: ac_rebuy_batch [YYYY-MM-DD] + */ +EXEC SQL INCLUDE ac_rebuy_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cb, h_cb2, h_rf; + 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(*) FILTER (WHERE cancel_type = 'CB'), + count(*) FILTER (WHERE cancel_type = 'CB2'), + count(*) FILTER (WHERE cancel_type = 'REFUND') + INTO :h_cb, :h_cb2, :h_rf FROM ac_cancel WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_rebuy_batch 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(">>> ac_rebuy_batch COMMIT: bizdate=%s cb=%ld cb2=%ld refund=%ld\n", bizdate, h_cb, h_cb2, h_rf); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/svc/ACQ_ALERTAMT.h b/app/src/ac/svc/ACQ_ALERTAMT.h new file mode 100644 index 0000000..d3685c7 --- /dev/null +++ b/app/src/ac/svc/ACQ_ALERTAMT.h @@ -0,0 +1,18 @@ +/* + * ACQ_ALERTAMT.h - ac 서비스 ACQ_ALERTAMT 카피북 (copybook / record header). + * 매입 금액초과 알림: 가맹점의 영업일 매입 중 임계금액(T_AMOUNT)을 초과한 + */ +#ifndef ACQ_ALERTAMT_H +#define ACQ_ALERTAMT_H + +#include "ac_svc.h" + +typedef struct { + long threshold; + long over_count; + long max_amount; +} acq_alertamt_rec_t; + +void ACQ_ALERTAMT(TPSVCINFO *p); + +#endif /* ACQ_ALERTAMT_H */ diff --git a/app/src/ac/svc/ACQ_ALERTAMT.pgc b/app/src/ac/svc/ACQ_ALERTAMT.pgc new file mode 100644 index 0000000..6e605c3 --- /dev/null +++ b/app/src/ac/svc/ACQ_ALERTAMT.pgc @@ -0,0 +1,30 @@ +/* + * ACQ_ALERTAMT.pgc - ac 모듈 서비스 ACQ_ALERTAMT (one service = one file). + * 매입 금액초과 알림: 가맹점의 영업일 매입 중 임계금액(T_AMOUNT)을 초과한 + * 고액 건수와 최고 매입액을 조회해 경보 여부를 판정한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_ALERTAMT.h" + +void ACQ_ALERTAMT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_lim, h_over = 0, h_max = 0; + char h_merch[64], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_lim = getl(b, T_AMOUNT); if (h_lim <= 0) h_lim = 1000000; + gets_(b, T_MERCHANT, h_merch, sizeof(h_merch)); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT count(*) FILTER (WHERE amount > :h_lim), coalesce(max(amount),0) + INTO :h_over, :h_max FROM purchase + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_over); + setl(b, T_AMT1, h_max); + setl(b, T_RC, h_over > 0 ? 1 : 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_ALERTDUP.h b/app/src/ac/svc/ACQ_ALERTDUP.h new file mode 100644 index 0000000..76ee096 --- /dev/null +++ b/app/src/ac/svc/ACQ_ALERTDUP.h @@ -0,0 +1,18 @@ +/* + * ACQ_ALERTDUP.h - ac 서비스 ACQ_ALERTDUP 카피북 (copybook / record header). + * 매입 중복 알림: 가맹점의 영업일 매입 중 동일 금액이 2건 이상 반복된 + */ +#ifndef ACQ_ALERTDUP_H +#define ACQ_ALERTDUP_H + +#include "ac_svc.h" + +typedef struct { + char merchant[64]; + long dup_groups; + long rc; +} acq_alertdup_rec_t; + +void ACQ_ALERTDUP(TPSVCINFO *p); + +#endif /* ACQ_ALERTDUP_H */ diff --git a/app/src/ac/svc/ACQ_ALERTDUP.pgc b/app/src/ac/svc/ACQ_ALERTDUP.pgc new file mode 100644 index 0000000..8730ce3 --- /dev/null +++ b/app/src/ac/svc/ACQ_ALERTDUP.pgc @@ -0,0 +1,28 @@ +/* + * ACQ_ALERTDUP.pgc - ac 모듈 서비스 ACQ_ALERTDUP (one service = one file). + * 매입 중복 알림: 가맹점의 영업일 매입 중 동일 금액이 2건 이상 반복된 + * 금액 그룹 수를 세어 중복 의심 여부를 알린다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_ALERTDUP.h" + +void ACQ_ALERTDUP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_grp = 0; + 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(*) INTO :h_grp FROM ( + SELECT amount FROM purchase WHERE merchant_id = :h_merch + AND biz_date = :h_bizdate GROUP BY amount HAVING count(*) > 1) d; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_grp); + setl(b, T_RC, h_grp > 0 ? 1 : 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_ALERTQRY.h b/app/src/ac/svc/ACQ_ALERTQRY.h new file mode 100644 index 0000000..6606be4 --- /dev/null +++ b/app/src/ac/svc/ACQ_ALERTQRY.h @@ -0,0 +1,18 @@ +/* + * ACQ_ALERTQRY.h - ac 서비스 ACQ_ALERTQRY 카피북 (copybook / record header). + * 매입 알림 유형별 조회: 미매입 원장을 알림 유형(속도/한도/중복) 접두어로 + */ +#ifndef ACQ_ALERTQRY_H +#define ACQ_ALERTQRY_H + +#include "ac_svc.h" + +typedef struct { + long velocity; + long limit; + long dup; +} acq_alertqry_rec_t; + +void ACQ_ALERTQRY(TPSVCINFO *p); + +#endif /* ACQ_ALERTQRY_H */ diff --git a/app/src/ac/svc/ACQ_ALERTQRY.pgc b/app/src/ac/svc/ACQ_ALERTQRY.pgc new file mode 100644 index 0000000..94d9142 --- /dev/null +++ b/app/src/ac/svc/ACQ_ALERTQRY.pgc @@ -0,0 +1,30 @@ +/* + * ACQ_ALERTQRY.pgc - ac 모듈 서비스 ACQ_ALERTQRY (one service = one file). + * 매입 알림 유형별 조회: 미매입 원장을 알림 유형(속도/한도/중복) 접두어로 + * 한 번에 분해 집계해 유형별 미해소 알림 건수를 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_ALERTQRY.h" + +void ACQ_ALERTQRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_vel = 0, h_lim = 0, h_dup = 0; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT count(*) FILTER (WHERE reason LIKE 'VEL%'), + count(*) FILTER (WHERE reason LIKE 'LIMIT%'), + count(*) FILTER (WHERE reason LIKE 'DUP%') + INTO :h_vel, :h_lim, :h_dup FROM ac_unmatch WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_ARG1, h_vel); + setl(b, T_ARG2, h_lim); + setl(b, T_ARG3, h_dup); + setl(b, T_COUNT, h_vel + h_lim + h_dup); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_ALERTVEL.h b/app/src/ac/svc/ACQ_ALERTVEL.h new file mode 100644 index 0000000..e8c329a --- /dev/null +++ b/app/src/ac/svc/ACQ_ALERTVEL.h @@ -0,0 +1,18 @@ +/* + * ACQ_ALERTVEL.h - ac 서비스 ACQ_ALERTVEL 카피북 (copybook / record header). + * 매입 속도 알림: 채널의 최근 5분간 유입 건수를 재어 분당 허용치를 넘으면 + */ +#ifndef ACQ_ALERTVEL_H +#define ACQ_ALERTVEL_H + +#include "ac_svc.h" + +typedef struct { + char channel[16]; + long win5m; + long win30m; +} acq_alertvel_rec_t; + +void ACQ_ALERTVEL(TPSVCINFO *p); + +#endif /* ACQ_ALERTVEL_H */ diff --git a/app/src/ac/svc/ACQ_ALERTVEL.pgc b/app/src/ac/svc/ACQ_ALERTVEL.pgc new file mode 100644 index 0000000..869738c --- /dev/null +++ b/app/src/ac/svc/ACQ_ALERTVEL.pgc @@ -0,0 +1,30 @@ +/* + * ACQ_ALERTVEL.pgc - ac 모듈 서비스 ACQ_ALERTVEL (one service = one file). + * 매입 속도 알림: 채널의 최근 5분간 유입 건수를 재어 분당 허용치를 넘으면 + * 경보한다. 참고로 직전 30분 건수도 함께 조회한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_ALERTVEL.h" + +void ACQ_ALERTVEL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long allow; + EXEC SQL BEGIN DECLARE SECTION; + long h_5m = 0, h_30m = 0; + char h_chan[16]; + EXEC SQL END DECLARE SECTION; + + allow = getl(b, T_ARG1); if (allow <= 0) allow = 50; + gets_(b, T_CHANNEL, h_chan, sizeof(h_chan)); + + EXEC SQL SELECT count(*) FILTER (WHERE created_at >= now() - interval '5 minutes'), + count(*) FILTER (WHERE created_at >= now() - interval '30 minutes') + INTO :h_5m, :h_30m FROM purchase WHERE channel = :h_chan; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_ARG2, h_5m); + setl(b, T_ARG3, h_30m); + setl(b, T_RC, h_5m > allow ? 1 : 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_BRANDQRY.h b/app/src/ac/svc/ACQ_BRANDQRY.h new file mode 100644 index 0000000..1c42fc9 --- /dev/null +++ b/app/src/ac/svc/ACQ_BRANDQRY.h @@ -0,0 +1,18 @@ +/* + * ACQ_BRANDQRY.h - ac 서비스 ACQ_BRANDQRY 카피북 (copybook / record header). + * 매입 브랜드별 조회: 지정 발급사(T_ISSUER)의 영업일 매입 건수와 총액, + */ +#ifndef ACQ_BRANDQRY_H +#define ACQ_BRANDQRY_H + +#include "ac_svc.h" + +typedef struct { + char issuer[32]; + long count; + long gross; +} acq_brandqry_rec_t; + +void ACQ_BRANDQRY(TPSVCINFO *p); + +#endif /* ACQ_BRANDQRY_H */ diff --git a/app/src/ac/svc/ACQ_BRANDQRY.pgc b/app/src/ac/svc/ACQ_BRANDQRY.pgc new file mode 100644 index 0000000..a963d63 --- /dev/null +++ b/app/src/ac/svc/ACQ_BRANDQRY.pgc @@ -0,0 +1,30 @@ +/* + * ACQ_BRANDQRY.pgc - ac 모듈 서비스 ACQ_BRANDQRY (one service = one file). + * 매입 브랜드별 조회: 지정 발급사(T_ISSUER)의 영업일 매입 건수와 총액, + * 평균 수수료를 단건으로 조회해 응답 버퍼에 채운다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_BRANDQRY.h" + +void ACQ_BRANDQRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_n = 0, h_sum = 0, h_fee = 0; + char h_iss[32], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_ISSUER, h_iss, sizeof(h_iss)); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT count(*), coalesce(sum(amount),0), coalesce(sum(fee),0) + INTO :h_n, :h_sum, :h_fee FROM purchase + WHERE issuer = :h_iss AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_n); + setl(b, T_GROSS, h_sum); + setl(b, T_FEE, h_fee); + setl(b, T_RC, h_n > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_BRANDRANK.h b/app/src/ac/svc/ACQ_BRANDRANK.h new file mode 100644 index 0000000..f4e1eac --- /dev/null +++ b/app/src/ac/svc/ACQ_BRANDRANK.h @@ -0,0 +1,18 @@ +/* + * ACQ_BRANDRANK.h - ac 서비스 ACQ_BRANDRANK 카피북 (copybook / record header). + * 매입 브랜드별 순위: 발급사 매출 상위 순으로 커서 조회하며 누적 점유율을 + */ +#ifndef ACQ_BRANDRANK_H +#define ACQ_BRANDRANK_H + +#include "ac_svc.h" + +typedef struct { + char issuer[32]; + long grand; + long half_at; +} acq_brandrank_rec_t; + +void ACQ_BRANDRANK(TPSVCINFO *p); + +#endif /* ACQ_BRANDRANK_H */ diff --git a/app/src/ac/svc/ACQ_BRANDRANK.pgc b/app/src/ac/svc/ACQ_BRANDRANK.pgc new file mode 100644 index 0000000..84f6d16 --- /dev/null +++ b/app/src/ac/svc/ACQ_BRANDRANK.pgc @@ -0,0 +1,42 @@ +/* + * ACQ_BRANDRANK.pgc - ac 모듈 서비스 ACQ_BRANDRANK (one service = one file). + * 매입 브랜드별 순위: 발급사 매출 상위 순으로 커서 조회하며 누적 점유율을 + * 계산해 상위 브랜드가 전체의 절반을 넘는 지점(집중도)을 함께 낸다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_BRANDRANK.h" + +void ACQ_BRANDRANK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rank = 0, run = 0, half_at = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_sum, h_grand = 0; + char h_iss[32], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_grand FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL DECLARE brr_cur CURSOR FOR + SELECT issuer, sum(amount) FROM purchase WHERE biz_date = :h_bizdate + GROUP BY issuer ORDER BY sum(amount) DESC; + EXEC SQL OPEN brr_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH brr_cur INTO :h_iss, :h_sum; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE brr_cur; FAIL(b); } + rank++; run += h_sum; + if (half_at == 0 && h_grand > 0 && run * 2 >= h_grand) half_at = rank; + if (rank <= 6) Badd(b, T_ISSUER, h_iss, 0L); + } + EXEC SQL CLOSE brr_cur; + + setl(b, T_GROSS, h_grand); + setl(b, T_ARG1, rank); + setl(b, T_ARG2, half_at); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_BRANDSUM.h b/app/src/ac/svc/ACQ_BRANDSUM.h new file mode 100644 index 0000000..d93386f --- /dev/null +++ b/app/src/ac/svc/ACQ_BRANDSUM.h @@ -0,0 +1,18 @@ +/* + * ACQ_BRANDSUM.h - ac 서비스 ACQ_BRANDSUM 카피북 (copybook / record header). + * 매입 브랜드별 집계: 발급사(카드 브랜드) 기준으로 매출 합계를 그룹 집계해 + */ +#ifndef ACQ_BRANDSUM_H +#define ACQ_BRANDSUM_H + +#include "ac_svc.h" + +typedef struct { + char issuer[32]; + long amount; + long kinds; +} acq_brandsum_rec_t; + +void ACQ_BRANDSUM(TPSVCINFO *p); + +#endif /* ACQ_BRANDSUM_H */ diff --git a/app/src/ac/svc/ACQ_BRANDSUM.pgc b/app/src/ac/svc/ACQ_BRANDSUM.pgc new file mode 100644 index 0000000..058adf6 --- /dev/null +++ b/app/src/ac/svc/ACQ_BRANDSUM.pgc @@ -0,0 +1,37 @@ +/* + * ACQ_BRANDSUM.pgc - ac 모듈 서비스 ACQ_BRANDSUM (one service = one file). + * 매입 브랜드별 집계: 발급사(카드 브랜드) 기준으로 매출 합계를 그룹 집계해 + * 브랜드명·매출액 쌍을 occurrence 로 내리고 최다 브랜드 건수를 함께 계산한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_BRANDSUM.h" + +void ACQ_BRANDSUM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long grand = 0, kinds = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_sum, h_n; + char h_iss[32], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL DECLARE brs_cur CURSOR FOR + SELECT issuer, sum(amount), count(*) FROM purchase + WHERE biz_date = :h_bizdate GROUP BY issuer ORDER BY sum(amount) DESC; + EXEC SQL OPEN brs_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH brs_cur INTO :h_iss, :h_sum, :h_n; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE brs_cur; FAIL(b); } + if (kinds < 8) { Badd(b, T_ISSUER, h_iss, 0L); Badd(b, T_AMT1, (char *)&h_sum, 0L); } + grand += h_sum; kinds++; + } + EXEC SQL CLOSE brs_cur; + + setl(b, T_GROSS, grand); + setl(b, T_ARG1, kinds); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_BULKCHK.h b/app/src/ac/svc/ACQ_BULKCHK.h new file mode 100644 index 0000000..08cc3fe --- /dev/null +++ b/app/src/ac/svc/ACQ_BULKCHK.h @@ -0,0 +1,18 @@ +/* + * ACQ_BULKCHK.h - ac 서비스 ACQ_BULKCHK 카피북 (copybook / record header). + * 대량매입 배치검증: 영업일 접수(A) 매입을 커서로 순회하며 금액 양수 및 + */ +#ifndef ACQ_BULKCHK_H +#define ACQ_BULKCHK_H + +#include "ac_svc.h" + +typedef struct { + long ok_count; + long bad_count; + long rc; +} acq_bulkchk_rec_t; + +void ACQ_BULKCHK(TPSVCINFO *p); + +#endif /* ACQ_BULKCHK_H */ diff --git a/app/src/ac/svc/ACQ_BULKCHK.pgc b/app/src/ac/svc/ACQ_BULKCHK.pgc new file mode 100644 index 0000000..19cb829 --- /dev/null +++ b/app/src/ac/svc/ACQ_BULKCHK.pgc @@ -0,0 +1,38 @@ +/* + * ACQ_BULKCHK.pgc - ac 모듈 서비스 ACQ_BULKCHK (one service = one file). + * 대량매입 배치검증: 영업일 접수(A) 매입을 커서로 순회하며 금액 양수 및 + * 수수료 정합(net=amount-fee)을 점검해 정상/오류 건수를 분리 집계한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_BULKCHK.h" + +void ACQ_BULKCHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long ok = 0, bad = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_amt, h_fee, h_net; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL DECLARE blk_cur CURSOR FOR + SELECT purchase_id, amount, fee, net FROM purchase + WHERE biz_date = :h_bizdate AND status = 'A'; + EXEC SQL OPEN blk_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH blk_cur INTO :h_pid, :h_amt, :h_fee, :h_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE blk_cur; FAIL(b); } + if (h_amt > 0 && h_net == h_amt - h_fee) ok++; + else { bad++; if (bad <= 8) Badd(b, T_PURCHASE_ID, (char *)&h_pid, 0L); } + } + EXEC SQL CLOSE blk_cur; + + setl(b, T_ARG1, ok); + setl(b, T_ARG2, bad); + setl(b, T_RC, bad > 0 ? 1 : 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_BULKRPT.h b/app/src/ac/svc/ACQ_BULKRPT.h new file mode 100644 index 0000000..f50d0f8 --- /dev/null +++ b/app/src/ac/svc/ACQ_BULKRPT.h @@ -0,0 +1,18 @@ +/* + * ACQ_BULKRPT.h - ac 서비스 ACQ_BULKRPT 카피북 (copybook / record header). + * 대량매입 리포트: 영업일 매입을 상태(접수/대사/정산/취소)별로 한 번에 분해 + */ +#ifndef ACQ_BULKRPT_H +#define ACQ_BULKRPT_H + +#include "ac_svc.h" + +typedef struct { + long a; + long m; + long settled; +} acq_bulkrpt_rec_t; + +void ACQ_BULKRPT(TPSVCINFO *p); + +#endif /* ACQ_BULKRPT_H */ diff --git a/app/src/ac/svc/ACQ_BULKRPT.pgc b/app/src/ac/svc/ACQ_BULKRPT.pgc new file mode 100644 index 0000000..1ea55bb --- /dev/null +++ b/app/src/ac/svc/ACQ_BULKRPT.pgc @@ -0,0 +1,32 @@ +/* + * ACQ_BULKRPT.pgc - ac 모듈 서비스 ACQ_BULKRPT (one service = one file). + * 대량매입 리포트: 영업일 매입을 상태(접수/대사/정산/취소)별로 한 번에 분해 + * 집계해 상태별 건수와 총 매출·순정산 금액을 요약 리포트로 낸다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_BULKRPT.h" + +void ACQ_BULKRPT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_a = 0, h_m = 0, h_s = 0, h_c = 0, h_g = 0, h_net = 0; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT count(*) FILTER (WHERE status = 'A'), count(*) FILTER (WHERE status = 'M'), + count(*) FILTER (WHERE status = 'S'), count(*) FILTER (WHERE status = 'C'), + coalesce(sum(amount),0), coalesce(sum(net) FILTER (WHERE status = 'S'),0) + INTO :h_a, :h_m, :h_s, :h_c, :h_g, :h_net FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_ARG1, h_a); + setl(b, T_ARG2, h_m); + setl(b, T_ARG3, h_s); + setl(b, T_ARG4, h_c); + setl(b, T_GROSS, h_g); + setl(b, T_NET, h_net); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CB_ARB.h b/app/src/ac/svc/ACQ_CB_ARB.h new file mode 100644 index 0000000..23210b5 --- /dev/null +++ b/app/src/ac/svc/ACQ_CB_ARB.h @@ -0,0 +1,18 @@ +/* + * ACQ_CB_ARB.h - ac 서비스 ACQ_CB_ARB 카피북 (copybook / record header). + * 지불거절 중재: 라운드2까지 미해결 건을 카드사 중재로 넘긴다. 해당 CB + */ +#ifndef ACQ_CB_ARB_H +#define ACQ_CB_ARB_H + +#include "ac_svc.h" + +typedef struct { + long cancel_id; + long purchase_id; + long rc; +} acq_cb_arb_rec_t; + +void ACQ_CB_ARB(TPSVCINFO *p); + +#endif /* ACQ_CB_ARB_H */ diff --git a/app/src/ac/svc/ACQ_CB_ARB.pgc b/app/src/ac/svc/ACQ_CB_ARB.pgc new file mode 100644 index 0000000..a69d1e6 --- /dev/null +++ b/app/src/ac/svc/ACQ_CB_ARB.pgc @@ -0,0 +1,36 @@ +/* + * ACQ_CB_ARB.pgc - ac 모듈 서비스 ACQ_CB_ARB (one service = one file). + * 지불거절 중재: 라운드2까지 미해결 건을 카드사 중재로 넘긴다. 해당 CB + * 행 사유를 ARB 로 갱신하고 원매입을 보류(H)로 유지한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_CB_ARB.h" + +void ACQ_CB_ARB(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char note[80]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cid, h_pid = 0; + char h_reason[100]; + EXEC SQL END DECLARE SECTION; + + h_cid = getl(b, T_CANCEL_ID); + gets_(b, T_REASON, note, sizeof(note)); + snprintf(h_reason, sizeof(h_reason), "ARB:%s", note[0] ? note : "PENDING"); + + EXEC SQL SELECT purchase_id INTO :h_pid FROM ac_cancel + WHERE cancel_id = :h_cid AND cancel_type IN ('CB', 'CB2'); + if (sqlca.sqlcode == 100) { setl(b, T_RC, 4); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL UPDATE ac_cancel SET reason = :h_reason WHERE cancel_id = :h_cid; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL UPDATE purchase SET status = 'H', updated_at = now() + WHERE purchase_id = :h_pid AND status <> 'C'; + if (sqlca.sqlcode < 0) { userlog("ACQ_CB_ARB FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + + setl(b, T_PURCHASE_ID, h_pid); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CB_FINAL.h b/app/src/ac/svc/ACQ_CB_FINAL.h new file mode 100644 index 0000000..e3104dd --- /dev/null +++ b/app/src/ac/svc/ACQ_CB_FINAL.h @@ -0,0 +1,18 @@ +/* + * ACQ_CB_FINAL.h - ac 서비스 ACQ_CB_FINAL 카피북 (copybook / record header). + * 지불거절 최종확정: 중재 결과로 매입사 패소가 확정되면 원매입을 취소(C)로 + */ +#ifndef ACQ_CB_FINAL_H +#define ACQ_CB_FINAL_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long affected; + long rc; +} acq_cb_final_rec_t; + +void ACQ_CB_FINAL(TPSVCINFO *p); + +#endif /* ACQ_CB_FINAL_H */ diff --git a/app/src/ac/svc/ACQ_CB_FINAL.pgc b/app/src/ac/svc/ACQ_CB_FINAL.pgc new file mode 100644 index 0000000..1eb80ca --- /dev/null +++ b/app/src/ac/svc/ACQ_CB_FINAL.pgc @@ -0,0 +1,26 @@ +/* + * ACQ_CB_FINAL.pgc - ac 모듈 서비스 ACQ_CB_FINAL (one service = one file). + * 지불거절 최종확정: 중재 결과로 매입사 패소가 확정되면 원매입을 취소(C)로 + * 확정 전이하고, 확정 여부(반영 건수)를 결과코드로 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_CB_FINAL.h" + +void ACQ_CB_FINAL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_aff; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + + EXEC SQL UPDATE purchase SET status = 'C', updated_at = now() + WHERE purchase_id = :h_pid AND status = 'H'; + if (sqlca.sqlcode < 0) { userlog("ACQ_CB_FINAL FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + h_aff = sqlca.sqlerrd[2]; + if (h_aff > 0) Bchg(b, T_STATUS, 0, "C", 0L); + setl(b, T_COUNT, h_aff); + setl(b, T_RC, h_aff > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CB_REVERSE.h b/app/src/ac/svc/ACQ_CB_REVERSE.h new file mode 100644 index 0000000..e4ca9f0 --- /dev/null +++ b/app/src/ac/svc/ACQ_CB_REVERSE.h @@ -0,0 +1,18 @@ +/* + * ACQ_CB_REVERSE.h - ac 서비스 ACQ_CB_REVERSE 카피북 (copybook / record header). + * 지불거절 역전: 방어 승소로 CB 를 무효화한다. 최근 CB/CB2 행을 조회해 + */ +#ifndef ACQ_CB_REVERSE_H +#define ACQ_CB_REVERSE_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long cancel_id; + long restored; +} acq_cb_reverse_rec_t; + +void ACQ_CB_REVERSE(TPSVCINFO *p); + +#endif /* ACQ_CB_REVERSE_H */ diff --git a/app/src/ac/svc/ACQ_CB_REVERSE.pgc b/app/src/ac/svc/ACQ_CB_REVERSE.pgc new file mode 100644 index 0000000..8ed892c --- /dev/null +++ b/app/src/ac/svc/ACQ_CB_REVERSE.pgc @@ -0,0 +1,33 @@ +/* + * ACQ_CB_REVERSE.pgc - ac 모듈 서비스 ACQ_CB_REVERSE (one service = one file). + * 지불거절 역전: 방어 승소로 CB 를 무효화한다. 최근 CB/CB2 행을 조회해 + * 취소 사유를 REVERSED 로 바꾸고 보류 원매입을 대사완료(M)로 복원한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_CB_REVERSE.h" + +void ACQ_CB_REVERSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long restored; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_cid = 0, h_aff; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + + EXEC SQL SELECT max(cancel_id) INTO :h_cid FROM ac_cancel + WHERE purchase_id = :h_pid AND cancel_type IN ('CB', 'CB2'); + if (sqlca.sqlcode < 0 || h_cid == 0) { setl(b, T_RC, 4); FAIL(b); } + + EXEC SQL UPDATE ac_cancel SET reason = 'REVERSED' WHERE cancel_id = :h_cid; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL UPDATE purchase SET status = 'M', updated_at = now() + WHERE purchase_id = :h_pid AND status = 'H'; + if (sqlca.sqlcode < 0) { userlog("ACQ_CB_REVERSE FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + h_aff = sqlca.sqlerrd[2]; + restored = h_aff; + setl(b, T_CANCEL_ID, h_cid); + setl(b, T_RC, restored > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CB_RND2.h b/app/src/ac/svc/ACQ_CB_RND2.h new file mode 100644 index 0000000..0cf90d7 --- /dev/null +++ b/app/src/ac/svc/ACQ_CB_RND2.h @@ -0,0 +1,18 @@ +/* + * ACQ_CB_RND2.h - ac 서비스 ACQ_CB_RND2 카피북 (copybook / record header). + * 지불거절 2차(라운드2): 1차 방어 이후 재청구를 접수한다. 원매입에 이미 + */ +#ifndef ACQ_CB_RND2_H +#define ACQ_CB_RND2_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long cancel_id; + long prev_cb; +} acq_cb_rnd2_rec_t; + +void ACQ_CB_RND2(TPSVCINFO *p); + +#endif /* ACQ_CB_RND2_H */ diff --git a/app/src/ac/svc/ACQ_CB_RND2.pgc b/app/src/ac/svc/ACQ_CB_RND2.pgc new file mode 100644 index 0000000..4a965dd --- /dev/null +++ b/app/src/ac/svc/ACQ_CB_RND2.pgc @@ -0,0 +1,38 @@ +/* + * ACQ_CB_RND2.pgc - ac 모듈 서비스 ACQ_CB_RND2 (one service = one file). + * 지불거절 2차(라운드2): 1차 방어 이후 재청구를 접수한다. 원매입에 이미 + * 1차 CB 이력이 있어야 하며, ac_cancel 에 CB2 유형 행을 추가한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_CB_RND2.h" + +void ACQ_CB_RND2(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char why[80]; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_pid, h_amt, h_prev = 0; + char h_reason[100], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + h_amt = getl(b, T_AMOUNT); + gets_(b, T_REASON, why, sizeof(why)); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT count(*) INTO :h_prev FROM ac_cancel + WHERE purchase_id = :h_pid AND cancel_type = 'CB'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_prev == 0) { setl(b, T_RC, 4); FAIL(b); } + snprintf(h_reason, sizeof(h_reason), "CB2:%s", why[0] ? why : "REPRESENT"); + + EXEC SQL SELECT nextval('ac_cancel_seq') INTO :h_id; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO ac_cancel (cancel_id, purchase_id, cancel_type, amount, reason, biz_date) + VALUES (:h_id, :h_pid, 'CB2', :h_amt, :h_reason, :h_bizdate); + if (sqlca.sqlcode < 0) { userlog("ACQ_CB_RND2 FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + + setl(b, T_CANCEL_ID, h_id); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CCYQRY.h b/app/src/ac/svc/ACQ_CCYQRY.h new file mode 100644 index 0000000..20930e8 --- /dev/null +++ b/app/src/ac/svc/ACQ_CCYQRY.h @@ -0,0 +1,18 @@ +/* + * ACQ_CCYQRY.h - ac 서비스 ACQ_CCYQRY 카피북 (copybook / record header). + * 매입 통화별 조회: 지정 통화(T_CCY)의 원통화 합계와 원화 환산 합계를 + */ +#ifndef ACQ_CCYQRY_H +#define ACQ_CCYQRY_H + +#include "ac_svc.h" + +typedef struct { + char ccy[8]; + long fx_amount; + long krw_amount; +} acq_ccyqry_rec_t; + +void ACQ_CCYQRY(TPSVCINFO *p); + +#endif /* ACQ_CCYQRY_H */ diff --git a/app/src/ac/svc/ACQ_CCYQRY.pgc b/app/src/ac/svc/ACQ_CCYQRY.pgc new file mode 100644 index 0000000..913ab58 --- /dev/null +++ b/app/src/ac/svc/ACQ_CCYQRY.pgc @@ -0,0 +1,29 @@ +/* + * ACQ_CCYQRY.pgc - ac 모듈 서비스 ACQ_CCYQRY (one service = one file). + * 매입 통화별 조회: 지정 통화(T_CCY)의 원통화 합계와 원화 환산 합계를 + * 해외매입 원장에서 조회해 환산차(원화-원통화)를 계산한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_CCYQRY.h" + +void ACQ_CCYQRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long diff; + EXEC SQL BEGIN DECLARE SECTION; + long h_fx = 0, h_krw = 0; + char h_ccy[8]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CCY, h_ccy, sizeof(h_ccy)); + + EXEC SQL SELECT coalesce(sum(fx_amount),0), coalesce(sum(krw_amount),0) + INTO :h_fx, :h_krw FROM ac_fx WHERE ccy = :h_ccy; + if (sqlca.sqlcode < 0) FAIL(b); + + diff = h_krw - h_fx; + setl(b, T_FX_AMT, h_fx); + setl(b, T_GROSS, h_krw); + setl(b, T_DELTA, diff); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CCYRANK.h b/app/src/ac/svc/ACQ_CCYRANK.h new file mode 100644 index 0000000..68f2724 --- /dev/null +++ b/app/src/ac/svc/ACQ_CCYRANK.h @@ -0,0 +1,18 @@ +/* + * ACQ_CCYRANK.h - ac 서비스 ACQ_CCYRANK 카피북 (copybook / record header). + * 매입 통화별 순위: 해외매입 통화 그룹을 원화 합계 내림차순으로 커서 조회해 + */ +#ifndef ACQ_CCYRANK_H +#define ACQ_CCYRANK_H + +#include "ac_svc.h" + +typedef struct { + char top_ccy[8]; + long ranks; + long krw; +} acq_ccyrank_rec_t; + +void ACQ_CCYRANK(TPSVCINFO *p); + +#endif /* ACQ_CCYRANK_H */ diff --git a/app/src/ac/svc/ACQ_CCYRANK.pgc b/app/src/ac/svc/ACQ_CCYRANK.pgc new file mode 100644 index 0000000..5693d32 --- /dev/null +++ b/app/src/ac/svc/ACQ_CCYRANK.pgc @@ -0,0 +1,40 @@ +/* + * ACQ_CCYRANK.pgc - ac 모듈 서비스 ACQ_CCYRANK (one service = one file). + * 매입 통화별 순위: 해외매입 통화 그룹을 원화 합계 내림차순으로 커서 조회해 + * 상위 통화코드·원화액 쌍을 occurrence 로 내리고 1위 통화를 표시한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_CCYRANK.h" + +void ACQ_CCYRANK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rank = 0; + char top[8]; + EXEC SQL BEGIN DECLARE SECTION; + long h_krw; + char h_ccy[8], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + top[0] = 0; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL DECLARE ccr_cur CURSOR FOR + SELECT ccy, sum(krw_amount) FROM ac_fx WHERE biz_date = :h_bizdate + GROUP BY ccy ORDER BY sum(krw_amount) DESC; + EXEC SQL OPEN ccr_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH ccr_cur INTO :h_ccy, :h_krw; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ccr_cur; FAIL(b); } + if (rank == 0) { strncpy(top, h_ccy, sizeof(top)-1); top[sizeof(top)-1] = 0; } + if (rank < 5) { Badd(b, T_CCY, h_ccy, 0L); Badd(b, T_AMT1, (char *)&h_krw, 0L); } + rank++; + } + EXEC SQL CLOSE ccr_cur; + + if (top[0]) Bchg(b, T_STR1, 0, top, 0L); + setl(b, T_ARG1, rank); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CHANDETAIL.h b/app/src/ac/svc/ACQ_CHANDETAIL.h new file mode 100644 index 0000000..379a903 --- /dev/null +++ b/app/src/ac/svc/ACQ_CHANDETAIL.h @@ -0,0 +1,18 @@ +/* + * ACQ_CHANDETAIL.h - ac 서비스 ACQ_CHANDETAIL 카피북 (copybook / record header). + * 매입 채널별 상세 조회: 영업일 채널 그룹의 건수/총액/평균을 커서로 훑어 + */ +#ifndef ACQ_CHANDETAIL_H +#define ACQ_CHANDETAIL_H + +#include "ac_svc.h" + +typedef struct { + char channel[16]; + long avg_amount; + long kinds; +} acq_chandetail_rec_t; + +void ACQ_CHANDETAIL(TPSVCINFO *p); + +#endif /* ACQ_CHANDETAIL_H */ diff --git a/app/src/ac/svc/ACQ_CHANDETAIL.pgc b/app/src/ac/svc/ACQ_CHANDETAIL.pgc new file mode 100644 index 0000000..b2a7a3d --- /dev/null +++ b/app/src/ac/svc/ACQ_CHANDETAIL.pgc @@ -0,0 +1,39 @@ +/* + * ACQ_CHANDETAIL.pgc - ac 모듈 서비스 ACQ_CHANDETAIL (one service = one file). + * 매입 채널별 상세 조회: 영업일 채널 그룹의 건수/총액/평균을 커서로 훑어 + * 채널명·평균단가 쌍을 occurrence 로 내리고 전체 평균을 함께 반환한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_CHANDETAIL.h" + +void ACQ_CHANDETAIL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long tot = 0, cnt = 0, kinds = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_n, h_sum, h_avg; + char h_chan[16], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL DECLARE chd_cur CURSOR FOR + SELECT channel, count(*), sum(amount), avg(amount)::bigint FROM purchase + WHERE biz_date = :h_bizdate AND status <> 'C' GROUP BY channel ORDER BY channel; + EXEC SQL OPEN chd_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH chd_cur INTO :h_chan, :h_n, :h_sum, :h_avg; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE chd_cur; FAIL(b); } + if (kinds < 8) { Badd(b, T_STR1, h_chan, 0L); Badd(b, T_AMT1, (char *)&h_avg, 0L); } + tot += h_sum; cnt += h_n; kinds++; + } + EXEC SQL CLOSE chd_cur; + + setl(b, T_GROSS, tot); + setl(b, T_COUNT, cnt); + setl(b, T_ARG1, kinds); + setl(b, T_AMT2, cnt > 0 ? tot / cnt : 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CHANRANK.h b/app/src/ac/svc/ACQ_CHANRANK.h new file mode 100644 index 0000000..03b9621 --- /dev/null +++ b/app/src/ac/svc/ACQ_CHANRANK.h @@ -0,0 +1,18 @@ +/* + * ACQ_CHANRANK.h - ac 서비스 ACQ_CHANRANK 카피북 (copybook / record header). + * 매입 채널별 순위: 영업일 채널을 건수 내림차순으로 정렬해 커서 조회하고 + */ +#ifndef ACQ_CHANRANK_H +#define ACQ_CHANRANK_H + +#include "ac_svc.h" + +typedef struct { + char busiest[16]; + long count; + long ranks; +} acq_chanrank_rec_t; + +void ACQ_CHANRANK(TPSVCINFO *p); + +#endif /* ACQ_CHANRANK_H */ diff --git a/app/src/ac/svc/ACQ_CHANRANK.pgc b/app/src/ac/svc/ACQ_CHANRANK.pgc new file mode 100644 index 0000000..a4259d2 --- /dev/null +++ b/app/src/ac/svc/ACQ_CHANRANK.pgc @@ -0,0 +1,41 @@ +/* + * ACQ_CHANRANK.pgc - ac 모듈 서비스 ACQ_CHANRANK (one service = one file). + * 매입 채널별 순위: 영업일 채널을 건수 내림차순으로 정렬해 커서 조회하고 + * 채널명·건수 쌍을 occurrence 로 내려 최다 유입 채널을 판별한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_CHANRANK.h" + +void ACQ_CHANRANK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rank = 0, busiest = 0; + char topchan[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_n; + char h_chan[16], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + topchan[0] = 0; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL DECLARE chr_cur CURSOR FOR + SELECT channel, count(*) FROM purchase WHERE biz_date = :h_bizdate + GROUP BY channel ORDER BY count(*) DESC, channel; + EXEC SQL OPEN chr_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH chr_cur INTO :h_chan, :h_n; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE chr_cur; FAIL(b); } + if (rank == 0) { strncpy(topchan, h_chan, sizeof(topchan)-1); topchan[sizeof(topchan)-1] = 0; busiest = h_n; } + if (rank < 6) { Badd(b, T_CHANNEL, h_chan, 0L); Badd(b, T_ARG3, (char *)&h_n, 0L); } + rank++; + } + EXEC SQL CLOSE chr_cur; + + if (topchan[0]) Bchg(b, T_STR2, 0, topchan, 0L); + setl(b, T_COUNT, busiest); + setl(b, T_ARG1, rank); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CORRAPPR.h b/app/src/ac/svc/ACQ_CORRAPPR.h new file mode 100644 index 0000000..af7c07f --- /dev/null +++ b/app/src/ac/svc/ACQ_CORRAPPR.h @@ -0,0 +1,18 @@ +/* + * ACQ_CORRAPPR.h - ac 서비스 ACQ_CORRAPPR 카피북 (copybook / record header). + * 매입 정정 승인: 접수된 조정(ac_fee_adj) 행의 사유 앞에 OK 승인 표식을 + */ +#ifndef ACQ_CORRAPPR_H +#define ACQ_CORRAPPR_H + +#include "ac_svc.h" + +typedef struct { + long adj_id; + long affected; + long rc; +} acq_corrappr_rec_t; + +void ACQ_CORRAPPR(TPSVCINFO *p); + +#endif /* ACQ_CORRAPPR_H */ diff --git a/app/src/ac/svc/ACQ_CORRAPPR.pgc b/app/src/ac/svc/ACQ_CORRAPPR.pgc new file mode 100644 index 0000000..e469424 --- /dev/null +++ b/app/src/ac/svc/ACQ_CORRAPPR.pgc @@ -0,0 +1,30 @@ +/* + * ACQ_CORRAPPR.pgc - ac 모듈 서비스 ACQ_CORRAPPR (one service = one file). + * 매입 정정 승인: 접수된 조정(ac_fee_adj) 행의 사유 앞에 OK 승인 표식을 + * 덧대어 확정한다. 이미 승인된 건은 중복 승인하지 않는다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_CORRAPPR.h" + +void ACQ_CORRAPPR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_aff; + char h_reason[128]; + EXEC SQL END DECLARE SECTION; + + h_id = getl(b, T_ADJ); + + EXEC SQL SELECT reason INTO :h_reason FROM ac_fee_adj WHERE adj_id = :h_id; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 5); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strncmp(h_reason, "OK:", 3) == 0) { setl(b, T_RC, 2); FAIL(b); } + + EXEC SQL UPDATE ac_fee_adj SET reason = 'OK:' || reason WHERE adj_id = :h_id; + if (sqlca.sqlcode < 0) { userlog("ACQ_CORRAPPR FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + h_aff = sqlca.sqlerrd[2]; + setl(b, T_ADJ, h_id); + setl(b, T_RC, h_aff > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CORRRSN.h b/app/src/ac/svc/ACQ_CORRRSN.h new file mode 100644 index 0000000..5f82e01 --- /dev/null +++ b/app/src/ac/svc/ACQ_CORRRSN.h @@ -0,0 +1,18 @@ +/* + * ACQ_CORRRSN.h - ac 서비스 ACQ_CORRRSN 카피북 (copybook / record header). + * 매입 정정 사유별 처리: 수수료 조정 원장(ac_fee_adj)에 사유 코드가 붙은 + */ +#ifndef ACQ_CORRRSN_H +#define ACQ_CORRRSN_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long adj_id; + long delta; +} acq_corrrsn_rec_t; + +void ACQ_CORRRSN(TPSVCINFO *p); + +#endif /* ACQ_CORRRSN_H */ diff --git a/app/src/ac/svc/ACQ_CORRRSN.pgc b/app/src/ac/svc/ACQ_CORRRSN.pgc new file mode 100644 index 0000000..993d151 --- /dev/null +++ b/app/src/ac/svc/ACQ_CORRRSN.pgc @@ -0,0 +1,34 @@ +/* + * ACQ_CORRRSN.pgc - ac 모듈 서비스 ACQ_CORRRSN (one service = one file). + * 매입 정정 사유별 처리: 수수료 조정 원장(ac_fee_adj)에 사유 코드가 붙은 + * 증감 행을 남긴다. 증감액(T_DELTA)과 사유(T_REASON)를 그대로 기록한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_CORRRSN.h" + +void ACQ_CORRRSN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char why[80]; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_pid, h_delta; + char h_reason[100], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + h_delta = getl(b, T_DELTA); + gets_(b, T_REASON, why, sizeof(why)); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_delta == 0) { setl(b, T_RC, 3); FAIL(b); } + snprintf(h_reason, sizeof(h_reason), "CORR:%s", why[0] ? why : "MISC"); + + EXEC SQL SELECT nextval('ac_feeadj_seq') INTO :h_id; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO ac_fee_adj (adj_id, purchase_id, delta, reason, biz_date) + VALUES (:h_id, :h_pid, :h_delta, :h_reason, :h_bizdate); + if (sqlca.sqlcode < 0) { userlog("ACQ_CORRRSN FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + + setl(b, T_ADJ, h_id); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CORRSTAT.h b/app/src/ac/svc/ACQ_CORRSTAT.h new file mode 100644 index 0000000..b58884f --- /dev/null +++ b/app/src/ac/svc/ACQ_CORRSTAT.h @@ -0,0 +1,18 @@ +/* + * ACQ_CORRSTAT.h - ac 서비스 ACQ_CORRSTAT 카피북 (copybook / record header). + * 매입 정정 사유별 통계: 조정 원장을 사유 접두어별로 그룹 집계해 사유코드· + */ +#ifndef ACQ_CORRSTAT_H +#define ACQ_CORRSTAT_H + +#include "ac_svc.h" + +typedef struct { + char reason[64]; + long net; + long kinds; +} acq_corrstat_rec_t; + +void ACQ_CORRSTAT(TPSVCINFO *p); + +#endif /* ACQ_CORRSTAT_H */ diff --git a/app/src/ac/svc/ACQ_CORRSTAT.pgc b/app/src/ac/svc/ACQ_CORRSTAT.pgc new file mode 100644 index 0000000..d00c28f --- /dev/null +++ b/app/src/ac/svc/ACQ_CORRSTAT.pgc @@ -0,0 +1,37 @@ +/* + * ACQ_CORRSTAT.pgc - ac 모듈 서비스 ACQ_CORRSTAT (one service = one file). + * 매입 정정 사유별 통계: 조정 원장을 사유 접두어별로 그룹 집계해 사유코드· + * 증감합계 쌍을 occurrence 로 내리고 순증감 총액을 계산한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_CORRSTAT.h" + +void ACQ_CORRSTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long net = 0, kinds = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_sum; + char h_reason[64], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL DECLARE crs_cur CURSOR FOR + SELECT split_part(reason, ':', 1), sum(delta) FROM ac_fee_adj + WHERE biz_date = :h_bizdate GROUP BY split_part(reason, ':', 1) ORDER BY 1; + EXEC SQL OPEN crs_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH crs_cur INTO :h_reason, :h_sum; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE crs_cur; FAIL(b); } + if (kinds < 8) { Badd(b, T_REASON, h_reason, 0L); Badd(b, T_DELTA, (char *)&h_sum, 0L); } + net += h_sum; kinds++; + } + EXEC SQL CLOSE crs_cur; + + setl(b, T_GROSS, net); + setl(b, T_ARG1, kinds); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CTRYQRY.h b/app/src/ac/svc/ACQ_CTRYQRY.h new file mode 100644 index 0000000..b6eb47f --- /dev/null +++ b/app/src/ac/svc/ACQ_CTRYQRY.h @@ -0,0 +1,18 @@ +/* + * ACQ_CTRYQRY.h - ac 서비스 ACQ_CTRYQRY 카피북 (copybook / record header). + * 매입 국가별 조회: 통화권역 대표 통화(T_CCY)로 해외매입(ac_fx)을 필터해 + */ +#ifndef ACQ_CTRYQRY_H +#define ACQ_CTRYQRY_H + +#include "ac_svc.h" + +typedef struct { + char ccy[8]; + long krw; + long rate_bps; +} acq_ctryqry_rec_t; + +void ACQ_CTRYQRY(TPSVCINFO *p); + +#endif /* ACQ_CTRYQRY_H */ diff --git a/app/src/ac/svc/ACQ_CTRYQRY.pgc b/app/src/ac/svc/ACQ_CTRYQRY.pgc new file mode 100644 index 0000000..c7607c3 --- /dev/null +++ b/app/src/ac/svc/ACQ_CTRYQRY.pgc @@ -0,0 +1,29 @@ +/* + * ACQ_CTRYQRY.pgc - ac 모듈 서비스 ACQ_CTRYQRY (one service = one file). + * 매입 국가별 조회: 통화권역 대표 통화(T_CCY)로 해외매입(ac_fx)을 필터해 + * 권역 원화 합계와 건수를 조회하고 평균 환율(bps)을 함께 낸다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_CTRYQRY.h" + +void ACQ_CTRYQRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_n = 0, h_krw = 0, h_rate = 0; + char h_ccy[8], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CCY, h_ccy, sizeof(h_ccy)); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT count(*), coalesce(sum(krw_amount),0), coalesce(avg(rate_bps),0)::bigint + INTO :h_n, :h_krw, :h_rate FROM ac_fx + WHERE ccy = :h_ccy AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_n); + setl(b, T_GROSS, h_krw); + setl(b, T_ARG1, h_rate); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CTRYRANK.h b/app/src/ac/svc/ACQ_CTRYRANK.h new file mode 100644 index 0000000..6d212f4 --- /dev/null +++ b/app/src/ac/svc/ACQ_CTRYRANK.h @@ -0,0 +1,18 @@ +/* + * ACQ_CTRYRANK.h - ac 서비스 ACQ_CTRYRANK 카피북 (copybook / record header). + * 매입 국가별 순위: 통화코드를 권역(미주/일본/유럽/기타)으로 분류하는 C + */ +#ifndef ACQ_CTRYRANK_H +#define ACQ_CTRYRANK_H + +#include "ac_svc.h" + +typedef struct { + long buckets[4]; + long best; + long rc; +} acq_ctryrank_rec_t; + +void ACQ_CTRYRANK(TPSVCINFO *p); + +#endif /* ACQ_CTRYRANK_H */ diff --git a/app/src/ac/svc/ACQ_CTRYRANK.pgc b/app/src/ac/svc/ACQ_CTRYRANK.pgc new file mode 100644 index 0000000..09bae5f --- /dev/null +++ b/app/src/ac/svc/ACQ_CTRYRANK.pgc @@ -0,0 +1,49 @@ +/* + * ACQ_CTRYRANK.pgc - ac 모듈 서비스 ACQ_CTRYRANK (one service = one file). + * 매입 국가별 순위: 통화코드를 권역(미주/일본/유럽/기타)으로 분류하는 C + * 분류기로 원화 합계를 권역 버킷에 모아 최대 권역 인덱스를 판정한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_CTRYRANK.h" + +/* 통화 → 권역 인덱스 (0=미주 1=일본 2=유럽 3=기타) */ +static int ac_region(const char *c) +{ + if (strcmp(c, "USD") == 0 || strcmp(c, "CAD") == 0) return 0; + if (strcmp(c, "JPY") == 0) return 1; + if (strcmp(c, "EUR") == 0 || strcmp(c, "GBP") == 0) return 2; + return 3; +} + +void ACQ_CTRYRANK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long bucket[4] = {0, 0, 0, 0}; + int i, best = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_krw; + char h_ccy[8], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL DECLARE ctr_rk CURSOR FOR + SELECT ccy, sum(krw_amount) FROM ac_fx WHERE biz_date = :h_bizdate GROUP BY ccy; + EXEC SQL OPEN ctr_rk; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH ctr_rk INTO :h_ccy, :h_krw; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ctr_rk; FAIL(b); } + bucket[ac_region(h_ccy)] += h_krw; + } + EXEC SQL CLOSE ctr_rk; + + for (i = 1; i < 4; i++) if (bucket[i] > bucket[best]) best = i; + setl(b, T_ARG1, bucket[0]); + setl(b, T_ARG2, bucket[1]); + setl(b, T_ARG3, bucket[2]); + setl(b, T_ARG4, bucket[3]); + setl(b, T_RC, best); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_DOWSTAT.h b/app/src/ac/svc/ACQ_DOWSTAT.h new file mode 100644 index 0000000..591f65e --- /dev/null +++ b/app/src/ac/svc/ACQ_DOWSTAT.h @@ -0,0 +1,18 @@ +/* + * ACQ_DOWSTAT.h - ac 서비스 ACQ_DOWSTAT 카피북 (copybook / record header). + * 매입 요일별 통계: 지정 기간(T_STR1~T_STR2)의 매입을 요일(0~6)별로 그룹 + */ +#ifndef ACQ_DOWSTAT_H +#define ACQ_DOWSTAT_H + +#include "ac_svc.h" + +typedef struct { + long total; + long weekend; + long rc; +} acq_dowstat_rec_t; + +void ACQ_DOWSTAT(TPSVCINFO *p); + +#endif /* ACQ_DOWSTAT_H */ diff --git a/app/src/ac/svc/ACQ_DOWSTAT.pgc b/app/src/ac/svc/ACQ_DOWSTAT.pgc new file mode 100644 index 0000000..7f75ee9 --- /dev/null +++ b/app/src/ac/svc/ACQ_DOWSTAT.pgc @@ -0,0 +1,41 @@ +/* + * ACQ_DOWSTAT.pgc - ac 모듈 서비스 ACQ_DOWSTAT (one service = one file). + * 매입 요일별 통계: 지정 기간(T_STR1~T_STR2)의 매입을 요일(0~6)별로 그룹 + * 집계해 요일번호·건수 쌍을 occurrence 로 내리고 주말 비중을 계산한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_DOWSTAT.h" + +void ACQ_DOWSTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long total = 0, weekend = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_dow, h_n; + char h_from[16], h_to[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR1, h_from, sizeof(h_from)); + gets_(b, T_STR2, h_to, sizeof(h_to)); + + EXEC SQL DECLARE dow_cur CURSOR FOR + SELECT extract(dow from biz_date)::int, count(*) FROM purchase + WHERE biz_date BETWEEN :h_from AND :h_to GROUP BY 1 ORDER BY 1; + EXEC SQL OPEN dow_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH dow_cur INTO :h_dow, :h_n; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE dow_cur; FAIL(b); } + Badd(b, T_ARG5, (char *)&h_dow, 0L); + Badd(b, T_ARG6, (char *)&h_n, 0L); + total += h_n; + if (h_dow == 0 || h_dow == 6) weekend += h_n; + } + EXEC SQL CLOSE dow_cur; + + setl(b, T_COUNT, total); + setl(b, T_ARG1, weekend); + setl(b, T_RC, total > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_EDIQRY.h b/app/src/ac/svc/ACQ_EDIQRY.h new file mode 100644 index 0000000..0f829fa --- /dev/null +++ b/app/src/ac/svc/ACQ_EDIQRY.h @@ -0,0 +1,18 @@ +/* + * ACQ_EDIQRY.h - ac 서비스 ACQ_EDIQRY 카피북 (copybook / record header). + * EDI 조회: 지정 가맹점(T_MERCHANT)의 영업일 EDI 접수 문서 건수와 금액 + */ +#ifndef ACQ_EDIQRY_H +#define ACQ_EDIQRY_H + +#include "ac_svc.h" + +typedef struct { + char merchant[64]; + long count; + long sum; +} acq_ediqry_rec_t; + +void ACQ_EDIQRY(TPSVCINFO *p); + +#endif /* ACQ_EDIQRY_H */ diff --git a/app/src/ac/svc/ACQ_EDIQRY.pgc b/app/src/ac/svc/ACQ_EDIQRY.pgc new file mode 100644 index 0000000..ba999cb --- /dev/null +++ b/app/src/ac/svc/ACQ_EDIQRY.pgc @@ -0,0 +1,28 @@ +/* + * ACQ_EDIQRY.pgc - ac 모듈 서비스 ACQ_EDIQRY (one service = one file). + * EDI 조회: 지정 가맹점(T_MERCHANT)의 영업일 EDI 접수 문서 건수와 금액 + * 합계를 조회해 응답한다. 접수분이 없으면 결과코드 1. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_EDIQRY.h" + +void ACQ_EDIQRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_n = 0, h_sum = 0; + 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(amount),0) INTO :h_n, :h_sum + FROM ac_edi WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_n); + setl(b, T_GROSS, h_sum); + setl(b, T_RC, h_n > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_EDIRSNDCH.h b/app/src/ac/svc/ACQ_EDIRSNDCH.h new file mode 100644 index 0000000..b33445c --- /dev/null +++ b/app/src/ac/svc/ACQ_EDIRSNDCH.h @@ -0,0 +1,18 @@ +/* + * ACQ_EDIRSNDCH.h - ac 서비스 ACQ_EDIRSNDCH 카피북 (copybook / record header). + * EDI 재전송 채널별: 영업일 미수신 EDI 문서를 가맹점별로 커서 조회해 + */ +#ifndef ACQ_EDIRSNDCH_H +#define ACQ_EDIRSNDCH_H + +#include "ac_svc.h" + +typedef struct { + char doc_no[32]; + long count; + long total; +} acq_edirsndch_rec_t; + +void ACQ_EDIRSNDCH(TPSVCINFO *p); + +#endif /* ACQ_EDIRSNDCH_H */ diff --git a/app/src/ac/svc/ACQ_EDIRSNDCH.pgc b/app/src/ac/svc/ACQ_EDIRSNDCH.pgc new file mode 100644 index 0000000..e9e09bd --- /dev/null +++ b/app/src/ac/svc/ACQ_EDIRSNDCH.pgc @@ -0,0 +1,37 @@ +/* + * ACQ_EDIRSNDCH.pgc - ac 모듈 서비스 ACQ_EDIRSNDCH (one service = one file). + * EDI 재전송 채널별: 영업일 미수신 EDI 문서를 가맹점별로 커서 조회해 + * 문서번호·금액 쌍을 occurrence 로 내리고 재전송 대상 건수를 집계한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_EDIRSNDCH.h" + +void ACQ_EDIRSNDCH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long n = 0, tot = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_amt; + char h_doc[32], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL DECLARE edr_cur CURSOR FOR + SELECT doc_no, amount FROM ac_edi WHERE biz_date = :h_bizdate ORDER BY doc_no; + EXEC SQL OPEN edr_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH edr_cur INTO :h_doc, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE edr_cur; FAIL(b); } + if (n < 8) { Badd(b, T_DOCNO, h_doc, 0L); Badd(b, T_AMT1, (char *)&h_amt, 0L); } + n++; tot += h_amt; + } + EXEC SQL CLOSE edr_cur; + + setl(b, T_COUNT, n); + setl(b, T_GROSS, tot); + setl(b, T_RC, n > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_EDISTAT.h b/app/src/ac/svc/ACQ_EDISTAT.h new file mode 100644 index 0000000..644b9c9 --- /dev/null +++ b/app/src/ac/svc/ACQ_EDISTAT.h @@ -0,0 +1,18 @@ +/* + * ACQ_EDISTAT.h - ac 서비스 ACQ_EDISTAT 카피북 (copybook / record header). + * EDI 통계: 영업일 EDI 문서를 금액대(소액/중액/고액)로 나눠 대역별 건수를 + */ +#ifndef ACQ_EDISTAT_H +#define ACQ_EDISTAT_H + +#include "ac_svc.h" + +typedef struct { + long lo; + long mid; + long hi; +} acq_edistat_rec_t; + +void ACQ_EDISTAT(TPSVCINFO *p); + +#endif /* ACQ_EDISTAT_H */ diff --git a/app/src/ac/svc/ACQ_EDISTAT.pgc b/app/src/ac/svc/ACQ_EDISTAT.pgc new file mode 100644 index 0000000..b53aaaf --- /dev/null +++ b/app/src/ac/svc/ACQ_EDISTAT.pgc @@ -0,0 +1,30 @@ +/* + * ACQ_EDISTAT.pgc - ac 모듈 서비스 ACQ_EDISTAT (one service = one file). + * EDI 통계: 영업일 EDI 문서를 금액대(소액/중액/고액)로 나눠 대역별 건수를 + * 한 번의 집계에서 산출하고 전체 문서 금액 합계를 함께 낸다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_EDISTAT.h" + +void ACQ_EDISTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_lo = 0, h_mid = 0, h_hi = 0, h_sum = 0; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT count(*) FILTER (WHERE amount < 100000), + count(*) FILTER (WHERE amount >= 100000 AND amount < 1000000), + count(*) FILTER (WHERE amount >= 1000000), coalesce(sum(amount),0) + INTO :h_lo, :h_mid, :h_hi, :h_sum FROM ac_edi WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_ARG1, h_lo); + setl(b, T_ARG2, h_mid); + setl(b, T_ARG3, h_hi); + setl(b, T_GROSS, h_sum); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_ERRCLASS.h b/app/src/ac/svc/ACQ_ERRCLASS.h new file mode 100644 index 0000000..8603913 --- /dev/null +++ b/app/src/ac/svc/ACQ_ERRCLASS.h @@ -0,0 +1,18 @@ +/* + * ACQ_ERRCLASS.h - ac 서비스 ACQ_ERRCLASS 카피북 (copybook / record header). + * 매입 오류유형별 분류: 미매입 원장(ac_unmatch)의 사유 접두어를 읽어 + */ +#ifndef ACQ_ERRCLASS_H +#define ACQ_ERRCLASS_H + +#include "ac_svc.h" + +typedef struct { + long classes[4]; + long count; + long rc; +} acq_errclass_rec_t; + +void ACQ_ERRCLASS(TPSVCINFO *p); + +#endif /* ACQ_ERRCLASS_H */ diff --git a/app/src/ac/svc/ACQ_ERRCLASS.pgc b/app/src/ac/svc/ACQ_ERRCLASS.pgc new file mode 100644 index 0000000..ce4c541 --- /dev/null +++ b/app/src/ac/svc/ACQ_ERRCLASS.pgc @@ -0,0 +1,46 @@ +/* + * ACQ_ERRCLASS.pgc - ac 모듈 서비스 ACQ_ERRCLASS (one service = one file). + * 매입 오류유형별 분류: 미매입 원장(ac_unmatch)의 사유 접두어를 읽어 + * 중복/한도/환율/기타 4개 오류군으로 분류하고 군별 건수를 집계한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_ERRCLASS.h" + +/* 사유 접두어 → 오류군 (0=중복 1=한도 2=환율 3=기타) */ +static int ac_errkind(const char *r) +{ + if (strncmp(r, "DUP", 3) == 0) return 0; + if (strncmp(r, "LIMIT", 5) == 0) return 1; + if (strncmp(r, "FX", 2) == 0) return 2; + return 3; +} + +void ACQ_ERRCLASS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long cls[4] = {0, 0, 0, 0}; + EXEC SQL BEGIN DECLARE SECTION; + char h_reason[64], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL DECLARE erc_cur CURSOR FOR + SELECT reason FROM ac_unmatch WHERE biz_date = :h_bizdate; + EXEC SQL OPEN erc_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH erc_cur INTO :h_reason; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE erc_cur; FAIL(b); } + cls[ac_errkind(h_reason)]++; + } + EXEC SQL CLOSE erc_cur; + + setl(b, T_ARG1, cls[0]); + setl(b, T_ARG2, cls[1]); + setl(b, T_ARG3, cls[2]); + setl(b, T_ARG4, cls[3]); + setl(b, T_COUNT, cls[0] + cls[1] + cls[2] + cls[3]); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_ERRQRY.h b/app/src/ac/svc/ACQ_ERRQRY.h new file mode 100644 index 0000000..cad3f66 --- /dev/null +++ b/app/src/ac/svc/ACQ_ERRQRY.h @@ -0,0 +1,18 @@ +/* + * ACQ_ERRQRY.h - ac 서비스 ACQ_ERRQRY 카피북 (copybook / record header). + * 매입 오류유형별 조회: 사유 접두어(T_REASON)로 시작하는 미매입 건을 + */ +#ifndef ACQ_ERRQRY_H +#define ACQ_ERRQRY_H + +#include "ac_svc.h" + +typedef struct { + char pattern[64]; + long count; + long rc; +} acq_errqry_rec_t; + +void ACQ_ERRQRY(TPSVCINFO *p); + +#endif /* ACQ_ERRQRY_H */ diff --git a/app/src/ac/svc/ACQ_ERRQRY.pgc b/app/src/ac/svc/ACQ_ERRQRY.pgc new file mode 100644 index 0000000..a469274 --- /dev/null +++ b/app/src/ac/svc/ACQ_ERRQRY.pgc @@ -0,0 +1,28 @@ +/* + * ACQ_ERRQRY.pgc - ac 모듈 서비스 ACQ_ERRQRY (one service = one file). + * 매입 오류유형별 조회: 사유 접두어(T_REASON)로 시작하는 미매입 건을 + * 패턴 조회해 해당 오류유형의 건수를 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_ERRQRY.h" + +void ACQ_ERRQRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char pat[64]; + EXEC SQL BEGIN DECLARE SECTION; + long h_n = 0; + char h_like[70]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_REASON, pat, sizeof(pat)); + if (pat[0] == 0) strcpy(pat, "DUP"); + snprintf(h_like, sizeof(h_like), "%s%%", pat); + + EXEC SQL SELECT count(*) INTO :h_n FROM ac_unmatch WHERE reason LIKE :h_like; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_n); + setl(b, T_RC, h_n > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_ERRRETRY.h b/app/src/ac/svc/ACQ_ERRRETRY.h new file mode 100644 index 0000000..3b96d11 --- /dev/null +++ b/app/src/ac/svc/ACQ_ERRRETRY.h @@ -0,0 +1,18 @@ +/* + * ACQ_ERRRETRY.h - ac 서비스 ACQ_ERRRETRY 카피북 (copybook / record header). + * 매입 오류 재처리: 보류(H) 상태로 묶여 있던 오류 매입을 접수(A)로 되돌려 + */ +#ifndef ACQ_ERRRETRY_H +#define ACQ_ERRRETRY_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long affected; + long rc; +} acq_errretry_rec_t; + +void ACQ_ERRRETRY(TPSVCINFO *p); + +#endif /* ACQ_ERRRETRY_H */ diff --git a/app/src/ac/svc/ACQ_ERRRETRY.pgc b/app/src/ac/svc/ACQ_ERRRETRY.pgc new file mode 100644 index 0000000..ab3aecb --- /dev/null +++ b/app/src/ac/svc/ACQ_ERRRETRY.pgc @@ -0,0 +1,25 @@ +/* + * ACQ_ERRRETRY.pgc - ac 모듈 서비스 ACQ_ERRRETRY (one service = one file). + * 매입 오류 재처리: 보류(H) 상태로 묶여 있던 오류 매입을 접수(A)로 되돌려 + * 재처리 큐에 태우고 반영 건수를 결과코드로 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_ERRRETRY.h" + +void ACQ_ERRRETRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_aff; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + + EXEC SQL UPDATE purchase SET status = 'A', updated_at = now() + WHERE purchase_id = :h_pid AND status = 'H'; + if (sqlca.sqlcode < 0) { userlog("ACQ_ERRRETRY FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + h_aff = sqlca.sqlerrd[2]; + if (h_aff > 0) Bchg(b, T_STATUS, 0, "A", 0L); + setl(b, T_RC, h_aff > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_ERRSTAT.h b/app/src/ac/svc/ACQ_ERRSTAT.h new file mode 100644 index 0000000..a58348b --- /dev/null +++ b/app/src/ac/svc/ACQ_ERRSTAT.h @@ -0,0 +1,18 @@ +/* + * ACQ_ERRSTAT.h - ac 서비스 ACQ_ERRSTAT 카피북 (copybook / record header). + * 매입 오류유형별 통계: 미매입 원장을 사유 접두어 기준으로 한 번의 집계에서 + */ +#ifndef ACQ_ERRSTAT_H +#define ACQ_ERRSTAT_H + +#include "ac_svc.h" + +typedef struct { + long dup; + long limit; + long fx; +} acq_errstat_rec_t; + +void ACQ_ERRSTAT(TPSVCINFO *p); + +#endif /* ACQ_ERRSTAT_H */ diff --git a/app/src/ac/svc/ACQ_ERRSTAT.pgc b/app/src/ac/svc/ACQ_ERRSTAT.pgc new file mode 100644 index 0000000..de3dc78 --- /dev/null +++ b/app/src/ac/svc/ACQ_ERRSTAT.pgc @@ -0,0 +1,30 @@ +/* + * ACQ_ERRSTAT.pgc - ac 모듈 서비스 ACQ_ERRSTAT (one service = one file). + * 매입 오류유형별 통계: 미매입 원장을 사유 접두어 기준으로 한 번의 집계에서 + * 중복/한도/환율 오류 건수를 FILTER 로 분해해 동시에 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_ERRSTAT.h" + +void ACQ_ERRSTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_dup = 0, h_lim = 0, h_fx = 0, h_all = 0; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT count(*) FILTER (WHERE reason LIKE 'DUP%'), + count(*) FILTER (WHERE reason LIKE 'LIMIT%'), + count(*) FILTER (WHERE reason LIKE 'FX%'), count(*) + INTO :h_dup, :h_lim, :h_fx, :h_all FROM ac_unmatch WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_ARG1, h_dup); + setl(b, T_ARG2, h_lim); + setl(b, T_ARG3, h_fx); + setl(b, T_COUNT, h_all); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_ESCROW.h b/app/src/ac/svc/ACQ_ESCROW.h new file mode 100644 index 0000000..e0f429e --- /dev/null +++ b/app/src/ac/svc/ACQ_ESCROW.h @@ -0,0 +1,18 @@ +/* + * ACQ_ESCROW.h - ac 서비스 ACQ_ESCROW 카피북 (copybook / record header). + * 매입 에스크로 예치: 분쟁 소지 매입 금액을 정산 전 유보하기 위해 원장에 + */ +#ifndef ACQ_ESCROW_H +#define ACQ_ESCROW_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long ledger_id; + long amount; +} acq_escrow_rec_t; + +void ACQ_ESCROW(TPSVCINFO *p); + +#endif /* ACQ_ESCROW_H */ diff --git a/app/src/ac/svc/ACQ_ESCROW.pgc b/app/src/ac/svc/ACQ_ESCROW.pgc new file mode 100644 index 0000000..8447a8d --- /dev/null +++ b/app/src/ac/svc/ACQ_ESCROW.pgc @@ -0,0 +1,31 @@ +/* + * ACQ_ESCROW.pgc - ac 모듈 서비스 ACQ_ESCROW (one service = one file). + * 매입 에스크로 예치: 분쟁 소지 매입 금액을 정산 전 유보하기 위해 원장에 + * ESCROW 항목으로 예치액을 적립한다. 금액이 0 이하면 거절. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_ESCROW.h" + +void ACQ_ESCROW(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_amt, h_lid; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + h_amt = getl(b, T_AMOUNT); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_amt <= 0) { setl(b, T_RC, 3); 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, :h_pid, 'ESCROW', :h_amt, :h_bizdate); + if (sqlca.sqlcode < 0) { userlog("ACQ_ESCROW FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + + setl(b, T_SETTLE_ID, h_lid); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_ESCROWREL.h b/app/src/ac/svc/ACQ_ESCROWREL.h new file mode 100644 index 0000000..5623aec --- /dev/null +++ b/app/src/ac/svc/ACQ_ESCROWREL.h @@ -0,0 +1,18 @@ +/* + * ACQ_ESCROWREL.h - ac 서비스 ACQ_ESCROWREL 카피북 (copybook / record header). + * 매입 에스크로 해제: 예치(ESCROW)와 해제(ESCROW_REL) 원장 잔액을 계산해 + */ +#ifndef ACQ_ESCROWREL_H +#define ACQ_ESCROWREL_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long balance; + long rc; +} acq_escrowrel_rec_t; + +void ACQ_ESCROWREL(TPSVCINFO *p); + +#endif /* ACQ_ESCROWREL_H */ diff --git a/app/src/ac/svc/ACQ_ESCROWREL.pgc b/app/src/ac/svc/ACQ_ESCROWREL.pgc new file mode 100644 index 0000000..98e08c5 --- /dev/null +++ b/app/src/ac/svc/ACQ_ESCROWREL.pgc @@ -0,0 +1,36 @@ +/* + * ACQ_ESCROWREL.pgc - ac 모듈 서비스 ACQ_ESCROWREL (one service = one file). + * 매입 에스크로 해제: 예치(ESCROW)와 해제(ESCROW_REL) 원장 잔액을 계산해 + * 잔여 예치금을 해제 항목으로 전액 반제하고 잔액을 0 으로 만든다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_ESCROWREL.h" + +void ACQ_ESCROWREL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_esc = 0, h_rel = 0, h_lid, h_bal; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT coalesce(sum(amount) FILTER (WHERE entry_type = 'ESCROW'),0), + coalesce(sum(amount) FILTER (WHERE entry_type = 'ESCROW_REL'),0) + INTO :h_esc, :h_rel FROM ledger WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + h_bal = h_esc - h_rel; + if (h_bal <= 0) { setl(b, T_GROSS, 0); setl(b, T_RC, 1); OK(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, :h_pid, 'ESCROW_REL', :h_bal, :h_bizdate); + if (sqlca.sqlcode < 0) { userlog("ACQ_ESCROWREL FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + + setl(b, T_GROSS, h_bal); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_EVENTBUY.h b/app/src/ac/svc/ACQ_EVENTBUY.h new file mode 100644 index 0000000..f4b21d2 --- /dev/null +++ b/app/src/ac/svc/ACQ_EVENTBUY.h @@ -0,0 +1,18 @@ +/* + * ACQ_EVENTBUY.h - ac 서비스 ACQ_EVENTBUY 카피북 (copybook / record header). + * 이벤트 매입: 프로모션 행사 매입을 EVENT 채널로 접수한다. 활성 가맹점만 + */ +#ifndef ACQ_EVENTBUY_H +#define ACQ_EVENTBUY_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long fee; + long net; +} acq_eventbuy_rec_t; + +void ACQ_EVENTBUY(TPSVCINFO *p); + +#endif /* ACQ_EVENTBUY_H */ diff --git a/app/src/ac/svc/ACQ_EVENTBUY.pgc b/app/src/ac/svc/ACQ_EVENTBUY.pgc new file mode 100644 index 0000000..f00a85a --- /dev/null +++ b/app/src/ac/svc/ACQ_EVENTBUY.pgc @@ -0,0 +1,41 @@ +/* + * ACQ_EVENTBUY.pgc - ac 모듈 서비스 ACQ_EVENTBUY (one service = one file). + * 이벤트 매입: 프로모션 행사 매입을 EVENT 채널로 접수한다. 활성 가맹점만 + * 허용하며 이벤트 할인율(T_ARG1, bps)만큼 수수료를 감면해 net 을 올린다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_EVENTBUY.h" + +void ACQ_EVENTBUY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long fee, disc; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_amt, h_bps = 0, h_disc, h_fee, h_net; + char h_merch[64], h_mst[16], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_amt = getl(b, T_AMOUNT); + disc = getl(b, T_ARG1); if (disc < 0) disc = 0; + gets_(b, T_MERCHANT, h_merch, sizeof(h_merch)); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_amt <= 0) { setl(b, T_RC, 3); FAIL(b); } + + EXEC SQL SELECT status, mdr_bps INTO :h_mst, :h_bps FROM merchant WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 5); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_mst, "ACTIVE") != 0) { setl(b, T_RC, 6); FAIL(b); } + h_disc = (h_bps > disc) ? h_bps - disc : 0; + fee = h_amt * h_disc / 10000; h_fee = fee; h_net = h_amt - fee; + + EXEC SQL SELECT nextval('purchase_seq') INTO :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO purchase (purchase_id, merchant_id, amount, fee, net, status, biz_date, channel, category) + VALUES (:h_pid, :h_merch, :h_amt, :h_fee, :h_net, 'A', :h_bizdate, 'EVENT', 'PROMO'); + if (sqlca.sqlcode < 0) { userlog("ACQ_EVENTBUY FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + + setl(b, T_PURCHASE_ID, h_pid); + setl(b, T_FEE, h_fee); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_EXCHANGE.h b/app/src/ac/svc/ACQ_EXCHANGE.h new file mode 100644 index 0000000..60501a4 --- /dev/null +++ b/app/src/ac/svc/ACQ_EXCHANGE.h @@ -0,0 +1,18 @@ +/* + * ACQ_EXCHANGE.h - ac 서비스 ACQ_EXCHANGE 카피북 (copybook / record header). + * 매입 교환 처리: 반품 후 동일가 재구매를 한 번에 처리한다. 원매입에 대해 + */ +#ifndef ACQ_EXCHANGE_H +#define ACQ_EXCHANGE_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long cancel_id; + long amount; +} acq_exchange_rec_t; + +void ACQ_EXCHANGE(TPSVCINFO *p); + +#endif /* ACQ_EXCHANGE_H */ diff --git a/app/src/ac/svc/ACQ_EXCHANGE.pgc b/app/src/ac/svc/ACQ_EXCHANGE.pgc new file mode 100644 index 0000000..93a52d7 --- /dev/null +++ b/app/src/ac/svc/ACQ_EXCHANGE.pgc @@ -0,0 +1,38 @@ +/* + * ACQ_EXCHANGE.pgc - ac 모듈 서비스 ACQ_EXCHANGE (one service = one file). + * 매입 교환 처리: 반품 후 동일가 재구매를 한 번에 처리한다. 원매입에 대해 + * EXCHANGE 취소 행을 남기고 원매입 상태를 대사완료(M)로 유지한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_EXCHANGE.h" + +void ACQ_EXCHANGE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char why[80]; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_pid, h_amt, h_orig = 0; + char h_reason[100], h_st[4], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + h_amt = getl(b, T_AMOUNT); + gets_(b, T_REASON, why, sizeof(why)); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT status, amount INTO :h_st, :h_orig FROM purchase WHERE purchase_id = :h_pid; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 5); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (h_amt <= 0 || h_amt > h_orig) { setl(b, T_RC, 3); FAIL(b); } + snprintf(h_reason, sizeof(h_reason), "EXCHANGE:%s", why[0] ? why : "SWAP"); + + EXEC SQL SELECT nextval('ac_cancel_seq') INTO :h_id; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO ac_cancel (cancel_id, purchase_id, cancel_type, amount, reason, biz_date) + VALUES (:h_id, :h_pid, 'EXCHANGE', :h_amt, :h_reason, :h_bizdate); + if (sqlca.sqlcode < 0) { userlog("ACQ_EXCHANGE FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + + setl(b, T_CANCEL_ID, h_id); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_GRADESTAT.h b/app/src/ac/svc/ACQ_GRADESTAT.h new file mode 100644 index 0000000..c4f2375 --- /dev/null +++ b/app/src/ac/svc/ACQ_GRADESTAT.h @@ -0,0 +1,18 @@ +/* + * ACQ_GRADESTAT.h - ac 서비스 ACQ_GRADESTAT 카피북 (copybook / record header). + * 매입 가맹점등급별 통계: 가맹점 MDR(bps)을 프리미엄/일반/기본 등급으로 나눠 + */ +#ifndef ACQ_GRADESTAT_H +#define ACQ_GRADESTAT_H + +#include "ac_svc.h" + +typedef struct { + long grades[3]; + long gross; + long rc; +} acq_gradestat_rec_t; + +void ACQ_GRADESTAT(TPSVCINFO *p); + +#endif /* ACQ_GRADESTAT_H */ diff --git a/app/src/ac/svc/ACQ_GRADESTAT.pgc b/app/src/ac/svc/ACQ_GRADESTAT.pgc new file mode 100644 index 0000000..0bd3e8e --- /dev/null +++ b/app/src/ac/svc/ACQ_GRADESTAT.pgc @@ -0,0 +1,47 @@ +/* + * ACQ_GRADESTAT.pgc - ac 모듈 서비스 ACQ_GRADESTAT (one service = one file). + * 매입 가맹점등급별 통계: 가맹점 MDR(bps)을 프리미엄/일반/기본 등급으로 나눠 + * 등급별 매출 합계를 버킷 집계한다. 등급 경계는 C 분류기로 판정한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_GRADESTAT.h" + +/* MDR(bps) → 등급 (0=프리미엄 1=일반 2=기본) */ +static int ac_grade(long bps) +{ + if (bps <= 20) return 0; + if (bps <= 28) return 1; + return 2; +} + +void ACQ_GRADESTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long g[3] = {0, 0, 0}; + EXEC SQL BEGIN DECLARE SECTION; + long h_bps, h_sum; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL DECLARE grd_cur CURSOR FOR + SELECT m.mdr_bps, coalesce(sum(p.amount),0) FROM merchant m + JOIN purchase p ON p.merchant_id = m.merchant_id AND p.biz_date = :h_bizdate + GROUP BY m.merchant_id, m.mdr_bps; + EXEC SQL OPEN grd_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH grd_cur INTO :h_bps, :h_sum; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE grd_cur; FAIL(b); } + g[ac_grade(h_bps)] += h_sum; + } + EXEC SQL CLOSE grd_cur; + + setl(b, T_ARG1, g[0]); + setl(b, T_ARG2, g[1]); + setl(b, T_ARG3, g[2]); + setl(b, T_GROSS, g[0] + g[1] + g[2]); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_GUARANTEE.h b/app/src/ac/svc/ACQ_GUARANTEE.h new file mode 100644 index 0000000..fd15f0d --- /dev/null +++ b/app/src/ac/svc/ACQ_GUARANTEE.h @@ -0,0 +1,18 @@ +/* + * ACQ_GUARANTEE.h - ac 서비스 ACQ_GUARANTEE 카피북 (copybook / record header). + * 매입 보증 등록: 대형 매입에 대한 이행보증금을 원장에 GUARANTEE 항목으로 + */ +#ifndef ACQ_GUARANTEE_H +#define ACQ_GUARANTEE_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long guarantee; + long ledger_id; +} acq_guarantee_rec_t; + +void ACQ_GUARANTEE(TPSVCINFO *p); + +#endif /* ACQ_GUARANTEE_H */ diff --git a/app/src/ac/svc/ACQ_GUARANTEE.pgc b/app/src/ac/svc/ACQ_GUARANTEE.pgc new file mode 100644 index 0000000..86dca04 --- /dev/null +++ b/app/src/ac/svc/ACQ_GUARANTEE.pgc @@ -0,0 +1,34 @@ +/* + * ACQ_GUARANTEE.pgc - ac 모듈 서비스 ACQ_GUARANTEE (one service = one file). + * 매입 보증 등록: 대형 매입에 대한 이행보증금을 원장에 GUARANTEE 항목으로 + * 적립한다. 보증률(T_ARG1, bps)로 보증금을 산출하며 기본 10%. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_GUARANTEE.h" + +void ACQ_GUARANTEE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_amt, h_bps, h_lid, h_guar; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + h_amt = getl(b, T_AMOUNT); + h_bps = getl(b, T_ARG1); if (h_bps <= 0) h_bps = 1000; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + h_guar = h_amt * h_bps / 10000; + if (h_guar <= 0) { setl(b, T_RC, 3); 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, :h_pid, 'GUARANTEE', :h_guar, :h_bizdate); + if (sqlca.sqlcode < 0) { userlog("ACQ_GUARANTEE FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + + setl(b, T_AMT1, h_guar); + setl(b, T_SETTLE_ID, h_lid); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_GUARQRY.h b/app/src/ac/svc/ACQ_GUARQRY.h new file mode 100644 index 0000000..6c58383 --- /dev/null +++ b/app/src/ac/svc/ACQ_GUARQRY.h @@ -0,0 +1,18 @@ +/* + * ACQ_GUARQRY.h - ac 서비스 ACQ_GUARQRY 카피북 (copybook / record header). + * 매입 보증 조회: 한 매입에 적립된 보증금(GUARANTEE) 총액과 건수를 원장에서 + */ +#ifndef ACQ_GUARQRY_H +#define ACQ_GUARQRY_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long guarantee; + long count; +} acq_guarqry_rec_t; + +void ACQ_GUARQRY(TPSVCINFO *p); + +#endif /* ACQ_GUARQRY_H */ diff --git a/app/src/ac/svc/ACQ_GUARQRY.pgc b/app/src/ac/svc/ACQ_GUARQRY.pgc new file mode 100644 index 0000000..297e6cc --- /dev/null +++ b/app/src/ac/svc/ACQ_GUARQRY.pgc @@ -0,0 +1,26 @@ +/* + * ACQ_GUARQRY.pgc - ac 모듈 서비스 ACQ_GUARQRY (one service = one file). + * 매입 보증 조회: 한 매입에 적립된 보증금(GUARANTEE) 총액과 건수를 원장에서 + * 조회해 응답한다. 보증 이력이 없으면 결과코드 1. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_GUARQRY.h" + +void ACQ_GUARQRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_sum = 0, h_n = 0; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + + EXEC SQL SELECT coalesce(sum(amount),0), count(*) INTO :h_sum, :h_n + FROM ledger WHERE purchase_id = :h_pid AND entry_type = 'GUARANTEE'; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_AMT1, h_sum); + setl(b, T_COUNT, h_n); + setl(b, T_RC, h_n > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_HALFSTAT.h b/app/src/ac/svc/ACQ_HALFSTAT.h new file mode 100644 index 0000000..f105188 --- /dev/null +++ b/app/src/ac/svc/ACQ_HALFSTAT.h @@ -0,0 +1,18 @@ +/* + * ACQ_HALFSTAT.h - ac 서비스 ACQ_HALFSTAT 카피북 (copybook / record header). + * 매입 반기별 통계: 지정 연도(T_ARG1) 매입을 상반기(1~6월)/하반기(7~12월)로 + */ +#ifndef ACQ_HALFSTAT_H +#define ACQ_HALFSTAT_H + +#include "ac_svc.h" + +typedef struct { + long h1; + long h2; + long share; +} acq_halfstat_rec_t; + +void ACQ_HALFSTAT(TPSVCINFO *p); + +#endif /* ACQ_HALFSTAT_H */ diff --git a/app/src/ac/svc/ACQ_HALFSTAT.pgc b/app/src/ac/svc/ACQ_HALFSTAT.pgc new file mode 100644 index 0000000..dbbd9bf --- /dev/null +++ b/app/src/ac/svc/ACQ_HALFSTAT.pgc @@ -0,0 +1,32 @@ +/* + * ACQ_HALFSTAT.pgc - ac 모듈 서비스 ACQ_HALFSTAT (one service = one file). + * 매입 반기별 통계: 지정 연도(T_ARG1) 매입을 상반기(1~6월)/하반기(7~12월)로 + * 나눠 반기별 매출과 건수를 산출하고 상반기 비중을 계산한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_HALFSTAT.h" + +void ACQ_HALFSTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long share = 0, total; + EXEC SQL BEGIN DECLARE SECTION; + long h_h1 = 0, h_h2 = 0, h_n = 0; + int h_yr; + EXEC SQL END DECLARE SECTION; + + h_yr = (int)getl(b, T_ARG1); if (h_yr < 2000) h_yr = 2026; + + EXEC SQL SELECT coalesce(sum(amount) FILTER (WHERE extract(month from biz_date) <= 6),0), + coalesce(sum(amount) FILTER (WHERE extract(month from biz_date) > 6),0), count(*) + INTO :h_h1, :h_h2, :h_n FROM purchase WHERE extract(year from biz_date) = :h_yr; + if (sqlca.sqlcode < 0) FAIL(b); + total = h_h1 + h_h2; + if (total > 0) share = h_h1 * 100 / total; + + setl(b, T_ARG2, h_h1); + setl(b, T_ARG3, h_h2); + setl(b, T_COUNT, h_n); + setl(b, T_RC, share); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_HOURSTAT.h b/app/src/ac/svc/ACQ_HOURSTAT.h new file mode 100644 index 0000000..4af04d1 --- /dev/null +++ b/app/src/ac/svc/ACQ_HOURSTAT.h @@ -0,0 +1,18 @@ +/* + * ACQ_HOURSTAT.h - ac 서비스 ACQ_HOURSTAT 카피북 (copybook / record header). + * 매입 시간대별 통계: 접수시각을 새벽/오전/오후/야간 네 구간으로 나눠 구간별 + */ +#ifndef ACQ_HOURSTAT_H +#define ACQ_HOURSTAT_H + +#include "ac_svc.h" + +typedef struct { + long bands[4]; + long peak; + long rc; +} acq_hourstat_rec_t; + +void ACQ_HOURSTAT(TPSVCINFO *p); + +#endif /* ACQ_HOURSTAT_H */ diff --git a/app/src/ac/svc/ACQ_HOURSTAT.pgc b/app/src/ac/svc/ACQ_HOURSTAT.pgc new file mode 100644 index 0000000..b0e7669 --- /dev/null +++ b/app/src/ac/svc/ACQ_HOURSTAT.pgc @@ -0,0 +1,35 @@ +/* + * ACQ_HOURSTAT.pgc - ac 모듈 서비스 ACQ_HOURSTAT (one service = one file). + * 매입 시간대별 통계: 접수시각을 새벽/오전/오후/야간 네 구간으로 나눠 구간별 + * 매입 건수를 한 번의 집계로 분해하고 피크 구간 인덱스를 판정한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_HOURSTAT.h" + +void ACQ_HOURSTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long v[4]; int i, peak = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_d = 0, h_m = 0, h_a = 0, h_n = 0; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT count(*) FILTER (WHERE extract(hour from created_at) < 6), + count(*) FILTER (WHERE extract(hour from created_at) BETWEEN 6 AND 11), + count(*) FILTER (WHERE extract(hour from created_at) BETWEEN 12 AND 17), + count(*) FILTER (WHERE extract(hour from created_at) >= 18) + INTO :h_d, :h_m, :h_a, :h_n FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + v[0] = h_d; v[1] = h_m; v[2] = h_a; v[3] = h_n; + for (i = 1; i < 4; i++) if (v[i] > v[peak]) peak = i; + setl(b, T_ARG1, h_d); + setl(b, T_ARG2, h_m); + setl(b, T_ARG3, h_a); + setl(b, T_ARG4, h_n); + setl(b, T_RC, peak); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_INSTCXL.h b/app/src/ac/svc/ACQ_INSTCXL.h new file mode 100644 index 0000000..2a4e325 --- /dev/null +++ b/app/src/ac/svc/ACQ_INSTCXL.h @@ -0,0 +1,18 @@ +/* + * ACQ_INSTCXL.h - ac 서비스 ACQ_INSTCXL 카피북 (copybook / record header). + * 할부 취소: 한 매입의 할부 스케줄 전 회차를 원장에서 삭제해 할부를 해지한다. + */ +#ifndef ACQ_INSTCXL_H +#define ACQ_INSTCXL_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long deleted; + long rc; +} acq_instcxl_rec_t; + +void ACQ_INSTCXL(TPSVCINFO *p); + +#endif /* ACQ_INSTCXL_H */ diff --git a/app/src/ac/svc/ACQ_INSTCXL.pgc b/app/src/ac/svc/ACQ_INSTCXL.pgc new file mode 100644 index 0000000..16f1b0c --- /dev/null +++ b/app/src/ac/svc/ACQ_INSTCXL.pgc @@ -0,0 +1,24 @@ +/* + * ACQ_INSTCXL.pgc - ac 모듈 서비스 ACQ_INSTCXL (one service = one file). + * 할부 취소: 한 매입의 할부 스케줄 전 회차를 원장에서 삭제해 할부를 해지한다. + * 삭제된 회차수를 건수로 응답하며, 없으면 결과코드 1. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_INSTCXL.h" + +void ACQ_INSTCXL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_del; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + + EXEC SQL DELETE FROM ac_install WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0) { userlog("ACQ_INSTCXL FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + h_del = sqlca.sqlerrd[2]; + setl(b, T_COUNT, h_del); + setl(b, T_RC, h_del > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_INSTPLAN.h b/app/src/ac/svc/ACQ_INSTPLAN.h new file mode 100644 index 0000000..785dc8d --- /dev/null +++ b/app/src/ac/svc/ACQ_INSTPLAN.h @@ -0,0 +1,18 @@ +/* + * ACQ_INSTPLAN.h - ac 서비스 ACQ_INSTPLAN 카피북 (copybook / record header). + * 할부 계획 조회: 한 매입의 약정 개월수와 회차 합계, 회차당 금액을 단건 + */ +#ifndef ACQ_INSTPLAN_H +#define ACQ_INSTPLAN_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long months; + long total; +} acq_instplan_rec_t; + +void ACQ_INSTPLAN(TPSVCINFO *p); + +#endif /* ACQ_INSTPLAN_H */ diff --git a/app/src/ac/svc/ACQ_INSTPLAN.pgc b/app/src/ac/svc/ACQ_INSTPLAN.pgc new file mode 100644 index 0000000..6c97b3c --- /dev/null +++ b/app/src/ac/svc/ACQ_INSTPLAN.pgc @@ -0,0 +1,28 @@ +/* + * ACQ_INSTPLAN.pgc - ac 모듈 서비스 ACQ_INSTPLAN (one service = one file). + * 할부 계획 조회: 한 매입의 약정 개월수와 회차 합계, 회차당 금액을 단건 + * 조회해 계획 개요를 응답한다. 할부가 없으면 결과코드 1. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_INSTPLAN.h" + +void ACQ_INSTPLAN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_months = 0, h_sum = 0, h_first = 0; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + + EXEC SQL SELECT coalesce(max(months),0), coalesce(sum(month_amount),0), + coalesce(min(month_amount),0) + INTO :h_months, :h_sum, :h_first FROM ac_install WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_INSTALL_N, h_months); + setl(b, T_GROSS, h_sum); + setl(b, T_AMT1, h_first); + setl(b, T_RC, h_months > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_INSTPREPAY.h b/app/src/ac/svc/ACQ_INSTPREPAY.h new file mode 100644 index 0000000..324e0ca --- /dev/null +++ b/app/src/ac/svc/ACQ_INSTPREPAY.h @@ -0,0 +1,18 @@ +/* + * ACQ_INSTPREPAY.h - ac 서비스 ACQ_INSTPREPAY 카피북 (copybook / record header). + * 할부 조기상환: 지정 회차(T_ARG1) 이후 남은 할부금 합계를 계산해 원장에 + */ +#ifndef ACQ_INSTPREPAY_H +#define ACQ_INSTPREPAY_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long remaining; + long count; +} acq_instprepay_rec_t; + +void ACQ_INSTPREPAY(TPSVCINFO *p); + +#endif /* ACQ_INSTPREPAY_H */ diff --git a/app/src/ac/svc/ACQ_INSTPREPAY.pgc b/app/src/ac/svc/ACQ_INSTPREPAY.pgc new file mode 100644 index 0000000..fc0be1c --- /dev/null +++ b/app/src/ac/svc/ACQ_INSTPREPAY.pgc @@ -0,0 +1,36 @@ +/* + * ACQ_INSTPREPAY.pgc - ac 모듈 서비스 ACQ_INSTPREPAY (one service = one file). + * 할부 조기상환: 지정 회차(T_ARG1) 이후 남은 할부금 합계를 계산해 원장에 + * PREPAY 항목으로 선지급을 기록하고 잔여 회차수를 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_INSTPREPAY.h" + +void ACQ_INSTPREPAY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_from, h_left = 0, h_cnt = 0, h_lid; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + h_from = getl(b, T_ARG1); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT coalesce(sum(month_amount),0), count(*) INTO :h_left, :h_cnt + FROM ac_install WHERE purchase_id = :h_pid AND seq_no > :h_from; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt == 0) { setl(b, T_RC, 1); OK(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, :h_pid, 'PREPAY', :h_left, :h_bizdate); + if (sqlca.sqlcode < 0) { userlog("ACQ_INSTPREPAY FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + + setl(b, T_GROSS, h_left); + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_INSTQRY.h b/app/src/ac/svc/ACQ_INSTQRY.h new file mode 100644 index 0000000..118cfba --- /dev/null +++ b/app/src/ac/svc/ACQ_INSTQRY.h @@ -0,0 +1,18 @@ +/* + * ACQ_INSTQRY.h - ac 서비스 ACQ_INSTQRY 카피북 (copybook / record header). + * 할부 회차조회: 한 매입의 할부 스케줄(ac_install)을 회차 순으로 커서 조회해 + */ +#ifndef ACQ_INSTQRY_H +#define ACQ_INSTQRY_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long months; + long total; +} acq_instqry_rec_t; + +void ACQ_INSTQRY(TPSVCINFO *p); + +#endif /* ACQ_INSTQRY_H */ diff --git a/app/src/ac/svc/ACQ_INSTQRY.pgc b/app/src/ac/svc/ACQ_INSTQRY.pgc new file mode 100644 index 0000000..dbffaaa --- /dev/null +++ b/app/src/ac/svc/ACQ_INSTQRY.pgc @@ -0,0 +1,38 @@ +/* + * ACQ_INSTQRY.pgc - ac 모듈 서비스 ACQ_INSTQRY (one service = one file). + * 할부 회차조회: 한 매입의 할부 스케줄(ac_install)을 회차 순으로 커서 조회해 + * 회차·회차금액 쌍을 occurrence 로 내리고 총 회차수와 합계를 계산한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_INSTQRY.h" + +void ACQ_INSTQRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long n = 0, tot = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_seq, h_mamt; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + + EXEC SQL DECLARE isq_cur CURSOR FOR + SELECT seq_no, month_amount FROM ac_install WHERE purchase_id = :h_pid + ORDER BY seq_no; + EXEC SQL OPEN isq_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH isq_cur INTO :h_seq, :h_mamt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE isq_cur; FAIL(b); } + Badd(b, T_ARG5, (char *)&h_seq, 0L); + Badd(b, T_AMT1, (char *)&h_mamt, 0L); + n++; tot += h_mamt; + } + EXEC SQL CLOSE isq_cur; + + setl(b, T_INSTALL_N, n); + setl(b, T_GROSS, tot); + setl(b, T_RC, n > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_LIMITEXC.h b/app/src/ac/svc/ACQ_LIMITEXC.h new file mode 100644 index 0000000..72ea645 --- /dev/null +++ b/app/src/ac/svc/ACQ_LIMITEXC.h @@ -0,0 +1,18 @@ +/* + * ACQ_LIMITEXC.h - ac 서비스 ACQ_LIMITEXC 카피북 (copybook / record header). + * 매입 한도 예외처리: 가맹점 일한도와 당일 누적 매입을 비교해 한도 초과 시 + */ +#ifndef ACQ_LIMITEXC_H +#define ACQ_LIMITEXC_H + +#include "ac_svc.h" + +typedef struct { + char merchant[64]; + long room; + long rc; +} acq_limitexc_rec_t; + +void ACQ_LIMITEXC(TPSVCINFO *p); + +#endif /* ACQ_LIMITEXC_H */ diff --git a/app/src/ac/svc/ACQ_LIMITEXC.pgc b/app/src/ac/svc/ACQ_LIMITEXC.pgc new file mode 100644 index 0000000..19f4e79 --- /dev/null +++ b/app/src/ac/svc/ACQ_LIMITEXC.pgc @@ -0,0 +1,40 @@ +/* + * ACQ_LIMITEXC.pgc - ac 모듈 서비스 ACQ_LIMITEXC (one service = one file). + * 매입 한도 예외처리: 가맹점 일한도와 당일 누적 매입을 비교해 한도 초과 시 + * 미매입 원장에 LIMIT_EXC 예외를 기록하고, 여유 한도를 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_LIMITEXC.h" + +void ACQ_LIMITEXC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long room; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_lim = 0, h_used = 0; + char h_merch[64], h_bizdate[16], h_reason[64]; + 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 == 100) { setl(b, T_RC, 5); FAIL(b); } + if (sqlca.sqlcode < 0) 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); + room = h_lim - h_used; + if (room >= 0) { setl(b, T_DELTA, room); setl(b, T_RC, 0); OK(b); } + + snprintf(h_reason, sizeof(h_reason), "LIMIT_EXC:%ld", -room); + EXEC SQL SELECT nextval('ac_unmatch_seq') INTO :h_id; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO ac_unmatch (unmatch_id, purchase_id, reason, biz_date) + VALUES (:h_id, 0, :h_reason, :h_bizdate); + if (sqlca.sqlcode < 0) { userlog("ACQ_LIMITEXC FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + + setl(b, T_DELTA, room); + setl(b, T_RC, 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_LIMITOVR.h b/app/src/ac/svc/ACQ_LIMITOVR.h new file mode 100644 index 0000000..587b895 --- /dev/null +++ b/app/src/ac/svc/ACQ_LIMITOVR.h @@ -0,0 +1,18 @@ +/* + * ACQ_LIMITOVR.h - ac 서비스 ACQ_LIMITOVR 카피북 (copybook / record header). + * 매입 한도 초과 승인: 한도 초과로 보류(H)된 매입을 관리자가 예외 승인해 + */ +#ifndef ACQ_LIMITOVR_H +#define ACQ_LIMITOVR_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long affected; + long rc; +} acq_limitovr_rec_t; + +void ACQ_LIMITOVR(TPSVCINFO *p); + +#endif /* ACQ_LIMITOVR_H */ diff --git a/app/src/ac/svc/ACQ_LIMITOVR.pgc b/app/src/ac/svc/ACQ_LIMITOVR.pgc new file mode 100644 index 0000000..2a78a29 --- /dev/null +++ b/app/src/ac/svc/ACQ_LIMITOVR.pgc @@ -0,0 +1,26 @@ +/* + * ACQ_LIMITOVR.pgc - ac 모듈 서비스 ACQ_LIMITOVR (one service = one file). + * 매입 한도 초과 승인: 한도 초과로 보류(H)된 매입을 관리자가 예외 승인해 + * 접수(A)로 되살린다. 승인 표식(net 유지)만 남기고 반영 건수를 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_LIMITOVR.h" + +void ACQ_LIMITOVR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_aff; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + + EXEC SQL UPDATE purchase SET status = 'A', updated_at = now() + WHERE purchase_id = :h_pid AND status = 'H' AND channel <> 'RSV'; + if (sqlca.sqlcode < 0) { userlog("ACQ_LIMITOVR FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + h_aff = sqlca.sqlerrd[2]; + if (h_aff > 0) Bchg(b, T_STATUS, 0, "A", 0L); + setl(b, T_COUNT, h_aff); + setl(b, T_RC, h_aff > 0 ? 0 : 2); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_PROMOQRY.h b/app/src/ac/svc/ACQ_PROMOQRY.h new file mode 100644 index 0000000..88fc424 --- /dev/null +++ b/app/src/ac/svc/ACQ_PROMOQRY.h @@ -0,0 +1,18 @@ +/* + * ACQ_PROMOQRY.h - ac 서비스 ACQ_PROMOQRY 카피북 (copybook / record header). + * 프로모션 조회: 영업일 프로모션(category=PROMO) 매입의 건수와 매출, 감면 + */ +#ifndef ACQ_PROMOQRY_H +#define ACQ_PROMOQRY_H + +#include "ac_svc.h" + +typedef struct { + long count; + long gross; + long fee; +} acq_promoqry_rec_t; + +void ACQ_PROMOQRY(TPSVCINFO *p); + +#endif /* ACQ_PROMOQRY_H */ diff --git a/app/src/ac/svc/ACQ_PROMOQRY.pgc b/app/src/ac/svc/ACQ_PROMOQRY.pgc new file mode 100644 index 0000000..d0e90cb --- /dev/null +++ b/app/src/ac/svc/ACQ_PROMOQRY.pgc @@ -0,0 +1,29 @@ +/* + * ACQ_PROMOQRY.pgc - ac 모듈 서비스 ACQ_PROMOQRY (one service = one file). + * 프로모션 조회: 영업일 프로모션(category=PROMO) 매입의 건수와 매출, 감면 + * 수수료 합계를 조회해 행사 성과를 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_PROMOQRY.h" + +void ACQ_PROMOQRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_n = 0, h_sum = 0, h_fee = 0; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT count(*), coalesce(sum(amount),0), coalesce(sum(fee),0) + INTO :h_n, :h_sum, :h_fee FROM purchase + WHERE category = 'PROMO' AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_n); + setl(b, T_GROSS, h_sum); + setl(b, T_FEE, h_fee); + setl(b, T_RC, h_n > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_PROMOSTAT.h b/app/src/ac/svc/ACQ_PROMOSTAT.h new file mode 100644 index 0000000..679ec2a --- /dev/null +++ b/app/src/ac/svc/ACQ_PROMOSTAT.h @@ -0,0 +1,18 @@ +/* + * ACQ_PROMOSTAT.h - ac 서비스 ACQ_PROMOSTAT 카피북 (copybook / record header). + * 프로모션 통계: 영업일 업종/분류별 매출을 그룹 집계해 분류코드·매출 쌍을 + */ +#ifndef ACQ_PROMOSTAT_H +#define ACQ_PROMOSTAT_H + +#include "ac_svc.h" + +typedef struct { + char category[32]; + long grand; + long promo; +} acq_promostat_rec_t; + +void ACQ_PROMOSTAT(TPSVCINFO *p); + +#endif /* ACQ_PROMOSTAT_H */ diff --git a/app/src/ac/svc/ACQ_PROMOSTAT.pgc b/app/src/ac/svc/ACQ_PROMOSTAT.pgc new file mode 100644 index 0000000..e9113cd --- /dev/null +++ b/app/src/ac/svc/ACQ_PROMOSTAT.pgc @@ -0,0 +1,40 @@ +/* + * ACQ_PROMOSTAT.pgc - ac 모듈 서비스 ACQ_PROMOSTAT (one service = one file). + * 프로모션 통계: 영업일 업종/분류별 매출을 그룹 집계해 분류코드·매출 쌍을 + * occurrence 로 내리고 프로모션 분류가 차지하는 비중을 계산한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_PROMOSTAT.h" + +void ACQ_PROMOSTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long grand = 0, promo = 0, kinds = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_sum; + char h_cat[32], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL DECLARE pms_cur CURSOR FOR + SELECT category, sum(amount) FROM purchase WHERE biz_date = :h_bizdate + GROUP BY category ORDER BY sum(amount) DESC; + EXEC SQL OPEN pms_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH pms_cur INTO :h_cat, :h_sum; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pms_cur; FAIL(b); } + if (kinds < 8) { Badd(b, T_CATEGORY, h_cat, 0L); Badd(b, T_AMT1, (char *)&h_sum, 0L); } + grand += h_sum; + if (strcmp(h_cat, "PROMO") == 0) promo = h_sum; + kinds++; + } + EXEC SQL CLOSE pms_cur; + + setl(b, T_GROSS, grand); + setl(b, T_AMT2, promo); + setl(b, T_RC, grand > 0 ? promo * 100 / grand : 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_QTRSTAT.h b/app/src/ac/svc/ACQ_QTRSTAT.h new file mode 100644 index 0000000..6df62f7 --- /dev/null +++ b/app/src/ac/svc/ACQ_QTRSTAT.h @@ -0,0 +1,18 @@ +/* + * ACQ_QTRSTAT.h - ac 서비스 ACQ_QTRSTAT 카피북 (copybook / record header). + * 매입 분기별 통계: 지정 연도(T_ARG1)의 매입 매출을 1~4분기로 나눠 분기별 + */ +#ifndef ACQ_QTRSTAT_H +#define ACQ_QTRSTAT_H + +#include "ac_svc.h" + +typedef struct { + long year; + long q[4]; + long gross; +} acq_qtrstat_rec_t; + +void ACQ_QTRSTAT(TPSVCINFO *p); + +#endif /* ACQ_QTRSTAT_H */ diff --git a/app/src/ac/svc/ACQ_QTRSTAT.pgc b/app/src/ac/svc/ACQ_QTRSTAT.pgc new file mode 100644 index 0000000..84e6c7f --- /dev/null +++ b/app/src/ac/svc/ACQ_QTRSTAT.pgc @@ -0,0 +1,35 @@ +/* + * ACQ_QTRSTAT.pgc - ac 모듈 서비스 ACQ_QTRSTAT (one service = one file). + * 매입 분기별 통계: 지정 연도(T_ARG1)의 매입 매출을 1~4분기로 나눠 분기별 + * 합계를 한 번의 집계로 산출하고 연간 총매출을 함께 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_QTRSTAT.h" + +void ACQ_QTRSTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long yr; + EXEC SQL BEGIN DECLARE SECTION; + long h_q1 = 0, h_q2 = 0, h_q3 = 0, h_q4 = 0; + int h_yr; + EXEC SQL END DECLARE SECTION; + + yr = getl(b, T_ARG1); if (yr < 2000) yr = 2026; + h_yr = (int)yr; + + EXEC SQL SELECT coalesce(sum(amount) FILTER (WHERE extract(quarter from biz_date) = 1),0), + coalesce(sum(amount) FILTER (WHERE extract(quarter from biz_date) = 2),0), + coalesce(sum(amount) FILTER (WHERE extract(quarter from biz_date) = 3),0), + coalesce(sum(amount) FILTER (WHERE extract(quarter from biz_date) = 4),0) + INTO :h_q1, :h_q2, :h_q3, :h_q4 FROM purchase + WHERE extract(year from biz_date) = :h_yr; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_ARG2, h_q1); + setl(b, T_ARG3, h_q2); + setl(b, T_ARG4, h_q3); + setl(b, T_ARG5, h_q4); + setl(b, T_GROSS, h_q1 + h_q2 + h_q3 + h_q4); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_REBUYAPPR.h b/app/src/ac/svc/ACQ_REBUYAPPR.h new file mode 100644 index 0000000..534b8a2 --- /dev/null +++ b/app/src/ac/svc/ACQ_REBUYAPPR.h @@ -0,0 +1,18 @@ +/* + * ACQ_REBUYAPPR.h - ac 서비스 ACQ_REBUYAPPR 카피북 (copybook / record header). + * 재매입 승인: 취소(C)된 원매입에 한해 승인 원장(approval)에 재승인 행을 + */ +#ifndef ACQ_REBUYAPPR_H +#define ACQ_REBUYAPPR_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long approval_id; + long rc; +} acq_rebuyappr_rec_t; + +void ACQ_REBUYAPPR(TPSVCINFO *p); + +#endif /* ACQ_REBUYAPPR_H */ diff --git a/app/src/ac/svc/ACQ_REBUYAPPR.pgc b/app/src/ac/svc/ACQ_REBUYAPPR.pgc new file mode 100644 index 0000000..88c6833 --- /dev/null +++ b/app/src/ac/svc/ACQ_REBUYAPPR.pgc @@ -0,0 +1,34 @@ +/* + * ACQ_REBUYAPPR.pgc - ac 모듈 서비스 ACQ_REBUYAPPR (one service = one file). + * 재매입 승인: 취소(C)된 원매입에 한해 승인 원장(approval)에 재승인 행을 + * 남기고 원매입을 접수(A)로 되살려 재매입을 개시한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_REBUYAPPR.h" + +void ACQ_REBUYAPPR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_aid; + char st[4]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + + EXEC SQL SELECT status INTO :st FROM purchase WHERE purchase_id = :h_pid; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 5); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (st[0] != 'C') { setl(b, T_RC, 2); FAIL(b); } + + EXEC SQL SELECT nextval('approval_seq') INTO :h_aid; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO approval (approval_id, purchase_id, approved) VALUES (:h_aid, :h_pid, true); + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL UPDATE purchase SET status = 'A', updated_at = now() WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0) { userlog("ACQ_REBUYAPPR FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + + setl(b, T_ID1, h_aid); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_REBUYHIST.h b/app/src/ac/svc/ACQ_REBUYHIST.h new file mode 100644 index 0000000..7cb3f14 --- /dev/null +++ b/app/src/ac/svc/ACQ_REBUYHIST.h @@ -0,0 +1,18 @@ +/* + * ACQ_REBUYHIST.h - ac 서비스 ACQ_REBUYHIST 카피북 (copybook / record header). + * 재매입 이력: 한 매입의 승인 원장을 승인시각 순으로 커서 조회해 승인 ID를 + */ +#ifndef ACQ_REBUYHIST_H +#define ACQ_REBUYHIST_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long count; + long rc; +} acq_rebuyhist_rec_t; + +void ACQ_REBUYHIST(TPSVCINFO *p); + +#endif /* ACQ_REBUYHIST_H */ diff --git a/app/src/ac/svc/ACQ_REBUYHIST.pgc b/app/src/ac/svc/ACQ_REBUYHIST.pgc new file mode 100644 index 0000000..ac35412 --- /dev/null +++ b/app/src/ac/svc/ACQ_REBUYHIST.pgc @@ -0,0 +1,37 @@ +/* + * ACQ_REBUYHIST.pgc - ac 모듈 서비스 ACQ_REBUYHIST (one service = one file). + * 재매입 이력: 한 매입의 승인 원장을 승인시각 순으로 커서 조회해 승인 ID를 + * occurrence 로 내리고 총 승인 횟수와 최근 승인 여부를 계산한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_REBUYHIST.h" + +void ACQ_REBUYHIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long n = 0; int last = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_aid; + int h_ok; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + + EXEC SQL DECLARE rbh_cur CURSOR FOR + SELECT approval_id, approved FROM approval WHERE purchase_id = :h_pid + ORDER BY approved_at; + EXEC SQL OPEN rbh_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH rbh_cur INTO :h_aid, :h_ok; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE rbh_cur; FAIL(b); } + Badd(b, T_ID1, (char *)&h_aid, 0L); + last = h_ok; n++; + } + EXEC SQL CLOSE rbh_cur; + + setl(b, T_COUNT, n); + setl(b, T_RC, last ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_REBUYREJ.h b/app/src/ac/svc/ACQ_REBUYREJ.h new file mode 100644 index 0000000..7822386 --- /dev/null +++ b/app/src/ac/svc/ACQ_REBUYREJ.h @@ -0,0 +1,18 @@ +/* + * ACQ_REBUYREJ.h - ac 서비스 ACQ_REBUYREJ 카피북 (copybook / record header). + * 재매입 거절: 재매입 요청을 반려하고 미매입 원장에 REBUY_REJ 사유로 + */ +#ifndef ACQ_REBUYREJ_H +#define ACQ_REBUYREJ_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long unmatch_id; + long rc; +} acq_rebuyrej_rec_t; + +void ACQ_REBUYREJ(TPSVCINFO *p); + +#endif /* ACQ_REBUYREJ_H */ diff --git a/app/src/ac/svc/ACQ_REBUYREJ.pgc b/app/src/ac/svc/ACQ_REBUYREJ.pgc new file mode 100644 index 0000000..efe97d2 --- /dev/null +++ b/app/src/ac/svc/ACQ_REBUYREJ.pgc @@ -0,0 +1,32 @@ +/* + * ACQ_REBUYREJ.pgc - ac 모듈 서비스 ACQ_REBUYREJ (one service = one file). + * 재매입 거절: 재매입 요청을 반려하고 미매입 원장에 REBUY_REJ 사유로 + * 거절 이력을 남긴다. 반려 사유는 T_REASON 을 덧붙인다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_REBUYREJ.h" + +void ACQ_REBUYREJ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char why[80]; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_pid; + char h_reason[100], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + gets_(b, T_REASON, why, sizeof(why)); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + snprintf(h_reason, sizeof(h_reason), "REBUY_REJ:%s", why[0] ? why : "POLICY"); + + EXEC SQL SELECT nextval('ac_unmatch_seq') INTO :h_id; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO ac_unmatch (unmatch_id, purchase_id, reason, biz_date) + VALUES (:h_id, :h_pid, :h_reason, :h_bizdate); + if (sqlca.sqlcode < 0) { userlog("ACQ_REBUYREJ FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + + setl(b, T_ID1, h_id); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_RETURNQRY.h b/app/src/ac/svc/ACQ_RETURNQRY.h new file mode 100644 index 0000000..df7d874 --- /dev/null +++ b/app/src/ac/svc/ACQ_RETURNQRY.h @@ -0,0 +1,18 @@ +/* + * ACQ_RETURNQRY.h - ac 서비스 ACQ_RETURNQRY 카피북 (copybook / record header). + * 매입 반품 조회: 가맹점의 영업일 반품/교환(RETURN·EXCHANGE) 취소 이력을 + */ +#ifndef ACQ_RETURNQRY_H +#define ACQ_RETURNQRY_H + +#include "ac_svc.h" + +typedef struct { + long count; + long total; + long rc; +} acq_returnqry_rec_t; + +void ACQ_RETURNQRY(TPSVCINFO *p); + +#endif /* ACQ_RETURNQRY_H */ diff --git a/app/src/ac/svc/ACQ_RETURNQRY.pgc b/app/src/ac/svc/ACQ_RETURNQRY.pgc new file mode 100644 index 0000000..04b9f4c --- /dev/null +++ b/app/src/ac/svc/ACQ_RETURNQRY.pgc @@ -0,0 +1,39 @@ +/* + * ACQ_RETURNQRY.pgc - ac 모듈 서비스 ACQ_RETURNQRY (one service = one file). + * 매입 반품 조회: 가맹점의 영업일 반품/교환(RETURN·EXCHANGE) 취소 이력을 + * 커서로 조회해 취소 ID·금액 쌍을 occurrence 로 내리고 총 반품액을 합산한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_RETURNQRY.h" + +void ACQ_RETURNQRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long n = 0, tot = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cid, h_amt; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL DECLARE rtq_cur CURSOR FOR + SELECT cancel_id, amount FROM ac_cancel + WHERE biz_date = :h_bizdate AND cancel_type IN ('RETURN', 'EXCHANGE') + ORDER BY cancel_id; + EXEC SQL OPEN rtq_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH rtq_cur INTO :h_cid, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE rtq_cur; FAIL(b); } + if (n < 8) { Badd(b, T_CANCEL_ID, (char *)&h_cid, 0L); Badd(b, T_AMT1, (char *)&h_amt, 0L); } + n++; tot += h_amt; + } + EXEC SQL CLOSE rtq_cur; + + setl(b, T_COUNT, n); + setl(b, T_GROSS, tot); + setl(b, T_RC, n > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_RETURNSTAT.h b/app/src/ac/svc/ACQ_RETURNSTAT.h new file mode 100644 index 0000000..ea6847c --- /dev/null +++ b/app/src/ac/svc/ACQ_RETURNSTAT.h @@ -0,0 +1,18 @@ +/* + * ACQ_RETURNSTAT.h - ac 서비스 ACQ_RETURNSTAT 카피북 (copybook / record header). + * 매입 반품 통계: 영업일 취소 원장을 반품/교환/환불 유형별로 한 번에 분해 + */ +#ifndef ACQ_RETURNSTAT_H +#define ACQ_RETURNSTAT_H + +#include "ac_svc.h" + +typedef struct { + long returns; + long exchanges; + long refunds; +} acq_returnstat_rec_t; + +void ACQ_RETURNSTAT(TPSVCINFO *p); + +#endif /* ACQ_RETURNSTAT_H */ diff --git a/app/src/ac/svc/ACQ_RETURNSTAT.pgc b/app/src/ac/svc/ACQ_RETURNSTAT.pgc new file mode 100644 index 0000000..6d36096 --- /dev/null +++ b/app/src/ac/svc/ACQ_RETURNSTAT.pgc @@ -0,0 +1,31 @@ +/* + * ACQ_RETURNSTAT.pgc - ac 모듈 서비스 ACQ_RETURNSTAT (one service = one file). + * 매입 반품 통계: 영업일 취소 원장을 반품/교환/환불 유형별로 한 번에 분해 + * 집계해 유형별 건수와 금액을 산출하고 전체 반품 금액을 함께 낸다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_RETURNSTAT.h" + +void ACQ_RETURNSTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_rn = 0, h_ex = 0, h_rf = 0, h_amt = 0; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT count(*) FILTER (WHERE cancel_type = 'RETURN'), + count(*) FILTER (WHERE cancel_type = 'EXCHANGE'), + count(*) FILTER (WHERE cancel_type = 'REFUND'), + coalesce(sum(amount) FILTER (WHERE cancel_type IN ('RETURN','EXCHANGE','REFUND')),0) + INTO :h_rn, :h_ex, :h_rf, :h_amt FROM ac_cancel WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_ARG1, h_rn); + setl(b, T_ARG2, h_ex); + setl(b, T_ARG3, h_rf); + setl(b, T_GROSS, h_amt); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_SETTLEEXP.h b/app/src/ac/svc/ACQ_SETTLEEXP.h new file mode 100644 index 0000000..5ed4cf6 --- /dev/null +++ b/app/src/ac/svc/ACQ_SETTLEEXP.h @@ -0,0 +1,18 @@ +/* + * ACQ_SETTLEEXP.h - ac 서비스 ACQ_SETTLEEXP 카피북 (copybook / record header). + * 매입 정산 예정 조회: 가맹점의 대사완료(M) 매입 건수와 순정산 예정 총액을 + */ +#ifndef ACQ_SETTLEEXP_H +#define ACQ_SETTLEEXP_H + +#include "ac_svc.h" + +typedef struct { + char merchant[64]; + long count; + long net; +} acq_settleexp_rec_t; + +void ACQ_SETTLEEXP(TPSVCINFO *p); + +#endif /* ACQ_SETTLEEXP_H */ diff --git a/app/src/ac/svc/ACQ_SETTLEEXP.pgc b/app/src/ac/svc/ACQ_SETTLEEXP.pgc new file mode 100644 index 0000000..178dd3a --- /dev/null +++ b/app/src/ac/svc/ACQ_SETTLEEXP.pgc @@ -0,0 +1,28 @@ +/* + * ACQ_SETTLEEXP.pgc - ac 모듈 서비스 ACQ_SETTLEEXP (one service = one file). + * 매입 정산 예정 조회: 가맹점의 대사완료(M) 매입 건수와 순정산 예정 총액을 + * 조회해 다음 정산 사이클의 예상 지급액을 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_SETTLEEXP.h" + +void ACQ_SETTLEEXP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_n = 0, h_net = 0; + 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_n, :h_net FROM purchase + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status = 'M'; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_n); + setl(b, T_NET, h_net); + setl(b, T_RC, h_n > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_SETTLEPRE.h b/app/src/ac/svc/ACQ_SETTLEPRE.h new file mode 100644 index 0000000..3b4914f --- /dev/null +++ b/app/src/ac/svc/ACQ_SETTLEPRE.h @@ -0,0 +1,18 @@ +/* + * ACQ_SETTLEPRE.h - ac 서비스 ACQ_SETTLEPRE 카피북 (copybook / record header). + * 매입-정산 예정대사: 가맹점의 영업일 대사완료(M) 매입 순정산 예정액과 이미 + */ +#ifndef ACQ_SETTLEPRE_H +#define ACQ_SETTLEPRE_H + +#include "ac_svc.h" + +typedef struct { + char merchant[64]; + long expected; + long settled; +} acq_settlepre_rec_t; + +void ACQ_SETTLEPRE(TPSVCINFO *p); + +#endif /* ACQ_SETTLEPRE_H */ diff --git a/app/src/ac/svc/ACQ_SETTLEPRE.pgc b/app/src/ac/svc/ACQ_SETTLEPRE.pgc new file mode 100644 index 0000000..fa77991 --- /dev/null +++ b/app/src/ac/svc/ACQ_SETTLEPRE.pgc @@ -0,0 +1,34 @@ +/* + * ACQ_SETTLEPRE.pgc - ac 모듈 서비스 ACQ_SETTLEPRE (one service = one file). + * 매입-정산 예정대사: 가맹점의 영업일 대사완료(M) 매입 순정산 예정액과 이미 + * 정산(settlement)된 금액을 비교해 미정산 차액과 대사 일치 여부를 낸다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_SETTLEPRE.h" + +void ACQ_SETTLEPRE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long diff; + EXEC SQL BEGIN DECLARE SECTION; + long h_exp = 0, h_done = 0; + 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 coalesce(sum(net),0) INTO :h_exp FROM purchase + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status = 'M'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_done FROM settlement + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + diff = h_exp - h_done; + + setl(b, T_NET, h_exp); + setl(b, T_AMT1, h_done); + setl(b, T_DELTA, diff); + setl(b, T_RC, diff == 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_TAXMODIFY.h b/app/src/ac/svc/ACQ_TAXMODIFY.h new file mode 100644 index 0000000..59fc37e --- /dev/null +++ b/app/src/ac/svc/ACQ_TAXMODIFY.h @@ -0,0 +1,18 @@ +/* + * ACQ_TAXMODIFY.h - ac 서비스 ACQ_TAXMODIFY 카피북 (copybook / record header). + * 세금계산서 수정발행: 기 발행된 계산서(ac_tax_invoice)의 공급가·부가세를 + */ +#ifndef ACQ_TAXMODIFY_H +#define ACQ_TAXMODIFY_H + +#include "ac_svc.h" + +typedef struct { + long inv_id; + long supply; + long vat; +} acq_taxmodify_rec_t; + +void ACQ_TAXMODIFY(TPSVCINFO *p); + +#endif /* ACQ_TAXMODIFY_H */ diff --git a/app/src/ac/svc/ACQ_TAXMODIFY.pgc b/app/src/ac/svc/ACQ_TAXMODIFY.pgc new file mode 100644 index 0000000..4c846bf --- /dev/null +++ b/app/src/ac/svc/ACQ_TAXMODIFY.pgc @@ -0,0 +1,32 @@ +/* + * ACQ_TAXMODIFY.pgc - ac 모듈 서비스 ACQ_TAXMODIFY (one service = one file). + * 세금계산서 수정발행: 기 발행된 계산서(ac_tax_invoice)의 공급가·부가세를 + * 정정 금액으로 갱신한다. 부가세는 공급가의 10%로 재계산해 반영한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_TAXMODIFY.h" + +void ACQ_TAXMODIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long supply, vat; + EXEC SQL BEGIN DECLARE SECTION; + long h_inv, h_supply, h_vat, h_aff; + EXEC SQL END DECLARE SECTION; + + h_inv = getl(b, T_ID1); + supply = getl(b, T_GROSS); + if (supply <= 0) { setl(b, T_RC, 3); FAIL(b); } + vat = supply / 10; + h_supply = supply; h_vat = vat; + + EXEC SQL UPDATE ac_tax_invoice SET supply = :h_supply, vat = :h_vat + WHERE inv_id = :h_inv; + if (sqlca.sqlcode < 0) { userlog("ACQ_TAXMODIFY FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + h_aff = sqlca.sqlerrd[2]; + if (h_aff == 0) { setl(b, T_RC, 5); FAIL(b); } + setl(b, T_GROSS, supply); + setl(b, T_TAX, vat); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_TAXQRY.h b/app/src/ac/svc/ACQ_TAXQRY.h new file mode 100644 index 0000000..8110efa --- /dev/null +++ b/app/src/ac/svc/ACQ_TAXQRY.h @@ -0,0 +1,18 @@ +/* + * ACQ_TAXQRY.h - ac 서비스 ACQ_TAXQRY 카피북 (copybook / record header). + * 세금계산서 조회: 한 매입에 발행된 계산서의 공급가·부가세 누계와 발행 + */ +#ifndef ACQ_TAXQRY_H +#define ACQ_TAXQRY_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long supply; + long vat; +} acq_taxqry_rec_t; + +void ACQ_TAXQRY(TPSVCINFO *p); + +#endif /* ACQ_TAXQRY_H */ diff --git a/app/src/ac/svc/ACQ_TAXQRY.pgc b/app/src/ac/svc/ACQ_TAXQRY.pgc new file mode 100644 index 0000000..23ee058 --- /dev/null +++ b/app/src/ac/svc/ACQ_TAXQRY.pgc @@ -0,0 +1,27 @@ +/* + * ACQ_TAXQRY.pgc - ac 모듈 서비스 ACQ_TAXQRY (one service = one file). + * 세금계산서 조회: 한 매입에 발행된 계산서의 공급가·부가세 누계와 발행 + * 건수(역발행 포함)를 조회해 순 공급가를 계산한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_TAXQRY.h" + +void ACQ_TAXQRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_supply = 0, h_vat = 0, h_n = 0; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + + EXEC SQL SELECT coalesce(sum(supply),0), coalesce(sum(vat),0), count(*) + INTO :h_supply, :h_vat, :h_n FROM ac_tax_invoice WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_GROSS, h_supply); + setl(b, T_TAX, h_vat); + setl(b, T_COUNT, h_n); + setl(b, T_RC, h_n > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_TAXREVERSE.h b/app/src/ac/svc/ACQ_TAXREVERSE.h new file mode 100644 index 0000000..8de8208 --- /dev/null +++ b/app/src/ac/svc/ACQ_TAXREVERSE.h @@ -0,0 +1,18 @@ +/* + * ACQ_TAXREVERSE.h - ac 서비스 ACQ_TAXREVERSE 카피북 (copybook / record header). + * 세금계산서 역발행: 매출 취소분에 대한 (-)공급가 계산서를 새로 발행한다. + */ +#ifndef ACQ_TAXREVERSE_H +#define ACQ_TAXREVERSE_H + +#include "ac_svc.h" + +typedef struct { + long src_inv; + long new_inv; + long supply; +} acq_taxreverse_rec_t; + +void ACQ_TAXREVERSE(TPSVCINFO *p); + +#endif /* ACQ_TAXREVERSE_H */ diff --git a/app/src/ac/svc/ACQ_TAXREVERSE.pgc b/app/src/ac/svc/ACQ_TAXREVERSE.pgc new file mode 100644 index 0000000..ae2db8f --- /dev/null +++ b/app/src/ac/svc/ACQ_TAXREVERSE.pgc @@ -0,0 +1,34 @@ +/* + * ACQ_TAXREVERSE.pgc - ac 모듈 서비스 ACQ_TAXREVERSE (one service = one file). + * 세금계산서 역발행: 매출 취소분에 대한 (-)공급가 계산서를 새로 발행한다. + * 원 계산서의 공급가·부가세 부호를 뒤집어 상계 계산서를 생성한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_TAXREVERSE.h" + +void ACQ_TAXREVERSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_src, h_new, h_pid = 0, h_supply = 0, h_vat = 0; + char h_merch[64], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_src = getl(b, T_ID1); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT purchase_id, merchant_id, supply, vat + INTO :h_pid, :h_merch, :h_supply, :h_vat FROM ac_tax_invoice WHERE inv_id = :h_src; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 5); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT nextval('ac_taxinv_seq') INTO :h_new; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO ac_tax_invoice (inv_id, purchase_id, merchant_id, supply, vat, biz_date) + VALUES (:h_new, :h_pid, :h_merch, -:h_supply, -:h_vat, :h_bizdate); + if (sqlca.sqlcode < 0) { userlog("ACQ_TAXREVERSE FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + + setl(b, T_ID2, h_new); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/au/au_svr.pgc b/app/src/au/au_svr.pgc index ab51f18..94c212f 100644 --- a/app/src/au/au_svr.pgc +++ b/app/src/au/au_svr.pgc @@ -94,6 +94,65 @@ #include "RISK_SCORE_INQ.h" #include "APPROVAL_RATE.h" #include "AUTH_DUP_CHK.h" +/* --- scale-out services (승인/한도 심화) --- */ +#include "LIMIT_MONTH_CHK.h" +#include "LIMIT_MONTH_INQ.h" +#include "LIMIT_PERTX_CHK.h" +#include "LIMIT_CUMUL_INQ.h" +#include "LIMIT_DAILY_INQ.h" +#include "LIMIT_ADJUST.h" +#include "LIMIT_FREEZE.h" +#include "LIMIT_UNFREEZE.h" +#include "LIMIT_TIER_INQ.h" +#include "LIMIT_OVER_ALERT.h" +#include "CARD_LOST_REG.h" +#include "CARD_STOLEN_REG.h" +#include "CARD_REACTIVATE.h" +#include "CARD_STATE_HIST.h" +#include "CARD_EXPIRE_CHK.h" +#include "CARD_ACTIVATE.h" +#include "CHANNEL_POLICY.h" +#include "BRAND_POLICY.h" +#include "MCC_POLICY.h" +#include "CHANNEL_STAT.h" +#include "BRAND_AUTH_STAT.h" +#include "MCC_AUTH_STAT.h" +#include "AUTH_PARTAPPR.h" +#include "PREAUTH_PARTUSE.h" +#include "CANCEL_AMT_INQ.h" +#include "CANCEL_FULL.h" +#include "CANCEL_HISTORY.h" +#include "CANCEL_SUMMARY.h" +#include "VELOCITY_DAY.h" +#include "VELOCITY_AMT.h" +#include "VELOCITY_MCC.h" +#include "VELOCITY_CHAN.h" +#include "VELOCITY_RESET.h" +#include "FRAUD_RULE_NIGHT.h" +#include "FRAUD_RULE_INTL.h" +#include "FRAUD_RULE_MCC.h" +#include "FRAUD_SCORE_SUM.h" +#include "FRAUD_CASE_CLOSE.h" +#include "FRAUD_CASE_LIST.h" +#include "WHITELIST_ADD.h" +#include "WHITELIST_DEL.h" +#include "WHITELIST_CHK.h" +#include "BLACKLIST_COUNT.h" +#include "BLACKLIST_PROMO.h" +#include "TOKEN_ISSUE.h" +#include "TOKEN_REVOKE.h" +#include "TOKEN_INQ.h" +#include "KEY_ROTATE_LOG.h" +#include "STANDIN_STAT.h" +#include "STANDIN_RECON.h" +#include "STANDIN_LIMIT.h" +#include "STAT_DECLINE.h" +#include "STAT_DAILY_SUM.h" +#include "STAT_AMT_BAND.h" +#include "STAT_APPROVE_TR.h" +#include "AUTH_REPROC_REQ.h" +#include "AUTH_REPROC_STAT.h" +#include "LIMIT_EXHAUST_LST.h" /* ----- advertise table: {service name, function} (fn is extern from its file) */ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { @@ -135,6 +194,36 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"MERCH_AUTH_RANK", MERCH_AUTH_RANK}, {"CARD_DAILY_RPT", CARD_DAILY_RPT}, {"RISK_SCORE_INQ", RISK_SCORE_INQ}, {"APPROVAL_RATE", APPROVAL_RATE}, {"AUTH_DUP_CHK", AUTH_DUP_CHK}, + /* --- scale-out services (승인/한도 심화) --- */ + {"LIMIT_MONTH_CHK", LIMIT_MONTH_CHK}, {"LIMIT_MONTH_INQ", LIMIT_MONTH_INQ}, + {"LIMIT_PERTX_CHK", LIMIT_PERTX_CHK}, {"LIMIT_CUMUL_INQ", LIMIT_CUMUL_INQ}, + {"LIMIT_DAILY_INQ", LIMIT_DAILY_INQ}, {"LIMIT_ADJUST", LIMIT_ADJUST}, + {"LIMIT_FREEZE", LIMIT_FREEZE}, {"LIMIT_UNFREEZE", LIMIT_UNFREEZE}, + {"LIMIT_TIER_INQ", LIMIT_TIER_INQ}, {"LIMIT_OVER_ALERT", LIMIT_OVER_ALERT}, + {"CARD_LOST_REG", CARD_LOST_REG}, {"CARD_STOLEN_REG", CARD_STOLEN_REG}, + {"CARD_REACTIVATE", CARD_REACTIVATE}, {"CARD_STATE_HIST", CARD_STATE_HIST}, + {"CARD_EXPIRE_CHK", CARD_EXPIRE_CHK}, {"CARD_ACTIVATE", CARD_ACTIVATE}, + {"CHANNEL_POLICY", CHANNEL_POLICY}, {"BRAND_POLICY", BRAND_POLICY}, + {"MCC_POLICY", MCC_POLICY}, {"CHANNEL_STAT", CHANNEL_STAT}, + {"BRAND_AUTH_STAT", BRAND_AUTH_STAT}, {"MCC_AUTH_STAT", MCC_AUTH_STAT}, + {"AUTH_PARTAPPR", AUTH_PARTAPPR}, {"PREAUTH_PARTUSE", PREAUTH_PARTUSE}, + {"CANCEL_AMT_INQ", CANCEL_AMT_INQ}, {"CANCEL_FULL", CANCEL_FULL}, + {"CANCEL_HISTORY", CANCEL_HISTORY}, {"CANCEL_SUMMARY", CANCEL_SUMMARY}, + {"VELOCITY_DAY", VELOCITY_DAY}, {"VELOCITY_AMT", VELOCITY_AMT}, + {"VELOCITY_MCC", VELOCITY_MCC}, {"VELOCITY_CHAN", VELOCITY_CHAN}, + {"VELOCITY_RESET", VELOCITY_RESET}, {"FRAUD_RULE_NIGHT", FRAUD_RULE_NIGHT}, + {"FRAUD_RULE_INTL", FRAUD_RULE_INTL}, {"FRAUD_RULE_MCC", FRAUD_RULE_MCC}, + {"FRAUD_SCORE_SUM", FRAUD_SCORE_SUM}, {"FRAUD_CASE_CLOSE", FRAUD_CASE_CLOSE}, + {"FRAUD_CASE_LIST", FRAUD_CASE_LIST}, {"WHITELIST_ADD", WHITELIST_ADD}, + {"WHITELIST_DEL", WHITELIST_DEL}, {"WHITELIST_CHK", WHITELIST_CHK}, + {"BLACKLIST_COUNT", BLACKLIST_COUNT}, {"BLACKLIST_PROMO", BLACKLIST_PROMO}, + {"TOKEN_ISSUE", TOKEN_ISSUE}, {"TOKEN_REVOKE", TOKEN_REVOKE}, + {"TOKEN_INQ", TOKEN_INQ}, {"KEY_ROTATE_LOG", KEY_ROTATE_LOG}, + {"STANDIN_STAT", STANDIN_STAT}, {"STANDIN_RECON", STANDIN_RECON}, + {"STANDIN_LIMIT", STANDIN_LIMIT}, {"STAT_DECLINE", STAT_DECLINE}, + {"STAT_DAILY_SUM", STAT_DAILY_SUM}, {"STAT_AMT_BAND", STAT_AMT_BAND}, + {"STAT_APPROVE_TR", STAT_APPROVE_TR}, {"AUTH_REPROC_REQ", AUTH_REPROC_REQ}, + {"AUTH_REPROC_STAT", AUTH_REPROC_STAT}, {"LIMIT_EXHAUST_LST", LIMIT_EXHAUST_LST}, {NULL, NULL} }; diff --git a/app/src/au/batch/au_brand_mix_batch.h b/app/src/au/batch/au_brand_mix_batch.h new file mode 100644 index 0000000..b0f8d4e --- /dev/null +++ b/app/src/au/batch/au_brand_mix_batch.h @@ -0,0 +1,16 @@ +/* + * au_brand_mix_batch.h - au 배치 au_brand_mix_batch 카피북 (batch copybook / record layout). + * 배치가 EXEC SQL INCLUDE 로 인라인하는 레코드 구조 정의. 런타임 인클루드는 + * 배치 본문(.pgc)이 직접 가진다. dbio(libaudbio.a) 경유, XA 브랜치 실행. + */ +#ifndef AU_BRAND_MIX_BATCH_H +#define AU_BRAND_MIX_BATCH_H + +typedef struct { + char card_no[24]; + char biz_date[16]; + long amount; + long rows; +} au_au_brand_mix_batch_rec_t; + +#endif /* AU_BRAND_MIX_BATCH_H */ diff --git a/app/src/au/batch/au_brand_mix_batch.pgc b/app/src/au/batch/au_brand_mix_batch.pgc new file mode 100644 index 0000000..69ef725 --- /dev/null +++ b/app/src/au/batch/au_brand_mix_batch.pgc @@ -0,0 +1,54 @@ +/* + * au_brand_mix_batch.pgc - au 브랜드별 승인 믹스 집계 배치 (substr GROUP BY 커서 + XA). + * + * Groups the day's authorizations by card brand (leading digit) reporting count + * and amount, and accumulates the top brand by amount, under one global XA + * transaction. Read-only report. Usage: au_brand_mix_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "au_dbio.h" +EXEC SQL INCLUDE au_brand_mix_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long total = 0, top_amt = 0; + char top_lead[4] = ""; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_lead[4]; + long h_cnt, h_amt; + 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 bmx CURSOR FOR + SELECT substr(card_no, 1, 1), count(*), coalesce(sum(amount),0) + FROM au_authorization + WHERE biz_date = :h_bizdate AND status IN ('A','M','S') + GROUP BY substr(card_no, 1, 1) ORDER BY coalesce(sum(amount),0) DESC; + EXEC SQL OPEN bmx; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH bmx INTO :h_lead, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE bmx; tpabort(0); return 1; } + total += h_cnt; + if (h_amt > top_amt) { top_amt = h_amt; strncpy(top_lead, h_lead, sizeof(top_lead)-1); } + printf(" brand_lead=%s cnt=%ld amt=%ld\n", h_lead, h_cnt, h_amt); + } + EXEC SQL CLOSE bmx; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> au_brand_mix_batch COMMIT: bizdate=%s total=%ld top_lead=%s top_amt=%ld\n", + bizdate, total, top_lead, top_amt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/au/batch/au_card_dormant_batch.h b/app/src/au/batch/au_card_dormant_batch.h new file mode 100644 index 0000000..95e97ff --- /dev/null +++ b/app/src/au/batch/au_card_dormant_batch.h @@ -0,0 +1,16 @@ +/* + * au_card_dormant_batch.h - au 배치 au_card_dormant_batch 카피북 (batch copybook / record layout). + * 배치가 EXEC SQL INCLUDE 로 인라인하는 레코드 구조 정의. 런타임 인클루드는 + * 배치 본문(.pgc)이 직접 가진다. dbio(libaudbio.a) 경유, XA 브랜치 실행. + */ +#ifndef AU_CARD_DORMANT_BATCH_H +#define AU_CARD_DORMANT_BATCH_H + +typedef struct { + char card_no[24]; + char biz_date[16]; + long amount; + long rows; +} au_au_card_dormant_batch_rec_t; + +#endif /* AU_CARD_DORMANT_BATCH_H */ diff --git a/app/src/au/batch/au_card_dormant_batch.pgc b/app/src/au/batch/au_card_dormant_batch.pgc new file mode 100644 index 0000000..ee7c760 --- /dev/null +++ b/app/src/au/batch/au_card_dormant_batch.pgc @@ -0,0 +1,43 @@ +/* + * au_card_dormant_batch.pgc - au 휴면 카드 일괄 표시 배치 (집합 UPDATE + XA). + * + * Counts, then set-updates every ACTIVE card with no authorization in the last + * 90 days to status DORMANT in a single UPDATE, under one global XA transaction. + * No cursor, no dbio. Usage: au_card_dormant_batch + */ +#include +#include +#include +#include +#include "au_dbio.h" +EXEC SQL INCLUDE au_card_dormant_batch; + +int main(int argc, char **argv) +{ + EXEC SQL BEGIN DECLARE SECTION; + long h_cand = 0; + EXEC SQL END DECLARE SECTION; + + (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 SELECT count(*) INTO :h_cand FROM card_limit + WHERE status = 'ACTIVE' + AND card_no NOT IN (SELECT DISTINCT card_no FROM au_authorization + WHERE created_at >= now() - interval '90 days'); + if (sqlca.sqlcode < 0) { fprintf(stderr, "SELECT FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + + EXEC SQL UPDATE card_limit SET status = 'DORMANT', updated_at = now() + WHERE status = 'ACTIVE' + AND card_no NOT IN (SELECT DISTINCT card_no FROM au_authorization + WHERE created_at >= now() - interval '90 days'); + if (sqlca.sqlcode < 0) { fprintf(stderr, "UPDATE 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(">>> au_card_dormant_batch COMMIT: dormant_marked=%ld\n", h_cand); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/au/batch/au_channel_stat_batch.h b/app/src/au/batch/au_channel_stat_batch.h new file mode 100644 index 0000000..c995c26 --- /dev/null +++ b/app/src/au/batch/au_channel_stat_batch.h @@ -0,0 +1,16 @@ +/* + * au_channel_stat_batch.h - au 배치 au_channel_stat_batch 카피북 (batch copybook / record layout). + * 배치가 EXEC SQL INCLUDE 로 인라인하는 레코드 구조 정의. 런타임 인클루드는 + * 배치 본문(.pgc)이 직접 가진다. dbio(libaudbio.a) 경유, XA 브랜치 실행. + */ +#ifndef AU_CHANNEL_STAT_BATCH_H +#define AU_CHANNEL_STAT_BATCH_H + +typedef struct { + char card_no[24]; + char biz_date[16]; + long amount; + long rows; +} au_au_channel_stat_batch_rec_t; + +#endif /* AU_CHANNEL_STAT_BATCH_H */ diff --git a/app/src/au/batch/au_channel_stat_batch.pgc b/app/src/au/batch/au_channel_stat_batch.pgc new file mode 100644 index 0000000..bc2fbeb --- /dev/null +++ b/app/src/au/batch/au_channel_stat_batch.pgc @@ -0,0 +1,52 @@ +/* + * au_channel_stat_batch.pgc - au 채널별 매입 집계 배치 (GROUP BY 커서 + XA). + * + * Reports per-channel count / sum / average purchase amount for a business day, + * scanning a GROUP BY cursor under one global XA transaction. Read-only report. + * Usage: au_channel_stat_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "au_dbio.h" +EXEC SQL INCLUDE au_channel_stat_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long chans = 0, total = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_chan[16]; + long h_cnt, h_sum, h_avg; + 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 cstb CURSOR FOR + SELECT channel, count(*), coalesce(sum(amount),0), + CAST(coalesce(avg(amount),0) AS bigint) + FROM purchase WHERE biz_date = :h_bizdate + GROUP BY channel ORDER BY count(*) DESC; + EXEC SQL OPEN cstb; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH cstb INTO :h_chan, :h_cnt, :h_sum, :h_avg; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE cstb; tpabort(0); return 1; } + chans++; + total += h_cnt; + printf(" channel=%s cnt=%ld sum=%ld avg=%ld\n", h_chan, h_cnt, h_sum, h_avg); + } + EXEC SQL CLOSE cstb; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> au_channel_stat_batch COMMIT: bizdate=%s channels=%ld total=%ld\n", bizdate, chans, total); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/au/batch/au_exhaust_alert_batch.h b/app/src/au/batch/au_exhaust_alert_batch.h new file mode 100644 index 0000000..92daa99 --- /dev/null +++ b/app/src/au/batch/au_exhaust_alert_batch.h @@ -0,0 +1,16 @@ +/* + * au_exhaust_alert_batch.h - au 배치 au_exhaust_alert_batch 카피북 (batch copybook / record layout). + * 배치가 EXEC SQL INCLUDE 로 인라인하는 레코드 구조 정의. 런타임 인클루드는 + * 배치 본문(.pgc)이 직접 가진다. dbio(libaudbio.a) 경유, XA 브랜치 실행. + */ +#ifndef AU_EXHAUST_ALERT_BATCH_H +#define AU_EXHAUST_ALERT_BATCH_H + +typedef struct { + char card_no[24]; + char biz_date[16]; + long amount; + long rows; +} au_au_exhaust_alert_batch_rec_t; + +#endif /* AU_EXHAUST_ALERT_BATCH_H */ diff --git a/app/src/au/batch/au_exhaust_alert_batch.pgc b/app/src/au/batch/au_exhaust_alert_batch.pgc new file mode 100644 index 0000000..38c4550 --- /dev/null +++ b/app/src/au/batch/au_exhaust_alert_batch.pgc @@ -0,0 +1,50 @@ +/* + * au_exhaust_alert_batch.pgc - au 한도 소진 경보 로그 배치 (커서 + userlog + XA). + * + * Cursor-scans cards that have burned >= 90% of their daily limit and emits a + * userlog alert for each, counting alerts, under one global XA transaction. + * No dbio writes. Usage: au_exhaust_alert_batch + */ +#include +#include +#include +#include +#include "au_dbio.h" +EXEC SQL INCLUDE au_exhaust_alert_batch; +#include + +int main(int argc, char **argv) +{ + long alerts = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[24]; + long h_bps; + EXEC SQL END DECLARE SECTION; + + (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 exa CURSOR FOR + SELECT card_no, used_amount * 10000 / daily_limit + FROM card_limit + WHERE daily_limit > 0 AND used_amount * 10 >= daily_limit * 9 + ORDER BY used_amount * 10000 / daily_limit DESC; + EXEC SQL OPEN exa; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH exa INTO :h_card, :h_bps; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE exa; tpabort(0); return 1; } + userlog("au_exhaust_alert 소진임박 card=%s %ldbps", h_card, h_bps); + alerts++; + } + EXEC SQL CLOSE exa; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> au_exhaust_alert_batch COMMIT: alerts=%ld\n", alerts); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/au/batch/au_mcc_risk_batch.h b/app/src/au/batch/au_mcc_risk_batch.h new file mode 100644 index 0000000..d6a4432 --- /dev/null +++ b/app/src/au/batch/au_mcc_risk_batch.h @@ -0,0 +1,16 @@ +/* + * au_mcc_risk_batch.h - au 배치 au_mcc_risk_batch 카피북 (batch copybook / record layout). + * 배치가 EXEC SQL INCLUDE 로 인라인하는 레코드 구조 정의. 런타임 인클루드는 + * 배치 본문(.pgc)이 직접 가진다. dbio(libaudbio.a) 경유, XA 브랜치 실행. + */ +#ifndef AU_MCC_RISK_BATCH_H +#define AU_MCC_RISK_BATCH_H + +typedef struct { + char card_no[24]; + char biz_date[16]; + long amount; + long rows; +} au_au_mcc_risk_batch_rec_t; + +#endif /* AU_MCC_RISK_BATCH_H */ diff --git a/app/src/au/batch/au_mcc_risk_batch.pgc b/app/src/au/batch/au_mcc_risk_batch.pgc new file mode 100644 index 0000000..ae27fc7 --- /dev/null +++ b/app/src/au/batch/au_mcc_risk_batch.pgc @@ -0,0 +1,52 @@ +/* + * au_mcc_risk_batch.pgc - au 고위험 업종 매입 집계 배치 (GROUP BY HAVING 커서 + XA). + * + * Reports categories whose day's purchase volume exceeds 3,000,000 (HAVING), + * scanning count/sum/max per category under one global XA transaction. Read-only + * report. Usage: au_mcc_risk_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "au_dbio.h" +EXEC SQL INCLUDE au_mcc_risk_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long groups = 0, grand = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_cat[32]; + long h_cnt, h_sum, h_max; + 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 mrk CURSOR FOR + SELECT category, count(*), coalesce(sum(amount),0), coalesce(max(amount),0) + FROM purchase WHERE biz_date = :h_bizdate + GROUP BY category HAVING coalesce(sum(amount),0) > 3000000 + ORDER BY coalesce(sum(amount),0) DESC; + EXEC SQL OPEN mrk; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH mrk INTO :h_cat, :h_cnt, :h_sum, :h_max; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE mrk; tpabort(0); return 1; } + groups++; + grand += h_sum; + printf(" risk category=%s cnt=%ld sum=%ld max=%ld\n", h_cat, h_cnt, h_sum, h_max); + } + EXEC SQL CLOSE mrk; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> au_mcc_risk_batch COMMIT: bizdate=%s risk_groups=%ld grand=%ld\n", bizdate, groups, grand); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/au/batch/au_month_limit_batch.h b/app/src/au/batch/au_month_limit_batch.h new file mode 100644 index 0000000..d9c2078 --- /dev/null +++ b/app/src/au/batch/au_month_limit_batch.h @@ -0,0 +1,16 @@ +/* + * au_month_limit_batch.h - au 배치 au_month_limit_batch 카피북 (batch copybook / record layout). + * 배치가 EXEC SQL INCLUDE 로 인라인하는 레코드 구조 정의. 런타임 인클루드는 + * 배치 본문(.pgc)이 직접 가진다. dbio(libaudbio.a) 경유, XA 브랜치 실행. + */ +#ifndef AU_MONTH_LIMIT_BATCH_H +#define AU_MONTH_LIMIT_BATCH_H + +typedef struct { + char card_no[24]; + char biz_date[16]; + long amount; + long rows; +} au_au_month_limit_batch_rec_t; + +#endif /* AU_MONTH_LIMIT_BATCH_H */ diff --git a/app/src/au/batch/au_month_limit_batch.pgc b/app/src/au/batch/au_month_limit_batch.pgc new file mode 100644 index 0000000..4c5652f --- /dev/null +++ b/app/src/au/batch/au_month_limit_batch.pgc @@ -0,0 +1,37 @@ +/* + * au_month_limit_batch.pgc - au 월 한도 소진 카드 집계 배치 (단일 집계 + XA). + * + * Aggregates in ONE query how many active cards have burned >= 80% of their + * daily limit and their total used amount, under one global XA transaction. + * No cursor. Usage: au_month_limit_batch + */ +#include +#include +#include +#include +#include "au_dbio.h" +EXEC SQL INCLUDE au_month_limit_batch; + +int main(int argc, char **argv) +{ + EXEC SQL BEGIN DECLARE SECTION; + long h_cards = 0, h_used = 0; + EXEC SQL END DECLARE SECTION; + + (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 SELECT count(*), coalesce(sum(used_amount),0) + INTO :h_cards, :h_used + FROM card_limit + WHERE daily_limit > 0 AND used_amount * 100 >= daily_limit * 80; + 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(">>> au_month_limit_batch COMMIT: cards_over80=%ld used_total=%ld\n", h_cards, h_used); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/au/batch/au_pertx_audit_batch.h b/app/src/au/batch/au_pertx_audit_batch.h new file mode 100644 index 0000000..ab632d8 --- /dev/null +++ b/app/src/au/batch/au_pertx_audit_batch.h @@ -0,0 +1,16 @@ +/* + * au_pertx_audit_batch.h - au 배치 au_pertx_audit_batch 카피북 (batch copybook / record layout). + * 배치가 EXEC SQL INCLUDE 로 인라인하는 레코드 구조 정의. 런타임 인클루드는 + * 배치 본문(.pgc)이 직접 가진다. dbio(libaudbio.a) 경유, XA 브랜치 실행. + */ +#ifndef AU_PERTX_AUDIT_BATCH_H +#define AU_PERTX_AUDIT_BATCH_H + +typedef struct { + char card_no[24]; + char biz_date[16]; + long amount; + long rows; +} au_au_pertx_audit_batch_rec_t; + +#endif /* AU_PERTX_AUDIT_BATCH_H */ diff --git a/app/src/au/batch/au_pertx_audit_batch.pgc b/app/src/au/batch/au_pertx_audit_batch.pgc new file mode 100644 index 0000000..0d72353 --- /dev/null +++ b/app/src/au/batch/au_pertx_audit_batch.pgc @@ -0,0 +1,51 @@ +/* + * au_pertx_audit_batch.pgc - au 건별 한도 초과 승인 감사 배치 (JOIN 커서 + XA). + * + * Joins the day's authorizations to card_limit and, for single transactions + * exceeding 20% of the daily limit (per-tx cap), logs a fraud row via dbio, + * all under ONE global XA transaction. Usage: au_pertx_audit_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "au_dbio.h" +EXEC SQL INCLUDE au_pertx_audit_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_card[24]; + long h_amt; + 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 pxa CURSOR FOR + SELECT a.card_no, a.amount + FROM au_authorization a JOIN card_limit l ON l.card_no = a.card_no + WHERE a.biz_date = :h_bizdate AND a.amount > l.daily_limit / 5 + ORDER BY a.amount DESC; + EXEC SQL OPEN pxa; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH pxa INTO :h_card, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE pxa; tpabort(0); return 1; } + if (audb_insert_fraud(0, h_card, 500, "PERTX_OVER", bizdate) < 0) { EXEC SQL CLOSE pxa; tpabort(0); return 1; } + rows++; + } + EXEC SQL CLOSE pxa; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> au_pertx_audit_batch COMMIT: bizdate=%s flagged=%ld\n", bizdate, rows); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/au/batch/au_reproc_queue_batch.h b/app/src/au/batch/au_reproc_queue_batch.h new file mode 100644 index 0000000..b1a4de7 --- /dev/null +++ b/app/src/au/batch/au_reproc_queue_batch.h @@ -0,0 +1,16 @@ +/* + * au_reproc_queue_batch.h - au 배치 au_reproc_queue_batch 카피북 (batch copybook / record layout). + * 배치가 EXEC SQL INCLUDE 로 인라인하는 레코드 구조 정의. 런타임 인클루드는 + * 배치 본문(.pgc)이 직접 가진다. dbio(libaudbio.a) 경유, XA 브랜치 실행. + */ +#ifndef AU_REPROC_QUEUE_BATCH_H +#define AU_REPROC_QUEUE_BATCH_H + +typedef struct { + char card_no[24]; + char biz_date[16]; + long amount; + long rows; +} au_au_reproc_queue_batch_rec_t; + +#endif /* AU_REPROC_QUEUE_BATCH_H */ diff --git a/app/src/au/batch/au_reproc_queue_batch.pgc b/app/src/au/batch/au_reproc_queue_batch.pgc new file mode 100644 index 0000000..63b9ee7 --- /dev/null +++ b/app/src/au/batch/au_reproc_queue_batch.pgc @@ -0,0 +1,49 @@ +/* + * au_reproc_queue_batch.pgc - au 보류 승인 재처리 큐 배치 (커서 + dbio + XA). + * + * Cursor-scans the day's HELD (H) authorizations and re-approves each by driving + * the dbio status transition to A, under one global XA transaction. + * Usage: au_reproc_queue_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "au_dbio.h" +EXEC SQL INCLUDE au_reproc_queue_batch; + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_auth; + 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 rpq CURSOR FOR + SELECT auth_id FROM au_authorization + WHERE biz_date = :h_bizdate AND status = 'H' ORDER BY auth_id; + EXEC SQL OPEN rpq; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH rpq INTO :h_auth; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE rpq; tpabort(0); return 1; } + if (audb_update_auth_status(h_auth, "A") < 0) { EXEC SQL CLOSE rpq; tpabort(0); return 1; } + rows++; + } + EXEC SQL CLOSE rpq; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> au_reproc_queue_batch COMMIT: bizdate=%s reprocessed=%ld\n", bizdate, rows); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/au/batch/au_stolen_sweep_batch.h b/app/src/au/batch/au_stolen_sweep_batch.h new file mode 100644 index 0000000..301adc3 --- /dev/null +++ b/app/src/au/batch/au_stolen_sweep_batch.h @@ -0,0 +1,16 @@ +/* + * au_stolen_sweep_batch.h - au 배치 au_stolen_sweep_batch 카피북 (batch copybook / record layout). + * 배치가 EXEC SQL INCLUDE 로 인라인하는 레코드 구조 정의. 런타임 인클루드는 + * 배치 본문(.pgc)이 직접 가진다. dbio(libaudbio.a) 경유, XA 브랜치 실행. + */ +#ifndef AU_STOLEN_SWEEP_BATCH_H +#define AU_STOLEN_SWEEP_BATCH_H + +typedef struct { + char card_no[24]; + char biz_date[16]; + long amount; + long rows; +} au_au_stolen_sweep_batch_rec_t; + +#endif /* AU_STOLEN_SWEEP_BATCH_H */ diff --git a/app/src/au/batch/au_stolen_sweep_batch.pgc b/app/src/au/batch/au_stolen_sweep_batch.pgc new file mode 100644 index 0000000..1a35f84 --- /dev/null +++ b/app/src/au/batch/au_stolen_sweep_batch.pgc @@ -0,0 +1,48 @@ +/* + * au_stolen_sweep_batch.pgc - au 도난/분실 카드 잔여승인 정리 배치 (커서 + XA). + * + * Cursor-scans blacklist entries flagged STOLEN/LOST and cancels each card's + * still-open (A/H) authorizations with an inner UPDATE, under one global XA + * transaction. Usage: au_stolen_sweep_batch + */ +#include +#include +#include +#include +#include "au_dbio.h" +EXEC SQL INCLUDE au_stolen_sweep_batch; + +int main(int argc, char **argv) +{ + long cards = 0, cancelled = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + (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 ssw CURSOR FOR + SELECT card_no FROM au_blacklist WHERE reason IN ('STOLEN','LOST') ORDER BY card_no; + EXEC SQL OPEN ssw; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH ssw INTO :h_card; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE ssw; tpabort(0); return 1; } + EXEC SQL UPDATE au_authorization SET status = 'C', updated_at = now() + WHERE card_no = :h_card AND status IN ('A','H'); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ssw; tpabort(0); return 1; } + if (sqlca.sqlcode != 100) cancelled++; + cards++; + } + EXEC SQL CLOSE ssw; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> au_stolen_sweep_batch COMMIT: cards=%ld cancelled_batches=%ld\n", cards, cancelled); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/au/batch/au_token_revoke_batch.h b/app/src/au/batch/au_token_revoke_batch.h new file mode 100644 index 0000000..b99b84e --- /dev/null +++ b/app/src/au/batch/au_token_revoke_batch.h @@ -0,0 +1,16 @@ +/* + * au_token_revoke_batch.h - au 배치 au_token_revoke_batch 카피북 (batch copybook / record layout). + * 배치가 EXEC SQL INCLUDE 로 인라인하는 레코드 구조 정의. 런타임 인클루드는 + * 배치 본문(.pgc)이 직접 가진다. dbio(libaudbio.a) 경유, XA 브랜치 실행. + */ +#ifndef AU_TOKEN_REVOKE_BATCH_H +#define AU_TOKEN_REVOKE_BATCH_H + +typedef struct { + char card_no[24]; + char biz_date[16]; + long amount; + long rows; +} au_au_token_revoke_batch_rec_t; + +#endif /* AU_TOKEN_REVOKE_BATCH_H */ diff --git a/app/src/au/batch/au_token_revoke_batch.pgc b/app/src/au/batch/au_token_revoke_batch.pgc new file mode 100644 index 0000000..bd27898 --- /dev/null +++ b/app/src/au/batch/au_token_revoke_batch.pgc @@ -0,0 +1,49 @@ +/* + * au_token_revoke_batch.pgc - au 폐기 토큰 카드 원장 표시 배치 (커서 + XA). + * + * Cursor-scans TOKEN_REVOKED blacklist rows (card + reason) and marks each + * card_limit row REVOKED with an inner UPDATE, under one global XA transaction. + * Usage: au_token_revoke_batch + */ +#include +#include +#include +#include +#include "au_dbio.h" +EXEC SQL INCLUDE au_token_revoke_batch; + +int main(int argc, char **argv) +{ + long seen = 0, marked = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[24], h_reason[64]; + EXEC SQL END DECLARE SECTION; + + (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 trv CURSOR FOR + SELECT card_no, reason FROM au_blacklist + WHERE reason = 'TOKEN_REVOKED' ORDER BY created_at; + EXEC SQL OPEN trv; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH trv INTO :h_card, :h_reason; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE trv; tpabort(0); return 1; } + EXEC SQL UPDATE card_limit SET status = 'REVOKED', updated_at = now() + WHERE card_no = :h_card AND status <> 'REVOKED'; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE trv; tpabort(0); return 1; } + if (sqlca.sqlcode != 100) marked++; + seen++; + } + EXEC SQL CLOSE trv; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> au_token_revoke_batch COMMIT: revoked_tokens=%ld cards_marked=%ld\n", seen, marked); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/au/batch/au_whitelist_clean_batch.h b/app/src/au/batch/au_whitelist_clean_batch.h new file mode 100644 index 0000000..71005ac --- /dev/null +++ b/app/src/au/batch/au_whitelist_clean_batch.h @@ -0,0 +1,16 @@ +/* + * au_whitelist_clean_batch.h - au 배치 au_whitelist_clean_batch 카피북 (batch copybook / record layout). + * 배치가 EXEC SQL INCLUDE 로 인라인하는 레코드 구조 정의. 런타임 인클루드는 + * 배치 본문(.pgc)이 직접 가진다. dbio(libaudbio.a) 경유, XA 브랜치 실행. + */ +#ifndef AU_WHITELIST_CLEAN_BATCH_H +#define AU_WHITELIST_CLEAN_BATCH_H + +typedef struct { + char card_no[24]; + char biz_date[16]; + long amount; + long rows; +} au_au_whitelist_clean_batch_rec_t; + +#endif /* AU_WHITELIST_CLEAN_BATCH_H */ diff --git a/app/src/au/batch/au_whitelist_clean_batch.pgc b/app/src/au/batch/au_whitelist_clean_batch.pgc new file mode 100644 index 0000000..7df2810 --- /dev/null +++ b/app/src/au/batch/au_whitelist_clean_batch.pgc @@ -0,0 +1,39 @@ +/* + * au_whitelist_clean_batch.pgc - au 만료 폐기토큰 블랙 정리 배치 (집합 DELETE + XA). + * + * Counts, then set-deletes TOKEN_REVOKED blacklist rows older than 30 days in a + * single DELETE (auto-whitelisting expired revocations), under one global XA + * transaction. No cursor. Usage: au_whitelist_clean_batch + */ +#include +#include +#include +#include +#include "au_dbio.h" +EXEC SQL INCLUDE au_whitelist_clean_batch; + +int main(int argc, char **argv) +{ + EXEC SQL BEGIN DECLARE SECTION; + long h_stale = 0; + EXEC SQL END DECLARE SECTION; + + (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 SELECT count(*) INTO :h_stale FROM au_blacklist + WHERE reason = 'TOKEN_REVOKED' AND created_at < now() - interval '30 days'; + if (sqlca.sqlcode < 0) { fprintf(stderr, "SELECT FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + + EXEC SQL DELETE FROM au_blacklist + WHERE reason = 'TOKEN_REVOKED' AND created_at < now() - interval '30 days'; + if (sqlca.sqlcode < 0) { fprintf(stderr, "DELETE 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(">>> au_whitelist_clean_batch COMMIT: expired_revocations_cleared=%ld\n", h_stale); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/au/svc/AUTH_PARTAPPR.h b/app/src/au/svc/AUTH_PARTAPPR.h new file mode 100644 index 0000000..72df9f4 --- /dev/null +++ b/app/src/au/svc/AUTH_PARTAPPR.h @@ -0,0 +1,20 @@ +/* + * AUTH_PARTAPPR.h - au 서비스 AUTH_PARTAPPR 카피북 (copybook / record header). + * 요청: 카드/금액. 응답: 승인금액/부분승인여부 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef AUTH_PARTAPPR_H +#define AUTH_PARTAPPR_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_auth_partappr_rec_t; + +void AUTH_PARTAPPR(TPSVCINFO *p); + +#endif /* AUTH_PARTAPPR_H */ diff --git a/app/src/au/svc/AUTH_PARTAPPR.pgc b/app/src/au/svc/AUTH_PARTAPPR.pgc new file mode 100644 index 0000000..79807ad --- /dev/null +++ b/app/src/au/svc/AUTH_PARTAPPR.pgc @@ -0,0 +1,44 @@ +/* + * AUTH_PARTAPPR.pgc - au 모듈 서비스 AUTH_PARTAPPR (one service = one file). + * 부분 승인: 가용한도(일한도-사용액)가 요청액보다 작으면 가용한도만큼만 + * 승인해 원장에 기록하고 한도를 차감한다. 전액 RC=0, 부분 RC=1, 불가 RC=2. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "AUTH_PARTAPPR.h" + +void AUTH_PARTAPPR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24], merch[64], bizdate[16]; + long amount, avail, appr, aid, rc; + EXEC SQL BEGIN DECLARE SECTION; + long h_daily = 0, h_used = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + amount = getl(b, T_AMOUNT); + if (card[0] == 0 || amount <= 0 || bizdate[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT daily_limit, used_amount INTO :h_daily, :h_used + FROM card_limit WHERE card_no = :h_card; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + + avail = h_daily - h_used; + if (avail <= 0) { setl(b, T_AMT1, 0); setl(b, T_RC, 2); OK(b); } + + appr = (amount <= avail) ? amount : avail; + rc = (appr == amount) ? 0 : 1; + aid = audb_next_auth_id(); + if (aid < 0) FAIL(b); + if (audb_insert_auth(aid, card, merch, appr, "PART", "AUTH", "A", bizdate) < 0) FAIL(b); + if (audb_limit_dec(card, appr) < 0) FAIL(b); + + setl(b, T_ID2, aid); + setl(b, T_AMT1, appr); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/au/svc/AUTH_REPROC_REQ.h b/app/src/au/svc/AUTH_REPROC_REQ.h new file mode 100644 index 0000000..e016e58 --- /dev/null +++ b/app/src/au/svc/AUTH_REPROC_REQ.h @@ -0,0 +1,20 @@ +/* + * AUTH_REPROC_REQ.h - au 서비스 AUTH_REPROC_REQ 카피북 (copybook / record header). + * 요청: 승인ID. 응답: 재처리 결과 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef AUTH_REPROC_REQ_H +#define AUTH_REPROC_REQ_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_auth_reproc_req_rec_t; + +void AUTH_REPROC_REQ(TPSVCINFO *p); + +#endif /* AUTH_REPROC_REQ_H */ diff --git a/app/src/au/svc/AUTH_REPROC_REQ.pgc b/app/src/au/svc/AUTH_REPROC_REQ.pgc new file mode 100644 index 0000000..4a5f6eb --- /dev/null +++ b/app/src/au/svc/AUTH_REPROC_REQ.pgc @@ -0,0 +1,34 @@ +/* + * AUTH_REPROC_REQ.pgc - au 모듈 서비스 AUTH_REPROC_REQ (one service = one file). + * 승인 재처리 요청: 거절(D)/보류(H) 상태의 승인을 재처리 대상으로 다시 + * 승인(A)으로 되돌린다(재시도 워크플로 진입). 대상 아님이면 RC=1. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "AUTH_REPROC_REQ.h" + +void AUTH_REPROC_REQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_auth; + char h_old[4]; + EXEC SQL END DECLARE SECTION; + + h_auth = getl(b, T_ID2); + if (h_auth <= 0) FAIL(b); + memset(h_old, 0, sizeof(h_old)); + + EXEC SQL SELECT status INTO :h_old FROM au_authorization + WHERE auth_id = :h_auth AND status IN ('D','H'); + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + + EXEC SQL UPDATE au_authorization SET status = 'A', updated_at = now() + WHERE auth_id = :h_auth; + if (sqlca.sqlcode < 0) FAIL(b); + + userlog("AUTH_REPROC_REQ auth=%ld %s->A", h_auth, h_old); + Bchg(b, T_STATUS, 0, h_old, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/au/svc/AUTH_REPROC_STAT.h b/app/src/au/svc/AUTH_REPROC_STAT.h new file mode 100644 index 0000000..e6d8cb7 --- /dev/null +++ b/app/src/au/svc/AUTH_REPROC_STAT.h @@ -0,0 +1,20 @@ +/* + * AUTH_REPROC_STAT.h - au 서비스 AUTH_REPROC_STAT 카피북 (copybook / record header). + * 요청: 영업일. 응답: 보류건수/재처리건수 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef AUTH_REPROC_STAT_H +#define AUTH_REPROC_STAT_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_auth_reproc_stat_rec_t; + +void AUTH_REPROC_STAT(TPSVCINFO *p); + +#endif /* AUTH_REPROC_STAT_H */ diff --git a/app/src/au/svc/AUTH_REPROC_STAT.pgc b/app/src/au/svc/AUTH_REPROC_STAT.pgc new file mode 100644 index 0000000..97339b8 --- /dev/null +++ b/app/src/au/svc/AUTH_REPROC_STAT.pgc @@ -0,0 +1,32 @@ +/* + * AUTH_REPROC_STAT.pgc - au 모듈 서비스 AUTH_REPROC_STAT (one service = one file). + * 재처리 현황: 영업일 승인 원장에서 보류(H) 대기 건수와, 갱신시각이 생성시각 + * 이후로 밀린(재처리·상태변경된) 건수를 집계해 재처리 큐 상태를 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "AUTH_REPROC_STAT.h" + +void AUTH_REPROC_STAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_hold = 0, h_reproc = 0; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) FAIL(b); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL SELECT count(*) FILTER (WHERE status = 'H'), + count(*) FILTER (WHERE updated_at > created_at) + INTO :h_hold, :h_reproc + FROM au_authorization WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_ARG1, h_hold); + setl(b, T_ARG2, h_reproc); + setl(b, T_RC, (h_hold > 0) ? 1 : 0); + OK(b); +} diff --git a/app/src/au/svc/BLACKLIST_COUNT.h b/app/src/au/svc/BLACKLIST_COUNT.h new file mode 100644 index 0000000..ff04c81 --- /dev/null +++ b/app/src/au/svc/BLACKLIST_COUNT.h @@ -0,0 +1,20 @@ +/* + * BLACKLIST_COUNT.h - au 서비스 BLACKLIST_COUNT 카피북 (copybook / record header). + * 요청: 없음. 응답: 총건수/도난건수/당일등재 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef BLACKLIST_COUNT_H +#define BLACKLIST_COUNT_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_blacklist_count_rec_t; + +void BLACKLIST_COUNT(TPSVCINFO *p); + +#endif /* BLACKLIST_COUNT_H */ diff --git a/app/src/au/svc/BLACKLIST_COUNT.pgc b/app/src/au/svc/BLACKLIST_COUNT.pgc new file mode 100644 index 0000000..ab5b9ab --- /dev/null +++ b/app/src/au/svc/BLACKLIST_COUNT.pgc @@ -0,0 +1,27 @@ +/* + * BLACKLIST_COUNT.pgc - au 모듈 서비스 BLACKLIST_COUNT (one service = one file). + * 블랙리스트 통계: 전체 등재 수, 도난(REPORTED_STOLEN/STOLEN) 등재 수, + * 당일 신규 등재 수를 한 번의 집계로 뽑아 대시보드용으로 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "BLACKLIST_COUNT.h" + +void BLACKLIST_COUNT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_total = 0, h_stolen = 0, h_today = 0; + EXEC SQL END DECLARE SECTION; + + EXEC SQL SELECT count(*), + count(*) FILTER (WHERE reason IN ('REPORTED_STOLEN','STOLEN')), + count(*) FILTER (WHERE created_at >= current_date) + INTO :h_total, :h_stolen, :h_today + FROM au_blacklist; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_total); + setl(b, T_ARG1, h_stolen); + setl(b, T_ARG2, h_today); + OK(b); +} diff --git a/app/src/au/svc/BLACKLIST_PROMO.h b/app/src/au/svc/BLACKLIST_PROMO.h new file mode 100644 index 0000000..597fd30 --- /dev/null +++ b/app/src/au/svc/BLACKLIST_PROMO.h @@ -0,0 +1,20 @@ +/* + * BLACKLIST_PROMO.h - au 서비스 BLACKLIST_PROMO 카피북 (copybook / record header). + * 요청: 영업일. 응답: 승격후보 문자열/후보수 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef BLACKLIST_PROMO_H +#define BLACKLIST_PROMO_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_blacklist_promo_rec_t; + +void BLACKLIST_PROMO(TPSVCINFO *p); + +#endif /* BLACKLIST_PROMO_H */ diff --git a/app/src/au/svc/BLACKLIST_PROMO.pgc b/app/src/au/svc/BLACKLIST_PROMO.pgc new file mode 100644 index 0000000..1bdba8c --- /dev/null +++ b/app/src/au/svc/BLACKLIST_PROMO.pgc @@ -0,0 +1,44 @@ +/* + * BLACKLIST_PROMO.pgc - au 모듈 서비스 BLACKLIST_PROMO (one service = one file). + * 블랙 승격 후보 조회: 영업일 부정 적발 점수 합이 1500 이상이면서 아직 + * 블랙리스트에 없는 카드를 커서로 골라 "카드:점수합;" 후보 목록을 만든다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "BLACKLIST_PROMO.h" + +void BLACKLIST_PROMO(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], cand[256] = "", item[40]; + long n = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_sum; + char h_bizdate[16], h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) FAIL(b); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL DECLARE cur_blpromo CURSOR FOR + SELECT f.card_no, sum(f.score) FROM au_fraud_log f + WHERE f.biz_date = :h_bizdate + AND f.card_no NOT IN (SELECT card_no FROM au_blacklist) + GROUP BY f.card_no HAVING sum(f.score) >= 1500 + ORDER BY sum(f.score) DESC; + EXEC SQL OPEN cur_blpromo; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_blpromo INTO :h_card, :h_sum; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_blpromo; FAIL(b); } + n++; + snprintf(item, sizeof(item), "%s:%ld;", h_card, h_sum); + if (strlen(cand) + strlen(item) < sizeof(cand) - 1) strcat(cand, item); + } + EXEC SQL CLOSE cur_blpromo; + + Bchg(b, T_STR1, 0, cand, 0L); + setl(b, T_COUNT, n); + OK(b); +} diff --git a/app/src/au/svc/BRAND_AUTH_STAT.h b/app/src/au/svc/BRAND_AUTH_STAT.h new file mode 100644 index 0000000..24221d8 --- /dev/null +++ b/app/src/au/svc/BRAND_AUTH_STAT.h @@ -0,0 +1,20 @@ +/* + * BRAND_AUTH_STAT.h - au 서비스 BRAND_AUTH_STAT 카피북 (copybook / record header). + * 요청: 영업일. 응답: 브랜드별 접수/승인 분포/총건수 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef BRAND_AUTH_STAT_H +#define BRAND_AUTH_STAT_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_brand_auth_stat_rec_t; + +void BRAND_AUTH_STAT(TPSVCINFO *p); + +#endif /* BRAND_AUTH_STAT_H */ diff --git a/app/src/au/svc/BRAND_AUTH_STAT.pgc b/app/src/au/svc/BRAND_AUTH_STAT.pgc new file mode 100644 index 0000000..d05ecde --- /dev/null +++ b/app/src/au/svc/BRAND_AUTH_STAT.pgc @@ -0,0 +1,45 @@ +/* + * BRAND_AUTH_STAT.pgc - au 모듈 서비스 BRAND_AUTH_STAT (one service = one file). + * 브랜드별 승인 통계: 영업일 승인 원장을 카드 선두자리(브랜드)로 그룹핑해 + * 접수/승인 건수를 함께 뽑는 커서를 돌며 "선두=접수/승인;" 문자열을 만든다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "BRAND_AUTH_STAT.h" + +void BRAND_AUTH_STAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], mix[256] = "", item[48]; + long total = 0, appr = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_all, h_ok; + char h_bizdate[16], h_lead[4]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) FAIL(b); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL DECLARE cur_brstat CURSOR FOR + SELECT substr(card_no, 1, 1), count(*), + count(*) FILTER (WHERE status IN ('A','M','S')) + FROM au_authorization WHERE biz_date = :h_bizdate + GROUP BY substr(card_no, 1, 1) ORDER BY 1; + EXEC SQL OPEN cur_brstat; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_brstat INTO :h_lead, :h_all, :h_ok; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_brstat; FAIL(b); } + total += h_all; + appr += h_ok; + snprintf(item, sizeof(item), "%s=%ld/%ld;", h_lead, h_all, h_ok); + if (strlen(mix) + strlen(item) < sizeof(mix) - 1) strcat(mix, item); + } + EXEC SQL CLOSE cur_brstat; + + Bchg(b, T_STR1, 0, mix, 0L); + setl(b, T_COUNT, total); + setl(b, T_ARG1, appr); + OK(b); +} diff --git a/app/src/au/svc/BRAND_POLICY.h b/app/src/au/svc/BRAND_POLICY.h new file mode 100644 index 0000000..35faa94 --- /dev/null +++ b/app/src/au/svc/BRAND_POLICY.h @@ -0,0 +1,20 @@ +/* + * BRAND_POLICY.h - au 서비스 BRAND_POLICY 카피북 (copybook / record header). + * 요청: 카드. 응답: 브랜드/당일건수/정책판정 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef BRAND_POLICY_H +#define BRAND_POLICY_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_brand_policy_rec_t; + +void BRAND_POLICY(TPSVCINFO *p); + +#endif /* BRAND_POLICY_H */ diff --git a/app/src/au/svc/BRAND_POLICY.pgc b/app/src/au/svc/BRAND_POLICY.pgc new file mode 100644 index 0000000..414d43c --- /dev/null +++ b/app/src/au/svc/BRAND_POLICY.pgc @@ -0,0 +1,41 @@ +/* + * BRAND_POLICY.pgc - au 모듈 서비스 BRAND_POLICY (one service = one file). + * 브랜드별 승인 정책: 카드 선두자리로 브랜드(VISA/MC/AMEX/LOCAL)를 판별하고 + * 당일 동일 브랜드 승인건수를 세어 AMEX 는 3건, 그 외는 8건 초과 시 제한한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "BRAND_POLICY.h" + +void BRAND_POLICY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + const char *brand; + long cap; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt = 0; + char h_card[24], h_lead[4]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + h_lead[0] = card[0]; h_lead[1] = 0; + + switch (card[0]) { + case '4': brand = "VISA"; cap = 8; break; + case '5': brand = "MC"; cap = 8; break; + case '3': brand = "AMEX"; cap = 3; break; + default: brand = "LOCAL"; cap = 8; break; + } + + EXEC SQL SELECT count(*) INTO :h_cnt FROM au_authorization + WHERE substr(card_no, 1, 1) = :h_lead + AND biz_date = current_date AND status = 'A'; + if (sqlca.sqlcode < 0) FAIL(b); + + Bchg(b, T_ISSUER, 0, (char *)brand, 0L); + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, (h_cnt > cap) ? 1 : 0); + OK(b); +} diff --git a/app/src/au/svc/CANCEL_AMT_INQ.h b/app/src/au/svc/CANCEL_AMT_INQ.h new file mode 100644 index 0000000..f0ad9df --- /dev/null +++ b/app/src/au/svc/CANCEL_AMT_INQ.h @@ -0,0 +1,20 @@ +/* + * CANCEL_AMT_INQ.h - au 서비스 CANCEL_AMT_INQ 카피북 (copybook / record header). + * 요청: 승인ID. 응답: 원승인액/기취소액/취소가능액 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef CANCEL_AMT_INQ_H +#define CANCEL_AMT_INQ_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_cancel_amt_inq_rec_t; + +void CANCEL_AMT_INQ(TPSVCINFO *p); + +#endif /* CANCEL_AMT_INQ_H */ diff --git a/app/src/au/svc/CANCEL_AMT_INQ.pgc b/app/src/au/svc/CANCEL_AMT_INQ.pgc new file mode 100644 index 0000000..1022b96 --- /dev/null +++ b/app/src/au/svc/CANCEL_AMT_INQ.pgc @@ -0,0 +1,36 @@ +/* + * CANCEL_AMT_INQ.pgc - au 모듈 서비스 CANCEL_AMT_INQ (one service = one file). + * 취소가능액 조회: 원승인 금액에서 이미 취소된 누계를 빼 취소가능 잔액을 + * 계산한다. 승인상태가 취소불가(취소완료/무효)면 RC=1 로 알린다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "CANCEL_AMT_INQ.h" + +void CANCEL_AMT_INQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long cancelable; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_amt = 0, h_cxl = 0; + char h_status[4]; + EXEC SQL END DECLARE SECTION; + + h_id = getl(b, T_ID2); + if (h_id <= 0) FAIL(b); + memset(h_status, 0, sizeof(h_status)); + + EXEC SQL SELECT amount, status INTO :h_amt, :h_status + FROM au_authorization WHERE auth_id = :h_id; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_cxl + FROM au_cancel WHERE auth_id = :h_id; + if (sqlca.sqlcode < 0) FAIL(b); + + cancelable = h_amt - h_cxl; + setl(b, T_AMT1, h_amt); + setl(b, T_AMT2, h_cxl); + setl(b, T_AMT3, cancelable); + setl(b, T_RC, (h_status[0] == 'C' || h_status[0] == 'V') ? 1 : 0); + OK(b); +} diff --git a/app/src/au/svc/CANCEL_FULL.h b/app/src/au/svc/CANCEL_FULL.h new file mode 100644 index 0000000..e63e18a --- /dev/null +++ b/app/src/au/svc/CANCEL_FULL.h @@ -0,0 +1,20 @@ +/* + * CANCEL_FULL.h - au 서비스 CANCEL_FULL 카피북 (copybook / record header). + * 요청: 승인ID/사유. 응답: 취소ID/취소액 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef CANCEL_FULL_H +#define CANCEL_FULL_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_cancel_full_rec_t; + +void CANCEL_FULL(TPSVCINFO *p); + +#endif /* CANCEL_FULL_H */ diff --git a/app/src/au/svc/CANCEL_FULL.pgc b/app/src/au/svc/CANCEL_FULL.pgc new file mode 100644 index 0000000..e757ca7 --- /dev/null +++ b/app/src/au/svc/CANCEL_FULL.pgc @@ -0,0 +1,42 @@ +/* + * CANCEL_FULL.pgc - au 모듈 서비스 CANCEL_FULL (one service = one file). + * 전액 취소: 승인(A)/매입확정(M) 상태의 원승인을 전액 취소 처리한다. 취소 + * 원장(au_cancel FULL)을 채번·삽입하고 원승인 상태를 C 로 바꾼다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "CANCEL_FULL.h" + +void CANCEL_FULL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char reason[128]; + EXEC SQL BEGIN DECLARE SECTION; + long h_auth, h_cid, h_amt = 0; + char h_bizdate[16], h_reason[128]; + EXEC SQL END DECLARE SECTION; + + h_auth = getl(b, T_ID2); + gets_(b, T_REASON, reason, sizeof(reason)); + if (h_auth <= 0) FAIL(b); + strncpy(h_reason, reason, sizeof(h_reason)-1); h_reason[sizeof(h_reason)-1] = 0; + + EXEC SQL SELECT amount, to_char(biz_date, 'YYYY-MM-DD') + INTO :h_amt, :h_bizdate + FROM au_authorization WHERE auth_id = :h_auth AND status IN ('A','M'); + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + + EXEC SQL SELECT nextval('au_cancel_seq') INTO :h_cid; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO au_cancel (cancel_id, auth_id, cancel_type, amount, reason, biz_date) + VALUES (:h_cid, :h_auth, 'FULL', :h_amt, :h_reason, :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL UPDATE au_authorization SET status = 'C', updated_at = now() + WHERE auth_id = :h_auth; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_ID1, h_cid); + setl(b, T_AMT1, h_amt); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/au/svc/CANCEL_HISTORY.h b/app/src/au/svc/CANCEL_HISTORY.h new file mode 100644 index 0000000..5644b9d --- /dev/null +++ b/app/src/au/svc/CANCEL_HISTORY.h @@ -0,0 +1,20 @@ +/* + * CANCEL_HISTORY.h - au 서비스 CANCEL_HISTORY 카피북 (copybook / record header). + * 요청: 승인ID. 응답: 취소내역 문자열/건수/합계 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef CANCEL_HISTORY_H +#define CANCEL_HISTORY_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_cancel_history_rec_t; + +void CANCEL_HISTORY(TPSVCINFO *p); + +#endif /* CANCEL_HISTORY_H */ diff --git a/app/src/au/svc/CANCEL_HISTORY.pgc b/app/src/au/svc/CANCEL_HISTORY.pgc new file mode 100644 index 0000000..1c4b641 --- /dev/null +++ b/app/src/au/svc/CANCEL_HISTORY.pgc @@ -0,0 +1,42 @@ +/* + * CANCEL_HISTORY.pgc - au 모듈 서비스 CANCEL_HISTORY (one service = one file). + * 승인건 취소 이력: 특정 원승인에 걸린 취소들을 시간순 커서로 순회하며 + * "유형:금액;" 이력 문자열과 취소 건수/취소 합계를 조립해 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "CANCEL_HISTORY.h" + +void CANCEL_HISTORY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char hist[256] = "", item[48]; + long cnt = 0, sum = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_auth, h_amt; + char h_type[12]; + EXEC SQL END DECLARE SECTION; + + h_auth = getl(b, T_ID2); + if (h_auth <= 0) FAIL(b); + + EXEC SQL DECLARE cur_cxhist CURSOR FOR + SELECT cancel_type, amount FROM au_cancel + WHERE auth_id = :h_auth ORDER BY created_at; + EXEC SQL OPEN cur_cxhist; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_cxhist INTO :h_type, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_cxhist; FAIL(b); } + cnt++; + sum += h_amt; + snprintf(item, sizeof(item), "%s:%ld;", h_type, h_amt); + if (strlen(hist) + strlen(item) < sizeof(hist) - 1) strcat(hist, item); + } + EXEC SQL CLOSE cur_cxhist; + + Bchg(b, T_STR1, 0, hist, 0L); + setl(b, T_COUNT, cnt); + setl(b, T_AMT1, sum); + OK(b); +} diff --git a/app/src/au/svc/CANCEL_SUMMARY.h b/app/src/au/svc/CANCEL_SUMMARY.h new file mode 100644 index 0000000..7e7350e --- /dev/null +++ b/app/src/au/svc/CANCEL_SUMMARY.h @@ -0,0 +1,20 @@ +/* + * CANCEL_SUMMARY.h - au 서비스 CANCEL_SUMMARY 카피북 (copybook / record header). + * 요청: 영업일. 응답: 취소건수/합계/전액/부분 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef CANCEL_SUMMARY_H +#define CANCEL_SUMMARY_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_cancel_summary_rec_t; + +void CANCEL_SUMMARY(TPSVCINFO *p); + +#endif /* CANCEL_SUMMARY_H */ diff --git a/app/src/au/svc/CANCEL_SUMMARY.pgc b/app/src/au/svc/CANCEL_SUMMARY.pgc new file mode 100644 index 0000000..76a74ca --- /dev/null +++ b/app/src/au/svc/CANCEL_SUMMARY.pgc @@ -0,0 +1,34 @@ +/* + * CANCEL_SUMMARY.pgc - au 모듈 서비스 CANCEL_SUMMARY (one service = one file). + * 일별 취소 집계: 영업일 취소 원장을 전체/전액(FULL)/부분(PARTIAL)로 나눠 + * 건수와 취소 합계를 한 번의 집계 쿼리로 뽑아 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "CANCEL_SUMMARY.h" + +void CANCEL_SUMMARY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt = 0, h_sum = 0, h_full = 0, h_part = 0; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) FAIL(b); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL SELECT count(*), coalesce(sum(amount),0), + count(*) FILTER (WHERE cancel_type = 'FULL'), + count(*) FILTER (WHERE cancel_type = 'PARTIAL') + INTO :h_cnt, :h_sum, :h_full, :h_part + FROM au_cancel WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_AMT1, h_sum); + setl(b, T_ARG1, h_full); + setl(b, T_ARG2, h_part); + OK(b); +} diff --git a/app/src/au/svc/CARD_ACTIVATE.h b/app/src/au/svc/CARD_ACTIVATE.h new file mode 100644 index 0000000..f7e9b65 --- /dev/null +++ b/app/src/au/svc/CARD_ACTIVATE.h @@ -0,0 +1,20 @@ +/* + * CARD_ACTIVATE.h - au 서비스 CARD_ACTIVATE 카피북 (copybook / record header). + * 요청: 카드/한도. 응답: 신규활성화 결과 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef CARD_ACTIVATE_H +#define CARD_ACTIVATE_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_card_activate_rec_t; + +void CARD_ACTIVATE(TPSVCINFO *p); + +#endif /* CARD_ACTIVATE_H */ diff --git a/app/src/au/svc/CARD_ACTIVATE.pgc b/app/src/au/svc/CARD_ACTIVATE.pgc new file mode 100644 index 0000000..3bc6f7c --- /dev/null +++ b/app/src/au/svc/CARD_ACTIVATE.pgc @@ -0,0 +1,33 @@ +/* + * CARD_ACTIVATE.pgc - au 모듈 서비스 CARD_ACTIVATE (one service = one file). + * 카드 신규 활성화: 원장이 없으면 요청 한도(기본 100만원)로 생성하고, 이미 + * 있으면 상태만 ACTIVE 로 되살리는 upsert 를 수행한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "CARD_ACTIVATE.h" + +void CARD_ACTIVATE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + EXEC SQL BEGIN DECLARE SECTION; + long h_daily; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + h_daily = getl(b, T_AMT1); + if (card[0] == 0) FAIL(b); + if (h_daily <= 0) h_daily = 1000000; + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL INSERT INTO card_limit (card_no, daily_limit, status) + VALUES (:h_card, :h_daily, 'ACTIVE') + ON CONFLICT (card_no) DO UPDATE SET status = 'ACTIVE', updated_at = now(); + if (sqlca.sqlcode < 0) FAIL(b); + + userlog("CARD_ACTIVATE card=%s limit=%ld", card, h_daily); + setl(b, T_AMT1, h_daily); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/au/svc/CARD_EXPIRE_CHK.h b/app/src/au/svc/CARD_EXPIRE_CHK.h new file mode 100644 index 0000000..bd6fe0f --- /dev/null +++ b/app/src/au/svc/CARD_EXPIRE_CHK.h @@ -0,0 +1,20 @@ +/* + * CARD_EXPIRE_CHK.h - au 서비스 CARD_EXPIRE_CHK 카피북 (copybook / record header). + * 요청: 카드. 응답: 활동건수/경과일/휴면여부 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef CARD_EXPIRE_CHK_H +#define CARD_EXPIRE_CHK_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_card_expire_chk_rec_t; + +void CARD_EXPIRE_CHK(TPSVCINFO *p); + +#endif /* CARD_EXPIRE_CHK_H */ diff --git a/app/src/au/svc/CARD_EXPIRE_CHK.pgc b/app/src/au/svc/CARD_EXPIRE_CHK.pgc new file mode 100644 index 0000000..27d530a --- /dev/null +++ b/app/src/au/svc/CARD_EXPIRE_CHK.pgc @@ -0,0 +1,37 @@ +/* + * CARD_EXPIRE_CHK.pgc - au 모듈 서비스 CARD_EXPIRE_CHK (one service = one file). + * 카드 휴면(만료 근사) 점검: 마지막 승인 이후 경과일을 EPOCH 로 환산해 + * 90일 넘게 활동이 없으면 휴면(RC=1)으로 판정한다. 활동건수도 함께 응답. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "CARD_EXPIRE_CHK.h" + +void CARD_EXPIRE_CHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt = 0, h_days = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT count(*), + CAST(coalesce(EXTRACT(EPOCH FROM now() - max(created_at)) / 86400, 9999) AS bigint) + INTO :h_cnt, :h_days + FROM au_authorization WHERE card_no = :h_card; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_ARG1, h_days); + if (h_days > 90) { + userlog("CARD_EXPIRE_CHK 휴면 card=%s %ld일", card, h_days); + setl(b, T_RC, 1); + } else { + setl(b, T_RC, 0); + } + OK(b); +} diff --git a/app/src/au/svc/CARD_LOST_REG.h b/app/src/au/svc/CARD_LOST_REG.h new file mode 100644 index 0000000..f66e68d --- /dev/null +++ b/app/src/au/svc/CARD_LOST_REG.h @@ -0,0 +1,20 @@ +/* + * CARD_LOST_REG.h - au 서비스 CARD_LOST_REG 카피북 (copybook / record header). + * 요청: 카드/사유. 응답: 분실등록 결과 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef CARD_LOST_REG_H +#define CARD_LOST_REG_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_card_lost_reg_rec_t; + +void CARD_LOST_REG(TPSVCINFO *p); + +#endif /* CARD_LOST_REG_H */ diff --git a/app/src/au/svc/CARD_LOST_REG.pgc b/app/src/au/svc/CARD_LOST_REG.pgc new file mode 100644 index 0000000..fdfa48f --- /dev/null +++ b/app/src/au/svc/CARD_LOST_REG.pgc @@ -0,0 +1,33 @@ +/* + * CARD_LOST_REG.pgc - au 모듈 서비스 CARD_LOST_REG (one service = one file). + * 분실 등록: 카드 원장 상태를 LOST 로 바꾸고 블랙리스트에 LOST 사유로 + * 등재(중복 시 무시)해 이후 승인 시도를 원천 차단한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "CARD_LOST_REG.h" + +void CARD_LOST_REG(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24], reason[128]; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + gets_(b, T_REASON, reason, sizeof(reason)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL UPDATE card_limit SET status = 'LOST', updated_at = now() + WHERE card_no = :h_card; + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL INSERT INTO au_blacklist (card_no, reason) + VALUES (:h_card, 'LOST') ON CONFLICT (card_no) DO NOTHING; + if (sqlca.sqlcode < 0) FAIL(b); + + userlog("CARD_LOST_REG 분실등록 card=%s reason=%s", card, reason); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/au/svc/CARD_REACTIVATE.h b/app/src/au/svc/CARD_REACTIVATE.h new file mode 100644 index 0000000..89119a6 --- /dev/null +++ b/app/src/au/svc/CARD_REACTIVATE.h @@ -0,0 +1,20 @@ +/* + * CARD_REACTIVATE.h - au 서비스 CARD_REACTIVATE 카피북 (copybook / record header). + * 요청: 카드. 응답: 재활성화 결과 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef CARD_REACTIVATE_H +#define CARD_REACTIVATE_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_card_reactivate_rec_t; + +void CARD_REACTIVATE(TPSVCINFO *p); + +#endif /* CARD_REACTIVATE_H */ diff --git a/app/src/au/svc/CARD_REACTIVATE.pgc b/app/src/au/svc/CARD_REACTIVATE.pgc new file mode 100644 index 0000000..722bc6d --- /dev/null +++ b/app/src/au/svc/CARD_REACTIVATE.pgc @@ -0,0 +1,34 @@ +/* + * CARD_REACTIVATE.pgc - au 모듈 서비스 CARD_REACTIVATE (one service = one file). + * 카드 재활성화: 정지/동결/분실 상태의 원장을 ACTIVE 로 되돌리고, 분실·토큰 + * 폐기 사유로 걸린 블랙리스트 항목을 삭제한다(도난은 보존). RC=재활성 여부. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "CARD_REACTIVATE.h" + +void CARD_REACTIVATE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + long restored; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL UPDATE card_limit SET status = 'ACTIVE', updated_at = now() + WHERE card_no = :h_card AND status IN ('SUSPEND','FROZEN','LOST'); + if (sqlca.sqlcode < 0) FAIL(b); + restored = (sqlca.sqlcode == 100) ? 0 : 1; + + EXEC SQL DELETE FROM au_blacklist + WHERE card_no = :h_card AND reason IN ('LOST','TOKEN_REVOKED'); + if (sqlca.sqlcode < 0) FAIL(b); + + userlog("CARD_REACTIVATE card=%s restored=%ld", card, restored); + setl(b, T_RC, restored); + OK(b); +} diff --git a/app/src/au/svc/CARD_STATE_HIST.h b/app/src/au/svc/CARD_STATE_HIST.h new file mode 100644 index 0000000..783b690 --- /dev/null +++ b/app/src/au/svc/CARD_STATE_HIST.h @@ -0,0 +1,20 @@ +/* + * CARD_STATE_HIST.h - au 서비스 CARD_STATE_HIST 카피북 (copybook / record header). + * 요청: 카드. 응답: 상태별 분포 문자열/총건수/상태수 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef CARD_STATE_HIST_H +#define CARD_STATE_HIST_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_card_state_hist_rec_t; + +void CARD_STATE_HIST(TPSVCINFO *p); + +#endif /* CARD_STATE_HIST_H */ diff --git a/app/src/au/svc/CARD_STATE_HIST.pgc b/app/src/au/svc/CARD_STATE_HIST.pgc new file mode 100644 index 0000000..13463a5 --- /dev/null +++ b/app/src/au/svc/CARD_STATE_HIST.pgc @@ -0,0 +1,43 @@ +/* + * CARD_STATE_HIST.pgc - au 모듈 서비스 CARD_STATE_HIST (one service = one file). + * 카드 승인상태 이력 분포: 카드의 전체 승인 건을 상태(status)별로 집계하는 + * 커서를 돌며 "상태=건수;" 분포 문자열과 총건수/상태종류 수를 만든다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "CARD_STATE_HIST.h" + +void CARD_STATE_HIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24], dist[256] = "", item[32]; + long total = 0, kinds = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt; + char h_card[24], h_st[4]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL DECLARE cur_cshist CURSOR FOR + SELECT status, count(*) FROM au_authorization + WHERE card_no = :h_card GROUP BY status ORDER BY count(*) DESC; + EXEC SQL OPEN cur_cshist; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_cshist INTO :h_st, :h_cnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_cshist; FAIL(b); } + kinds++; + total += h_cnt; + snprintf(item, sizeof(item), "%s=%ld;", h_st, h_cnt); + if (strlen(dist) + strlen(item) < sizeof(dist) - 1) strcat(dist, item); + } + EXEC SQL CLOSE cur_cshist; + + Bchg(b, T_STR1, 0, dist, 0L); + setl(b, T_COUNT, total); + setl(b, T_ARG1, kinds); + OK(b); +} diff --git a/app/src/au/svc/CARD_STOLEN_REG.h b/app/src/au/svc/CARD_STOLEN_REG.h new file mode 100644 index 0000000..53a8a8f --- /dev/null +++ b/app/src/au/svc/CARD_STOLEN_REG.h @@ -0,0 +1,20 @@ +/* + * CARD_STOLEN_REG.h - au 서비스 CARD_STOLEN_REG 카피북 (copybook / record header). + * 요청: 카드/사유/영업일. 응답: 도난등록+부정로그 ID + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef CARD_STOLEN_REG_H +#define CARD_STOLEN_REG_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_card_stolen_reg_rec_t; + +void CARD_STOLEN_REG(TPSVCINFO *p); + +#endif /* CARD_STOLEN_REG_H */ diff --git a/app/src/au/svc/CARD_STOLEN_REG.pgc b/app/src/au/svc/CARD_STOLEN_REG.pgc new file mode 100644 index 0000000..b834c1b --- /dev/null +++ b/app/src/au/svc/CARD_STOLEN_REG.pgc @@ -0,0 +1,40 @@ +/* + * CARD_STOLEN_REG.pgc - au 모듈 서비스 CARD_STOLEN_REG (one service = one file). + * 도난 등록: 원장 상태를 STOLEN 으로 전환하고 블랙리스트를 STOLEN 사유로 + * 갱신한 뒤, 최고점(999) 부정거래 로그를 남겨 사후추적 근거를 만든다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "CARD_STOLEN_REG.h" + +void CARD_STOLEN_REG(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24], reason[128], bizdate[16]; + long fid; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + gets_(b, T_REASON, reason, sizeof(reason)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (card[0] == 0 || bizdate[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL UPDATE card_limit SET status = 'STOLEN', updated_at = now() + WHERE card_no = :h_card; + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL INSERT INTO au_blacklist (card_no, reason) + VALUES (:h_card, 'STOLEN') + ON CONFLICT (card_no) DO UPDATE SET reason = 'STOLEN'; + if (sqlca.sqlcode < 0) FAIL(b); + + fid = audb_insert_fraud(0, card, 999, "CARD_STOLEN", bizdate); + if (fid < 0) FAIL(b); + + userlog("CARD_STOLEN_REG 도난등록 card=%s fraud=%ld", card, fid); + setl(b, T_ID1, fid); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/au/svc/CHANNEL_POLICY.h b/app/src/au/svc/CHANNEL_POLICY.h new file mode 100644 index 0000000..44ef5ef --- /dev/null +++ b/app/src/au/svc/CHANNEL_POLICY.h @@ -0,0 +1,20 @@ +/* + * CHANNEL_POLICY.h - au 서비스 CHANNEL_POLICY 카피북 (copybook / record header). + * 요청: 채널. 응답: 취소율/정책판정(ALLOW/THROTTLE) + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef CHANNEL_POLICY_H +#define CHANNEL_POLICY_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_channel_policy_rec_t; + +void CHANNEL_POLICY(TPSVCINFO *p); + +#endif /* CHANNEL_POLICY_H */ diff --git a/app/src/au/svc/CHANNEL_POLICY.pgc b/app/src/au/svc/CHANNEL_POLICY.pgc new file mode 100644 index 0000000..1ffbda6 --- /dev/null +++ b/app/src/au/svc/CHANNEL_POLICY.pgc @@ -0,0 +1,39 @@ +/* + * CHANNEL_POLICY.pgc - au 모듈 서비스 CHANNEL_POLICY (one service = one file). + * 채널별 승인 정책: 매입 원장에서 당일 해당 채널(POS/EDC/EDI/FOREIGN)의 + * 취소율(bps)을 계산해 15% 초과면 THROTTLE, 아니면 ALLOW 로 판정한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "CHANNEL_POLICY.h" + +void CHANNEL_POLICY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16]; + long rate = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_all = 0, h_cxl = 0; + char h_chan[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); + if (chan[0] == 0) strcpy(chan, "POS"); + strncpy(h_chan, chan, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0; + + EXEC SQL SELECT count(*), count(*) FILTER (WHERE status = 'C') + INTO :h_all, :h_cxl + FROM purchase WHERE channel = :h_chan AND biz_date = current_date; + if (sqlca.sqlcode < 0) FAIL(b); + + if (h_all > 0) rate = (h_cxl * 10000L) / h_all; + setl(b, T_COUNT, h_all); + setl(b, T_ARG1, rate); + if (rate > 1500) { + Bchg(b, T_STR1, 0, "THROTTLE", 0L); + setl(b, T_RC, 1); + } else { + Bchg(b, T_STR1, 0, "ALLOW", 0L); + setl(b, T_RC, 0); + } + OK(b); +} diff --git a/app/src/au/svc/CHANNEL_STAT.h b/app/src/au/svc/CHANNEL_STAT.h new file mode 100644 index 0000000..d1fb5c7 --- /dev/null +++ b/app/src/au/svc/CHANNEL_STAT.h @@ -0,0 +1,20 @@ +/* + * CHANNEL_STAT.h - au 서비스 CHANNEL_STAT 카피북 (copybook / record header). + * 요청: 영업일. 응답: 채널별 분포/최다채널/총건수 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef CHANNEL_STAT_H +#define CHANNEL_STAT_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_channel_stat_rec_t; + +void CHANNEL_STAT(TPSVCINFO *p); + +#endif /* CHANNEL_STAT_H */ diff --git a/app/src/au/svc/CHANNEL_STAT.pgc b/app/src/au/svc/CHANNEL_STAT.pgc new file mode 100644 index 0000000..5b3662d --- /dev/null +++ b/app/src/au/svc/CHANNEL_STAT.pgc @@ -0,0 +1,45 @@ +/* + * CHANNEL_STAT.pgc - au 모듈 서비스 CHANNEL_STAT (one service = one file). + * 채널별 매입 통계: 영업일 매입을 채널별로 그룹핑하는 커서를 순회하며 + * "채널=건수/금액;" 분포 문자열과 총건수, 최다 매출채널을 산출한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "CHANNEL_STAT.h" + +void CHANNEL_STAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], mix[256] = "", top_chan[16] = "", item[48]; + long total = 0, top_amt = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_amt; + char h_bizdate[16], h_chan[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) FAIL(b); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL DECLARE cur_chstat CURSOR FOR + SELECT channel, count(*), coalesce(sum(amount),0) + FROM purchase WHERE biz_date = :h_bizdate + GROUP BY channel ORDER BY coalesce(sum(amount),0) DESC; + EXEC SQL OPEN cur_chstat; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_chstat INTO :h_chan, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_chstat; FAIL(b); } + total += h_cnt; + if (h_amt > top_amt) { top_amt = h_amt; strncpy(top_chan, h_chan, sizeof(top_chan)-1); } + snprintf(item, sizeof(item), "%s=%ld/%ld;", h_chan, h_cnt, h_amt); + if (strlen(mix) + strlen(item) < sizeof(mix) - 1) strcat(mix, item); + } + EXEC SQL CLOSE cur_chstat; + + Bchg(b, T_STR1, 0, mix, 0L); + Bchg(b, T_CHANNEL, 0, top_chan, 0L); + setl(b, T_COUNT, total); + setl(b, T_GROSS, top_amt); + OK(b); +} diff --git a/app/src/au/svc/FRAUD_CASE_CLOSE.h b/app/src/au/svc/FRAUD_CASE_CLOSE.h new file mode 100644 index 0000000..66ff55d --- /dev/null +++ b/app/src/au/svc/FRAUD_CASE_CLOSE.h @@ -0,0 +1,20 @@ +/* + * FRAUD_CASE_CLOSE.h - au 서비스 FRAUD_CASE_CLOSE 카피북 (copybook / record header). + * 요청: 부정ID. 응답: 종결 결과 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef FRAUD_CASE_CLOSE_H +#define FRAUD_CASE_CLOSE_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_fraud_case_close_rec_t; + +void FRAUD_CASE_CLOSE(TPSVCINFO *p); + +#endif /* FRAUD_CASE_CLOSE_H */ diff --git a/app/src/au/svc/FRAUD_CASE_CLOSE.pgc b/app/src/au/svc/FRAUD_CASE_CLOSE.pgc new file mode 100644 index 0000000..82c19aa --- /dev/null +++ b/app/src/au/svc/FRAUD_CASE_CLOSE.pgc @@ -0,0 +1,27 @@ +/* + * FRAUD_CASE_CLOSE.pgc - au 모듈 서비스 FRAUD_CASE_CLOSE (one service = one file). + * 부정 케이스 종결: 미종결 부정로그의 사유 뒤에 '/CLOSED' 를 붙여 종결 표시 + * 한다. 이미 종결됐거나 존재하지 않으면 RC=1 로 알린다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "FRAUD_CASE_CLOSE.h" + +void FRAUD_CASE_CLOSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_fid; + EXEC SQL END DECLARE SECTION; + + h_fid = getl(b, T_ID1); + if (h_fid <= 0) FAIL(b); + + EXEC SQL UPDATE au_fraud_log SET reason = reason || '/CLOSED' + WHERE fraud_id = :h_fid AND reason NOT LIKE '%CLOSED%'; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + + userlog("FRAUD_CASE_CLOSE fraud=%ld 종결", h_fid); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/au/svc/FRAUD_CASE_LIST.h b/app/src/au/svc/FRAUD_CASE_LIST.h new file mode 100644 index 0000000..6ab1da6 --- /dev/null +++ b/app/src/au/svc/FRAUD_CASE_LIST.h @@ -0,0 +1,20 @@ +/* + * FRAUD_CASE_LIST.h - au 서비스 FRAUD_CASE_LIST 카피북 (copybook / record header). + * 요청: 영업일. 응답: 상위 케이스 문자열/건수 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef FRAUD_CASE_LIST_H +#define FRAUD_CASE_LIST_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_fraud_case_list_rec_t; + +void FRAUD_CASE_LIST(TPSVCINFO *p); + +#endif /* FRAUD_CASE_LIST_H */ diff --git a/app/src/au/svc/FRAUD_CASE_LIST.pgc b/app/src/au/svc/FRAUD_CASE_LIST.pgc new file mode 100644 index 0000000..42166ee --- /dev/null +++ b/app/src/au/svc/FRAUD_CASE_LIST.pgc @@ -0,0 +1,41 @@ +/* + * FRAUD_CASE_LIST.pgc - au 모듈 서비스 FRAUD_CASE_LIST (one service = one file). + * 부정 케이스 상위목록: 영업일 부정로그를 점수 내림차순 상위 5건 커서로 + * 뽑아 "카드:점수;" 목록 문자열과 케이스 건수를 만든다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "FRAUD_CASE_LIST.h" + +void FRAUD_CASE_LIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], list[256] = "", item[40]; + long cnt = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_score; + char h_bizdate[16], h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) FAIL(b); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL DECLARE cur_fclist CURSOR FOR + SELECT card_no, score FROM au_fraud_log + WHERE biz_date = :h_bizdate ORDER BY score DESC LIMIT 5; + EXEC SQL OPEN cur_fclist; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_fclist INTO :h_card, :h_score; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_fclist; FAIL(b); } + cnt++; + snprintf(item, sizeof(item), "%s:%ld;", h_card, h_score); + if (strlen(list) + strlen(item) < sizeof(list) - 1) strcat(list, item); + } + EXEC SQL CLOSE cur_fclist; + + Bchg(b, T_STR1, 0, list, 0L); + setl(b, T_COUNT, cnt); + OK(b); +} diff --git a/app/src/au/svc/FRAUD_RULE_INTL.h b/app/src/au/svc/FRAUD_RULE_INTL.h new file mode 100644 index 0000000..6b07877 --- /dev/null +++ b/app/src/au/svc/FRAUD_RULE_INTL.h @@ -0,0 +1,20 @@ +/* + * FRAUD_RULE_INTL.h - au 서비스 FRAUD_RULE_INTL 카피북 (copybook / record header). + * 요청: 카드/영업일. 응답: 고액건수/적발ID + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef FRAUD_RULE_INTL_H +#define FRAUD_RULE_INTL_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_fraud_rule_intl_rec_t; + +void FRAUD_RULE_INTL(TPSVCINFO *p); + +#endif /* FRAUD_RULE_INTL_H */ diff --git a/app/src/au/svc/FRAUD_RULE_INTL.pgc b/app/src/au/svc/FRAUD_RULE_INTL.pgc new file mode 100644 index 0000000..ff0ce9e --- /dev/null +++ b/app/src/au/svc/FRAUD_RULE_INTL.pgc @@ -0,0 +1,43 @@ +/* + * FRAUD_RULE_INTL.pgc - au 모듈 서비스 FRAUD_RULE_INTL (one service = one file). + * 고액(해외성) 이상거래 룰: 최근 1일 100만원 초과 승인이 3건 이상이면 + * 고액 반복으로 보고 점수(720 + 초과*25)로 적발한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "FRAUD_RULE_INTL.h" + +void FRAUD_RULE_INTL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24], bizdate[16]; + long score, fid; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt = 0, h_sum = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (card[0] == 0 || bizdate[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_cnt, :h_sum + FROM au_authorization + WHERE card_no = :h_card AND amount > 1000000 + AND created_at >= now() - interval '1 day'; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_sum); + if (h_cnt < 3) { setl(b, T_RC, 0); OK(b); } + + score = 720 + (h_cnt - 3) * 25; + if (score > 999) score = 999; + fid = audb_insert_fraud(0, card, score, "RULE_HIGH_AMT", bizdate); + if (fid < 0) FAIL(b); + + userlog("FRAUD_RULE_INTL card=%s cnt=%ld sum=%ld", card, h_cnt, h_sum); + setl(b, T_ID1, fid); + setl(b, T_RC, 1); + OK(b); +} diff --git a/app/src/au/svc/FRAUD_RULE_MCC.h b/app/src/au/svc/FRAUD_RULE_MCC.h new file mode 100644 index 0000000..508ca12 --- /dev/null +++ b/app/src/au/svc/FRAUD_RULE_MCC.h @@ -0,0 +1,20 @@ +/* + * FRAUD_RULE_MCC.h - au 서비스 FRAUD_RULE_MCC 카피북 (copybook / record header). + * 요청: 카드/영업일. 응답: 관찰가맹 건수/적발ID + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef FRAUD_RULE_MCC_H +#define FRAUD_RULE_MCC_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_fraud_rule_mcc_rec_t; + +void FRAUD_RULE_MCC(TPSVCINFO *p); + +#endif /* FRAUD_RULE_MCC_H */ diff --git a/app/src/au/svc/FRAUD_RULE_MCC.pgc b/app/src/au/svc/FRAUD_RULE_MCC.pgc new file mode 100644 index 0000000..95e188b --- /dev/null +++ b/app/src/au/svc/FRAUD_RULE_MCC.pgc @@ -0,0 +1,41 @@ +/* + * FRAUD_RULE_MCC.pgc - au 모듈 서비스 FRAUD_RULE_MCC (one service = one file). + * 관찰가맹 이상거래 룰: 카드의 당일 승인 중 상태가 WATCH 인 가맹점 건수를 + * 서브쿼리로 세어 2건 이상이면 고위험 가맹 반복으로 적발(점수 760)한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "FRAUD_RULE_MCC.h" + +void FRAUD_RULE_MCC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24], bizdate[16]; + long fid; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt = 0; + char h_card[24], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (card[0] == 0 || bizdate[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL SELECT count(*) INTO :h_cnt + FROM au_authorization a + WHERE a.card_no = :h_card AND a.biz_date = :h_bizdate + AND a.merchant_id IN (SELECT merchant_id FROM merchant WHERE status = 'WATCH'); + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + if (h_cnt < 2) { setl(b, T_RC, 0); OK(b); } + + fid = audb_insert_fraud(0, card, 760, "RULE_WATCH_MCC", bizdate); + if (fid < 0) FAIL(b); + + userlog("FRAUD_RULE_MCC card=%s watch=%ld", card, h_cnt); + setl(b, T_ID1, fid); + setl(b, T_RC, 1); + OK(b); +} diff --git a/app/src/au/svc/FRAUD_RULE_NIGHT.h b/app/src/au/svc/FRAUD_RULE_NIGHT.h new file mode 100644 index 0000000..9f6acc7 --- /dev/null +++ b/app/src/au/svc/FRAUD_RULE_NIGHT.h @@ -0,0 +1,20 @@ +/* + * FRAUD_RULE_NIGHT.h - au 서비스 FRAUD_RULE_NIGHT 카피북 (copybook / record header). + * 요청: 카드/영업일. 응답: 심야건수/적발ID + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef FRAUD_RULE_NIGHT_H +#define FRAUD_RULE_NIGHT_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_fraud_rule_night_rec_t; + +void FRAUD_RULE_NIGHT(TPSVCINFO *p); + +#endif /* FRAUD_RULE_NIGHT_H */ diff --git a/app/src/au/svc/FRAUD_RULE_NIGHT.pgc b/app/src/au/svc/FRAUD_RULE_NIGHT.pgc new file mode 100644 index 0000000..f61dd6b --- /dev/null +++ b/app/src/au/svc/FRAUD_RULE_NIGHT.pgc @@ -0,0 +1,42 @@ +/* + * FRAUD_RULE_NIGHT.pgc - au 모듈 서비스 FRAUD_RULE_NIGHT (one service = one file). + * 심야 이상거래 룰: 최근 3일 심야시간대(23~06시) 승인 시도가 6건 이상이면 + * 건수 비례 점수(650 + 초과*15)로 부정거래 로그에 적발한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "FRAUD_RULE_NIGHT.h" + +void FRAUD_RULE_NIGHT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24], bizdate[16]; + long score, fid; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (card[0] == 0 || bizdate[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT count(*) INTO :h_cnt FROM au_authorization + WHERE card_no = :h_card + AND created_at >= now() - interval '3 days' + AND (EXTRACT(HOUR FROM created_at) >= 23 OR EXTRACT(HOUR FROM created_at) < 6); + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + if (h_cnt < 6) { setl(b, T_RC, 0); OK(b); } + + score = 650 + (h_cnt - 6) * 15; + if (score > 999) score = 999; + fid = audb_insert_fraud(0, card, score, "RULE_NIGHT", bizdate); + if (fid < 0) FAIL(b); + + userlog("FRAUD_RULE_NIGHT card=%s night=%ld score=%ld", card, h_cnt, score); + setl(b, T_ID1, fid); + setl(b, T_RC, 1); + OK(b); +} diff --git a/app/src/au/svc/FRAUD_SCORE_SUM.h b/app/src/au/svc/FRAUD_SCORE_SUM.h new file mode 100644 index 0000000..baf433e --- /dev/null +++ b/app/src/au/svc/FRAUD_SCORE_SUM.h @@ -0,0 +1,20 @@ +/* + * FRAUD_SCORE_SUM.h - au 서비스 FRAUD_SCORE_SUM 카피북 (copybook / record header). + * 요청: 카드. 응답: 적발수/점수합/최고점수/위험등급 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef FRAUD_SCORE_SUM_H +#define FRAUD_SCORE_SUM_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_fraud_score_sum_rec_t; + +void FRAUD_SCORE_SUM(TPSVCINFO *p); + +#endif /* FRAUD_SCORE_SUM_H */ diff --git a/app/src/au/svc/FRAUD_SCORE_SUM.pgc b/app/src/au/svc/FRAUD_SCORE_SUM.pgc new file mode 100644 index 0000000..91116dd --- /dev/null +++ b/app/src/au/svc/FRAUD_SCORE_SUM.pgc @@ -0,0 +1,38 @@ +/* + * FRAUD_SCORE_SUM.pgc - au 모듈 서비스 FRAUD_SCORE_SUM (one service = one file). + * 카드 위험도 종합: 카드의 부정 적발 건수/점수합/최고점수를 집계해 최고점수 + * 구간으로 LOW/MID/HIGH/CRITICAL 위험등급을 매겨 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "FRAUD_SCORE_SUM.h" + +void FRAUD_SCORE_SUM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + const char *grade; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt = 0, h_sum = 0, h_max = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT count(*), coalesce(sum(score),0), coalesce(max(score),0) + INTO :h_cnt, :h_sum, :h_max + FROM au_fraud_log WHERE card_no = :h_card; + if (sqlca.sqlcode < 0) FAIL(b); + + if (h_max >= 900) grade = "CRITICAL"; + else if (h_max >= 700) grade = "HIGH"; + else if (h_max >= 400) grade = "MID"; + else grade = "LOW"; + + setl(b, T_COUNT, h_cnt); + setl(b, T_ARG1, h_sum); + setl(b, T_ARG2, h_max); + Bchg(b, T_STR1, 0, (char *)grade, 0L); + OK(b); +} diff --git a/app/src/au/svc/KEY_ROTATE_LOG.h b/app/src/au/svc/KEY_ROTATE_LOG.h new file mode 100644 index 0000000..42c107b --- /dev/null +++ b/app/src/au/svc/KEY_ROTATE_LOG.h @@ -0,0 +1,20 @@ +/* + * KEY_ROTATE_LOG.h - au 서비스 KEY_ROTATE_LOG 카피북 (copybook / record header). + * 요청: 키버전(T_ARG1). 응답: 차기버전/파생키값 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef KEY_ROTATE_LOG_H +#define KEY_ROTATE_LOG_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_key_rotate_log_rec_t; + +void KEY_ROTATE_LOG(TPSVCINFO *p); + +#endif /* KEY_ROTATE_LOG_H */ diff --git a/app/src/au/svc/KEY_ROTATE_LOG.pgc b/app/src/au/svc/KEY_ROTATE_LOG.pgc new file mode 100644 index 0000000..a50e868 --- /dev/null +++ b/app/src/au/svc/KEY_ROTATE_LOG.pgc @@ -0,0 +1,30 @@ +/* + * KEY_ROTATE_LOG.pgc - au 모듈 서비스 KEY_ROTATE_LOG (one service = one file). + * 암호키 회전 로그: 요청 키버전(T_ARG1)의 차기 버전을 산출하고 키식별자 + * 문자열에서 파생 체크값(문자합 mod 9973)을 계산한다(순수 연산, DB 접근 없음). + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "KEY_ROTATE_LOG.h" + +void KEY_ROTATE_LOG(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char keyid[64]; + long cur, next, chk = 0; + int i, len; + + cur = getl(b, T_ARG1); + gets_(b, T_KEY1, keyid, sizeof(keyid)); + if (keyid[0] == 0) FAIL(b); + + next = (cur < 0) ? 1 : cur + 1; + len = (int)strlen(keyid); + for (i = 0; i < len; i++) + chk = (chk * 31 + (unsigned char)keyid[i]) % 9973; + + userlog("KEY_ROTATE_LOG key=%s v%ld->v%ld chk=%ld", keyid, cur, next, chk); + setl(b, T_ARG2, next); + setl(b, T_ARG3, chk); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/au/svc/LIMIT_ADJUST.h b/app/src/au/svc/LIMIT_ADJUST.h new file mode 100644 index 0000000..1ce0242 --- /dev/null +++ b/app/src/au/svc/LIMIT_ADJUST.h @@ -0,0 +1,20 @@ +/* + * LIMIT_ADJUST.h - au 서비스 LIMIT_ADJUST 카피북 (copybook / record header). + * 요청: 카드/증감(T_DELTA). 응답: 조정후 한도/결과 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef LIMIT_ADJUST_H +#define LIMIT_ADJUST_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_limit_adjust_rec_t; + +void LIMIT_ADJUST(TPSVCINFO *p); + +#endif /* LIMIT_ADJUST_H */ diff --git a/app/src/au/svc/LIMIT_ADJUST.pgc b/app/src/au/svc/LIMIT_ADJUST.pgc new file mode 100644 index 0000000..7210bc9 --- /dev/null +++ b/app/src/au/svc/LIMIT_ADJUST.pgc @@ -0,0 +1,34 @@ +/* + * LIMIT_ADJUST.pgc - au 모듈 서비스 LIMIT_ADJUST (one service = one file). + * 한도 증감 조정: 요청 증감액(T_DELTA)을 일한도에 가감하되 음수 한도가 되지 + * 않도록 조건부 UPDATE 한다. 조건 불충족(음수화)이면 RC=1, 성공 시 새 한도. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "LIMIT_ADJUST.h" + +void LIMIT_ADJUST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + EXEC SQL BEGIN DECLARE SECTION; + long h_delta, h_new = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + h_delta = getl(b, T_DELTA); + if (card[0] == 0 || h_delta == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL UPDATE card_limit + SET daily_limit = daily_limit + :h_delta, updated_at = now() + WHERE card_no = :h_card AND daily_limit + :h_delta >= 0; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + + EXEC SQL SELECT daily_limit INTO :h_new FROM card_limit WHERE card_no = :h_card; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_AMT1, h_new); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/au/svc/LIMIT_CUMUL_INQ.h b/app/src/au/svc/LIMIT_CUMUL_INQ.h new file mode 100644 index 0000000..2e8c967 --- /dev/null +++ b/app/src/au/svc/LIMIT_CUMUL_INQ.h @@ -0,0 +1,20 @@ +/* + * LIMIT_CUMUL_INQ.h - au 서비스 LIMIT_CUMUL_INQ 카피북 (copybook / record header). + * 요청: 카드. 응답: 누적건수/누적합계/최대단건 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef LIMIT_CUMUL_INQ_H +#define LIMIT_CUMUL_INQ_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_limit_cumul_inq_rec_t; + +void LIMIT_CUMUL_INQ(TPSVCINFO *p); + +#endif /* LIMIT_CUMUL_INQ_H */ diff --git a/app/src/au/svc/LIMIT_CUMUL_INQ.pgc b/app/src/au/svc/LIMIT_CUMUL_INQ.pgc new file mode 100644 index 0000000..9466f47 --- /dev/null +++ b/app/src/au/svc/LIMIT_CUMUL_INQ.pgc @@ -0,0 +1,31 @@ +/* + * LIMIT_CUMUL_INQ.pgc - au 모듈 서비스 LIMIT_CUMUL_INQ (one service = one file). + * 전(全)기간 누적 사용 조회: 카드의 전체 승인 이력에서 누적 건수/누적 합계/ + * 최대 단건 금액을 뽑아 준다(기간 제한 없음). + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "LIMIT_CUMUL_INQ.h" + +void LIMIT_CUMUL_INQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt = 0, h_sum = 0, h_max = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT count(*), coalesce(sum(amount),0), coalesce(max(amount),0) + INTO :h_cnt, :h_sum, :h_max + FROM au_authorization WHERE card_no = :h_card AND status IN ('A','M','S'); + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_AMT1, h_sum); + setl(b, T_AMT2, h_max); + OK(b); +} diff --git a/app/src/au/svc/LIMIT_DAILY_INQ.h b/app/src/au/svc/LIMIT_DAILY_INQ.h new file mode 100644 index 0000000..4658e39 --- /dev/null +++ b/app/src/au/svc/LIMIT_DAILY_INQ.h @@ -0,0 +1,20 @@ +/* + * LIMIT_DAILY_INQ.h - au 서비스 LIMIT_DAILY_INQ 카피북 (copybook / record header). + * 요청: 카드. 응답: 일한도/사용액/임시한도/가용액/상태 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef LIMIT_DAILY_INQ_H +#define LIMIT_DAILY_INQ_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_limit_daily_inq_rec_t; + +void LIMIT_DAILY_INQ(TPSVCINFO *p); + +#endif /* LIMIT_DAILY_INQ_H */ diff --git a/app/src/au/svc/LIMIT_DAILY_INQ.pgc b/app/src/au/svc/LIMIT_DAILY_INQ.pgc new file mode 100644 index 0000000..9089fac --- /dev/null +++ b/app/src/au/svc/LIMIT_DAILY_INQ.pgc @@ -0,0 +1,34 @@ +/* + * LIMIT_DAILY_INQ.pgc - au 모듈 서비스 LIMIT_DAILY_INQ (one service = one file). + * 일 한도 상세 조회: 원장에서 일한도/사용액/임시한도/상태를 한 번에 읽어 + * 가용액(일한도+임시한도-사용액)까지 계산해 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "LIMIT_DAILY_INQ.h" + +void LIMIT_DAILY_INQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + EXEC SQL BEGIN DECLARE SECTION; + long h_daily = 0, h_used = 0, h_temp = 0; + char h_card[24], h_status[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + memset(h_status, 0, sizeof(h_status)); + + EXEC SQL SELECT daily_limit, used_amount, temp_limit, status + INTO :h_daily, :h_used, :h_temp, :h_status + FROM card_limit WHERE card_no = :h_card; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + + setl(b, T_AMT1, h_daily); + setl(b, T_AMT2, h_used); + setl(b, T_AMT3, h_temp); + setl(b, T_AMT4, h_daily + h_temp - h_used); + Bchg(b, T_STATUS, 0, h_status, 0L); + OK(b); +} diff --git a/app/src/au/svc/LIMIT_EXHAUST_LST.h b/app/src/au/svc/LIMIT_EXHAUST_LST.h new file mode 100644 index 0000000..3e9d835 --- /dev/null +++ b/app/src/au/svc/LIMIT_EXHAUST_LST.h @@ -0,0 +1,20 @@ +/* + * LIMIT_EXHAUST_LST.h - au 서비스 LIMIT_EXHAUST_LST 카피북 (copybook / record header). + * 요청: 기준율(T_ARG1). 응답: 소진카드 목록/건수 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef LIMIT_EXHAUST_LST_H +#define LIMIT_EXHAUST_LST_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_limit_exhaust_lst_rec_t; + +void LIMIT_EXHAUST_LST(TPSVCINFO *p); + +#endif /* LIMIT_EXHAUST_LST_H */ diff --git a/app/src/au/svc/LIMIT_EXHAUST_LST.pgc b/app/src/au/svc/LIMIT_EXHAUST_LST.pgc new file mode 100644 index 0000000..447bbe3 --- /dev/null +++ b/app/src/au/svc/LIMIT_EXHAUST_LST.pgc @@ -0,0 +1,44 @@ +/* + * LIMIT_EXHAUST_LST.pgc - au 모듈 서비스 LIMIT_EXHAUST_LST (one service = one file). + * 한도 소진 카드 목록: 요청 기준율(bps, 기본 9000=90%) 이상으로 한도를 소진한 + * 카드를 커서로 골라 "카드:소진율;" 목록과 대상 건수를 만든다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "LIMIT_EXHAUST_LST.h" + +void LIMIT_EXHAUST_LST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char list[256] = "", item[40]; + long n = 0, thr; + EXEC SQL BEGIN DECLARE SECTION; + long h_thr, h_bps; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + thr = getl(b, T_ARG1); + if (thr <= 0) thr = 9000; + h_thr = thr; + + EXEC SQL DECLARE cur_exlst CURSOR FOR + SELECT card_no, (used_amount * 10000 / nullif(daily_limit, 0)) AS bps + FROM card_limit + WHERE daily_limit > 0 AND used_amount * 10000 / daily_limit >= :h_thr + ORDER BY bps DESC; + EXEC SQL OPEN cur_exlst; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_exlst INTO :h_card, :h_bps; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_exlst; FAIL(b); } + n++; + snprintf(item, sizeof(item), "%s:%ld;", h_card, h_bps); + if (strlen(list) + strlen(item) < sizeof(list) - 1) strcat(list, item); + } + EXEC SQL CLOSE cur_exlst; + + Bchg(b, T_STR1, 0, list, 0L); + setl(b, T_COUNT, n); + setl(b, T_ARG1, thr); + OK(b); +} diff --git a/app/src/au/svc/LIMIT_FREEZE.h b/app/src/au/svc/LIMIT_FREEZE.h new file mode 100644 index 0000000..d372d80 --- /dev/null +++ b/app/src/au/svc/LIMIT_FREEZE.h @@ -0,0 +1,20 @@ +/* + * LIMIT_FREEZE.h - au 서비스 LIMIT_FREEZE 카피북 (copybook / record header). + * 요청: 카드. 응답: 동결 결과(T_RC) + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef LIMIT_FREEZE_H +#define LIMIT_FREEZE_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_limit_freeze_rec_t; + +void LIMIT_FREEZE(TPSVCINFO *p); + +#endif /* LIMIT_FREEZE_H */ diff --git a/app/src/au/svc/LIMIT_FREEZE.pgc b/app/src/au/svc/LIMIT_FREEZE.pgc new file mode 100644 index 0000000..a3a0515 --- /dev/null +++ b/app/src/au/svc/LIMIT_FREEZE.pgc @@ -0,0 +1,31 @@ +/* + * LIMIT_FREEZE.pgc - au 모듈 서비스 LIMIT_FREEZE (one service = one file). + * 한도 동결: ACTIVE 상태의 카드 원장만 FROZEN 으로 전환한다. 이미 다른 + * 상태면 대상 아님(RC=1)으로 응답하고 관제 로그를 남긴다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "LIMIT_FREEZE.h" + +void LIMIT_FREEZE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL UPDATE card_limit SET status = 'FROZEN', updated_at = now() + WHERE card_no = :h_card AND status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlcode == 100) { + userlog("LIMIT_FREEZE 대상아님 card=%s", card); + setl(b, T_RC, 1); + OK(b); + } + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/au/svc/LIMIT_MONTH_CHK.h b/app/src/au/svc/LIMIT_MONTH_CHK.h new file mode 100644 index 0000000..3eb19da --- /dev/null +++ b/app/src/au/svc/LIMIT_MONTH_CHK.h @@ -0,0 +1,20 @@ +/* + * LIMIT_MONTH_CHK.h - au 서비스 LIMIT_MONTH_CHK 카피북 (copybook / record header). + * 요청: 카드/금액. 응답: 월누적/월한도/초과여부(T_RC) + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef LIMIT_MONTH_CHK_H +#define LIMIT_MONTH_CHK_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_limit_month_chk_rec_t; + +void LIMIT_MONTH_CHK(TPSVCINFO *p); + +#endif /* LIMIT_MONTH_CHK_H */ diff --git a/app/src/au/svc/LIMIT_MONTH_CHK.pgc b/app/src/au/svc/LIMIT_MONTH_CHK.pgc new file mode 100644 index 0000000..8f4492a --- /dev/null +++ b/app/src/au/svc/LIMIT_MONTH_CHK.pgc @@ -0,0 +1,38 @@ +/* + * LIMIT_MONTH_CHK.pgc - au 모듈 서비스 LIMIT_MONTH_CHK (one service = one file). + * 월 한도 점검: 이번 달 승인(A/M/S) 누적액에 요청금액을 더해 월 한도 + * (일한도 x 25 영업일 근사)를 넘는지 판정한다(RC=1 초과). + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "LIMIT_MONTH_CHK.h" + +void LIMIT_MONTH_CHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + long amount, cap = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_month = 0, h_daily = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + amount = getl(b, T_AMOUNT); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_month + FROM au_authorization + WHERE card_no = :h_card AND status IN ('A','M','S') + AND biz_date >= date_trunc('month', current_date); + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT daily_limit INTO :h_daily FROM card_limit WHERE card_no = :h_card; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + + cap = h_daily * 25; + setl(b, T_AMT1, h_month); + setl(b, T_AMT2, cap); + setl(b, T_RC, (h_month + amount > cap) ? 1 : 0); + OK(b); +} diff --git a/app/src/au/svc/LIMIT_MONTH_INQ.h b/app/src/au/svc/LIMIT_MONTH_INQ.h new file mode 100644 index 0000000..9e23369 --- /dev/null +++ b/app/src/au/svc/LIMIT_MONTH_INQ.h @@ -0,0 +1,20 @@ +/* + * LIMIT_MONTH_INQ.h - au 서비스 LIMIT_MONTH_INQ 카피북 (copybook / record header). + * 요청: 카드. 응답: 월건수/월합계/월평균 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef LIMIT_MONTH_INQ_H +#define LIMIT_MONTH_INQ_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_limit_month_inq_rec_t; + +void LIMIT_MONTH_INQ(TPSVCINFO *p); + +#endif /* LIMIT_MONTH_INQ_H */ diff --git a/app/src/au/svc/LIMIT_MONTH_INQ.pgc b/app/src/au/svc/LIMIT_MONTH_INQ.pgc new file mode 100644 index 0000000..79c8ce4 --- /dev/null +++ b/app/src/au/svc/LIMIT_MONTH_INQ.pgc @@ -0,0 +1,34 @@ +/* + * LIMIT_MONTH_INQ.pgc - au 모듈 서비스 LIMIT_MONTH_INQ (one service = one file). + * 월 누적 사용 조회: 이번 달 승인건 기준으로 건수/합계/평균단가를 한 번의 + * 집계로 산출해 내려준다(카드 실적 리포트 용). + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "LIMIT_MONTH_INQ.h" + +void LIMIT_MONTH_INQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt = 0, h_sum = 0, h_avg = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT count(*), coalesce(sum(amount),0), + CAST(coalesce(avg(amount),0) AS bigint) + INTO :h_cnt, :h_sum, :h_avg + FROM au_authorization + WHERE card_no = :h_card AND status IN ('A','M','S') + AND biz_date >= date_trunc('month', current_date); + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_AMT1, h_sum); + setl(b, T_AMT2, h_avg); + OK(b); +} diff --git a/app/src/au/svc/LIMIT_OVER_ALERT.h b/app/src/au/svc/LIMIT_OVER_ALERT.h new file mode 100644 index 0000000..125cc16 --- /dev/null +++ b/app/src/au/svc/LIMIT_OVER_ALERT.h @@ -0,0 +1,20 @@ +/* + * LIMIT_OVER_ALERT.h - au 서비스 LIMIT_OVER_ALERT 카피북 (copybook / record header). + * 요청: 카드. 응답: 소진율(bps)/잔여/경보여부 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef LIMIT_OVER_ALERT_H +#define LIMIT_OVER_ALERT_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_limit_over_alert_rec_t; + +void LIMIT_OVER_ALERT(TPSVCINFO *p); + +#endif /* LIMIT_OVER_ALERT_H */ diff --git a/app/src/au/svc/LIMIT_OVER_ALERT.pgc b/app/src/au/svc/LIMIT_OVER_ALERT.pgc new file mode 100644 index 0000000..eb91960 --- /dev/null +++ b/app/src/au/svc/LIMIT_OVER_ALERT.pgc @@ -0,0 +1,37 @@ +/* + * LIMIT_OVER_ALERT.pgc - au 모듈 서비스 LIMIT_OVER_ALERT (one service = one file). + * 한도 소진 임박 알림: 사용액/일한도를 bps 로 환산해 90% 이상이면 경보 + * (RC=1)와 관제 로그를 내고, 잔여 한도액도 함께 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "LIMIT_OVER_ALERT.h" + +void LIMIT_OVER_ALERT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + long bps = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_daily = 0, h_used = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT daily_limit, used_amount INTO :h_daily, :h_used + FROM card_limit WHERE card_no = :h_card; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + + if (h_daily > 0) bps = (h_used * 10000L) / h_daily; + setl(b, T_ARG1, bps); + setl(b, T_AMT1, h_daily - h_used); + if (bps >= 9000) { + userlog("LIMIT_OVER_ALERT 소진임박 card=%s %ldbps", card, bps); + setl(b, T_RC, 1); + } else { + setl(b, T_RC, 0); + } + OK(b); +} diff --git a/app/src/au/svc/LIMIT_PERTX_CHK.h b/app/src/au/svc/LIMIT_PERTX_CHK.h new file mode 100644 index 0000000..0ca4c1c --- /dev/null +++ b/app/src/au/svc/LIMIT_PERTX_CHK.h @@ -0,0 +1,20 @@ +/* + * LIMIT_PERTX_CHK.h - au 서비스 LIMIT_PERTX_CHK 카피북 (copybook / record header). + * 요청: 카드/금액. 응답: 건별한도/초과여부 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef LIMIT_PERTX_CHK_H +#define LIMIT_PERTX_CHK_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_limit_pertx_chk_rec_t; + +void LIMIT_PERTX_CHK(TPSVCINFO *p); + +#endif /* LIMIT_PERTX_CHK_H */ diff --git a/app/src/au/svc/LIMIT_PERTX_CHK.pgc b/app/src/au/svc/LIMIT_PERTX_CHK.pgc new file mode 100644 index 0000000..afb3691 --- /dev/null +++ b/app/src/au/svc/LIMIT_PERTX_CHK.pgc @@ -0,0 +1,37 @@ +/* + * LIMIT_PERTX_CHK.pgc - au 모듈 서비스 LIMIT_PERTX_CHK (one service = one file). + * 건별(1회) 한도 점검: 일한도의 20% 를 건별 상한으로 삼아 요청 단건 금액이 + * 이를 넘으면 차단(RC=1)한다. 초과 시 관제 로그를 남긴다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "LIMIT_PERTX_CHK.h" + +void LIMIT_PERTX_CHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + long amount, cap; + EXEC SQL BEGIN DECLARE SECTION; + long h_daily = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + amount = getl(b, T_AMOUNT); + if (card[0] == 0 || amount <= 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT daily_limit INTO :h_daily FROM card_limit WHERE card_no = :h_card; + if (sqlca.sqlcode == 100) FAIL(b); + if (sqlca.sqlcode < 0) FAIL(b); + + cap = h_daily / 5; + setl(b, T_AMT1, cap); + if (amount > cap) { + userlog("LIMIT_PERTX_CHK 초과 card=%s req=%ld cap=%ld", card, amount, cap); + setl(b, T_RC, 1); + } else { + setl(b, T_RC, 0); + } + OK(b); +} diff --git a/app/src/au/svc/LIMIT_TIER_INQ.h b/app/src/au/svc/LIMIT_TIER_INQ.h new file mode 100644 index 0000000..d46ec3f --- /dev/null +++ b/app/src/au/svc/LIMIT_TIER_INQ.h @@ -0,0 +1,20 @@ +/* + * LIMIT_TIER_INQ.h - au 서비스 LIMIT_TIER_INQ 카피북 (copybook / record header). + * 요청: 카드. 응답: 한도등급 문자열/일한도 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef LIMIT_TIER_INQ_H +#define LIMIT_TIER_INQ_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_limit_tier_inq_rec_t; + +void LIMIT_TIER_INQ(TPSVCINFO *p); + +#endif /* LIMIT_TIER_INQ_H */ diff --git a/app/src/au/svc/LIMIT_TIER_INQ.pgc b/app/src/au/svc/LIMIT_TIER_INQ.pgc new file mode 100644 index 0000000..764e557 --- /dev/null +++ b/app/src/au/svc/LIMIT_TIER_INQ.pgc @@ -0,0 +1,34 @@ +/* + * LIMIT_TIER_INQ.pgc - au 모듈 서비스 LIMIT_TIER_INQ (one service = one file). + * 한도 등급 조회: 일한도 구간으로 BASIC/SILVER/GOLD/PLATINUM 등급을 매겨 + * 등급 문자열과 원 한도를 함께 응답한다(마케팅/우대 판정 근거). + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "LIMIT_TIER_INQ.h" + +void LIMIT_TIER_INQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + const char *tier; + EXEC SQL BEGIN DECLARE SECTION; + long h_daily = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT daily_limit INTO :h_daily FROM card_limit WHERE card_no = :h_card; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + + if (h_daily <= 1000000) tier = "BASIC"; + else if (h_daily <= 3000000) tier = "SILVER"; + else if (h_daily <= 5000000) tier = "GOLD"; + else tier = "PLATINUM"; + + Bchg(b, T_STR1, 0, (char *)tier, 0L); + setl(b, T_AMT1, h_daily); + OK(b); +} diff --git a/app/src/au/svc/LIMIT_UNFREEZE.h b/app/src/au/svc/LIMIT_UNFREEZE.h new file mode 100644 index 0000000..1855f14 --- /dev/null +++ b/app/src/au/svc/LIMIT_UNFREEZE.h @@ -0,0 +1,20 @@ +/* + * LIMIT_UNFREEZE.h - au 서비스 LIMIT_UNFREEZE 카피북 (copybook / record header). + * 요청: 카드. 응답: 동결해제 결과(T_RC) + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef LIMIT_UNFREEZE_H +#define LIMIT_UNFREEZE_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_limit_unfreeze_rec_t; + +void LIMIT_UNFREEZE(TPSVCINFO *p); + +#endif /* LIMIT_UNFREEZE_H */ diff --git a/app/src/au/svc/LIMIT_UNFREEZE.pgc b/app/src/au/svc/LIMIT_UNFREEZE.pgc new file mode 100644 index 0000000..35daa1b --- /dev/null +++ b/app/src/au/svc/LIMIT_UNFREEZE.pgc @@ -0,0 +1,26 @@ +/* + * LIMIT_UNFREEZE.pgc - au 모듈 서비스 LIMIT_UNFREEZE (one service = one file). + * 한도 동결 해제: FROZEN 상태의 카드 원장만 ACTIVE 로 되돌린다. 대상이 + * 없으면 RC=1, 성공하면 RC=0 을 rowcount 로 판정해 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "LIMIT_UNFREEZE.h" + +void LIMIT_UNFREEZE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL UPDATE card_limit SET status = 'ACTIVE', updated_at = now() + WHERE card_no = :h_card AND status = 'FROZEN'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_RC, (sqlca.sqlcode == 100) ? 1 : 0); + OK(b); +} diff --git a/app/src/au/svc/MCC_AUTH_STAT.h b/app/src/au/svc/MCC_AUTH_STAT.h new file mode 100644 index 0000000..87d2b54 --- /dev/null +++ b/app/src/au/svc/MCC_AUTH_STAT.h @@ -0,0 +1,20 @@ +/* + * MCC_AUTH_STAT.h - au 서비스 MCC_AUTH_STAT 카피북 (copybook / record header). + * 요청: 영업일. 응답: 업종별 금액 분포/최다업종 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef MCC_AUTH_STAT_H +#define MCC_AUTH_STAT_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_mcc_auth_stat_rec_t; + +void MCC_AUTH_STAT(TPSVCINFO *p); + +#endif /* MCC_AUTH_STAT_H */ diff --git a/app/src/au/svc/MCC_AUTH_STAT.pgc b/app/src/au/svc/MCC_AUTH_STAT.pgc new file mode 100644 index 0000000..6e43146 --- /dev/null +++ b/app/src/au/svc/MCC_AUTH_STAT.pgc @@ -0,0 +1,44 @@ +/* + * MCC_AUTH_STAT.pgc - au 모듈 서비스 MCC_AUTH_STAT (one service = one file). + * 업종별 매입 금액 통계: 영업일 매입을 업종(category)별 합계로 그룹핑하는 + * 커서를 돌며 "업종=금액;" 문자열과 총액, 최다 매출 업종을 뽑는다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "MCC_AUTH_STAT.h" + +void MCC_AUTH_STAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], mix[256] = "", top_cat[32] = "", item[48]; + long grand = 0, top = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_amt; + char h_bizdate[16], h_cat[32]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) FAIL(b); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL DECLARE cur_mccstat CURSOR FOR + SELECT category, coalesce(sum(amount),0) + FROM purchase WHERE biz_date = :h_bizdate + GROUP BY category ORDER BY coalesce(sum(amount),0) DESC LIMIT 8; + EXEC SQL OPEN cur_mccstat; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_mccstat INTO :h_cat, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_mccstat; FAIL(b); } + grand += h_amt; + if (h_amt > top) { top = h_amt; strncpy(top_cat, h_cat, sizeof(top_cat)-1); } + snprintf(item, sizeof(item), "%s=%ld;", h_cat, h_amt); + if (strlen(mix) + strlen(item) < sizeof(mix) - 1) strcat(mix, item); + } + EXEC SQL CLOSE cur_mccstat; + + Bchg(b, T_STR1, 0, mix, 0L); + Bchg(b, T_CATEGORY, 0, top_cat, 0L); + setl(b, T_GROSS, grand); + OK(b); +} diff --git a/app/src/au/svc/MCC_POLICY.h b/app/src/au/svc/MCC_POLICY.h new file mode 100644 index 0000000..b975262 --- /dev/null +++ b/app/src/au/svc/MCC_POLICY.h @@ -0,0 +1,20 @@ +/* + * MCC_POLICY.h - au 서비스 MCC_POLICY 카피북 (copybook / record header). + * 요청: 업종. 응답: 건수/합계/고위험 정책판정 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef MCC_POLICY_H +#define MCC_POLICY_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_mcc_policy_rec_t; + +void MCC_POLICY(TPSVCINFO *p); + +#endif /* MCC_POLICY_H */ diff --git a/app/src/au/svc/MCC_POLICY.pgc b/app/src/au/svc/MCC_POLICY.pgc new file mode 100644 index 0000000..be99b67 --- /dev/null +++ b/app/src/au/svc/MCC_POLICY.pgc @@ -0,0 +1,39 @@ +/* + * MCC_POLICY.pgc - au 모듈 서비스 MCC_POLICY (one service = one file). + * 업종(MCC)별 승인 정책: 매입 원장의 당일 업종 실적(건수/합계)을 읽어 + * 고위험 업종(CRYPTO/GAMBLING/ADULT)이면서 합계가 300만원을 넘으면 제한한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "MCC_POLICY.h" + +void MCC_POLICY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char cat[32]; + int risky; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt = 0, h_sum = 0; + char h_cat[32]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CATEGORY, cat, sizeof(cat)); + if (cat[0] == 0) strcpy(cat, "GEN"); + strncpy(h_cat, cat, sizeof(h_cat)-1); h_cat[sizeof(h_cat)-1] = 0; + + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_cnt, :h_sum + FROM purchase WHERE category = :h_cat AND biz_date = current_date; + if (sqlca.sqlcode < 0) FAIL(b); + + risky = (strcmp(cat, "CRYPTO") == 0 || strcmp(cat, "GAMBLING") == 0 || + strcmp(cat, "ADULT") == 0); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_sum); + if (risky && h_sum > 3000000) { + Bchg(b, T_STR1, 0, "DENY", 0L); + setl(b, T_RC, 1); + } else { + Bchg(b, T_STR1, 0, "PASS", 0L); + setl(b, T_RC, 0); + } + OK(b); +} diff --git a/app/src/au/svc/PREAUTH_PARTUSE.h b/app/src/au/svc/PREAUTH_PARTUSE.h new file mode 100644 index 0000000..cd42f9c --- /dev/null +++ b/app/src/au/svc/PREAUTH_PARTUSE.h @@ -0,0 +1,20 @@ +/* + * PREAUTH_PARTUSE.h - au 서비스 PREAUTH_PARTUSE 카피북 (copybook / record header). + * 요청: 예비승인ID/금액. 응답: 잔여 예비승인액 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef PREAUTH_PARTUSE_H +#define PREAUTH_PARTUSE_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_preauth_partuse_rec_t; + +void PREAUTH_PARTUSE(TPSVCINFO *p); + +#endif /* PREAUTH_PARTUSE_H */ diff --git a/app/src/au/svc/PREAUTH_PARTUSE.pgc b/app/src/au/svc/PREAUTH_PARTUSE.pgc new file mode 100644 index 0000000..a79587d --- /dev/null +++ b/app/src/au/svc/PREAUTH_PARTUSE.pgc @@ -0,0 +1,41 @@ +/* + * PREAUTH_PARTUSE.pgc - au 모듈 서비스 PREAUTH_PARTUSE (one service = one file). + * 사전승인 부분 사용: 예비승인(P) 잔액에서 사용액을 차감(부분 소진)한다. + * 사용액이 잔액을 넘으면 실패, 전액 소진 시 상태를 확정(C)으로 바꾼다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "PREAUTH_PARTUSE.h" + +void PREAUTH_PARTUSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long use, remain; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_amt = 0, h_use, h_rem; + EXEC SQL END DECLARE SECTION; + + h_id = getl(b, T_ID2); + use = getl(b, T_AMOUNT); + if (h_id <= 0 || use <= 0) FAIL(b); + + EXEC SQL SELECT amount INTO :h_amt FROM au_preauth + WHERE preauth_id = :h_id AND status = 'P'; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (use > h_amt) { setl(b, T_RC, 2); setl(b, T_AMT1, h_amt); OK(b); } + + remain = h_amt - use; + h_rem = remain; + h_use = use; + if (remain == 0) { + EXEC SQL UPDATE au_preauth SET amount = 0, status = 'C' WHERE preauth_id = :h_id; + } else { + EXEC SQL UPDATE au_preauth SET amount = :h_rem WHERE preauth_id = :h_id; + } + if (sqlca.sqlcode < 0) FAIL(b); + + userlog("PREAUTH_PARTUSE id=%ld use=%ld remain=%ld", h_id, h_use, remain); + setl(b, T_AMT1, remain); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/au/svc/STANDIN_LIMIT.h b/app/src/au/svc/STANDIN_LIMIT.h new file mode 100644 index 0000000..c0c6bb7 --- /dev/null +++ b/app/src/au/svc/STANDIN_LIMIT.h @@ -0,0 +1,20 @@ +/* + * STANDIN_LIMIT.h - au 서비스 STANDIN_LIMIT 카피북 (copybook / record header). + * 요청: 카드/금액. 응답: 대행 가용한도/초과여부 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef STANDIN_LIMIT_H +#define STANDIN_LIMIT_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_standin_limit_rec_t; + +void STANDIN_LIMIT(TPSVCINFO *p); + +#endif /* STANDIN_LIMIT_H */ diff --git a/app/src/au/svc/STANDIN_LIMIT.pgc b/app/src/au/svc/STANDIN_LIMIT.pgc new file mode 100644 index 0000000..07c8af2 --- /dev/null +++ b/app/src/au/svc/STANDIN_LIMIT.pgc @@ -0,0 +1,33 @@ +/* + * STANDIN_LIMIT.pgc - au 모듈 서비스 STANDIN_LIMIT (one service = one file). + * 대행 한도 점검: 발급사 미응답 시 대행 승인은 일한도의 절반을 상한으로 두어, + * 사용액+요청액이 이 대행상한을 넘으면 차단(RC=1)한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "STANDIN_LIMIT.h" + +void STANDIN_LIMIT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + long amount, cap; + EXEC SQL BEGIN DECLARE SECTION; + long h_daily = 0, h_used = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + amount = getl(b, T_AMOUNT); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT daily_limit, used_amount INTO :h_daily, :h_used + FROM card_limit WHERE card_no = :h_card; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + + cap = h_daily / 2; + setl(b, T_AMT1, cap); + setl(b, T_AMT2, cap - h_used); + setl(b, T_RC, (h_used + amount > cap) ? 1 : 0); + OK(b); +} diff --git a/app/src/au/svc/STANDIN_RECON.h b/app/src/au/svc/STANDIN_RECON.h new file mode 100644 index 0000000..502272e --- /dev/null +++ b/app/src/au/svc/STANDIN_RECON.h @@ -0,0 +1,20 @@ +/* + * STANDIN_RECON.h - au 서비스 STANDIN_RECON 카피북 (copybook / record header). + * 요청: 영업일. 응답: 대행건수/확정건수/미대사 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef STANDIN_RECON_H +#define STANDIN_RECON_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_standin_recon_rec_t; + +void STANDIN_RECON(TPSVCINFO *p); + +#endif /* STANDIN_RECON_H */ diff --git a/app/src/au/svc/STANDIN_RECON.pgc b/app/src/au/svc/STANDIN_RECON.pgc new file mode 100644 index 0000000..a24918d --- /dev/null +++ b/app/src/au/svc/STANDIN_RECON.pgc @@ -0,0 +1,36 @@ +/* + * STANDIN_RECON.pgc - au 모듈 서비스 STANDIN_RECON (one service = one file). + * 대행 정산 대사: 영업일 대행 승인 중 매입확정(captured_at NOT NULL) 건과 + * 미확정 건을 구분해 미대사 잔량을 계산하고, 남아 있으면 RC=1 로 알린다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "STANDIN_RECON.h" + +void STANDIN_RECON(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long unrec; + EXEC SQL BEGIN DECLARE SECTION; + long h_all = 0, h_cap = 0; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) FAIL(b); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL SELECT count(*), count(*) FILTER (WHERE captured_at IS NOT NULL) + INTO :h_all, :h_cap + FROM au_authorization + WHERE auth_type = 'STANDIN' AND biz_date = :h_bizdate + AND status IN ('A','M','S'); + if (sqlca.sqlcode < 0) FAIL(b); + + unrec = h_all - h_cap; + setl(b, T_COUNT, h_all); + setl(b, T_ARG1, h_cap); + setl(b, T_ARG2, unrec); + setl(b, T_RC, (unrec > 0) ? 1 : 0); + OK(b); +} diff --git a/app/src/au/svc/STANDIN_STAT.h b/app/src/au/svc/STANDIN_STAT.h new file mode 100644 index 0000000..41dd200 --- /dev/null +++ b/app/src/au/svc/STANDIN_STAT.h @@ -0,0 +1,20 @@ +/* + * STANDIN_STAT.h - au 서비스 STANDIN_STAT 카피북 (copybook / record header). + * 요청: 영업일. 응답: 대행건수/합계/승인건수 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef STANDIN_STAT_H +#define STANDIN_STAT_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_standin_stat_rec_t; + +void STANDIN_STAT(TPSVCINFO *p); + +#endif /* STANDIN_STAT_H */ diff --git a/app/src/au/svc/STANDIN_STAT.pgc b/app/src/au/svc/STANDIN_STAT.pgc new file mode 100644 index 0000000..45e9ae8 --- /dev/null +++ b/app/src/au/svc/STANDIN_STAT.pgc @@ -0,0 +1,33 @@ +/* + * STANDIN_STAT.pgc - au 모듈 서비스 STANDIN_STAT (one service = one file). + * 대행(stand-in) 승인 통계: 영업일 auth_type='STANDIN' 승인을 대상으로 + * 총건수/총액/성공건수(A/M/S)를 한 번에 집계해 대행 부하를 리포트한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "STANDIN_STAT.h" + +void STANDIN_STAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt = 0, h_sum = 0, h_ok = 0; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) FAIL(b); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL SELECT count(*), coalesce(sum(amount),0), + count(*) FILTER (WHERE status IN ('A','M','S')) + INTO :h_cnt, :h_sum, :h_ok + FROM au_authorization + WHERE auth_type = 'STANDIN' AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_sum); + setl(b, T_ARG1, h_ok); + OK(b); +} diff --git a/app/src/au/svc/STAT_AMT_BAND.h b/app/src/au/svc/STAT_AMT_BAND.h new file mode 100644 index 0000000..9d5c088 --- /dev/null +++ b/app/src/au/svc/STAT_AMT_BAND.h @@ -0,0 +1,20 @@ +/* + * STAT_AMT_BAND.h - au 서비스 STAT_AMT_BAND 카피북 (copybook / record header). + * 요청: 영업일. 응답: 금액대별 분포/최다구간 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef STAT_AMT_BAND_H +#define STAT_AMT_BAND_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_stat_amt_band_rec_t; + +void STAT_AMT_BAND(TPSVCINFO *p); + +#endif /* STAT_AMT_BAND_H */ diff --git a/app/src/au/svc/STAT_AMT_BAND.pgc b/app/src/au/svc/STAT_AMT_BAND.pgc new file mode 100644 index 0000000..86221ee --- /dev/null +++ b/app/src/au/svc/STAT_AMT_BAND.pgc @@ -0,0 +1,50 @@ +/* + * STAT_AMT_BAND.pgc - au 모듈 서비스 STAT_AMT_BAND (one service = one file). + * 금액대별 승인 통계: 영업일 승인을 1만/5만/10만/50만/그이상 구간(CASE)으로 + * 분류해 그룹핑하는 커서를 돌며 "구간=건수;" 분포와 최다 구간을 산출한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "STAT_AMT_BAND.h" + +void STAT_AMT_BAND(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], dist[256] = "", top_band[16] = "", item[32]; + long total = 0, top = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt; + char h_bizdate[16], h_band[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) FAIL(b); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL DECLARE cur_amtband CURSOR FOR + SELECT CASE WHEN amount < 10000 THEN 'B1' + WHEN amount < 50000 THEN 'B2' + WHEN amount < 100000 THEN 'B3' + WHEN amount < 500000 THEN 'B4' + ELSE 'B5' END AS band, + count(*) + FROM au_authorization WHERE biz_date = :h_bizdate + GROUP BY band ORDER BY band; + EXEC SQL OPEN cur_amtband; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_amtband INTO :h_band, :h_cnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_amtband; FAIL(b); } + total += h_cnt; + if (h_cnt > top) { top = h_cnt; strncpy(top_band, h_band, sizeof(top_band)-1); } + snprintf(item, sizeof(item), "%s=%ld;", h_band, h_cnt); + if (strlen(dist) + strlen(item) < sizeof(dist) - 1) strcat(dist, item); + } + EXEC SQL CLOSE cur_amtband; + + Bchg(b, T_STR1, 0, dist, 0L); + Bchg(b, T_STR2, 0, top_band, 0L); + setl(b, T_COUNT, total); + setl(b, T_ARG1, top); + OK(b); +} diff --git a/app/src/au/svc/STAT_APPROVE_TR.h b/app/src/au/svc/STAT_APPROVE_TR.h new file mode 100644 index 0000000..b653f04 --- /dev/null +++ b/app/src/au/svc/STAT_APPROVE_TR.h @@ -0,0 +1,20 @@ +/* + * STAT_APPROVE_TR.h - au 서비스 STAT_APPROVE_TR 카피북 (copybook / record header). + * 요청: 카드. 응답: 최근7일/직전7일 건수/추세 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef STAT_APPROVE_TR_H +#define STAT_APPROVE_TR_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_stat_approve_tr_rec_t; + +void STAT_APPROVE_TR(TPSVCINFO *p); + +#endif /* STAT_APPROVE_TR_H */ diff --git a/app/src/au/svc/STAT_APPROVE_TR.pgc b/app/src/au/svc/STAT_APPROVE_TR.pgc new file mode 100644 index 0000000..142715b --- /dev/null +++ b/app/src/au/svc/STAT_APPROVE_TR.pgc @@ -0,0 +1,42 @@ +/* + * STAT_APPROVE_TR.pgc - au 모듈 서비스 STAT_APPROVE_TR (one service = one file). + * 카드 승인 추세: 최근 7일과 그 직전 7일의 승인 건수를 각각 필터 집계로 뽑아 + * 증감(delta)과 방향(UP/DOWN/FLAT)을 판정해 추세를 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "STAT_APPROVE_TR.h" + +void STAT_APPROVE_TR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + const char *dir; + long delta; + EXEC SQL BEGIN DECLARE SECTION; + long h_cur = 0, h_prev = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT + count(*) FILTER (WHERE created_at >= now() - interval '7 days'), + count(*) FILTER (WHERE created_at >= now() - interval '14 days' + AND created_at < now() - interval '7 days') + INTO :h_cur, :h_prev + FROM au_authorization WHERE card_no = :h_card AND status IN ('A','M','S'); + if (sqlca.sqlcode < 0) FAIL(b); + + delta = h_cur - h_prev; + if (delta > 0) dir = "UP"; + else if (delta < 0) dir = "DOWN"; + else dir = "FLAT"; + + setl(b, T_ARG1, h_cur); + setl(b, T_ARG2, h_prev); + setl(b, T_DELTA, delta); + Bchg(b, T_STR1, 0, (char *)dir, 0L); + OK(b); +} diff --git a/app/src/au/svc/STAT_DAILY_SUM.h b/app/src/au/svc/STAT_DAILY_SUM.h new file mode 100644 index 0000000..c4b901f --- /dev/null +++ b/app/src/au/svc/STAT_DAILY_SUM.h @@ -0,0 +1,20 @@ +/* + * STAT_DAILY_SUM.h - au 서비스 STAT_DAILY_SUM 카피북 (copybook / record header). + * 요청: 영업일. 응답: 접수/승인/거절/취소/합계 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef STAT_DAILY_SUM_H +#define STAT_DAILY_SUM_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_stat_daily_sum_rec_t; + +void STAT_DAILY_SUM(TPSVCINFO *p); + +#endif /* STAT_DAILY_SUM_H */ diff --git a/app/src/au/svc/STAT_DAILY_SUM.pgc b/app/src/au/svc/STAT_DAILY_SUM.pgc new file mode 100644 index 0000000..ee96e59 --- /dev/null +++ b/app/src/au/svc/STAT_DAILY_SUM.pgc @@ -0,0 +1,37 @@ +/* + * STAT_DAILY_SUM.pgc - au 모듈 서비스 STAT_DAILY_SUM (one service = one file). + * 일별 승인 종합: 영업일 승인 원장의 접수/승인/거절/취소 건수와 승인 합계를 + * 한 번의 필터 집계로 뽑아 관제 대시보드용 요약을 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "STAT_DAILY_SUM.h" + +void STAT_DAILY_SUM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_all = 0, h_ok = 0, h_dec = 0, h_cxl = 0, h_amt = 0; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) FAIL(b); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL SELECT count(*), + count(*) FILTER (WHERE status = 'A'), + count(*) FILTER (WHERE status = 'D'), + count(*) FILTER (WHERE status = 'C'), + coalesce(sum(amount) FILTER (WHERE status IN ('A','M','S')),0) + INTO :h_all, :h_ok, :h_dec, :h_cxl, :h_amt + FROM au_authorization WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_all); + setl(b, T_ARG1, h_ok); + setl(b, T_ARG2, h_dec); + setl(b, T_ARG3, h_cxl); + setl(b, T_GROSS, h_amt); + OK(b); +} diff --git a/app/src/au/svc/STAT_DECLINE.h b/app/src/au/svc/STAT_DECLINE.h new file mode 100644 index 0000000..9f09dfa --- /dev/null +++ b/app/src/au/svc/STAT_DECLINE.h @@ -0,0 +1,20 @@ +/* + * STAT_DECLINE.h - au 서비스 STAT_DECLINE 카피북 (copybook / record header). + * 요청: 영업일. 응답: 거절사유 분포/최다사유/사유수 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef STAT_DECLINE_H +#define STAT_DECLINE_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_stat_decline_rec_t; + +void STAT_DECLINE(TPSVCINFO *p); + +#endif /* STAT_DECLINE_H */ diff --git a/app/src/au/svc/STAT_DECLINE.pgc b/app/src/au/svc/STAT_DECLINE.pgc new file mode 100644 index 0000000..98c1f60 --- /dev/null +++ b/app/src/au/svc/STAT_DECLINE.pgc @@ -0,0 +1,47 @@ +/* + * STAT_DECLINE.pgc - au 모듈 서비스 STAT_DECLINE (one service = one file). + * 거절사유별 통계: 영업일 거절(D) 건을 사유별로 그룹핑하는 커서를 순회하며 + * "사유=건수;" 분포 문자열, 총 거절건수, 사유 종류 수, 최다 사유를 만든다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "STAT_DECLINE.h" + +void STAT_DECLINE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], dist[256] = "", top[64] = "", item[64]; + long total = 0, kinds = 0, top_cnt = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt; + char h_bizdate[16], h_reason[64]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) FAIL(b); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL DECLARE cur_stdec CURSOR FOR + SELECT coalesce(nullif(decline_reason, ''), 'UNSPECIFIED'), count(*) + FROM au_authorization + WHERE biz_date = :h_bizdate AND status = 'D' + GROUP BY 1 ORDER BY 2 DESC; + EXEC SQL OPEN cur_stdec; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_stdec INTO :h_reason, :h_cnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_stdec; FAIL(b); } + kinds++; + total += h_cnt; + if (h_cnt > top_cnt) { top_cnt = h_cnt; strncpy(top, h_reason, sizeof(top)-1); } + snprintf(item, sizeof(item), "%s=%ld;", h_reason, h_cnt); + if (strlen(dist) + strlen(item) < sizeof(dist) - 1) strcat(dist, item); + } + EXEC SQL CLOSE cur_stdec; + + Bchg(b, T_STR1, 0, dist, 0L); + Bchg(b, T_REASON, 0, top, 0L); + setl(b, T_COUNT, total); + setl(b, T_ARG1, kinds); + OK(b); +} diff --git a/app/src/au/svc/TOKEN_INQ.h b/app/src/au/svc/TOKEN_INQ.h new file mode 100644 index 0000000..1f92334 --- /dev/null +++ b/app/src/au/svc/TOKEN_INQ.h @@ -0,0 +1,20 @@ +/* + * TOKEN_INQ.h - au 서비스 TOKEN_INQ 카피북 (copybook / record header). + * 요청: 카드. 응답: 원장상태/블랙여부/토큰상태 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef TOKEN_INQ_H +#define TOKEN_INQ_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_token_inq_rec_t; + +void TOKEN_INQ(TPSVCINFO *p); + +#endif /* TOKEN_INQ_H */ diff --git a/app/src/au/svc/TOKEN_INQ.pgc b/app/src/au/svc/TOKEN_INQ.pgc new file mode 100644 index 0000000..c7312f3 --- /dev/null +++ b/app/src/au/svc/TOKEN_INQ.pgc @@ -0,0 +1,40 @@ +/* + * TOKEN_INQ.pgc - au 모듈 서비스 TOKEN_INQ (one service = one file). + * 토큰 상태 조회: 원장 상태와 블랙리스트 등재 여부를 합쳐 토큰의 종합 상태 + * (REVOKED/BLOCKED/ACTIVE/UNKNOWN)를 판정해 문자열로 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "TOKEN_INQ.h" + +void TOKEN_INQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + const char *state; + EXEC SQL BEGIN DECLARE SECTION; + long h_reg = 0, h_black = 0; + char h_card[24], h_reason[64]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + memset(h_reason, 0, sizeof(h_reason)); + + EXEC SQL SELECT count(*) INTO :h_reg FROM card_limit WHERE card_no = :h_card; + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT coalesce(max(reason), '') INTO :h_reason + FROM au_blacklist WHERE card_no = :h_card; + if (sqlca.sqlcode < 0) FAIL(b); + h_black = (h_reason[0] != 0) ? 1 : 0; + + if (strcmp(h_reason, "TOKEN_REVOKED") == 0) state = "REVOKED"; + else if (h_black) state = "BLOCKED"; + else if (h_reg > 0) state = "ACTIVE"; + else state = "UNKNOWN"; + + Bchg(b, T_STR1, 0, (char *)state, 0L); + setl(b, T_RC, h_black); + OK(b); +} diff --git a/app/src/au/svc/TOKEN_ISSUE.h b/app/src/au/svc/TOKEN_ISSUE.h new file mode 100644 index 0000000..74b5d76 --- /dev/null +++ b/app/src/au/svc/TOKEN_ISSUE.h @@ -0,0 +1,20 @@ +/* + * TOKEN_ISSUE.h - au 서비스 TOKEN_ISSUE 카피북 (copybook / record header). + * 요청: 카드. 응답: 토큰 문자열/발급결과 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef TOKEN_ISSUE_H +#define TOKEN_ISSUE_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_token_issue_rec_t; + +void TOKEN_ISSUE(TPSVCINFO *p); + +#endif /* TOKEN_ISSUE_H */ diff --git a/app/src/au/svc/TOKEN_ISSUE.pgc b/app/src/au/svc/TOKEN_ISSUE.pgc new file mode 100644 index 0000000..c3bf7c5 --- /dev/null +++ b/app/src/au/svc/TOKEN_ISSUE.pgc @@ -0,0 +1,34 @@ +/* + * TOKEN_ISSUE.pgc - au 모듈 서비스 TOKEN_ISSUE (one service = one file). + * 결제 토큰 발급: PAN 의 Luhn 검증 후 앞6·뒤4 자리만 남기고 가운데를 마스킹한 + * 토큰 문자열을 만든다. 원장 미등록 카드면 RC=1 로 발급 보류를 알린다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "TOKEN_ISSUE.h" + +void TOKEN_ISSUE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24], token[24]; + int len, i; + EXEC SQL BEGIN DECLARE SECTION; + long h_reg = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + len = (int)strlen(card); + if (len < 13 || !acq_luhn_ok(card)) FAIL(b); + + for (i = 0; i < len; i++) + token[i] = (i < 6 || i >= len - 4) ? card[i] : '*'; + token[len] = 0; + + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + EXEC SQL SELECT count(*) INTO :h_reg FROM card_limit WHERE card_no = :h_card; + if (sqlca.sqlcode < 0) FAIL(b); + + Bchg(b, T_STR1, 0, token, 0L); + setl(b, T_RC, (h_reg == 0) ? 1 : 0); + OK(b); +} diff --git a/app/src/au/svc/TOKEN_REVOKE.h b/app/src/au/svc/TOKEN_REVOKE.h new file mode 100644 index 0000000..9f66181 --- /dev/null +++ b/app/src/au/svc/TOKEN_REVOKE.h @@ -0,0 +1,20 @@ +/* + * TOKEN_REVOKE.h - au 서비스 TOKEN_REVOKE 카피북 (copybook / record header). + * 요청: 카드. 응답: 토큰 폐기 결과 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef TOKEN_REVOKE_H +#define TOKEN_REVOKE_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_token_revoke_rec_t; + +void TOKEN_REVOKE(TPSVCINFO *p); + +#endif /* TOKEN_REVOKE_H */ diff --git a/app/src/au/svc/TOKEN_REVOKE.pgc b/app/src/au/svc/TOKEN_REVOKE.pgc new file mode 100644 index 0000000..d1ee445 --- /dev/null +++ b/app/src/au/svc/TOKEN_REVOKE.pgc @@ -0,0 +1,28 @@ +/* + * TOKEN_REVOKE.pgc - au 모듈 서비스 TOKEN_REVOKE (one service = one file). + * 토큰 폐기: 카드(토큰 원본 PAN)를 TOKEN_REVOKED 사유로 블랙리스트에 등재해 + * 이후 토큰 검증/승인을 차단한다(중복 등재는 무시). + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "TOKEN_REVOKE.h" + +void TOKEN_REVOKE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL INSERT INTO au_blacklist (card_no, reason) + VALUES (:h_card, 'TOKEN_REVOKED') ON CONFLICT (card_no) DO NOTHING; + if (sqlca.sqlcode < 0) FAIL(b); + + userlog("TOKEN_REVOKE card=%s 폐기", card); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/au/svc/VELOCITY_AMT.h b/app/src/au/svc/VELOCITY_AMT.h new file mode 100644 index 0000000..833088f --- /dev/null +++ b/app/src/au/svc/VELOCITY_AMT.h @@ -0,0 +1,20 @@ +/* + * VELOCITY_AMT.h - au 서비스 VELOCITY_AMT 카피북 (copybook / record header). + * 요청: 카드/금액. 응답: 직전1시간 합계/금액속도 위반 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef VELOCITY_AMT_H +#define VELOCITY_AMT_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_velocity_amt_rec_t; + +void VELOCITY_AMT(TPSVCINFO *p); + +#endif /* VELOCITY_AMT_H */ diff --git a/app/src/au/svc/VELOCITY_AMT.pgc b/app/src/au/svc/VELOCITY_AMT.pgc new file mode 100644 index 0000000..29d4503 --- /dev/null +++ b/app/src/au/svc/VELOCITY_AMT.pgc @@ -0,0 +1,37 @@ +/* + * VELOCITY_AMT.pgc - au 모듈 서비스 VELOCITY_AMT (one service = one file). + * 금액 velocity 룰: 직전 1시간 승인 합계에 요청금액을 더해 200만원을 넘으면 + * 금액 속도위반(RC=1)으로 본다. 직전 합계도 함께 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "VELOCITY_AMT.h" + +void VELOCITY_AMT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + long amount; + EXEC SQL BEGIN DECLARE SECTION; + long h_sum = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + amount = getl(b, T_AMOUNT); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_sum + FROM au_authorization + WHERE card_no = :h_card AND created_at >= now() - interval '1 hour'; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_GROSS, h_sum); + if (h_sum + amount > 2000000) { + userlog("VELOCITY_AMT 위반 card=%s 1h=%ld req=%ld", card, h_sum, amount); + setl(b, T_RC, 1); + } else { + setl(b, T_RC, 0); + } + OK(b); +} diff --git a/app/src/au/svc/VELOCITY_CHAN.h b/app/src/au/svc/VELOCITY_CHAN.h new file mode 100644 index 0000000..53d78b0 --- /dev/null +++ b/app/src/au/svc/VELOCITY_CHAN.h @@ -0,0 +1,20 @@ +/* + * VELOCITY_CHAN.h - au 서비스 VELOCITY_CHAN 카피북 (copybook / record header). + * 요청: 카드. 응답: 승인유형별 분포/채널수 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef VELOCITY_CHAN_H +#define VELOCITY_CHAN_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_velocity_chan_rec_t; + +void VELOCITY_CHAN(TPSVCINFO *p); + +#endif /* VELOCITY_CHAN_H */ diff --git a/app/src/au/svc/VELOCITY_CHAN.pgc b/app/src/au/svc/VELOCITY_CHAN.pgc new file mode 100644 index 0000000..cdfcef5 --- /dev/null +++ b/app/src/au/svc/VELOCITY_CHAN.pgc @@ -0,0 +1,44 @@ +/* + * VELOCITY_CHAN.pgc - au 모듈 서비스 VELOCITY_CHAN (one service = one file). + * 승인유형 velocity 룰: 직전 1시간 카드 승인을 승인유형(auth_type)별로 + * 그룹핑하는 커서를 돌며 사용된 유형 수와 총건수를 세어 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "VELOCITY_CHAN.h" + +void VELOCITY_CHAN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24], mix[128] = "", item[32]; + long kinds = 0, total = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt; + char h_card[24], h_type[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL DECLARE cur_vchan CURSOR FOR + SELECT auth_type, count(*) FROM au_authorization + WHERE card_no = :h_card AND created_at >= now() - interval '1 hour' + GROUP BY auth_type; + EXEC SQL OPEN cur_vchan; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_vchan INTO :h_type, :h_cnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_vchan; FAIL(b); } + kinds++; + total += h_cnt; + snprintf(item, sizeof(item), "%s=%ld;", h_type, h_cnt); + if (strlen(mix) + strlen(item) < sizeof(mix) - 1) strcat(mix, item); + } + EXEC SQL CLOSE cur_vchan; + + Bchg(b, T_STR1, 0, mix, 0L); + setl(b, T_ARG1, kinds); + setl(b, T_COUNT, total); + OK(b); +} diff --git a/app/src/au/svc/VELOCITY_DAY.h b/app/src/au/svc/VELOCITY_DAY.h new file mode 100644 index 0000000..3b58859 --- /dev/null +++ b/app/src/au/svc/VELOCITY_DAY.h @@ -0,0 +1,20 @@ +/* + * VELOCITY_DAY.h - au 서비스 VELOCITY_DAY 카피북 (copybook / record header). + * 요청: 카드. 응답: 당일 건수/합계/속도위반여부 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef VELOCITY_DAY_H +#define VELOCITY_DAY_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_velocity_day_rec_t; + +void VELOCITY_DAY(TPSVCINFO *p); + +#endif /* VELOCITY_DAY_H */ diff --git a/app/src/au/svc/VELOCITY_DAY.pgc b/app/src/au/svc/VELOCITY_DAY.pgc new file mode 100644 index 0000000..37862f8 --- /dev/null +++ b/app/src/au/svc/VELOCITY_DAY.pgc @@ -0,0 +1,31 @@ +/* + * VELOCITY_DAY.pgc - au 모듈 서비스 VELOCITY_DAY (one service = one file). + * 일 velocity 룰: 당일 카드 승인 건수와 합계를 세어 20건 초과 또는 합계 + * 300만원 초과면 속도위반(RC=1)으로 판정한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "VELOCITY_DAY.h" + +void VELOCITY_DAY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt = 0, h_sum = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_cnt, :h_sum + FROM au_authorization + WHERE card_no = :h_card AND biz_date = current_date; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_sum); + setl(b, T_RC, (h_cnt > 20 || h_sum > 3000000) ? 1 : 0); + OK(b); +} diff --git a/app/src/au/svc/VELOCITY_MCC.h b/app/src/au/svc/VELOCITY_MCC.h new file mode 100644 index 0000000..264b0bf --- /dev/null +++ b/app/src/au/svc/VELOCITY_MCC.h @@ -0,0 +1,20 @@ +/* + * VELOCITY_MCC.h - au 서비스 VELOCITY_MCC 카피북 (copybook / record header). + * 요청: 카드. 응답: 직전1시간 가맹점수/분산위반 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef VELOCITY_MCC_H +#define VELOCITY_MCC_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_velocity_mcc_rec_t; + +void VELOCITY_MCC(TPSVCINFO *p); + +#endif /* VELOCITY_MCC_H */ diff --git a/app/src/au/svc/VELOCITY_MCC.pgc b/app/src/au/svc/VELOCITY_MCC.pgc new file mode 100644 index 0000000..e72a82f --- /dev/null +++ b/app/src/au/svc/VELOCITY_MCC.pgc @@ -0,0 +1,30 @@ +/* + * VELOCITY_MCC.pgc - au 모듈 서비스 VELOCITY_MCC (one service = one file). + * 가맹점 분산 velocity 룰: 직전 1시간 동안 서로 다른 가맹점 수(카드가 돌린 + * 매장 수)를 세어 5곳 초과면 카드 도용 의심(RC=1)으로 본다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "VELOCITY_MCC.h" + +void VELOCITY_MCC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + EXEC SQL BEGIN DECLARE SECTION; + long h_shops = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT count(DISTINCT merchant_id) INTO :h_shops + FROM au_authorization + WHERE card_no = :h_card AND created_at >= now() - interval '1 hour'; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_shops); + setl(b, T_RC, (h_shops > 5) ? 1 : 0); + OK(b); +} diff --git a/app/src/au/svc/VELOCITY_RESET.h b/app/src/au/svc/VELOCITY_RESET.h new file mode 100644 index 0000000..646e4c7 --- /dev/null +++ b/app/src/au/svc/VELOCITY_RESET.h @@ -0,0 +1,20 @@ +/* + * VELOCITY_RESET.h - au 서비스 VELOCITY_RESET 카피북 (copybook / record header). + * 요청: 카드/영업일. 응답: 리셋된 velocity 적발 수 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef VELOCITY_RESET_H +#define VELOCITY_RESET_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_velocity_reset_rec_t; + +void VELOCITY_RESET(TPSVCINFO *p); + +#endif /* VELOCITY_RESET_H */ diff --git a/app/src/au/svc/VELOCITY_RESET.pgc b/app/src/au/svc/VELOCITY_RESET.pgc new file mode 100644 index 0000000..9be7105 --- /dev/null +++ b/app/src/au/svc/VELOCITY_RESET.pgc @@ -0,0 +1,38 @@ +/* + * VELOCITY_RESET.pgc - au 모듈 서비스 VELOCITY_RESET (one service = one file). + * velocity 적발 리셋: 특정 카드의 당일 velocity 계열 부정 적발(reason 접두 + * VELOCITY/RULE_HIGH_FREQ)을 삭제해 오탐을 해제하고 삭제 건수를 응답한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "VELOCITY_RESET.h" + +void VELOCITY_RESET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24], bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_hit = 0; + char h_card[24], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (card[0] == 0 || bizdate[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL SELECT count(*) INTO :h_hit FROM au_fraud_log + WHERE card_no = :h_card AND biz_date = :h_bizdate + AND (reason LIKE 'VELOCITY%' OR reason = 'RULE_HIGH_FREQ'); + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL DELETE FROM au_fraud_log + WHERE card_no = :h_card AND biz_date = :h_bizdate + AND (reason LIKE 'VELOCITY%' OR reason = 'RULE_HIGH_FREQ'); + if (sqlca.sqlcode < 0) FAIL(b); + + userlog("VELOCITY_RESET card=%s cleared=%ld", card, h_hit); + setl(b, T_COUNT, h_hit); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/au/svc/WHITELIST_ADD.h b/app/src/au/svc/WHITELIST_ADD.h new file mode 100644 index 0000000..fa926a7 --- /dev/null +++ b/app/src/au/svc/WHITELIST_ADD.h @@ -0,0 +1,20 @@ +/* + * WHITELIST_ADD.h - au 서비스 WHITELIST_ADD 카피북 (copybook / record header). + * 요청: 카드. 응답: 화이트 등록(블랙해제) 결과 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef WHITELIST_ADD_H +#define WHITELIST_ADD_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_whitelist_add_rec_t; + +void WHITELIST_ADD(TPSVCINFO *p); + +#endif /* WHITELIST_ADD_H */ diff --git a/app/src/au/svc/WHITELIST_ADD.pgc b/app/src/au/svc/WHITELIST_ADD.pgc new file mode 100644 index 0000000..8da8447 --- /dev/null +++ b/app/src/au/svc/WHITELIST_ADD.pgc @@ -0,0 +1,33 @@ +/* + * WHITELIST_ADD.pgc - au 모듈 서비스 WHITELIST_ADD (one service = one file). + * 화이트리스트 등록: 카드의 블랙리스트 등재를 삭제하고 원장 상태가 ACTIVE 가 + * 아니면 되살려 정상 승인 경로로 복귀시킨다. RC=블랙 해제 여부. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "WHITELIST_ADD.h" + +void WHITELIST_ADD(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + long removed; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL DELETE FROM au_blacklist WHERE card_no = :h_card; + if (sqlca.sqlcode < 0) FAIL(b); + removed = (sqlca.sqlcode == 100) ? 0 : 1; + + EXEC SQL UPDATE card_limit SET status = 'ACTIVE', updated_at = now() + WHERE card_no = :h_card AND status <> 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + + userlog("WHITELIST_ADD card=%s unblacked=%ld", card, removed); + setl(b, T_RC, removed); + OK(b); +} diff --git a/app/src/au/svc/WHITELIST_CHK.h b/app/src/au/svc/WHITELIST_CHK.h new file mode 100644 index 0000000..ac60c9d --- /dev/null +++ b/app/src/au/svc/WHITELIST_CHK.h @@ -0,0 +1,20 @@ +/* + * WHITELIST_CHK.h - au 서비스 WHITELIST_CHK 카피북 (copybook / record header). + * 요청: 카드. 응답: 화이트 여부 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef WHITELIST_CHK_H +#define WHITELIST_CHK_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_whitelist_chk_rec_t; + +void WHITELIST_CHK(TPSVCINFO *p); + +#endif /* WHITELIST_CHK_H */ diff --git a/app/src/au/svc/WHITELIST_CHK.pgc b/app/src/au/svc/WHITELIST_CHK.pgc new file mode 100644 index 0000000..4f2ec30 --- /dev/null +++ b/app/src/au/svc/WHITELIST_CHK.pgc @@ -0,0 +1,30 @@ +/* + * WHITELIST_CHK.pgc - au 모듈 서비스 WHITELIST_CHK (one service = one file). + * 화이트 여부 판정: 블랙리스트 미등재이면서 원장이 존재하는 카드를 화이트 + * (RC=1)로 본다. 두 조건을 스칼라 서브쿼리로 한 번에 읽어 판정한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "WHITELIST_CHK.h" + +void WHITELIST_CHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24]; + EXEC SQL BEGIN DECLARE SECTION; + long h_black = 0, h_reg = 0; + char h_card[24]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + if (card[0] == 0) FAIL(b); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + + EXEC SQL SELECT (SELECT count(*) FROM au_blacklist WHERE card_no = :h_card), + (SELECT count(*) FROM card_limit WHERE card_no = :h_card) + INTO :h_black, :h_reg; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_ARG1, h_black); + setl(b, T_RC, (h_black == 0 && h_reg > 0) ? 1 : 0); + OK(b); +} diff --git a/app/src/au/svc/WHITELIST_DEL.h b/app/src/au/svc/WHITELIST_DEL.h new file mode 100644 index 0000000..f640027 --- /dev/null +++ b/app/src/au/svc/WHITELIST_DEL.h @@ -0,0 +1,20 @@ +/* + * WHITELIST_DEL.h - au 서비스 WHITELIST_DEL 카피북 (copybook / record header). + * 요청: 카드/사유. 응답: 화이트 해제(블랙등재) 결과 + * 공통 베이스(au_svc.h)에서 ATMI/UBF 런타임과 UBF 헬퍼/tpreturn 매크로를 상속한다. + */ +#ifndef WHITELIST_DEL_H +#define WHITELIST_DEL_H + +#include "au_svc.h" + +typedef struct { + char card_no[24]; + long v_amount; + long v_count; + long v_result; +} au_whitelist_del_rec_t; + +void WHITELIST_DEL(TPSVCINFO *p); + +#endif /* WHITELIST_DEL_H */ diff --git a/app/src/au/svc/WHITELIST_DEL.pgc b/app/src/au/svc/WHITELIST_DEL.pgc new file mode 100644 index 0000000..7441105 --- /dev/null +++ b/app/src/au/svc/WHITELIST_DEL.pgc @@ -0,0 +1,32 @@ +/* + * WHITELIST_DEL.pgc - au 모듈 서비스 WHITELIST_DEL (one service = one file). + * 화이트리스트 해제: 카드를 다시 블랙리스트에 (사유와 함께) 등재/갱신해 + * 화이트 상태를 취소한다. 사유 미지정 시 기본값 MANUAL 을 쓴다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT); advertised by au_svr. + */ +#include "WHITELIST_DEL.h" + +void WHITELIST_DEL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char card[24], reason[128]; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[24], h_reason[128]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, card, sizeof(card)); + gets_(b, T_REASON, reason, sizeof(reason)); + if (card[0] == 0) FAIL(b); + if (reason[0] == 0) strcpy(reason, "MANUAL"); + strncpy(h_card, card, sizeof(h_card)-1); h_card[sizeof(h_card)-1] = 0; + strncpy(h_reason, reason, sizeof(h_reason)-1); h_reason[sizeof(h_reason)-1] = 0; + + EXEC SQL INSERT INTO au_blacklist (card_no, reason) + VALUES (:h_card, :h_reason) + ON CONFLICT (card_no) DO UPDATE SET reason = :h_reason; + if (sqlca.sqlcode < 0) FAIL(b); + + userlog("WHITELIST_DEL card=%s reason=%s", card, reason); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cl/batch/cl_apprstat_batch.pgc b/app/src/cl/batch/cl_apprstat_batch.pgc new file mode 100644 index 0000000..2ac6769 --- /dev/null +++ b/app/src/cl/batch/cl_apprstat_batch.pgc @@ -0,0 +1,35 @@ +/* + * cl_apprstat_batch.pgc - cl 마감 승인현황 집계 배치 (단계별 집계 리포트, 조회 전용). + * + * 마감이력의 승인 단계(1차 APPR:L1 / 2차 APPR:L2 / 최종 status=A) 건수를 집계해 출력한다. + * host 변수는 전용 카피북 cl_apprstat_cpy.h 를 EXEC SQL INCLUDE 로 가져온다. + * Usage: cl_apprstat_batch + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + (void)argc; (void)argv; + + EXEC SQL INCLUDE cl_apprstat_cpy; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(30, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL SELECT + count(*) FILTER (WHERE memo = 'APPR:L1'), + count(*) FILTER (WHERE memo = 'APPR:L2'), + count(*) FILTER (WHERE status = 'A') + INTO :as_l1, :as_l2, :as_fin FROM cl_close_log WHERE status <> 'X'; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + printf(">>> cl_apprstat_batch 승인현황: 1차=%ld 2차=%ld 최종=%ld\n", as_l1, as_l2, as_fin); + + tpcommit(0); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cl/batch/cl_apprstat_cpy.h b/app/src/cl/batch/cl_apprstat_cpy.h new file mode 100644 index 0000000..e270065 --- /dev/null +++ b/app/src/cl/batch/cl_apprstat_cpy.h @@ -0,0 +1,6 @@ +/* cl_apprstat_cpy.h - cl_apprstat_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */ +EXEC SQL BEGIN DECLARE SECTION; +long as_l1; +long as_l2; +long as_fin; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/cl/batch/cl_cxlstat_batch.pgc b/app/src/cl/batch/cl_cxlstat_batch.pgc new file mode 100644 index 0000000..f814898 --- /dev/null +++ b/app/src/cl/batch/cl_cxlstat_batch.pgc @@ -0,0 +1,37 @@ +/* + * cl_cxlstat_batch.pgc - cl 마감 취소현황 집계 배치 (사유별 집계 리포트, 조회 전용). + * + * 취소(status=X) 마감이력을 취소 사유(오류정정/지연자료/중복마감)별로 집계해 출력한다. + * host 변수는 전용 카피북 cl_cxlstat_cpy.h 를 EXEC SQL INCLUDE 로 가져온다. + * Usage: cl_cxlstat_batch + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + (void)argc; (void)argv; + + EXEC SQL INCLUDE cl_cxlstat_cpy; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(30, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL SELECT + count(*) FILTER (WHERE memo LIKE '취소:오류%'), + count(*) FILTER (WHERE memo LIKE '취소:지연%'), + count(*) FILTER (WHERE memo LIKE '취소:중복%'), + count(*) + INTO :cs_err, :cs_late, :cs_dup, :cs_tot FROM cl_close_log WHERE status = 'X'; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + printf(">>> cl_cxlstat_batch 취소현황: 오류정정=%ld 지연자료=%ld 중복마감=%ld 총=%ld\n", + cs_err, cs_late, cs_dup, cs_tot); + + tpcommit(0); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cl/batch/cl_cxlstat_cpy.h b/app/src/cl/batch/cl_cxlstat_cpy.h new file mode 100644 index 0000000..af6ccbb --- /dev/null +++ b/app/src/cl/batch/cl_cxlstat_cpy.h @@ -0,0 +1,7 @@ +/* cl_cxlstat_cpy.h - cl_cxlstat_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */ +EXEC SQL BEGIN DECLARE SECTION; +long cs_err; +long cs_late; +long cs_dup; +long cs_tot; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/cl/batch/cl_duelist_batch.pgc b/app/src/cl/batch/cl_duelist_batch.pgc new file mode 100644 index 0000000..b9ca1cf --- /dev/null +++ b/app/src/cl/batch/cl_duelist_batch.pgc @@ -0,0 +1,54 @@ +/* + * cl_duelist_batch.pgc - cl 마감 예정 목록 배치 (미마감 영업일 커서 출력, 조회 전용). + * + * 기준일 이전 14일 내 영업일(주말 제외) 중 DAILY 마감이 없는 미마감 대상일을 커서로 출력한다. + * host 변수는 전용 카피북 cl_duelist_cpy.h 를 EXEC SQL INCLUDE 로 가져온다. + * Usage: cl_duelist_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long due = 0; + + EXEC SQL INCLUDE cl_duelist_cpy; + + strncpy(dl_bizdate, bizdate, sizeof(dl_bizdate)-1); dl_bizdate[sizeof(dl_bizdate)-1] = 0; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(30, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL DECLARE cdueb CURSOR FOR + SELECT to_char(d,'YYYY-MM-DD') FROM ( + SELECT (:dl_bizdate::date - s) AS d, + extract(dow from (:dl_bizdate::date - s)) AS dw + FROM generate_series(0,13) s) t + WHERE dw NOT IN (0,6) + AND NOT EXISTS (SELECT 1 FROM cl_close_log + WHERE close_type = 'DAILY' + AND period_key = to_char(t.d,'YYYY-MM-DD') AND status <> 'X') + ORDER BY d DESC; + EXEC SQL OPEN cdueb; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + printf(">>> cl_duelist_batch %s 기준 마감 예정(미마감) 목록:\n", bizdate); + for (;;) { + EXEC SQL FETCH cdueb INTO :dl_day; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cdueb; tpabort(0); return 1; } + printf(" - %s 미마감\n", dl_day); + due++; + } + EXEC SQL CLOSE cdueb; + + tpcommit(0); + printf(">>> cl_duelist_batch 미마감 대상일 %ld건\n", due); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cl/batch/cl_duelist_cpy.h b/app/src/cl/batch/cl_duelist_cpy.h new file mode 100644 index 0000000..3f77169 --- /dev/null +++ b/app/src/cl/batch/cl_duelist_cpy.h @@ -0,0 +1,5 @@ +/* cl_duelist_cpy.h - cl_duelist_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */ +EXEC SQL BEGIN DECLARE SECTION; +char dl_bizdate[16]; +char dl_day[16]; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/cl/batch/cl_issclose_batch.pgc b/app/src/cl/batch/cl_issclose_batch.pgc new file mode 100644 index 0000000..ec9b934 --- /dev/null +++ b/app/src/cl/batch/cl_issclose_batch.pgc @@ -0,0 +1,55 @@ +/* + * cl_issclose_batch.pgc - cl 발급사별 마감 배치 (발급사 커서 + ISS 마감 + XA). + * + * 당일 매입을 발급사(issuer)별로 커서 순회하며 발급사마다 ISS 마감이력을 1건씩 기록한다. + * host 변수는 전용 카피북 cl_issclose_cpy.h 를 EXEC SQL INCLUDE 로 가져온다. + * Usage: cl_issclose_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cl_dbio.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long made = 0, cid, totfee = 0; + char memo[48]; + + EXEC SQL INCLUDE cl_issclose_cpy; + + strncpy(ic_bizdate, bizdate, sizeof(ic_bizdate)-1); ic_bizdate[sizeof(ic_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 cissb CURSOR FOR + SELECT issuer, count(*), coalesce(sum(amount),0), coalesce(sum(fee),0) + FROM purchase WHERE biz_date = :ic_bizdate + GROUP BY issuer ORDER BY 3 DESC; + EXEC SQL OPEN cissb; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH cissb INTO :ic_iss, :ic_cnt, :ic_amt, :ic_fee; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cissb; tpabort(0); return 1; } + cid = cldb_next_close_id(); + if (cid < 0) { EXEC SQL CLOSE cissb; tpabort(0); return 1; } + snprintf(memo, sizeof(memo), "발급사마감(배치):%s", ic_iss); + if (cldb_insert_close(cid, "ISS", ic_bizdate, ic_bizdate, "C", ic_cnt, ic_amt, memo) < 0) { + EXEC SQL CLOSE cissb; tpabort(0); return 1; + } + totfee += ic_fee; + made++; + } + EXEC SQL CLOSE cissb; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cl_issclose_batch COMMIT: %s 발급사마감 %ld건 수수료합=%ld\n", bizdate, made, totfee); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cl/batch/cl_issclose_cpy.h b/app/src/cl/batch/cl_issclose_cpy.h new file mode 100644 index 0000000..9afb2f3 --- /dev/null +++ b/app/src/cl/batch/cl_issclose_cpy.h @@ -0,0 +1,8 @@ +/* cl_issclose_cpy.h - cl_issclose_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */ +EXEC SQL BEGIN DECLARE SECTION; +char ic_bizdate[16]; +char ic_iss[24]; +long ic_cnt; +long ic_amt; +long ic_fee; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/cl/batch/cl_mainclose_batch.pgc b/app/src/cl/batch/cl_mainclose_batch.pgc new file mode 100644 index 0000000..c4ecbee --- /dev/null +++ b/app/src/cl/batch/cl_mainclose_batch.pgc @@ -0,0 +1,42 @@ +/* + * cl_mainclose_batch.pgc - cl 본마감 승격 배치 (가마감 재집계 + O->C 승격 + XA). + * + * 당일 가마감(TENT, status=O) 이력을 당일 전체 매입으로 재집계해 확정(status=C)으로 승격. + * host 변수는 전용 카피북 cl_mainclose_cpy.h 를 EXEC SQL INCLUDE 로 가져온다. + * Usage: cl_mainclose_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cl_dbio.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long promoted; + + EXEC SQL INCLUDE cl_mainclose_cpy; + + strncpy(mc_bizdate, bizdate, sizeof(mc_bizdate)-1); mc_bizdate[sizeof(mc_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(*), coalesce(sum(amount),0) INTO :mc_cnt, :mc_amt + FROM purchase WHERE biz_date = :mc_bizdate; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + EXEC SQL UPDATE cl_close_log + SET status = 'C', txn_count = :mc_cnt, total_amount = :mc_amt, memo = '본마감(배치)', updated_at = now() + WHERE close_type = 'TENT' AND period_key = :mc_bizdate AND status = 'O'; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + promoted = (long)sqlca.sqlerrd[2]; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cl_mainclose_batch COMMIT: %s 본마감 승격 %ld건 확정건수=%ld\n", bizdate, promoted, mc_cnt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cl/batch/cl_mainclose_cpy.h b/app/src/cl/batch/cl_mainclose_cpy.h new file mode 100644 index 0000000..b9fb28a --- /dev/null +++ b/app/src/cl/batch/cl_mainclose_cpy.h @@ -0,0 +1,6 @@ +/* cl_mainclose_cpy.h - cl_mainclose_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */ +EXEC SQL BEGIN DECLARE SECTION; +char mc_bizdate[16]; +long mc_cnt; +long mc_amt; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/cl/batch/cl_netcross_batch.pgc b/app/src/cl/batch/cl_netcross_batch.pgc new file mode 100644 index 0000000..f07770d --- /dev/null +++ b/app/src/cl/batch/cl_netcross_batch.pgc @@ -0,0 +1,59 @@ +/* + * cl_netcross_batch.pgc - cl 정산액 크로스검증 배치 (마감별 스냅샷 net vs 매입 net + XA). + * + * 당일 확정(C) 마감이력을 커서 순회하며 각 마감의 스냅샷 net 합계와 당일 매입 net 합계를 + * 대사하고 편차가 있는 마감을 출력한다 (조회 전용 XA 브랜치). + * host 변수는 전용 카피북 cl_netcross_cpy.h 를 EXEC SQL INCLUDE 로 가져온다. + * Usage: cl_netcross_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long checked = 0, mism = 0, delta; + + EXEC SQL INCLUDE cl_netcross_cpy; + + strncpy(nx_bizdate, bizdate, sizeof(nx_bizdate)-1); nx_bizdate[sizeof(nx_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(net),0) INTO :nx_real + FROM purchase WHERE biz_date = :nx_bizdate; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + EXEC SQL DECLARE cnxb CURSOR FOR + SELECT close_id FROM cl_close_log + WHERE period_key = :nx_bizdate AND status = 'C' ORDER BY close_id; + EXEC SQL OPEN cnxb; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + printf(">>> cl_netcross_batch %s 정산액 크로스검증 (매입 net=%ld)\n", bizdate, nx_real); + for (;;) { + EXEC SQL FETCH cnxb INTO :nx_cid; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cnxb; tpabort(0); return 1; } + EXEC SQL SELECT coalesce(sum(net_amount),0) INTO :nx_snap + FROM cl_snapshot WHERE close_id = :nx_cid; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cnxb; tpabort(0); return 1; } + delta = nx_snap - nx_real; if (delta < 0) delta = -delta; + if (nx_snap != 0 && delta != 0) { + printf(" ! cid=%ld 스냅샷net=%ld 편차=%ld\n", nx_cid, nx_snap, delta); + mism++; + } + checked++; + } + EXEC SQL CLOSE cnxb; + + tpcommit(0); + printf(">>> cl_netcross_batch 점검=%ld건 편차=%ld건\n", checked, mism); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cl/batch/cl_netcross_cpy.h b/app/src/cl/batch/cl_netcross_cpy.h new file mode 100644 index 0000000..fdae785 --- /dev/null +++ b/app/src/cl/batch/cl_netcross_cpy.h @@ -0,0 +1,7 @@ +/* cl_netcross_cpy.h - cl_netcross_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */ +EXEC SQL BEGIN DECLARE SECTION; +char nx_bizdate[16]; +long nx_cid; +long nx_snap; +long nx_real; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/cl/batch/cl_segclose_batch.pgc b/app/src/cl/batch/cl_segclose_batch.pgc new file mode 100644 index 0000000..d4cf80c --- /dev/null +++ b/app/src/cl/batch/cl_segclose_batch.pgc @@ -0,0 +1,54 @@ +/* + * cl_segclose_batch.pgc - cl 부문별 마감 배치 (업종 커서 + 부문마다 SEG 마감 + XA). + * + * 당일 매입을 업종(category) 부문으로 커서 순회하며 부문마다 SEG 마감이력을 1건씩 기록한다. + * host 변수는 전용 카피북 cl_segclose_cpy.h 를 EXEC SQL INCLUDE 로 가져온다. + * Usage: cl_segclose_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cl_dbio.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long made = 0, cid; + char memo[48]; + + EXEC SQL INCLUDE cl_segclose_cpy; + + strncpy(sc_bizdate, bizdate, sizeof(sc_bizdate)-1); sc_bizdate[sizeof(sc_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 csegb CURSOR FOR + SELECT category, count(*), coalesce(sum(amount),0) + FROM purchase WHERE biz_date = :sc_bizdate + GROUP BY category ORDER BY category; + EXEC SQL OPEN csegb; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH csegb INTO :sc_cat, :sc_cnt, :sc_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE csegb; tpabort(0); return 1; } + cid = cldb_next_close_id(); + if (cid < 0) { EXEC SQL CLOSE csegb; tpabort(0); return 1; } + snprintf(memo, sizeof(memo), "부문마감(배치):%s", sc_cat); + if (cldb_insert_close(cid, "SEG", sc_bizdate, sc_bizdate, "C", sc_cnt, sc_amt, memo) < 0) { + EXEC SQL CLOSE csegb; tpabort(0); return 1; + } + made++; + } + EXEC SQL CLOSE csegb; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cl_segclose_batch COMMIT: %s 부문마감 %ld건 생성\n", bizdate, made); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cl/batch/cl_segclose_cpy.h b/app/src/cl/batch/cl_segclose_cpy.h new file mode 100644 index 0000000..29c9f41 --- /dev/null +++ b/app/src/cl/batch/cl_segclose_cpy.h @@ -0,0 +1,7 @@ +/* cl_segclose_cpy.h - cl_segclose_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */ +EXEC SQL BEGIN DECLARE SECTION; +char sc_bizdate[16]; +char sc_cat[16]; +long sc_cnt; +long sc_amt; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/cl/batch/cl_semimonth_batch.pgc b/app/src/cl/batch/cl_semimonth_batch.pgc new file mode 100644 index 0000000..a36200a --- /dev/null +++ b/app/src/cl/batch/cl_semimonth_batch.pgc @@ -0,0 +1,54 @@ +/* + * cl_semimonth_batch.pgc - cl 반월 마감 배치 (전반/후반 집계 + SEMIMON 마감 + XA). + * + * 기준일이 속한 반월(전반 1~15 / 후반 16~말) 구간 매입을 집계해 SEMIMON 마감이력을 기록. + * host 변수는 전용 카피북 cl_semimonth_cpy.h 를 EXEC SQL INCLUDE 로 가져온다. + * Usage: cl_semimonth_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cl_dbio.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long cid, half; + char period[24], memo[32]; + + EXEC SQL INCLUDE cl_semimonth_cpy; + + strncpy(sm_bizdate, bizdate, sizeof(sm_bizdate)-1); sm_bizdate[sizeof(sm_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 extract(day from :sm_bizdate::date)::int, + to_char(:sm_bizdate::date, 'YYYY-MM') INTO :sm_day, :sm_ym; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + half = (sm_day <= 15) ? 1 : 2; + sm_lo = (half == 1) ? 1 : 16; + sm_hi = (half == 1) ? 15 : 31; + + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :sm_cnt, :sm_amt + FROM purchase WHERE to_char(biz_date,'YYYY-MM') = :sm_ym + AND extract(day from biz_date) BETWEEN :sm_lo AND :sm_hi; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + snprintf(period, sizeof(period), "%s-H%ld", sm_ym, half); + snprintf(memo, sizeof(memo), "반월마감(배치):%s", (half == 1) ? "전반" : "후반"); + cid = cldb_next_close_id(); + if (cid < 0) { tpabort(0); return 1; } + if (cldb_insert_close(cid, "SEMIMON", period, bizdate, "C", sm_cnt, sm_amt, memo) < 0) { + tpabort(0); return 1; + } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cl_semimonth_batch COMMIT: %s %s cid=%ld 건수=%ld\n", + sm_ym, (half==1)?"전반":"후반", cid, sm_cnt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cl/batch/cl_semimonth_cpy.h b/app/src/cl/batch/cl_semimonth_cpy.h new file mode 100644 index 0000000..ba893fa --- /dev/null +++ b/app/src/cl/batch/cl_semimonth_cpy.h @@ -0,0 +1,10 @@ +/* cl_semimonth_cpy.h - cl_semimonth_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */ +EXEC SQL BEGIN DECLARE SECTION; +char sm_bizdate[16]; +char sm_ym[16]; +long sm_day; +long sm_lo; +long sm_hi; +long sm_cnt; +long sm_amt; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/cl/batch/cl_tenday_batch.pgc b/app/src/cl/batch/cl_tenday_batch.pgc new file mode 100644 index 0000000..f4317fb --- /dev/null +++ b/app/src/cl/batch/cl_tenday_batch.pgc @@ -0,0 +1,53 @@ +/* + * cl_tenday_batch.pgc - cl 순(10일) 마감 배치 (상/중/하순 집계 + TENDAY 마감 + XA). + * + * 기준일이 속한 순(1~10/11~20/21~말) 구간의 매입을 집계해 TENDAY 마감이력을 기록한다. + * host 변수는 전용 카피북 cl_tenday_cpy.h 를 EXEC SQL INCLUDE 로 가져온다. + * Usage: cl_tenday_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cl_dbio.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long cid, dec; + char period[24], memo[32]; + + EXEC SQL INCLUDE cl_tenday_cpy; + + strncpy(td_bizdate, bizdate, sizeof(td_bizdate)-1); td_bizdate[sizeof(td_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 extract(day from :td_bizdate::date)::int, + to_char(:td_bizdate::date, 'YYYY-MM') INTO :td_day, :td_ym; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + dec = (td_day <= 10) ? 1 : (td_day <= 20 ? 2 : 3); + td_lo = (dec - 1) * 10 + 1; + td_hi = (dec == 3) ? 31 : dec * 10; + + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :td_cnt, :td_amt + FROM purchase WHERE to_char(biz_date,'YYYY-MM') = :td_ym + AND extract(day from biz_date) BETWEEN :td_lo AND :td_hi; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + snprintf(period, sizeof(period), "%s-T%ld", td_ym, dec); + snprintf(memo, sizeof(memo), "순마감(배치):%ld순", dec); + cid = cldb_next_close_id(); + if (cid < 0) { tpabort(0); return 1; } + if (cldb_insert_close(cid, "TENDAY", period, bizdate, "C", td_cnt, td_amt, memo) < 0) { + tpabort(0); return 1; + } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cl_tenday_batch COMMIT: %s %ld순 cid=%ld 건수=%ld\n", td_ym, dec, cid, td_cnt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cl/batch/cl_tenday_cpy.h b/app/src/cl/batch/cl_tenday_cpy.h new file mode 100644 index 0000000..ae114c1 --- /dev/null +++ b/app/src/cl/batch/cl_tenday_cpy.h @@ -0,0 +1,10 @@ +/* cl_tenday_cpy.h - cl_tenday_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */ +EXEC SQL BEGIN DECLARE SECTION; +char td_bizdate[16]; +char td_ym[16]; +long td_day; +long td_lo; +long td_hi; +long td_cnt; +long td_amt; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/cl/batch/cl_tentclose_batch.pgc b/app/src/cl/batch/cl_tentclose_batch.pgc new file mode 100644 index 0000000..ce8a4dc --- /dev/null +++ b/app/src/cl/batch/cl_tentclose_batch.pgc @@ -0,0 +1,42 @@ +/* + * cl_tentclose_batch.pgc - cl 가마감(잠정) 배치 (접수/대사 잠정집계 + TENT(O) + XA). + * + * 당일 접수(A)/대사완료(M) 매입을 잠정 집계해 가마감(status=O) 이력을 1건 기록한다. + * host 변수는 전용 카피북 cl_tentclose_cpy.h 를 EXEC SQL INCLUDE 로 가져온다. + * Usage: cl_tentclose_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cl_dbio.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long cid; + + EXEC SQL INCLUDE cl_tentclose_cpy; + + strncpy(tc_bizdate, bizdate, sizeof(tc_bizdate)-1); tc_bizdate[sizeof(tc_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(*), coalesce(sum(amount),0) INTO :tc_cnt, :tc_amt + FROM purchase WHERE biz_date = :tc_bizdate AND status IN ('A','M'); + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + cid = cldb_next_close_id(); + if (cid < 0) { tpabort(0); return 1; } + if (cldb_insert_close(cid, "TENT", bizdate, bizdate, "O", tc_cnt, tc_amt, "가마감(배치)") < 0) { + tpabort(0); return 1; + } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cl_tentclose_batch COMMIT: %s 가마감 cid=%ld 잠정건수=%ld\n", bizdate, cid, tc_cnt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cl/batch/cl_tentclose_cpy.h b/app/src/cl/batch/cl_tentclose_cpy.h new file mode 100644 index 0000000..36f12e9 --- /dev/null +++ b/app/src/cl/batch/cl_tentclose_cpy.h @@ -0,0 +1,6 @@ +/* cl_tentclose_cpy.h - cl_tentclose_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */ +EXEC SQL BEGIN DECLARE SECTION; +char tc_bizdate[16]; +long tc_cnt; +long tc_amt; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/cl/batch/cl_weekly_batch.pgc b/app/src/cl/batch/cl_weekly_batch.pgc new file mode 100644 index 0000000..5de7bb2 --- /dev/null +++ b/app/src/cl/batch/cl_weekly_batch.pgc @@ -0,0 +1,44 @@ +/* + * cl_weekly_batch.pgc - cl 주 마감 배치 (주간 집계 + WEEKLY 마감이력 + XA). + * + * 기준일이 속한 주(월~일) 매입을 집계해 WEEKLY 마감이력(close_log)을 1건 기록한다. + * host 변수는 전용 카피북 cl_weekly_cpy.h 를 EXEC SQL INCLUDE 로 가져온다. + * Usage: cl_weekly_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cl_dbio.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long cid; + + EXEC SQL INCLUDE cl_weekly_cpy; + + strncpy(wk_bizdate, bizdate, sizeof(wk_bizdate)-1); wk_bizdate[sizeof(wk_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 to_char(date_trunc('week', :wk_bizdate::date), 'IYYY"W"IW'), + count(*), coalesce(sum(amount),0) + INTO :wk_key, :wk_cnt, :wk_amt FROM purchase + WHERE date_trunc('week', biz_date) = date_trunc('week', :wk_bizdate::date); + if (sqlca.sqlcode < 0) { fprintf(stderr, "AGG FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + cid = cldb_next_close_id(); + if (cid < 0) { tpabort(0); return 1; } + if (cldb_insert_close(cid, "WEEKLY", wk_key, bizdate, "C", wk_cnt, wk_amt, "주마감(배치)") < 0) { + tpabort(0); return 1; + } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cl_weekly_batch COMMIT: 주=%s cid=%ld 건수=%ld 금액=%ld\n", wk_key, cid, wk_cnt, wk_amt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cl/batch/cl_weekly_cpy.h b/app/src/cl/batch/cl_weekly_cpy.h new file mode 100644 index 0000000..7e47e4f --- /dev/null +++ b/app/src/cl/batch/cl_weekly_cpy.h @@ -0,0 +1,7 @@ +/* cl_weekly_cpy.h - cl_weekly_batch 전용 카피북 (host 변수 선언, EXEC SQL INCLUDE 대상). */ +EXEC SQL BEGIN DECLARE SECTION; +char wk_bizdate[16]; +char wk_key[16]; +long wk_cnt; +long wk_amt; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/cl/cl_svr.pgc b/app/src/cl/cl_svr.pgc index 35c040f..7af4c84 100644 --- a/app/src/cl/cl_svr.pgc +++ b/app/src/cl/cl_svr.pgc @@ -2,7 +2,7 @@ * cl_svr.pgc - cl (마감/closing) MODULE SERVER (thin dispatcher). * * The legacy "one module server, many services" pattern: this binary owns NO - * business logic. Each of cl's 75 online services now lives in its own file + * business logic. Each of cl's 133 online services now lives in its own file * (app/src/cl/svc/.pgc) with its own copybook header * (app/src/cl/svc/.h); those objects are archived into libclsvc.a and * linked into this server. Here we only: @@ -99,6 +99,66 @@ #include "CL_FORCECLOSE.h" #include "CL_SNAPDIFF.h" +/* --- cl 마감 확장 서비스 (svc/*.pgc, +58) --- */ +#include "CL_WEEKLY.h" +#include "CL_TENDAY.h" +#include "CL_SEMIMONTH.h" +#include "CL_HYCLOSE.h" +#include "CL_DAYVERIFY.h" +#include "CL_WEEKVERIFY.h" +#include "CL_MONVERIFY.h" +#include "CL_QTRVERIFY.h" +#include "CL_YEARVERIFY.h" +#include "CL_HYVERIFY.h" +#include "CL_SEGCLOSE.h" +#include "CL_ISSCLOSE.h" +#include "CL_POSCLOSE.h" +#include "CL_FXCLOSE.h" +#include "CL_SEGVERIFY.h" +#include "CL_CORPVERIFY.h" +#include "CL_CHKUNMATCH.h" +#include "CL_CHKPENDING.h" +#include "CL_CHKVOID.h" +#include "CL_CHKLIMIT.h" +#include "CL_CHKALL.h" +#include "CL_TENTCLOSE.h" +#include "CL_MAINCLOSE.h" +#include "CL_RECLOSEREG.h" +#include "CL_PROVCMP.h" +#include "CL_RECLOSECHK.h" +#include "CL_APPRL1.h" +#include "CL_APPRL2.h" +#include "CL_APPRFINAL.h" +#include "CL_APPRWD.h" +#include "CL_APPRDELEG.h" +#include "CL_APPRSTAT.h" +#include "CL_CXLERROR.h" +#include "CL_CXLLATE.h" +#include "CL_CXLDUP.h" +#include "CL_CXLREQ.h" +#include "CL_CXLSTAT.h" +#include "CL_STCROSS.h" +#include "CL_CNTVERIFY.h" +#include "CL_FEECROSS.h" +#include "CL_NETCROSS.h" +#include "CL_MERXCHECK.h" +#include "CL_RPTDIV.h" +#include "CL_RPTPERIOD.h" +#include "CL_RPTSTATUS.h" +#include "CL_RPTDELAY.h" +#include "CL_RPTAPPR.h" +#include "CL_RPTCXL.h" +#include "CL_SLASET.h" +#include "CL_SLABREACH.h" +#include "CL_SCHEDNEXT.h" +#include "CL_DUELIST.h" +#include "CL_HOLICHK.h" +#include "CL_NEXTBIZ.h" +#include "CL_TRIGSETTLE.h" +#include "CL_TRIGRPT.h" +#include "CL_STCHGAUDIT.h" +#include "CL_SNAPMER.h" + /* ----- advertise table: {service name, function} (fn is extern from its file) */ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"CL_DAILY", CL_DAILY}, {"CL_MONTHLY", CL_MONTHLY}, @@ -139,6 +199,35 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"CL_ESCALATE", CL_ESCALATE}, {"CL_CALENDAR", CL_CALENDAR}, {"CL_TRIGSTAT", CL_TRIGSTAT}, {"CL_FORCECLOSE", CL_FORCECLOSE}, {"CL_SNAPDIFF", CL_SNAPDIFF}, + {"CL_WEEKLY", CL_WEEKLY}, {"CL_TENDAY", CL_TENDAY}, + {"CL_SEMIMONTH", CL_SEMIMONTH}, {"CL_HYCLOSE", CL_HYCLOSE}, + {"CL_DAYVERIFY", CL_DAYVERIFY}, {"CL_WEEKVERIFY", CL_WEEKVERIFY}, + {"CL_MONVERIFY", CL_MONVERIFY}, {"CL_QTRVERIFY", CL_QTRVERIFY}, + {"CL_YEARVERIFY", CL_YEARVERIFY}, {"CL_HYVERIFY", CL_HYVERIFY}, + {"CL_SEGCLOSE", CL_SEGCLOSE}, {"CL_ISSCLOSE", CL_ISSCLOSE}, + {"CL_POSCLOSE", CL_POSCLOSE}, {"CL_FXCLOSE", CL_FXCLOSE}, + {"CL_SEGVERIFY", CL_SEGVERIFY}, {"CL_CORPVERIFY", CL_CORPVERIFY}, + {"CL_CHKUNMATCH", CL_CHKUNMATCH}, {"CL_CHKPENDING", CL_CHKPENDING}, + {"CL_CHKVOID", CL_CHKVOID}, {"CL_CHKLIMIT", CL_CHKLIMIT}, + {"CL_CHKALL", CL_CHKALL}, {"CL_TENTCLOSE", CL_TENTCLOSE}, + {"CL_MAINCLOSE", CL_MAINCLOSE}, {"CL_RECLOSEREG", CL_RECLOSEREG}, + {"CL_PROVCMP", CL_PROVCMP}, {"CL_RECLOSECHK", CL_RECLOSECHK}, + {"CL_APPRL1", CL_APPRL1}, {"CL_APPRL2", CL_APPRL2}, + {"CL_APPRFINAL", CL_APPRFINAL}, {"CL_APPRWD", CL_APPRWD}, + {"CL_APPRDELEG", CL_APPRDELEG}, {"CL_APPRSTAT", CL_APPRSTAT}, + {"CL_CXLERROR", CL_CXLERROR}, {"CL_CXLLATE", CL_CXLLATE}, + {"CL_CXLDUP", CL_CXLDUP}, {"CL_CXLREQ", CL_CXLREQ}, + {"CL_CXLSTAT", CL_CXLSTAT}, {"CL_STCROSS", CL_STCROSS}, + {"CL_CNTVERIFY", CL_CNTVERIFY}, {"CL_FEECROSS", CL_FEECROSS}, + {"CL_NETCROSS", CL_NETCROSS}, {"CL_MERXCHECK", CL_MERXCHECK}, + {"CL_RPTDIV", CL_RPTDIV}, {"CL_RPTPERIOD", CL_RPTPERIOD}, + {"CL_RPTSTATUS", CL_RPTSTATUS}, {"CL_RPTDELAY", CL_RPTDELAY}, + {"CL_RPTAPPR", CL_RPTAPPR}, {"CL_RPTCXL", CL_RPTCXL}, + {"CL_SLASET", CL_SLASET}, {"CL_SLABREACH", CL_SLABREACH}, + {"CL_SCHEDNEXT", CL_SCHEDNEXT}, {"CL_DUELIST", CL_DUELIST}, + {"CL_HOLICHK", CL_HOLICHK}, {"CL_NEXTBIZ", CL_NEXTBIZ}, + {"CL_TRIGSETTLE", CL_TRIGSETTLE}, {"CL_TRIGRPT", CL_TRIGRPT}, + {"CL_STCHGAUDIT", CL_STCHGAUDIT}, {"CL_SNAPMER", CL_SNAPMER}, {NULL, NULL} }; diff --git a/app/src/cl/svc/CL_APPRDELEG.h b/app/src/cl/svc/CL_APPRDELEG.h new file mode 100644 index 0000000..e90b666 --- /dev/null +++ b/app/src/cl/svc/CL_APPRDELEG.h @@ -0,0 +1,22 @@ +/* + * CL_APPRDELEG.h - cl 서비스 CL_APPRDELEG 카피북 (copybook / record header). + * 마감 승인 위임: 승인 위임 메모 기록 후 상태 유지. + */ +#ifndef CL_APPRDELEG_H +#define CL_APPRDELEG_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_apprdeleg_rec_t; + +void CL_APPRDELEG(TPSVCINFO *p); + +#endif /* CL_APPRDELEG_H */ diff --git a/app/src/cl/svc/CL_APPRDELEG.pgc b/app/src/cl/svc/CL_APPRDELEG.pgc new file mode 100644 index 0000000..e87dc72 --- /dev/null +++ b/app/src/cl/svc/CL_APPRDELEG.pgc @@ -0,0 +1,29 @@ +/* + * CL_APPRDELEG.pgc - cl 모듈 서비스 CL_APPRDELEG (one service = one file). + * 마감 승인 위임: 위임 대상자(T_STR1)를 memo(APPR:DELEG:..)에 기록하고 상태 유지. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_APPRDELEG.h" + +void CL_APPRDELEG(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char deleg[32]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cid; + char h_memo[64]; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + gets_(b, T_STR1, deleg, sizeof(deleg)); + if (deleg[0] == 0) strcpy(deleg, "DELEGATE"); + snprintf(h_memo, sizeof(h_memo), "APPR:DELEG:%s", deleg); + EXEC SQL UPDATE cl_close_log SET memo = :h_memo, updated_at = now() + WHERE close_id = :h_cid AND status IN ('O','C'); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ID1, h_cid); + setl(b, T_RC, (long)sqlca.sqlerrd[2]); + Bchg(b, T_STR1, 0, deleg, 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_APPRFINAL.h b/app/src/cl/svc/CL_APPRFINAL.h new file mode 100644 index 0000000..0244af4 --- /dev/null +++ b/app/src/cl/svc/CL_APPRFINAL.h @@ -0,0 +1,22 @@ +/* + * CL_APPRFINAL.h - cl 서비스 CL_APPRFINAL 카피북 (copybook / record header). + * 최종 마감 승인: 2차 승인 마감을 최종 승인(A)으로 확정. + */ +#ifndef CL_APPRFINAL_H +#define CL_APPRFINAL_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_apprfinal_rec_t; + +void CL_APPRFINAL(TPSVCINFO *p); + +#endif /* CL_APPRFINAL_H */ diff --git a/app/src/cl/svc/CL_APPRFINAL.pgc b/app/src/cl/svc/CL_APPRFINAL.pgc new file mode 100644 index 0000000..3c00bb9 --- /dev/null +++ b/app/src/cl/svc/CL_APPRFINAL.pgc @@ -0,0 +1,29 @@ +/* + * CL_APPRFINAL.pgc - cl 모듈 서비스 CL_APPRFINAL (one service = one file). + * 최종 마감 승인: 2차 승인(APPR:L2) 마감을 최종 승인(status=A)으로 확정. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_APPRFINAL.h" + +void CL_APPRFINAL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_cid, h_ok; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + EXEC SQL SELECT count(*) INTO :h_ok FROM cl_close_log + WHERE close_id = :h_cid AND memo = 'APPR:L2'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_ok == 0) { Bchg(b, T_MSG, 0, "2차 승인 선행 필요", 0L); FAIL(b); } + EXEC SQL UPDATE cl_close_log SET status = 'A', memo = 'APPR:FINAL', updated_at = now() + WHERE close_id = :h_cid; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ID1, h_cid); + setl(b, T_ARG1, 3); + Bchg(b, T_STATUS, 0, "A", 0L); + Bchg(b, T_MSG, 0, "최종 승인 완료", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_APPRL1.h b/app/src/cl/svc/CL_APPRL1.h new file mode 100644 index 0000000..286cd35 --- /dev/null +++ b/app/src/cl/svc/CL_APPRL1.h @@ -0,0 +1,22 @@ +/* + * CL_APPRL1.h - cl 서비스 CL_APPRL1 카피북 (copybook / record header). + * 1차 마감 승인: 개시(O)/완료(C) 마감을 1차 승인 단계로 전이. + */ +#ifndef CL_APPRL1_H +#define CL_APPRL1_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_apprl1_rec_t; + +void CL_APPRL1(TPSVCINFO *p); + +#endif /* CL_APPRL1_H */ diff --git a/app/src/cl/svc/CL_APPRL1.pgc b/app/src/cl/svc/CL_APPRL1.pgc new file mode 100644 index 0000000..89f0825 --- /dev/null +++ b/app/src/cl/svc/CL_APPRL1.pgc @@ -0,0 +1,27 @@ +/* + * CL_APPRL1.pgc - cl 모듈 서비스 CL_APPRL1 (one service = one file). + * 1차 마감 승인: 개시(O)/완료(C) 마감을 1차 승인(memo=APPR:L1) 단계로 전이. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_APPRL1.h" + +void CL_APPRL1(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char st[2]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cid; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + if (cldb_get_close_status(h_cid, st) < 0) FAIL(b); + if (st[0] != 'O' && st[0] != 'C') { Bchg(b, T_MSG, 0, "승인 대상 상태 아님", 0L); FAIL(b); } + EXEC SQL UPDATE cl_close_log SET memo = 'APPR:L1', updated_at = now() + WHERE close_id = :h_cid; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ID1, h_cid); + setl(b, T_ARG1, 1); + Bchg(b, T_MSG, 0, "1차 승인 완료", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_APPRL2.h b/app/src/cl/svc/CL_APPRL2.h new file mode 100644 index 0000000..bc8f13c --- /dev/null +++ b/app/src/cl/svc/CL_APPRL2.h @@ -0,0 +1,22 @@ +/* + * CL_APPRL2.h - cl 서비스 CL_APPRL2 카피북 (copybook / record header). + * 2차 마감 승인: 1차 승인 마감을 2차 승인 단계로 전이. + */ +#ifndef CL_APPRL2_H +#define CL_APPRL2_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_apprl2_rec_t; + +void CL_APPRL2(TPSVCINFO *p); + +#endif /* CL_APPRL2_H */ diff --git a/app/src/cl/svc/CL_APPRL2.pgc b/app/src/cl/svc/CL_APPRL2.pgc new file mode 100644 index 0000000..3daf8cc --- /dev/null +++ b/app/src/cl/svc/CL_APPRL2.pgc @@ -0,0 +1,28 @@ +/* + * CL_APPRL2.pgc - cl 모듈 서비스 CL_APPRL2 (one service = one file). + * 2차 마감 승인: 1차 승인(APPR:L1) 마감을 2차 승인(APPR:L2)으로 전이. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_APPRL2.h" + +void CL_APPRL2(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_cid, h_ok; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + EXEC SQL SELECT count(*) INTO :h_ok FROM cl_close_log + WHERE close_id = :h_cid AND memo = 'APPR:L1'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_ok == 0) { Bchg(b, T_MSG, 0, "1차 승인 선행 필요", 0L); FAIL(b); } + EXEC SQL UPDATE cl_close_log SET memo = 'APPR:L2', updated_at = now() + WHERE close_id = :h_cid; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ID1, h_cid); + setl(b, T_ARG1, 2); + Bchg(b, T_MSG, 0, "2차 승인 완료", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_APPRSTAT.h b/app/src/cl/svc/CL_APPRSTAT.h new file mode 100644 index 0000000..37e87c0 --- /dev/null +++ b/app/src/cl/svc/CL_APPRSTAT.h @@ -0,0 +1,22 @@ +/* + * CL_APPRSTAT.h - cl 서비스 CL_APPRSTAT 카피북 (copybook / record header). + * 마감 승인 현황: 승인 단계별 마감 건수 집계. + */ +#ifndef CL_APPRSTAT_H +#define CL_APPRSTAT_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_apprstat_rec_t; + +void CL_APPRSTAT(TPSVCINFO *p); + +#endif /* CL_APPRSTAT_H */ diff --git a/app/src/cl/svc/CL_APPRSTAT.pgc b/app/src/cl/svc/CL_APPRSTAT.pgc new file mode 100644 index 0000000..7bd08eb --- /dev/null +++ b/app/src/cl/svc/CL_APPRSTAT.pgc @@ -0,0 +1,27 @@ +/* + * CL_APPRSTAT.pgc - cl 모듈 서비스 CL_APPRSTAT (one service = one file). + * 마감 승인 현황: 승인 단계별(L1/L2/최종A) 마감 건수를 집계한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_APPRSTAT.h" + +void CL_APPRSTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_l1, h_l2, h_fin; + EXEC SQL END DECLARE SECTION; + EXEC SQL SELECT + count(*) FILTER (WHERE memo = 'APPR:L1'), + count(*) FILTER (WHERE memo = 'APPR:L2'), + count(*) FILTER (WHERE status = 'A') + INTO :h_l1, :h_l2, :h_fin FROM cl_close_log WHERE status <> 'X'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_l1); + setl(b, T_ARG2, h_l2); + setl(b, T_ARG3, h_fin); + setl(b, T_COUNT, h_l1 + h_l2 + h_fin); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_APPRWD.h b/app/src/cl/svc/CL_APPRWD.h new file mode 100644 index 0000000..7018ada --- /dev/null +++ b/app/src/cl/svc/CL_APPRWD.h @@ -0,0 +1,22 @@ +/* + * CL_APPRWD.h - cl 서비스 CL_APPRWD 카피북 (copybook / record header). + * 마감 승인 회수: 승인 마감을 완료(C) 상태로 되돌림. + */ +#ifndef CL_APPRWD_H +#define CL_APPRWD_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_apprwd_rec_t; + +void CL_APPRWD(TPSVCINFO *p); + +#endif /* CL_APPRWD_H */ diff --git a/app/src/cl/svc/CL_APPRWD.pgc b/app/src/cl/svc/CL_APPRWD.pgc new file mode 100644 index 0000000..0866640 --- /dev/null +++ b/app/src/cl/svc/CL_APPRWD.pgc @@ -0,0 +1,27 @@ +/* + * CL_APPRWD.pgc - cl 모듈 서비스 CL_APPRWD (one service = one file). + * 마감 승인 회수: 승인(A) 마감을 완료(C) 상태로 되돌리고 승인 이력 초기화. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_APPRWD.h" + +void CL_APPRWD(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char st[2]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cid; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + if (cldb_get_close_status(h_cid, st) < 0) FAIL(b); + if (st[0] != 'A') { Bchg(b, T_MSG, 0, "승인(A) 상태 아님-회수 불가", 0L); FAIL(b); } + if (cldb_update_close_status(h_cid, "C") < 0) FAIL(b); + EXEC SQL UPDATE cl_close_log SET memo = '', updated_at = now() WHERE close_id = :h_cid; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ID1, h_cid); + Bchg(b, T_STATUS, 0, "C", 0L); + Bchg(b, T_MSG, 0, "승인 회수 완료", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_CHKALL.h b/app/src/cl/svc/CL_CHKALL.h new file mode 100644 index 0000000..94ddf6d --- /dev/null +++ b/app/src/cl/svc/CL_CHKALL.h @@ -0,0 +1,22 @@ +/* + * CL_CHKALL.h - cl 서비스 CL_CHKALL 카피북 (copybook / record header). + * 마감 전 통합 체크: 미마감/미매칭/보류/한도 합산 점수 산출. + */ +#ifndef CL_CHKALL_H +#define CL_CHKALL_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_chkall_rec_t; + +void CL_CHKALL(TPSVCINFO *p); + +#endif /* CL_CHKALL_H */ diff --git a/app/src/cl/svc/CL_CHKALL.pgc b/app/src/cl/svc/CL_CHKALL.pgc new file mode 100644 index 0000000..d412b37 --- /dev/null +++ b/app/src/cl/svc/CL_CHKALL.pgc @@ -0,0 +1,41 @@ +/* + * CL_CHKALL.pgc - cl 모듈 서비스 CL_CHKALL (one service = one file). + * 마감 전 통합 체크: 미마감(M)/미매칭/보류(H)/void(V) 합산 점수 산출. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_CHKALL.h" + +void CL_CHKALL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long score; + char msg[128]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_m, h_u, h_h, h_v; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT count(*) INTO :h_m FROM purchase + WHERE biz_date = :h_bizdate AND status = 'M'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_u FROM ac_unmatch WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_h FROM purchase + WHERE biz_date = :h_bizdate AND status = 'H'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_v FROM purchase + WHERE biz_date = :h_bizdate AND status = 'V'; + if (sqlca.sqlcode < 0) FAIL(b); + score = h_m + h_u + h_h + h_v; + snprintf(msg, sizeof(msg), "미마감%ld/미매칭%ld/보류%ld/void%ld", h_m, h_u, h_h, h_v); + setl(b, T_ARG1, h_m); + setl(b, T_ARG2, h_u); + setl(b, T_ARG3, h_h); + setl(b, T_ARG4, h_v); + setl(b, T_COUNT, score); + setl(b, T_RC, (score > 0) ? 1 : 0); + Bchg(b, T_MSG, 0, msg, 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_CHKLIMIT.h b/app/src/cl/svc/CL_CHKLIMIT.h new file mode 100644 index 0000000..708ab2b --- /dev/null +++ b/app/src/cl/svc/CL_CHKLIMIT.h @@ -0,0 +1,22 @@ +/* + * CL_CHKLIMIT.h - cl 서비스 CL_CHKLIMIT 카피북 (copybook / record header). + * 마감 전 한도 체크: 가맹점 일한도 초과 매입 존재 여부 판정. + */ +#ifndef CL_CHKLIMIT_H +#define CL_CHKLIMIT_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_chklimit_rec_t; + +void CL_CHKLIMIT(TPSVCINFO *p); + +#endif /* CL_CHKLIMIT_H */ diff --git a/app/src/cl/svc/CL_CHKLIMIT.pgc b/app/src/cl/svc/CL_CHKLIMIT.pgc new file mode 100644 index 0000000..ff233fb --- /dev/null +++ b/app/src/cl/svc/CL_CHKLIMIT.pgc @@ -0,0 +1,29 @@ +/* + * CL_CHKLIMIT.pgc - cl 모듈 서비스 CL_CHKLIMIT (one service = one file). + * 마감 전 한도 체크: 가맹점 일한도(daily_limit) 초과 매입 존재 여부 판정. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_CHKLIMIT.h" + +void CL_CHKLIMIT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_over; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT count(*) INTO :h_over FROM ( + SELECT p.merchant_id + FROM purchase p JOIN merchant m ON m.merchant_id = p.merchant_id + WHERE p.biz_date = :h_bizdate + GROUP BY p.merchant_id, m.daily_limit + HAVING coalesce(sum(p.amount),0) > m.daily_limit) x; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_over); + setl(b, T_RC, (h_over > 0) ? 1 : 0); + Bchg(b, T_MSG, 0, (h_over > 0) ? "일한도 초과 가맹점 존재" : "한도 내 정상", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_CHKPENDING.h b/app/src/cl/svc/CL_CHKPENDING.h new file mode 100644 index 0000000..4924342 --- /dev/null +++ b/app/src/cl/svc/CL_CHKPENDING.h @@ -0,0 +1,22 @@ +/* + * CL_CHKPENDING.h - cl 서비스 CL_CHKPENDING 카피북 (copybook / record header). + * 마감 전 보류 체크: 당일 보류(H) 매입 존재 여부 판정. + */ +#ifndef CL_CHKPENDING_H +#define CL_CHKPENDING_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_chkpending_rec_t; + +void CL_CHKPENDING(TPSVCINFO *p); + +#endif /* CL_CHKPENDING_H */ diff --git a/app/src/cl/svc/CL_CHKPENDING.pgc b/app/src/cl/svc/CL_CHKPENDING.pgc new file mode 100644 index 0000000..09595dd --- /dev/null +++ b/app/src/cl/svc/CL_CHKPENDING.pgc @@ -0,0 +1,26 @@ +/* + * CL_CHKPENDING.pgc - cl 모듈 서비스 CL_CHKPENDING (one service = one file). + * 마감 전 보류 체크: 당일 보류(H) 매입 존재 여부 판정. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_CHKPENDING.h" + +void CL_CHKPENDING(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_n, h_amt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_n, :h_amt + FROM purchase WHERE biz_date = :h_bizdate AND status = 'H'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_n); + setl(b, T_GROSS, h_amt); + setl(b, T_RC, (h_n > 0) ? 1 : 0); + Bchg(b, T_MSG, 0, (h_n > 0) ? "보류(H) 매입 잔존" : "보류 없음", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_CHKUNMATCH.h b/app/src/cl/svc/CL_CHKUNMATCH.h new file mode 100644 index 0000000..a0738c5 --- /dev/null +++ b/app/src/cl/svc/CL_CHKUNMATCH.h @@ -0,0 +1,22 @@ +/* + * CL_CHKUNMATCH.h - cl 서비스 CL_CHKUNMATCH 카피북 (copybook / record header). + * 마감 전 미매칭 체크: 당일 미매칭(ac_unmatch) 존재 여부 판정. + */ +#ifndef CL_CHKUNMATCH_H +#define CL_CHKUNMATCH_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_chkunmatch_rec_t; + +void CL_CHKUNMATCH(TPSVCINFO *p); + +#endif /* CL_CHKUNMATCH_H */ diff --git a/app/src/cl/svc/CL_CHKUNMATCH.pgc b/app/src/cl/svc/CL_CHKUNMATCH.pgc new file mode 100644 index 0000000..ce8f331 --- /dev/null +++ b/app/src/cl/svc/CL_CHKUNMATCH.pgc @@ -0,0 +1,24 @@ +/* + * CL_CHKUNMATCH.pgc - cl 모듈 서비스 CL_CHKUNMATCH (one service = one file). + * 마감 전 미매칭 체크: 당일 미매칭(ac_unmatch) 존재 여부 판정. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_CHKUNMATCH.h" + +void CL_CHKUNMATCH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_n; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT count(*) INTO :h_n FROM ac_unmatch WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_n); + setl(b, T_RC, (h_n > 0) ? 1 : 0); + Bchg(b, T_MSG, 0, (h_n > 0) ? "미매칭 존재-마감 불가" : "미매칭 없음", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_CHKVOID.h b/app/src/cl/svc/CL_CHKVOID.h new file mode 100644 index 0000000..3d53adf --- /dev/null +++ b/app/src/cl/svc/CL_CHKVOID.h @@ -0,0 +1,22 @@ +/* + * CL_CHKVOID.h - cl 서비스 CL_CHKVOID 카피북 (copybook / record header). + * 마감 전 void 체크: 당일 void(V) 매입 잔존 여부 판정. + */ +#ifndef CL_CHKVOID_H +#define CL_CHKVOID_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_chkvoid_rec_t; + +void CL_CHKVOID(TPSVCINFO *p); + +#endif /* CL_CHKVOID_H */ diff --git a/app/src/cl/svc/CL_CHKVOID.pgc b/app/src/cl/svc/CL_CHKVOID.pgc new file mode 100644 index 0000000..bbdac93 --- /dev/null +++ b/app/src/cl/svc/CL_CHKVOID.pgc @@ -0,0 +1,25 @@ +/* + * CL_CHKVOID.pgc - cl 모듈 서비스 CL_CHKVOID (one service = one file). + * 마감 전 void 체크: 당일 void(V) 매입 잔존 여부 판정. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_CHKVOID.h" + +void CL_CHKVOID(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_n; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT count(*) INTO :h_n + FROM purchase WHERE biz_date = :h_bizdate AND status = 'V'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_n); + setl(b, T_RC, (h_n > 0) ? 1 : 0); + Bchg(b, T_MSG, 0, (h_n > 0) ? "void(V) 매입 잔존" : "void 없음", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_CNTVERIFY.h b/app/src/cl/svc/CL_CNTVERIFY.h new file mode 100644 index 0000000..46b6e4a --- /dev/null +++ b/app/src/cl/svc/CL_CNTVERIFY.h @@ -0,0 +1,22 @@ +/* + * CL_CNTVERIFY.h - cl 서비스 CL_CNTVERIFY 카피북 (copybook / record header). + * 건수 정합검증: 마감이력 건수와 매입 실건수 대사. + */ +#ifndef CL_CNTVERIFY_H +#define CL_CNTVERIFY_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_cntverify_rec_t; + +void CL_CNTVERIFY(TPSVCINFO *p); + +#endif /* CL_CNTVERIFY_H */ diff --git a/app/src/cl/svc/CL_CNTVERIFY.pgc b/app/src/cl/svc/CL_CNTVERIFY.pgc new file mode 100644 index 0000000..a155a92 --- /dev/null +++ b/app/src/cl/svc/CL_CNTVERIFY.pgc @@ -0,0 +1,30 @@ +/* + * CL_CNTVERIFY.pgc - cl 모듈 서비스 CL_CNTVERIFY (one service = one file). + * 건수 정합검증: 마감이력 txn_count 와 해당 영업일 매입 실건수를 대사한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_CNTVERIFY.h" + +void CL_CNTVERIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long delta; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16]; + long h_cid, h_lc, h_rc; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + EXEC SQL SELECT txn_count, to_char(biz_date,'YYYY-MM-DD') INTO :h_lc, :h_date + FROM cl_close_log WHERE close_id = :h_cid AND status <> 'X'; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_rc FROM purchase WHERE biz_date = :h_date; + if (sqlca.sqlcode < 0) FAIL(b); + delta = h_lc - h_rc; if (delta < 0) delta = -delta; + setl(b, T_ARG1, h_lc); + setl(b, T_COUNT, h_rc); + setl(b, T_DELTA, delta); + setl(b, T_RC, (delta == 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_CORPVERIFY.h b/app/src/cl/svc/CL_CORPVERIFY.h new file mode 100644 index 0000000..f4f9c8b --- /dev/null +++ b/app/src/cl/svc/CL_CORPVERIFY.h @@ -0,0 +1,22 @@ +/* + * CL_CORPVERIFY.h - cl 서비스 CL_CORPVERIFY 카피북 (copybook / record header). + * 법인별 마감 검증: 법인 마감이력 건수/금액 정합 점검. + */ +#ifndef CL_CORPVERIFY_H +#define CL_CORPVERIFY_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_corpverify_rec_t; + +void CL_CORPVERIFY(TPSVCINFO *p); + +#endif /* CL_CORPVERIFY_H */ diff --git a/app/src/cl/svc/CL_CORPVERIFY.pgc b/app/src/cl/svc/CL_CORPVERIFY.pgc new file mode 100644 index 0000000..6901d96 --- /dev/null +++ b/app/src/cl/svc/CL_CORPVERIFY.pgc @@ -0,0 +1,34 @@ +/* + * CL_CORPVERIFY.pgc - cl 모듈 서비스 CL_CORPVERIFY (one service = one file). + * 법인별 마감 검증: 마감이력 헤더 건수/금액과 스냅샷 집계 정합 점검. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_CORPVERIFY.h" + +void CL_CORPVERIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long mask = 0, dc, da; + EXEC SQL BEGIN DECLARE SECTION; + long h_cid, h_lc, h_la, h_sc, h_sa; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + EXEC SQL SELECT txn_count, total_amount INTO :h_lc, :h_la + FROM cl_close_log WHERE close_id = :h_cid AND status <> 'X'; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + EXEC SQL SELECT coalesce(sum(txn_count),0), coalesce(sum(gross_amount),0) + INTO :h_sc, :h_sa FROM cl_snapshot WHERE close_id = :h_cid; + if (sqlca.sqlcode < 0) FAIL(b); + dc = h_lc - h_sc; if (dc < 0) dc = -dc; + da = h_la - h_sa; if (da < 0) da = -da; + if (dc != 0) mask |= 1; + if (da != 0) mask |= 2; + setl(b, T_ARG1, h_lc); + setl(b, T_ARG2, h_sc); + setl(b, T_AMT1, h_la); + setl(b, T_AMT2, h_sa); + setl(b, T_RC, mask); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_CXLDUP.h b/app/src/cl/svc/CL_CXLDUP.h new file mode 100644 index 0000000..a851e0b --- /dev/null +++ b/app/src/cl/svc/CL_CXLDUP.h @@ -0,0 +1,22 @@ +/* + * CL_CXLDUP.h - cl 서비스 CL_CXLDUP 카피북 (copybook / record header). + * 중복마감 취소: 중복 마감 사유로 마감 취소 처리. + */ +#ifndef CL_CXLDUP_H +#define CL_CXLDUP_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_cxldup_rec_t; + +void CL_CXLDUP(TPSVCINFO *p); + +#endif /* CL_CXLDUP_H */ diff --git a/app/src/cl/svc/CL_CXLDUP.pgc b/app/src/cl/svc/CL_CXLDUP.pgc new file mode 100644 index 0000000..aee100a --- /dev/null +++ b/app/src/cl/svc/CL_CXLDUP.pgc @@ -0,0 +1,30 @@ +/* + * CL_CXLDUP.pgc - cl 모듈 서비스 CL_CXLDUP (one service = one file). + * 중복마감 취소: 동일 기간 중복 마감이력 중 최신을 남기고 취소(status=X) 처리. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_CXLDUP.h" + +void CL_CXLDUP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_ct[16], h_pk[24]; + long h_cid; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + EXEC SQL SELECT close_type, period_key INTO :h_ct, :h_pk + FROM cl_close_log WHERE close_id = :h_cid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + EXEC SQL UPDATE cl_close_log SET status = 'X', memo = '취소:중복마감', updated_at = now() + WHERE close_type = :h_ct AND period_key = :h_pk AND status <> 'X' + AND close_id <> (SELECT max(close_id) FROM cl_close_log + WHERE close_type = :h_ct AND period_key = :h_pk AND status <> 'X'); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_RC, (long)sqlca.sqlerrd[2]); + setl(b, T_COUNT, (long)sqlca.sqlerrd[2]); + Bchg(b, T_REASON, 0, "중복마감", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_CXLERROR.h b/app/src/cl/svc/CL_CXLERROR.h new file mode 100644 index 0000000..faf66fd --- /dev/null +++ b/app/src/cl/svc/CL_CXLERROR.h @@ -0,0 +1,22 @@ +/* + * CL_CXLERROR.h - cl 서비스 CL_CXLERROR 카피북 (copybook / record header). + * 오류정정 취소: 오류정정 사유로 마감 취소(X) 처리. + */ +#ifndef CL_CXLERROR_H +#define CL_CXLERROR_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_cxlerror_rec_t; + +void CL_CXLERROR(TPSVCINFO *p); + +#endif /* CL_CXLERROR_H */ diff --git a/app/src/cl/svc/CL_CXLERROR.pgc b/app/src/cl/svc/CL_CXLERROR.pgc new file mode 100644 index 0000000..bf41a03 --- /dev/null +++ b/app/src/cl/svc/CL_CXLERROR.pgc @@ -0,0 +1,26 @@ +/* + * CL_CXLERROR.pgc - cl 모듈 서비스 CL_CXLERROR (one service = one file). + * 오류정정 취소: 오류정정 사유로 마감을 취소(status=X)한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_CXLERROR.h" + +void CL_CXLERROR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_cid; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + EXEC SQL UPDATE cl_close_log SET status = 'X', memo = '취소:오류정정', updated_at = now() + WHERE close_id = :h_cid AND status <> 'X'; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlerrd[2] == 0) { Bchg(b, T_MSG, 0, "취소 대상 없음(이미 취소)", 0L); FAIL(b); } + setl(b, T_ID1, h_cid); + setl(b, T_RC, (long)sqlca.sqlerrd[2]); + Bchg(b, T_STATUS, 0, "X", 0L); + Bchg(b, T_REASON, 0, "오류정정", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_CXLLATE.h b/app/src/cl/svc/CL_CXLLATE.h new file mode 100644 index 0000000..72706fa --- /dev/null +++ b/app/src/cl/svc/CL_CXLLATE.h @@ -0,0 +1,22 @@ +/* + * CL_CXLLATE.h - cl 서비스 CL_CXLLATE 카피북 (copybook / record header). + * 지연자료 취소: 지연자료 반영 사유로 마감 취소 처리. + */ +#ifndef CL_CXLLATE_H +#define CL_CXLLATE_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_cxllate_rec_t; + +void CL_CXLLATE(TPSVCINFO *p); + +#endif /* CL_CXLLATE_H */ diff --git a/app/src/cl/svc/CL_CXLLATE.pgc b/app/src/cl/svc/CL_CXLLATE.pgc new file mode 100644 index 0000000..fe149cb --- /dev/null +++ b/app/src/cl/svc/CL_CXLLATE.pgc @@ -0,0 +1,26 @@ +/* + * CL_CXLLATE.pgc - cl 모듈 서비스 CL_CXLLATE (one service = one file). + * 지연자료 취소: 지연자료 반영 사유로 마감을 취소(status=X)한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_CXLLATE.h" + +void CL_CXLLATE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_cid; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + EXEC SQL UPDATE cl_close_log SET status = 'X', memo = '취소:지연자료', updated_at = now() + WHERE close_id = :h_cid AND status IN ('C','A'); + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlerrd[2] == 0) { Bchg(b, T_MSG, 0, "취소 가능 상태 아님", 0L); FAIL(b); } + setl(b, T_ID1, h_cid); + setl(b, T_RC, (long)sqlca.sqlerrd[2]); + Bchg(b, T_STATUS, 0, "X", 0L); + Bchg(b, T_REASON, 0, "지연자료", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_CXLREQ.h b/app/src/cl/svc/CL_CXLREQ.h new file mode 100644 index 0000000..4ba28cd --- /dev/null +++ b/app/src/cl/svc/CL_CXLREQ.h @@ -0,0 +1,22 @@ +/* + * CL_CXLREQ.h - cl 서비스 CL_CXLREQ 카피북 (copybook / record header). + * 마감 취소 요청: 취소 요청 사유 기록 후 요청 표기. + */ +#ifndef CL_CXLREQ_H +#define CL_CXLREQ_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_cxlreq_rec_t; + +void CL_CXLREQ(TPSVCINFO *p); + +#endif /* CL_CXLREQ_H */ diff --git a/app/src/cl/svc/CL_CXLREQ.pgc b/app/src/cl/svc/CL_CXLREQ.pgc new file mode 100644 index 0000000..c39195d --- /dev/null +++ b/app/src/cl/svc/CL_CXLREQ.pgc @@ -0,0 +1,30 @@ +/* + * CL_CXLREQ.pgc - cl 모듈 서비스 CL_CXLREQ (one service = one file). + * 마감 취소 요청: 취소 사유(T_REASON)를 memo(CXLREQ:..)에 기록해 요청 표기(상태 유지). + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_CXLREQ.h" + +void CL_CXLREQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char reason[32]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cid; + char h_memo[64]; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + gets_(b, T_REASON, reason, sizeof(reason)); + if (reason[0] == 0) strcpy(reason, "미기재"); + snprintf(h_memo, sizeof(h_memo), "CXLREQ:%s", reason); + EXEC SQL UPDATE cl_close_log SET memo = :h_memo, updated_at = now() + WHERE close_id = :h_cid AND status IN ('C','A'); + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlerrd[2] == 0) { Bchg(b, T_MSG, 0, "요청 대상 아님", 0L); FAIL(b); } + setl(b, T_ID1, h_cid); + setl(b, T_RC, (long)sqlca.sqlerrd[2]); + Bchg(b, T_REASON, 0, reason, 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_CXLSTAT.h b/app/src/cl/svc/CL_CXLSTAT.h new file mode 100644 index 0000000..5150d94 --- /dev/null +++ b/app/src/cl/svc/CL_CXLSTAT.h @@ -0,0 +1,22 @@ +/* + * CL_CXLSTAT.h - cl 서비스 CL_CXLSTAT 카피북 (copybook / record header). + * 마감 취소 현황: 취소 사유별 마감 취소 건수 집계. + */ +#ifndef CL_CXLSTAT_H +#define CL_CXLSTAT_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_cxlstat_rec_t; + +void CL_CXLSTAT(TPSVCINFO *p); + +#endif /* CL_CXLSTAT_H */ diff --git a/app/src/cl/svc/CL_CXLSTAT.pgc b/app/src/cl/svc/CL_CXLSTAT.pgc new file mode 100644 index 0000000..4181646 --- /dev/null +++ b/app/src/cl/svc/CL_CXLSTAT.pgc @@ -0,0 +1,28 @@ +/* + * CL_CXLSTAT.pgc - cl 모듈 서비스 CL_CXLSTAT (one service = one file). + * 마감 취소 현황: 취소 사유별(오류정정/지연자료/중복마감) 취소 건수를 집계한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_CXLSTAT.h" + +void CL_CXLSTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_err, h_late, h_dup, h_tot; + EXEC SQL END DECLARE SECTION; + EXEC SQL SELECT + count(*) FILTER (WHERE memo LIKE '취소:오류%'), + count(*) FILTER (WHERE memo LIKE '취소:지연%'), + count(*) FILTER (WHERE memo LIKE '취소:중복%'), + count(*) + INTO :h_err, :h_late, :h_dup, :h_tot FROM cl_close_log WHERE status = 'X'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_err); + setl(b, T_ARG2, h_late); + setl(b, T_ARG3, h_dup); + setl(b, T_COUNT, h_tot); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_DAYVERIFY.h b/app/src/cl/svc/CL_DAYVERIFY.h new file mode 100644 index 0000000..f4361f5 --- /dev/null +++ b/app/src/cl/svc/CL_DAYVERIFY.h @@ -0,0 +1,22 @@ +/* + * CL_DAYVERIFY.h - cl 서비스 CL_DAYVERIFY 카피북 (copybook / record header). + * 일 마감 검증: 당일 마감이력 집계와 매입 실집계 대사. + */ +#ifndef CL_DAYVERIFY_H +#define CL_DAYVERIFY_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_dayverify_rec_t; + +void CL_DAYVERIFY(TPSVCINFO *p); + +#endif /* CL_DAYVERIFY_H */ diff --git a/app/src/cl/svc/CL_DAYVERIFY.pgc b/app/src/cl/svc/CL_DAYVERIFY.pgc new file mode 100644 index 0000000..2d43db1 --- /dev/null +++ b/app/src/cl/svc/CL_DAYVERIFY.pgc @@ -0,0 +1,35 @@ +/* + * CL_DAYVERIFY.pgc - cl 모듈 서비스 CL_DAYVERIFY (one service = one file). + * 일 마감 검증: 당일 마감이력 집계와 매입 실집계 대사. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_DAYVERIFY.h" + +void CL_DAYVERIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long dc, da; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_lc, h_la, h_rc, h_ra; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT coalesce(sum(txn_count),0), coalesce(sum(total_amount),0) + INTO :h_lc, :h_la FROM cl_close_log + WHERE close_type = 'DAILY' AND period_key = :h_bizdate AND status <> 'X'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_rc, :h_ra + FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + dc = h_lc - h_rc; if (dc < 0) dc = -dc; + da = h_la - h_ra; if (da < 0) da = -da; + setl(b, T_COUNT, h_rc); + setl(b, T_ARG1, h_lc); + setl(b, T_AMT1, h_la); + setl(b, T_AMT2, h_ra); + setl(b, T_DELTA, da); + setl(b, T_RC, (dc == 0 && da == 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_DUELIST.h b/app/src/cl/svc/CL_DUELIST.h new file mode 100644 index 0000000..bd738a1 --- /dev/null +++ b/app/src/cl/svc/CL_DUELIST.h @@ -0,0 +1,22 @@ +/* + * CL_DUELIST.h - cl 서비스 CL_DUELIST 카피북 (copybook / record header). + * 마감 예정 목록: 기한 도래 마감 대상 기간 목록 산출. + */ +#ifndef CL_DUELIST_H +#define CL_DUELIST_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_duelist_rec_t; + +void CL_DUELIST(TPSVCINFO *p); + +#endif /* CL_DUELIST_H */ diff --git a/app/src/cl/svc/CL_DUELIST.pgc b/app/src/cl/svc/CL_DUELIST.pgc new file mode 100644 index 0000000..080965e --- /dev/null +++ b/app/src/cl/svc/CL_DUELIST.pgc @@ -0,0 +1,41 @@ +/* + * CL_DUELIST.pgc - cl 모듈 서비스 CL_DUELIST (one service = one file). + * 마감 예정 목록: 기준일 이전 14일 내 영업일 중 DAILY 마감이 없는 미마감 대상일을 나열. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_DUELIST.h" + +void CL_DUELIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long occ = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_day[16]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL DECLARE crdue CURSOR FOR + SELECT to_char(d,'YYYY-MM-DD') FROM ( + SELECT (:h_bizdate::date - s) AS d, + extract(dow from (:h_bizdate::date - s)) AS dw + FROM generate_series(0,13) s) t + WHERE dw NOT IN (0,6) + AND NOT EXISTS (SELECT 1 FROM cl_close_log + WHERE close_type = 'DAILY' + AND period_key = to_char(t.d,'YYYY-MM-DD') AND status <> 'X') + ORDER BY d DESC; + EXEC SQL OPEN crdue; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH crdue INTO :h_day; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE crdue; FAIL(b); } + Bchg(b, T_KEY1, (BFLDOCC)occ, h_day, 0L); + occ++; + } + EXEC SQL CLOSE crdue; + setl(b, T_COUNT, occ); + setl(b, T_RC, (occ > 0) ? 1 : 0); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_FEECROSS.h b/app/src/cl/svc/CL_FEECROSS.h new file mode 100644 index 0000000..ed8f5b1 --- /dev/null +++ b/app/src/cl/svc/CL_FEECROSS.h @@ -0,0 +1,22 @@ +/* + * CL_FEECROSS.h - cl 서비스 CL_FEECROSS 카피북 (copybook / record header). + * 수수료 크로스검증: 마감 수수료 합계와 매입 fee 합계 대사. + */ +#ifndef CL_FEECROSS_H +#define CL_FEECROSS_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_feecross_rec_t; + +void CL_FEECROSS(TPSVCINFO *p); + +#endif /* CL_FEECROSS_H */ diff --git a/app/src/cl/svc/CL_FEECROSS.pgc b/app/src/cl/svc/CL_FEECROSS.pgc new file mode 100644 index 0000000..1060014 --- /dev/null +++ b/app/src/cl/svc/CL_FEECROSS.pgc @@ -0,0 +1,34 @@ +/* + * CL_FEECROSS.pgc - cl 모듈 서비스 CL_FEECROSS (one service = one file). + * 수수료 크로스검증: 마감 스냅샷 fee 합계와 해당 영업일 매입 fee 합계를 대사한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_FEECROSS.h" + +void CL_FEECROSS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long delta; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16]; + long h_cid, h_sf, h_pf; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + EXEC SQL SELECT to_char(biz_date,'YYYY-MM-DD') INTO :h_date + FROM cl_close_log WHERE close_id = :h_cid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + EXEC SQL SELECT coalesce(sum(fee_amount),0) INTO :h_sf + FROM cl_snapshot WHERE close_id = :h_cid; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(fee),0) INTO :h_pf + FROM purchase WHERE biz_date = :h_date; + if (sqlca.sqlcode < 0) FAIL(b); + delta = h_sf - h_pf; if (delta < 0) delta = -delta; + setl(b, T_AMT1, h_sf); + setl(b, T_AMT2, h_pf); + setl(b, T_DELTA, delta); + setl(b, T_RC, (delta == 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_FXCLOSE.h b/app/src/cl/svc/CL_FXCLOSE.h new file mode 100644 index 0000000..37ee667 --- /dev/null +++ b/app/src/cl/svc/CL_FXCLOSE.h @@ -0,0 +1,22 @@ +/* + * CL_FXCLOSE.h - cl 서비스 CL_FXCLOSE 카피북 (copybook / record header). + * 해외(FOREIGN) 채널 마감: 해외 매입 집계 후 FX 마감이력. + */ +#ifndef CL_FXCLOSE_H +#define CL_FXCLOSE_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_fxclose_rec_t; + +void CL_FXCLOSE(TPSVCINFO *p); + +#endif /* CL_FXCLOSE_H */ diff --git a/app/src/cl/svc/CL_FXCLOSE.pgc b/app/src/cl/svc/CL_FXCLOSE.pgc new file mode 100644 index 0000000..593189f --- /dev/null +++ b/app/src/cl/svc/CL_FXCLOSE.pgc @@ -0,0 +1,34 @@ +/* + * CL_FXCLOSE.pgc - cl 모듈 서비스 CL_FXCLOSE (one service = one file). + * 해외(FOREIGN) 채널 마감: 해외 매입 집계 + ac_fx 원화환산 합계 후 FX 마감. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_FXCLOSE.h" + +void CL_FXCLOSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long cid; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_amt, h_krw; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_cnt, :h_amt + FROM purchase WHERE biz_date = :h_bizdate AND channel = 'FOREIGN'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(krw_amount),0) INTO :h_krw + FROM ac_fx WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + cid = cldb_next_close_id(); + if (cid < 0) FAIL(b); + if (cldb_insert_close(cid, "FX", h_bizdate, h_bizdate, "C", h_cnt, h_amt, "해외채널마감") < 0) FAIL(b); + setl(b, T_ID1, cid); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_amt); + setl(b, T_AMT1, h_krw); + setl(b, T_RC, (h_amt == h_krw) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_HOLICHK.h b/app/src/cl/svc/CL_HOLICHK.h new file mode 100644 index 0000000..49f8041 --- /dev/null +++ b/app/src/cl/svc/CL_HOLICHK.h @@ -0,0 +1,22 @@ +/* + * CL_HOLICHK.h - cl 서비스 CL_HOLICHK 카피북 (copybook / record header). + * 휴일 마감 판정: 주말/휴일 여부에 따른 마감정책 결정. + */ +#ifndef CL_HOLICHK_H +#define CL_HOLICHK_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_holichk_rec_t; + +void CL_HOLICHK(TPSVCINFO *p); + +#endif /* CL_HOLICHK_H */ diff --git a/app/src/cl/svc/CL_HOLICHK.pgc b/app/src/cl/svc/CL_HOLICHK.pgc new file mode 100644 index 0000000..52c3de3 --- /dev/null +++ b/app/src/cl/svc/CL_HOLICHK.pgc @@ -0,0 +1,26 @@ +/* + * CL_HOLICHK.pgc - cl 모듈 서비스 CL_HOLICHK (one service = one file). + * 휴일 마감 판정: 기준일의 요일(dow)로 주말 여부를 보고 마감정책(정상/이월)을 결정. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_HOLICHK.h" + +void CL_HOLICHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int weekend; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_dow; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT extract(dow from :h_bizdate::date)::int INTO :h_dow; + if (sqlca.sqlcode < 0) FAIL(b); + weekend = (h_dow == 0 || h_dow == 6) ? 1 : 0; + setl(b, T_ARG1, h_dow); + setl(b, T_RC, weekend); + Bchg(b, T_MSG, 0, weekend ? "휴일-마감 이월" : "영업일-정상 마감", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_HYCLOSE.h b/app/src/cl/svc/CL_HYCLOSE.h new file mode 100644 index 0000000..4acc848 --- /dev/null +++ b/app/src/cl/svc/CL_HYCLOSE.h @@ -0,0 +1,22 @@ +/* + * CL_HYCLOSE.h - cl 서비스 CL_HYCLOSE 카피북 (copybook / record header). + * 반기 마감: 상/하반기 6개월 매입 집계 후 HALFYR 마감이력 생성. + */ +#ifndef CL_HYCLOSE_H +#define CL_HYCLOSE_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_hyclose_rec_t; + +void CL_HYCLOSE(TPSVCINFO *p); + +#endif /* CL_HYCLOSE_H */ diff --git a/app/src/cl/svc/CL_HYCLOSE.pgc b/app/src/cl/svc/CL_HYCLOSE.pgc new file mode 100644 index 0000000..9bb090a --- /dev/null +++ b/app/src/cl/svc/CL_HYCLOSE.pgc @@ -0,0 +1,40 @@ +/* + * CL_HYCLOSE.pgc - cl 모듈 서비스 CL_HYCLOSE (one service = one file). + * 반기 마감: 상/하반기 6개월 매입 집계 후 HALFYR 마감이력 생성. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_HYCLOSE.h" + +void CL_HYCLOSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long cid, half; + char period[24], memo[32]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_yr[8]; + long h_mon, h_lo, h_hi, h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT extract(month from :h_bizdate::date)::int, + to_char(:h_bizdate::date, 'YYYY') INTO :h_mon, :h_yr; + if (sqlca.sqlcode < 0) FAIL(b); + half = (h_mon <= 6) ? 1 : 2; + h_lo = (half == 1) ? 1 : 7; + h_hi = (half == 1) ? 6 : 12; + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_cnt, :h_amt + FROM purchase WHERE to_char(biz_date,'YYYY') = :h_yr + AND extract(month from biz_date) BETWEEN :h_lo AND :h_hi; + if (sqlca.sqlcode < 0) FAIL(b); + snprintf(period, sizeof(period), "%s-HY%ld", h_yr, half); + snprintf(memo, sizeof(memo), "반기마감:%s반기", (half == 1) ? "상" : "하"); + cid = cldb_next_close_id(); + if (cid < 0) FAIL(b); + if (cldb_insert_close(cid, "HALFYR", period, h_bizdate, "C", h_cnt, h_amt, memo) < 0) FAIL(b); + setl(b, T_ID1, cid); + setl(b, T_ARG1, half); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_amt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_HYVERIFY.h b/app/src/cl/svc/CL_HYVERIFY.h new file mode 100644 index 0000000..9cafccd --- /dev/null +++ b/app/src/cl/svc/CL_HYVERIFY.h @@ -0,0 +1,22 @@ +/* + * CL_HYVERIFY.h - cl 서비스 CL_HYVERIFY 카피북 (copybook / record header). + * 반기 마감 검증: 반기 마감이력과 반기 매입 실집계 대사. + */ +#ifndef CL_HYVERIFY_H +#define CL_HYVERIFY_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_hyverify_rec_t; + +void CL_HYVERIFY(TPSVCINFO *p); + +#endif /* CL_HYVERIFY_H */ diff --git a/app/src/cl/svc/CL_HYVERIFY.pgc b/app/src/cl/svc/CL_HYVERIFY.pgc new file mode 100644 index 0000000..4679465 --- /dev/null +++ b/app/src/cl/svc/CL_HYVERIFY.pgc @@ -0,0 +1,42 @@ +/* + * CL_HYVERIFY.pgc - cl 모듈 서비스 CL_HYVERIFY (one service = one file). + * 반기 마감 검증: 반기 마감이력과 반기 매입 실집계 대사. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_HYVERIFY.h" + +void CL_HYVERIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long half, da; + char pk[24]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_yr[8], h_pk[24]; + long h_mon, h_lo, h_hi, h_la, h_ra; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT extract(month from :h_bizdate::date)::int, + to_char(:h_bizdate::date, 'YYYY') INTO :h_mon, :h_yr; + if (sqlca.sqlcode < 0) FAIL(b); + half = (h_mon <= 6) ? 1 : 2; + h_lo = (half == 1) ? 1 : 7; + h_hi = (half == 1) ? 6 : 12; + snprintf(pk, sizeof(pk), "%s-HY%ld", h_yr, half); + strncpy(h_pk, pk, sizeof(h_pk)-1); h_pk[sizeof(h_pk)-1] = 0; + EXEC SQL SELECT coalesce(sum(total_amount),0) INTO :h_la FROM cl_close_log + WHERE close_type = 'HALFYR' AND period_key = :h_pk AND status <> 'X'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_ra FROM purchase + WHERE to_char(biz_date,'YYYY') = :h_yr + AND extract(month from biz_date) BETWEEN :h_lo AND :h_hi; + if (sqlca.sqlcode < 0) FAIL(b); + da = h_la - h_ra; if (da < 0) da = -da; + setl(b, T_ARG1, half); + setl(b, T_AMT1, h_la); + setl(b, T_AMT2, h_ra); + setl(b, T_DELTA, da); + setl(b, T_RC, (da == 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_ISSCLOSE.h b/app/src/cl/svc/CL_ISSCLOSE.h new file mode 100644 index 0000000..91a52a4 --- /dev/null +++ b/app/src/cl/svc/CL_ISSCLOSE.h @@ -0,0 +1,22 @@ +/* + * CL_ISSCLOSE.h - cl 서비스 CL_ISSCLOSE 카피북 (copybook / record header). + * 발급사별 마감: 발급사(issuer) 단위 매입 집계 후 ISS 마감. + */ +#ifndef CL_ISSCLOSE_H +#define CL_ISSCLOSE_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_issclose_rec_t; + +void CL_ISSCLOSE(TPSVCINFO *p); + +#endif /* CL_ISSCLOSE_H */ diff --git a/app/src/cl/svc/CL_ISSCLOSE.pgc b/app/src/cl/svc/CL_ISSCLOSE.pgc new file mode 100644 index 0000000..3622e6a --- /dev/null +++ b/app/src/cl/svc/CL_ISSCLOSE.pgc @@ -0,0 +1,36 @@ +/* + * CL_ISSCLOSE.pgc - cl 모듈 서비스 CL_ISSCLOSE (one service = one file). + * 발급사별 마감: 발급사(issuer) 단위 매입 집계 후 ISS 마감. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_ISSCLOSE.h" + +void CL_ISSCLOSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char memo[48]; + long cid; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_iss[24]; + long h_cnt, h_amt, h_fee; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + gets_(b, T_ISSUER, h_iss, sizeof(h_iss)); + if (h_iss[0] == 0) strcpy(h_iss, "UNKNOWN"); + EXEC SQL SELECT count(*), coalesce(sum(amount),0), coalesce(sum(fee),0) + INTO :h_cnt, :h_amt, :h_fee + FROM purchase WHERE biz_date = :h_bizdate AND issuer = :h_iss; + if (sqlca.sqlcode < 0) FAIL(b); + cid = cldb_next_close_id(); + if (cid < 0) FAIL(b); + snprintf(memo, sizeof(memo), "발급사마감:%s", h_iss); + if (cldb_insert_close(cid, "ISS", h_bizdate, h_bizdate, "C", h_cnt, h_amt, memo) < 0) FAIL(b); + setl(b, T_ID1, cid); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_amt); + setl(b, T_FEE, h_fee); + Bchg(b, T_ISSUER, 0, h_iss, 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_MAINCLOSE.h b/app/src/cl/svc/CL_MAINCLOSE.h new file mode 100644 index 0000000..0d16593 --- /dev/null +++ b/app/src/cl/svc/CL_MAINCLOSE.h @@ -0,0 +1,22 @@ +/* + * CL_MAINCLOSE.h - cl 서비스 CL_MAINCLOSE 카피북 (copybook / record header). + * 본마감: 가마감 이력을 확정(status=C)으로 승격. + */ +#ifndef CL_MAINCLOSE_H +#define CL_MAINCLOSE_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_mainclose_rec_t; + +void CL_MAINCLOSE(TPSVCINFO *p); + +#endif /* CL_MAINCLOSE_H */ diff --git a/app/src/cl/svc/CL_MAINCLOSE.pgc b/app/src/cl/svc/CL_MAINCLOSE.pgc new file mode 100644 index 0000000..9ea90b7 --- /dev/null +++ b/app/src/cl/svc/CL_MAINCLOSE.pgc @@ -0,0 +1,34 @@ +/* + * CL_MAINCLOSE.pgc - cl 모듈 서비스 CL_MAINCLOSE (one service = one file). + * 본마감: 가마감(O) 이력을 당일 전체 매입으로 재집계해 확정(C)으로 승격. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_MAINCLOSE.h" + +void CL_MAINCLOSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char st[2]; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16]; + long h_cid, h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + if (cldb_get_close_status(h_cid, st) < 0) FAIL(b); + if (st[0] != 'O') { Bchg(b, T_MSG, 0, "가마감(O) 상태 아님-본마감 불가", 0L); FAIL(b); } + EXEC SQL SELECT to_char(biz_date,'YYYY-MM-DD') INTO :h_date + FROM cl_close_log WHERE close_id = :h_cid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_cnt, :h_amt + FROM purchase WHERE biz_date = :h_date; + if (sqlca.sqlcode < 0) FAIL(b); + if (cldb_update_close_totals(h_cid, h_cnt, h_amt) < 0) FAIL(b); + if (cldb_update_close_status(h_cid, "C") < 0) FAIL(b); + setl(b, T_ID1, h_cid); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_amt); + Bchg(b, T_STATUS, 0, "C", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_MERXCHECK.h b/app/src/cl/svc/CL_MERXCHECK.h new file mode 100644 index 0000000..f8ce2fa --- /dev/null +++ b/app/src/cl/svc/CL_MERXCHECK.h @@ -0,0 +1,22 @@ +/* + * CL_MERXCHECK.h - cl 서비스 CL_MERXCHECK 카피북 (copybook / record header). + * 가맹점별 크로스검증: 스냅샷 gross와 매입 amount 가맹점 대사. + */ +#ifndef CL_MERXCHECK_H +#define CL_MERXCHECK_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_merxcheck_rec_t; + +void CL_MERXCHECK(TPSVCINFO *p); + +#endif /* CL_MERXCHECK_H */ diff --git a/app/src/cl/svc/CL_MERXCHECK.pgc b/app/src/cl/svc/CL_MERXCHECK.pgc new file mode 100644 index 0000000..c4081a2 --- /dev/null +++ b/app/src/cl/svc/CL_MERXCHECK.pgc @@ -0,0 +1,44 @@ +/* + * CL_MERXCHECK.pgc - cl 모듈 서비스 CL_MERXCHECK (one service = one file). + * 가맹점별 크로스검증: 마감 스냅샷 gross 와 매입 amount 를 가맹점 단위로 대사해 + * 불일치 가맹점 수를 커서로 집계한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_MERXCHECK.h" + +void CL_MERXCHECK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long total = 0, mism = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16], h_mer[16]; + long h_cid, h_sg, h_pa; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + EXEC SQL SELECT to_char(biz_date,'YYYY-MM-DD') INTO :h_date + FROM cl_close_log WHERE close_id = :h_cid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + EXEC SQL DECLARE crmx CURSOR FOR + SELECT s.merchant_id, s.gross_amount, coalesce(pp.amt,0) + FROM cl_snapshot s + LEFT JOIN (SELECT merchant_id, sum(amount) AS amt FROM purchase + WHERE biz_date = :h_date GROUP BY merchant_id) pp + ON pp.merchant_id = s.merchant_id + WHERE s.close_id = :h_cid; + EXEC SQL OPEN crmx; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH crmx INTO :h_mer, :h_sg, :h_pa; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE crmx; FAIL(b); } + total++; + if (h_sg != h_pa) mism++; + } + EXEC SQL CLOSE crmx; + setl(b, T_COUNT, total); + setl(b, T_ARG1, mism); + setl(b, T_RC, (mism == 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_MONVERIFY.h b/app/src/cl/svc/CL_MONVERIFY.h new file mode 100644 index 0000000..b4c4a97 --- /dev/null +++ b/app/src/cl/svc/CL_MONVERIFY.h @@ -0,0 +1,22 @@ +/* + * CL_MONVERIFY.h - cl 서비스 CL_MONVERIFY 카피북 (copybook / record header). + * 월 마감 검증: 월 마감이력과 월 매입 실집계 대사. + */ +#ifndef CL_MONVERIFY_H +#define CL_MONVERIFY_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_monverify_rec_t; + +void CL_MONVERIFY(TPSVCINFO *p); + +#endif /* CL_MONVERIFY_H */ diff --git a/app/src/cl/svc/CL_MONVERIFY.pgc b/app/src/cl/svc/CL_MONVERIFY.pgc new file mode 100644 index 0000000..83c8967 --- /dev/null +++ b/app/src/cl/svc/CL_MONVERIFY.pgc @@ -0,0 +1,36 @@ +/* + * CL_MONVERIFY.pgc - cl 모듈 서비스 CL_MONVERIFY (one service = one file). + * 월 마감 검증: 월 마감이력과 월 매입 실집계 대사. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_MONVERIFY.h" + +void CL_MONVERIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long dc, da; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_lc, h_la, h_rc, h_ra; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT coalesce(sum(txn_count),0), coalesce(sum(total_amount),0) + INTO :h_lc, :h_la FROM cl_close_log + WHERE close_type = 'MONTHLY' + AND period_key = to_char(:h_bizdate::date, 'YYYY-MM') AND status <> 'X'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_rc, :h_ra + FROM purchase WHERE to_char(biz_date,'YYYY-MM') = to_char(:h_bizdate::date,'YYYY-MM'); + if (sqlca.sqlcode < 0) FAIL(b); + dc = h_lc - h_rc; if (dc < 0) dc = -dc; + da = h_la - h_ra; if (da < 0) da = -da; + setl(b, T_ARG1, h_lc); + setl(b, T_COUNT, h_rc); + setl(b, T_AMT1, h_la); + setl(b, T_AMT2, h_ra); + setl(b, T_DELTA, da); + setl(b, T_RC, (dc == 0 && da == 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_NETCROSS.h b/app/src/cl/svc/CL_NETCROSS.h new file mode 100644 index 0000000..432f539 --- /dev/null +++ b/app/src/cl/svc/CL_NETCROSS.h @@ -0,0 +1,22 @@ +/* + * CL_NETCROSS.h - cl 서비스 CL_NETCROSS 카피북 (copybook / record header). + * 정산액 크로스검증: 마감 정산액과 매입 net 합계 대사. + */ +#ifndef CL_NETCROSS_H +#define CL_NETCROSS_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_netcross_rec_t; + +void CL_NETCROSS(TPSVCINFO *p); + +#endif /* CL_NETCROSS_H */ diff --git a/app/src/cl/svc/CL_NETCROSS.pgc b/app/src/cl/svc/CL_NETCROSS.pgc new file mode 100644 index 0000000..e25ca54 --- /dev/null +++ b/app/src/cl/svc/CL_NETCROSS.pgc @@ -0,0 +1,34 @@ +/* + * CL_NETCROSS.pgc - cl 모듈 서비스 CL_NETCROSS (one service = one file). + * 정산액 크로스검증: 마감 스냅샷 net 합계와 해당 영업일 매입 net 합계를 대사한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_NETCROSS.h" + +void CL_NETCROSS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long delta; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16]; + long h_cid, h_sn, h_pn; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + EXEC SQL SELECT to_char(biz_date,'YYYY-MM-DD') INTO :h_date + FROM cl_close_log WHERE close_id = :h_cid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + EXEC SQL SELECT coalesce(sum(net_amount),0) INTO :h_sn + FROM cl_snapshot WHERE close_id = :h_cid; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_pn + FROM purchase WHERE biz_date = :h_date; + if (sqlca.sqlcode < 0) FAIL(b); + delta = h_sn - h_pn; if (delta < 0) delta = -delta; + setl(b, T_AMT1, h_sn); + setl(b, T_AMT2, h_pn); + setl(b, T_DELTA, delta); + setl(b, T_RC, (delta == 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_NEXTBIZ.h b/app/src/cl/svc/CL_NEXTBIZ.h new file mode 100644 index 0000000..0329672 --- /dev/null +++ b/app/src/cl/svc/CL_NEXTBIZ.h @@ -0,0 +1,22 @@ +/* + * CL_NEXTBIZ.h - cl 서비스 CL_NEXTBIZ 카피북 (copybook / record header). + * 익영업일 산출: 휴일 스킵하여 다음 영업일 산출. + */ +#ifndef CL_NEXTBIZ_H +#define CL_NEXTBIZ_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_nextbiz_rec_t; + +void CL_NEXTBIZ(TPSVCINFO *p); + +#endif /* CL_NEXTBIZ_H */ diff --git a/app/src/cl/svc/CL_NEXTBIZ.pgc b/app/src/cl/svc/CL_NEXTBIZ.pgc new file mode 100644 index 0000000..5f3af9a --- /dev/null +++ b/app/src/cl/svc/CL_NEXTBIZ.pgc @@ -0,0 +1,29 @@ +/* + * CL_NEXTBIZ.pgc - cl 모듈 서비스 CL_NEXTBIZ (one service = one file). + * 익영업일 산출: 기준일 포함 이후 주말을 건너뛴 첫 영업일과 건너뛴 일수를 반환. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_NEXTBIZ.h" + +void CL_NEXTBIZ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_next[16]; + long h_skip; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT to_char(d,'YYYY-MM-DD') INTO :h_next FROM ( + SELECT (:h_bizdate::date + s) AS d, + extract(dow from (:h_bizdate::date + s)) AS dw + FROM generate_series(0,7) s) t + WHERE dw NOT IN (0,6) ORDER BY d LIMIT 1; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + EXEC SQL SELECT (:h_next::date - :h_bizdate::date) INTO :h_skip; + if (sqlca.sqlcode < 0) FAIL(b); + Bchg(b, T_KEY1, 0, h_next, 0L); + setl(b, T_ARG1, h_skip); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_POSCLOSE.h b/app/src/cl/svc/CL_POSCLOSE.h new file mode 100644 index 0000000..3e2e1d4 --- /dev/null +++ b/app/src/cl/svc/CL_POSCLOSE.h @@ -0,0 +1,22 @@ +/* + * CL_POSCLOSE.h - cl 서비스 CL_POSCLOSE 카피북 (copybook / record header). + * POS 채널 마감: POS 채널 당일 매입 집계 후 POS 마감이력. + */ +#ifndef CL_POSCLOSE_H +#define CL_POSCLOSE_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_posclose_rec_t; + +void CL_POSCLOSE(TPSVCINFO *p); + +#endif /* CL_POSCLOSE_H */ diff --git a/app/src/cl/svc/CL_POSCLOSE.pgc b/app/src/cl/svc/CL_POSCLOSE.pgc new file mode 100644 index 0000000..3c31e3d --- /dev/null +++ b/app/src/cl/svc/CL_POSCLOSE.pgc @@ -0,0 +1,32 @@ +/* + * CL_POSCLOSE.pgc - cl 모듈 서비스 CL_POSCLOSE (one service = one file). + * POS 채널 마감: POS 채널 당일 매입 집계 후 POS 마감이력. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_POSCLOSE.h" + +void CL_POSCLOSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long cid; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_amt, h_fee, h_net; + EXEC SQL END DECLARE SECTION; + 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) + INTO :h_cnt, :h_amt, :h_fee, :h_net + FROM purchase WHERE biz_date = :h_bizdate AND channel = 'POS'; + if (sqlca.sqlcode < 0) FAIL(b); + cid = cldb_next_close_id(); + if (cid < 0) FAIL(b); + if (cldb_insert_close(cid, "POS", h_bizdate, h_bizdate, "C", h_cnt, h_amt, "POS채널마감") < 0) FAIL(b); + setl(b, T_ID1, cid); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_amt); + setl(b, T_FEE, h_fee); + setl(b, T_NET, h_net); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_PROVCMP.h b/app/src/cl/svc/CL_PROVCMP.h new file mode 100644 index 0000000..5413dea --- /dev/null +++ b/app/src/cl/svc/CL_PROVCMP.h @@ -0,0 +1,22 @@ +/* + * CL_PROVCMP.h - cl 서비스 CL_PROVCMP 카피북 (copybook / record header). + * 가마감/본마감 비교: 잠정 집계와 확정 집계 차이 산출. + */ +#ifndef CL_PROVCMP_H +#define CL_PROVCMP_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_provcmp_rec_t; + +void CL_PROVCMP(TPSVCINFO *p); + +#endif /* CL_PROVCMP_H */ diff --git a/app/src/cl/svc/CL_PROVCMP.pgc b/app/src/cl/svc/CL_PROVCMP.pgc new file mode 100644 index 0000000..e3abb37 --- /dev/null +++ b/app/src/cl/svc/CL_PROVCMP.pgc @@ -0,0 +1,31 @@ +/* + * CL_PROVCMP.pgc - cl 모듈 서비스 CL_PROVCMP (one service = one file). + * 가마감/본마감 비교: 동일 영업일의 가마감(TENT) 잠정 집계와 확정(DAILY C) 차이 산출. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_PROVCMP.h" + +void CL_PROVCMP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long delta; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_prov, h_main; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT coalesce(sum(total_amount),0) INTO :h_prov FROM cl_close_log + WHERE close_type = 'TENT' AND period_key = :h_bizdate AND status <> 'X'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(total_amount),0) INTO :h_main FROM cl_close_log + WHERE close_type = 'DAILY' AND period_key = :h_bizdate AND status = 'C'; + if (sqlca.sqlcode < 0) FAIL(b); + delta = h_main - h_prov; if (delta < 0) delta = -delta; + setl(b, T_AMT1, h_prov); + setl(b, T_AMT2, h_main); + setl(b, T_DELTA, delta); + setl(b, T_RC, (delta == 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_QTRVERIFY.h b/app/src/cl/svc/CL_QTRVERIFY.h new file mode 100644 index 0000000..7c4a0e1 --- /dev/null +++ b/app/src/cl/svc/CL_QTRVERIFY.h @@ -0,0 +1,22 @@ +/* + * CL_QTRVERIFY.h - cl 서비스 CL_QTRVERIFY 카피북 (copybook / record header). + * 분기 마감 검증: 분기 마감이력과 분기 매입 실집계 대사. + */ +#ifndef CL_QTRVERIFY_H +#define CL_QTRVERIFY_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_qtrverify_rec_t; + +void CL_QTRVERIFY(TPSVCINFO *p); + +#endif /* CL_QTRVERIFY_H */ diff --git a/app/src/cl/svc/CL_QTRVERIFY.pgc b/app/src/cl/svc/CL_QTRVERIFY.pgc new file mode 100644 index 0000000..a7d9ef1 --- /dev/null +++ b/app/src/cl/svc/CL_QTRVERIFY.pgc @@ -0,0 +1,32 @@ +/* + * CL_QTRVERIFY.pgc - cl 모듈 서비스 CL_QTRVERIFY (one service = one file). + * 분기 마감 검증: 분기 마감이력과 분기 매입 실집계 대사. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_QTRVERIFY.h" + +void CL_QTRVERIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long da; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_la, h_ra; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT coalesce(sum(total_amount),0) INTO :h_la FROM cl_close_log + WHERE close_type = 'QUARTER' + AND period_key = to_char(:h_bizdate::date, 'YYYY"Q"Q') AND status <> 'X'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_ra FROM purchase + WHERE to_char(biz_date,'YYYY"Q"Q') = to_char(:h_bizdate::date,'YYYY"Q"Q'); + if (sqlca.sqlcode < 0) FAIL(b); + da = h_la - h_ra; if (da < 0) da = -da; + setl(b, T_AMT1, h_la); + setl(b, T_AMT2, h_ra); + setl(b, T_DELTA, da); + setl(b, T_RC, (da == 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_RECLOSECHK.h b/app/src/cl/svc/CL_RECLOSECHK.h new file mode 100644 index 0000000..84119d7 --- /dev/null +++ b/app/src/cl/svc/CL_RECLOSECHK.h @@ -0,0 +1,22 @@ +/* + * CL_RECLOSECHK.h - cl 서비스 CL_RECLOSECHK 카피북 (copybook / record header). + * 재마감 가능여부: 승인/잠금 상태면 재마감 불가 판정. + */ +#ifndef CL_RECLOSECHK_H +#define CL_RECLOSECHK_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_reclosechk_rec_t; + +void CL_RECLOSECHK(TPSVCINFO *p); + +#endif /* CL_RECLOSECHK_H */ diff --git a/app/src/cl/svc/CL_RECLOSECHK.pgc b/app/src/cl/svc/CL_RECLOSECHK.pgc new file mode 100644 index 0000000..7c4cda1 --- /dev/null +++ b/app/src/cl/svc/CL_RECLOSECHK.pgc @@ -0,0 +1,22 @@ +/* + * CL_RECLOSECHK.pgc - cl 모듈 서비스 CL_RECLOSECHK (one service = one file). + * 재마감 가능여부: 승인(A)/잠금(L) 상태면 재마감 불가로 판정한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_RECLOSECHK.h" + +void CL_RECLOSECHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char st[2]; + int allowed; + long cid = getl(b, T_ID1); + if (cldb_get_close_status(cid, st) < 0) FAIL(b); + allowed = (st[0] == 'C' || st[0] == 'R') ? 1 : 0; + setl(b, T_RC, allowed ? 0 : 1); + Bchg(b, T_STATUS, 0, st, 0L); + Bchg(b, T_MSG, 0, allowed ? "재마감 가능" : "승인/잠금-재마감 불가", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_RECLOSEREG.h b/app/src/cl/svc/CL_RECLOSEREG.h new file mode 100644 index 0000000..9db43ca --- /dev/null +++ b/app/src/cl/svc/CL_RECLOSEREG.h @@ -0,0 +1,22 @@ +/* + * CL_RECLOSEREG.h - cl 서비스 CL_RECLOSEREG 카피북 (copybook / record header). + * 재마감 등록: 기존 마감을 재마감(R) 표기하고 신규 이력 개시. + */ +#ifndef CL_RECLOSEREG_H +#define CL_RECLOSEREG_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_reclosereg_rec_t; + +void CL_RECLOSEREG(TPSVCINFO *p); + +#endif /* CL_RECLOSEREG_H */ diff --git a/app/src/cl/svc/CL_RECLOSEREG.pgc b/app/src/cl/svc/CL_RECLOSEREG.pgc new file mode 100644 index 0000000..285a81e --- /dev/null +++ b/app/src/cl/svc/CL_RECLOSEREG.pgc @@ -0,0 +1,34 @@ +/* + * CL_RECLOSEREG.pgc - cl 모듈 서비스 CL_RECLOSEREG (one service = one file). + * 재마감 등록: 기존 마감을 재마감(R) 표기하고 동일 기간에 신규 마감이력 개시. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_RECLOSEREG.h" + +void CL_RECLOSEREG(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long newid; + EXEC SQL BEGIN DECLARE SECTION; + char h_ct[16], h_pk[24], h_date[16]; + long h_old, h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + h_old = getl(b, T_ID1); + EXEC SQL SELECT close_type, period_key, to_char(biz_date,'YYYY-MM-DD') + INTO :h_ct, :h_pk, :h_date FROM cl_close_log WHERE close_id = :h_old; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + if (cldb_update_close_status(h_old, "R") < 0) FAIL(b); + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_cnt, :h_amt + FROM purchase WHERE biz_date = :h_date; + if (sqlca.sqlcode < 0) FAIL(b); + newid = cldb_next_close_id(); + if (newid < 0) FAIL(b); + if (cldb_insert_close(newid, h_ct, h_pk, h_date, "C", h_cnt, h_amt, "재마감") < 0) FAIL(b); + setl(b, T_ID1, newid); + setl(b, T_ID2, h_old); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_amt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_RPTAPPR.h b/app/src/cl/svc/CL_RPTAPPR.h new file mode 100644 index 0000000..bcb3df5 --- /dev/null +++ b/app/src/cl/svc/CL_RPTAPPR.h @@ -0,0 +1,22 @@ +/* + * CL_RPTAPPR.h - cl 서비스 CL_RPTAPPR 카피북 (copybook / record header). + * 승인 마감 리포트: 승인(A) 마감 이력 목록 리포트. + */ +#ifndef CL_RPTAPPR_H +#define CL_RPTAPPR_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_rptappr_rec_t; + +void CL_RPTAPPR(TPSVCINFO *p); + +#endif /* CL_RPTAPPR_H */ diff --git a/app/src/cl/svc/CL_RPTAPPR.pgc b/app/src/cl/svc/CL_RPTAPPR.pgc new file mode 100644 index 0000000..efe14cb --- /dev/null +++ b/app/src/cl/svc/CL_RPTAPPR.pgc @@ -0,0 +1,38 @@ +/* + * CL_RPTAPPR.pgc - cl 모듈 서비스 CL_RPTAPPR (one service = one file). + * 승인 마감 리포트: 승인(A) 상태 마감이력을 최신순으로 나열한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_RPTAPPR.h" + +void CL_RPTAPPR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long occ = 0, totamt = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_pk[24]; + long h_id, h_amt; + EXEC SQL END DECLARE SECTION; + EXEC SQL DECLARE crappr CURSOR FOR + SELECT close_id, period_key, total_amount + FROM cl_close_log WHERE status = 'A' ORDER BY close_id DESC; + EXEC SQL OPEN crappr; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH crappr INTO :h_id, :h_pk, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE crappr; FAIL(b); } + if (occ >= 30) break; + Bchg(b, T_ARG1, (BFLDOCC)occ, (char *)&h_id, 0L); + Bchg(b, T_KEY1, (BFLDOCC)occ, h_pk, 0L); + Bchg(b, T_AMT1, (BFLDOCC)occ, (char *)&h_amt, 0L); + totamt += h_amt; + occ++; + } + EXEC SQL CLOSE crappr; + setl(b, T_COUNT, occ); + setl(b, T_GROSS, totamt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_RPTCXL.h b/app/src/cl/svc/CL_RPTCXL.h new file mode 100644 index 0000000..56b804b --- /dev/null +++ b/app/src/cl/svc/CL_RPTCXL.h @@ -0,0 +1,22 @@ +/* + * CL_RPTCXL.h - cl 서비스 CL_RPTCXL 카피북 (copybook / record header). + * 취소 마감 리포트: 취소(X) 마감 이력 목록 리포트. + */ +#ifndef CL_RPTCXL_H +#define CL_RPTCXL_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_rptcxl_rec_t; + +void CL_RPTCXL(TPSVCINFO *p); + +#endif /* CL_RPTCXL_H */ diff --git a/app/src/cl/svc/CL_RPTCXL.pgc b/app/src/cl/svc/CL_RPTCXL.pgc new file mode 100644 index 0000000..4130662 --- /dev/null +++ b/app/src/cl/svc/CL_RPTCXL.pgc @@ -0,0 +1,36 @@ +/* + * CL_RPTCXL.pgc - cl 모듈 서비스 CL_RPTCXL (one service = one file). + * 취소 마감 리포트: 취소(X) 상태 마감이력을 취소 사유(memo)와 함께 나열한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_RPTCXL.h" + +void CL_RPTCXL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long occ = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_memo[64]; + long h_id, h_amt; + EXEC SQL END DECLARE SECTION; + EXEC SQL DECLARE crcxl CURSOR FOR + SELECT close_id, memo, total_amount + FROM cl_close_log WHERE status = 'X' ORDER BY close_id DESC; + EXEC SQL OPEN crcxl; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH crcxl INTO :h_id, :h_memo, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE crcxl; FAIL(b); } + if (occ >= 30) break; + Bchg(b, T_ARG1, (BFLDOCC)occ, (char *)&h_id, 0L); + Bchg(b, T_STR1, (BFLDOCC)occ, h_memo, 0L); + Bchg(b, T_AMT1, (BFLDOCC)occ, (char *)&h_amt, 0L); + occ++; + } + EXEC SQL CLOSE crcxl; + setl(b, T_COUNT, occ); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_RPTDELAY.h b/app/src/cl/svc/CL_RPTDELAY.h new file mode 100644 index 0000000..8dc6267 --- /dev/null +++ b/app/src/cl/svc/CL_RPTDELAY.h @@ -0,0 +1,22 @@ +/* + * CL_RPTDELAY.h - cl 서비스 CL_RPTDELAY 카피북 (copybook / record header). + * 지연 마감 리포트: 예정일 경과 미마감 매입 리포트. + */ +#ifndef CL_RPTDELAY_H +#define CL_RPTDELAY_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_rptdelay_rec_t; + +void CL_RPTDELAY(TPSVCINFO *p); + +#endif /* CL_RPTDELAY_H */ diff --git a/app/src/cl/svc/CL_RPTDELAY.pgc b/app/src/cl/svc/CL_RPTDELAY.pgc new file mode 100644 index 0000000..352b334 --- /dev/null +++ b/app/src/cl/svc/CL_RPTDELAY.pgc @@ -0,0 +1,40 @@ +/* + * CL_RPTDELAY.pgc - cl 모듈 서비스 CL_RPTDELAY (one service = one file). + * 지연 마감 리포트: 기준일 이전인데 아직 대사완료(M) 상태로 남은 미마감 매입을 + * 영업일별로 집계한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_RPTDELAY.h" + +void CL_RPTDELAY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long occ = 0, totcnt = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_day[16]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL DECLARE crdly CURSOR FOR + SELECT to_char(biz_date,'YYYY-MM-DD'), count(*), coalesce(sum(amount),0) + FROM purchase WHERE status = 'M' AND biz_date < :h_bizdate::date + GROUP BY biz_date ORDER BY biz_date; + EXEC SQL OPEN crdly; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH crdly INTO :h_day, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE crdly; FAIL(b); } + Bchg(b, T_KEY1, (BFLDOCC)occ, h_day, 0L); + Bchg(b, T_ARG1, (BFLDOCC)occ, (char *)&h_cnt, 0L); + Bchg(b, T_AMT1, (BFLDOCC)occ, (char *)&h_amt, 0L); + totcnt += h_cnt; + occ++; + } + EXEC SQL CLOSE crdly; + setl(b, T_COUNT, occ); + setl(b, T_ARG2, totcnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_RPTDIV.h b/app/src/cl/svc/CL_RPTDIV.h new file mode 100644 index 0000000..73703d0 --- /dev/null +++ b/app/src/cl/svc/CL_RPTDIV.h @@ -0,0 +1,22 @@ +/* + * CL_RPTDIV.h - cl 서비스 CL_RPTDIV 카피북 (copybook / record header). + * 부문별 마감 리포트: 업종 부문별 건수/금액 occurrence 리포트. + */ +#ifndef CL_RPTDIV_H +#define CL_RPTDIV_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_rptdiv_rec_t; + +void CL_RPTDIV(TPSVCINFO *p); + +#endif /* CL_RPTDIV_H */ diff --git a/app/src/cl/svc/CL_RPTDIV.pgc b/app/src/cl/svc/CL_RPTDIV.pgc new file mode 100644 index 0000000..a6222ad --- /dev/null +++ b/app/src/cl/svc/CL_RPTDIV.pgc @@ -0,0 +1,40 @@ +/* + * CL_RPTDIV.pgc - cl 모듈 서비스 CL_RPTDIV (one service = one file). + * 부문별 마감 리포트: 영업일 매입을 업종(category) 부문으로 집계해 + * occurrence(T_CATEGORY/T_ARG1=건수/T_AMT1=금액) 리포트로 반환한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_RPTDIV.h" + +void CL_RPTDIV(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long occ = 0, totamt = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_cat[16]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL DECLARE crdiv CURSOR FOR + SELECT category, count(*), coalesce(sum(amount),0) + FROM purchase WHERE biz_date = :h_bizdate + GROUP BY category ORDER BY 3 DESC; + EXEC SQL OPEN crdiv; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH crdiv INTO :h_cat, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE crdiv; FAIL(b); } + Bchg(b, T_CATEGORY, (BFLDOCC)occ, h_cat, 0L); + Bchg(b, T_ARG1, (BFLDOCC)occ, (char *)&h_cnt, 0L); + Bchg(b, T_AMT1, (BFLDOCC)occ, (char *)&h_amt, 0L); + totamt += h_amt; + occ++; + } + EXEC SQL CLOSE crdiv; + setl(b, T_COUNT, occ); + setl(b, T_GROSS, totamt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_RPTPERIOD.h b/app/src/cl/svc/CL_RPTPERIOD.h new file mode 100644 index 0000000..0f3be91 --- /dev/null +++ b/app/src/cl/svc/CL_RPTPERIOD.h @@ -0,0 +1,22 @@ +/* + * CL_RPTPERIOD.h - cl 서비스 CL_RPTPERIOD 카피북 (copybook / record header). + * 기간별 마감 리포트: 최근 마감이력 기간별 집계 리포트. + */ +#ifndef CL_RPTPERIOD_H +#define CL_RPTPERIOD_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_rptperiod_rec_t; + +void CL_RPTPERIOD(TPSVCINFO *p); + +#endif /* CL_RPTPERIOD_H */ diff --git a/app/src/cl/svc/CL_RPTPERIOD.pgc b/app/src/cl/svc/CL_RPTPERIOD.pgc new file mode 100644 index 0000000..b8ceb9c --- /dev/null +++ b/app/src/cl/svc/CL_RPTPERIOD.pgc @@ -0,0 +1,41 @@ +/* + * CL_RPTPERIOD.pgc - cl 모듈 서비스 CL_RPTPERIOD (one service = one file). + * 기간별 마감 리포트: close_type(T_KEY1) 의 최근 마감이력을 기간별로 나열한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_RPTPERIOD.h" + +void CL_RPTPERIOD(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long occ = 0, totamt = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_ct[16], h_pk[24]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_KEY1, h_ct, sizeof(h_ct)); + if (h_ct[0] == 0) strcpy(h_ct, "DAILY"); + EXEC SQL DECLARE crper CURSOR FOR + SELECT period_key, txn_count, total_amount + FROM cl_close_log WHERE close_type = :h_ct AND status <> 'X' + ORDER BY period_key DESC; + EXEC SQL OPEN crper; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH crper INTO :h_pk, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE crper; FAIL(b); } + if (occ >= 20) break; + Bchg(b, T_KEY1, (BFLDOCC)occ, h_pk, 0L); + Bchg(b, T_ARG1, (BFLDOCC)occ, (char *)&h_cnt, 0L); + Bchg(b, T_AMT1, (BFLDOCC)occ, (char *)&h_amt, 0L); + totamt += h_amt; + occ++; + } + EXEC SQL CLOSE crper; + setl(b, T_COUNT, occ); + setl(b, T_GROSS, totamt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_RPTSTATUS.h b/app/src/cl/svc/CL_RPTSTATUS.h new file mode 100644 index 0000000..9250261 --- /dev/null +++ b/app/src/cl/svc/CL_RPTSTATUS.h @@ -0,0 +1,22 @@ +/* + * CL_RPTSTATUS.h - cl 서비스 CL_RPTSTATUS 카피북 (copybook / record header). + * 상태별 마감 리포트: 마감이력 상태별 건수 리포트. + */ +#ifndef CL_RPTSTATUS_H +#define CL_RPTSTATUS_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_rptstatus_rec_t; + +void CL_RPTSTATUS(TPSVCINFO *p); + +#endif /* CL_RPTSTATUS_H */ diff --git a/app/src/cl/svc/CL_RPTSTATUS.pgc b/app/src/cl/svc/CL_RPTSTATUS.pgc new file mode 100644 index 0000000..7cf1d24 --- /dev/null +++ b/app/src/cl/svc/CL_RPTSTATUS.pgc @@ -0,0 +1,35 @@ +/* + * CL_RPTSTATUS.pgc - cl 모듈 서비스 CL_RPTSTATUS (one service = one file). + * 상태별 마감 리포트: 마감이력을 상태(O/C/A/L/X/R)별 건수/총액으로 집계한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_RPTSTATUS.h" + +void CL_RPTSTATUS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long occ = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_st[4]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + EXEC SQL DECLARE crsta CURSOR FOR + SELECT status, count(*), coalesce(sum(total_amount),0) + FROM cl_close_log GROUP BY status ORDER BY 1; + EXEC SQL OPEN crsta; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH crsta INTO :h_st, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE crsta; FAIL(b); } + Bchg(b, T_STATUS, (BFLDOCC)occ, h_st, 0L); + Bchg(b, T_ARG1, (BFLDOCC)occ, (char *)&h_cnt, 0L); + Bchg(b, T_AMT1, (BFLDOCC)occ, (char *)&h_amt, 0L); + occ++; + } + EXEC SQL CLOSE crsta; + setl(b, T_COUNT, occ); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_SCHEDNEXT.h b/app/src/cl/svc/CL_SCHEDNEXT.h new file mode 100644 index 0000000..43ed5ae --- /dev/null +++ b/app/src/cl/svc/CL_SCHEDNEXT.h @@ -0,0 +1,22 @@ +/* + * CL_SCHEDNEXT.h - cl 서비스 CL_SCHEDNEXT 카피북 (copybook / record header). + * 다음 마감 일정: 기준일 기준 익영업일 마감 예정일 산출. + */ +#ifndef CL_SCHEDNEXT_H +#define CL_SCHEDNEXT_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_schednext_rec_t; + +void CL_SCHEDNEXT(TPSVCINFO *p); + +#endif /* CL_SCHEDNEXT_H */ diff --git a/app/src/cl/svc/CL_SCHEDNEXT.pgc b/app/src/cl/svc/CL_SCHEDNEXT.pgc new file mode 100644 index 0000000..ef28334 --- /dev/null +++ b/app/src/cl/svc/CL_SCHEDNEXT.pgc @@ -0,0 +1,26 @@ +/* + * CL_SCHEDNEXT.pgc - cl 모듈 서비스 CL_SCHEDNEXT (one service = one file). + * 다음 마감 일정: 기준일 다음날부터 주말(토/일)을 건너뛴 첫 영업일을 마감 예정일로 산출. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_SCHEDNEXT.h" + +void CL_SCHEDNEXT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_next[16]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT to_char(d,'YYYY-MM-DD') INTO :h_next FROM ( + SELECT (:h_bizdate::date + s) AS d, + extract(dow from (:h_bizdate::date + s)) AS dw + FROM generate_series(1,7) s) t + WHERE dw NOT IN (0,6) ORDER BY d LIMIT 1; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + Bchg(b, T_KEY1, 0, h_next, 0L); + Bchg(b, T_MSG, 0, "다음 마감 예정 영업일", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_SEGCLOSE.h b/app/src/cl/svc/CL_SEGCLOSE.h new file mode 100644 index 0000000..3102a26 --- /dev/null +++ b/app/src/cl/svc/CL_SEGCLOSE.h @@ -0,0 +1,22 @@ +/* + * CL_SEGCLOSE.h - cl 서비스 CL_SEGCLOSE 카피북 (copybook / record header). + * 부문별 마감: 업종(category) 부문 단위 매입 집계 후 SEG 마감. + */ +#ifndef CL_SEGCLOSE_H +#define CL_SEGCLOSE_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_segclose_rec_t; + +void CL_SEGCLOSE(TPSVCINFO *p); + +#endif /* CL_SEGCLOSE_H */ diff --git a/app/src/cl/svc/CL_SEGCLOSE.pgc b/app/src/cl/svc/CL_SEGCLOSE.pgc new file mode 100644 index 0000000..51c00ef --- /dev/null +++ b/app/src/cl/svc/CL_SEGCLOSE.pgc @@ -0,0 +1,35 @@ +/* + * CL_SEGCLOSE.pgc - cl 모듈 서비스 CL_SEGCLOSE (one service = one file). + * 부문별 마감: 업종(category) 부문 단위 매입 집계 후 SEG 마감. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_SEGCLOSE.h" + +void CL_SEGCLOSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char memo[48]; + long cid; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_cat[16]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + gets_(b, T_CATEGORY, h_cat, sizeof(h_cat)); + if (h_cat[0] == 0) strcpy(h_cat, "GEN"); + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_cnt, :h_amt + FROM purchase WHERE biz_date = :h_bizdate AND category = :h_cat; + if (sqlca.sqlcode < 0) FAIL(b); + cid = cldb_next_close_id(); + if (cid < 0) FAIL(b); + snprintf(memo, sizeof(memo), "부문마감:%s", h_cat); + if (cldb_insert_close(cid, "SEG", h_bizdate, h_bizdate, "C", h_cnt, h_amt, memo) < 0) FAIL(b); + setl(b, T_ID1, cid); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_amt); + Bchg(b, T_CATEGORY, 0, h_cat, 0L); + userlog("CL_SEGCLOSE cid=%ld cat=%s cnt=%ld 부문마감", cid, h_cat, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_SEGVERIFY.h b/app/src/cl/svc/CL_SEGVERIFY.h new file mode 100644 index 0000000..3aa9932 --- /dev/null +++ b/app/src/cl/svc/CL_SEGVERIFY.h @@ -0,0 +1,22 @@ +/* + * CL_SEGVERIFY.h - cl 서비스 CL_SEGVERIFY 카피북 (copybook / record header). + * 부문별 마감 검증: 부문 마감 스냅샷과 매입 집계 대사. + */ +#ifndef CL_SEGVERIFY_H +#define CL_SEGVERIFY_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_segverify_rec_t; + +void CL_SEGVERIFY(TPSVCINFO *p); + +#endif /* CL_SEGVERIFY_H */ diff --git a/app/src/cl/svc/CL_SEGVERIFY.pgc b/app/src/cl/svc/CL_SEGVERIFY.pgc new file mode 100644 index 0000000..b0da0b3 --- /dev/null +++ b/app/src/cl/svc/CL_SEGVERIFY.pgc @@ -0,0 +1,33 @@ +/* + * CL_SEGVERIFY.pgc - cl 모듈 서비스 CL_SEGVERIFY (one service = one file). + * 부문별 마감 검증: 부문 마감이력 총액과 해당 부문 매입 집계 대사. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_SEGVERIFY.h" + +void CL_SEGVERIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long delta; + EXEC SQL BEGIN DECLARE SECTION; + char h_cat[16], h_date[16]; + long h_cid, h_la, h_ra; + EXEC SQL END DECLARE SECTION; + h_cid = getl(b, T_ID1); + gets_(b, T_CATEGORY, h_cat, sizeof(h_cat)); + if (h_cat[0] == 0) strcpy(h_cat, "GEN"); + EXEC SQL SELECT total_amount, to_char(biz_date,'YYYY-MM-DD') INTO :h_la, :h_date + FROM cl_close_log WHERE close_id = :h_cid AND status <> 'X'; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_ra + FROM purchase WHERE biz_date = :h_date AND category = :h_cat; + if (sqlca.sqlcode < 0) FAIL(b); + delta = h_la - h_ra; if (delta < 0) delta = -delta; + setl(b, T_AMT1, h_la); + setl(b, T_AMT2, h_ra); + setl(b, T_DELTA, delta); + setl(b, T_RC, (delta == 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_SEMIMONTH.h b/app/src/cl/svc/CL_SEMIMONTH.h new file mode 100644 index 0000000..bff1461 --- /dev/null +++ b/app/src/cl/svc/CL_SEMIMONTH.h @@ -0,0 +1,22 @@ +/* + * CL_SEMIMONTH.h - cl 서비스 CL_SEMIMONTH 카피북 (copybook / record header). + * 반월 마감: 전반(1~15)/후반(16~말) 구간 집계 후 SEMIMON 마감. + */ +#ifndef CL_SEMIMONTH_H +#define CL_SEMIMONTH_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_semimonth_rec_t; + +void CL_SEMIMONTH(TPSVCINFO *p); + +#endif /* CL_SEMIMONTH_H */ diff --git a/app/src/cl/svc/CL_SEMIMONTH.pgc b/app/src/cl/svc/CL_SEMIMONTH.pgc new file mode 100644 index 0000000..0a25b2e --- /dev/null +++ b/app/src/cl/svc/CL_SEMIMONTH.pgc @@ -0,0 +1,40 @@ +/* + * CL_SEMIMONTH.pgc - cl 모듈 서비스 CL_SEMIMONTH (one service = one file). + * 반월 마감: 전반(1~15)/후반(16~말) 구간 집계 후 SEMIMON 마감. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_SEMIMONTH.h" + +void CL_SEMIMONTH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long cid, half; + char period[24], memo[32]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_ym[16]; + long h_day, h_lo, h_hi, h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT extract(day from :h_bizdate::date)::int, + to_char(:h_bizdate::date, 'YYYY-MM') INTO :h_day, :h_ym; + if (sqlca.sqlcode < 0) FAIL(b); + half = (h_day <= 15) ? 1 : 2; + h_lo = (half == 1) ? 1 : 16; + h_hi = (half == 1) ? 15 : 31; + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_cnt, :h_amt + FROM purchase WHERE to_char(biz_date,'YYYY-MM') = :h_ym + AND extract(day from biz_date) BETWEEN :h_lo AND :h_hi; + if (sqlca.sqlcode < 0) FAIL(b); + snprintf(period, sizeof(period), "%s-H%ld", h_ym, half); + snprintf(memo, sizeof(memo), "반월마감:%s", (half == 1) ? "전반" : "후반"); + cid = cldb_next_close_id(); + if (cid < 0) FAIL(b); + if (cldb_insert_close(cid, "SEMIMON", period, h_bizdate, "C", h_cnt, h_amt, memo) < 0) FAIL(b); + setl(b, T_ID1, cid); + setl(b, T_ARG1, half); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_amt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_SLABREACH.h b/app/src/cl/svc/CL_SLABREACH.h new file mode 100644 index 0000000..57dcc97 --- /dev/null +++ b/app/src/cl/svc/CL_SLABREACH.h @@ -0,0 +1,22 @@ +/* + * CL_SLABREACH.h - cl 서비스 CL_SLABREACH 카피북 (copybook / record header). + * 마감 SLA 위반: 목표시간 경과 미마감 마감 건 판정. + */ +#ifndef CL_SLABREACH_H +#define CL_SLABREACH_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_slabreach_rec_t; + +void CL_SLABREACH(TPSVCINFO *p); + +#endif /* CL_SLABREACH_H */ diff --git a/app/src/cl/svc/CL_SLABREACH.pgc b/app/src/cl/svc/CL_SLABREACH.pgc new file mode 100644 index 0000000..f7e86dc --- /dev/null +++ b/app/src/cl/svc/CL_SLABREACH.pgc @@ -0,0 +1,25 @@ +/* + * CL_SLABREACH.pgc - cl 모듈 서비스 CL_SLABREACH (one service = one file). + * 마감 SLA 위반: 기준일 이전인데 아직 개시(O)에 머무는 미완결 마감 건을 위반으로 판정. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_SLABREACH.h" + +void CL_SLABREACH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_n; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT count(*) INTO :h_n FROM cl_close_log + WHERE status = 'O' AND biz_date < :h_bizdate::date; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_n); + setl(b, T_RC, (h_n > 0) ? 1 : 0); + Bchg(b, T_MSG, 0, (h_n > 0) ? "SLA 위반 미완결 마감 존재" : "SLA 위반 없음", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_SLASET.h b/app/src/cl/svc/CL_SLASET.h new file mode 100644 index 0000000..5b1709d --- /dev/null +++ b/app/src/cl/svc/CL_SLASET.h @@ -0,0 +1,22 @@ +/* + * CL_SLASET.h - cl 서비스 CL_SLASET 카피북 (copybook / record header). + * 마감 SLA 등록: close_type별 마감 SLA 목표(seq_reg) 기록. + */ +#ifndef CL_SLASET_H +#define CL_SLASET_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_slaset_rec_t; + +void CL_SLASET(TPSVCINFO *p); + +#endif /* CL_SLASET_H */ diff --git a/app/src/cl/svc/CL_SLASET.pgc b/app/src/cl/svc/CL_SLASET.pgc new file mode 100644 index 0000000..1029af5 --- /dev/null +++ b/app/src/cl/svc/CL_SLASET.pgc @@ -0,0 +1,29 @@ +/* + * CL_SLASET.pgc - cl 모듈 서비스 CL_SLASET (one service = one file). + * 마감 SLA 등록: close_type(T_KEY1)별 마감 목표시간(T_ARG1, 시간)을 seq_reg 에 upsert. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_SLASET.h" + +void CL_SLASET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_ct[16]; + long h_hours; + EXEC SQL END DECLARE SECTION; + gets_(b, T_KEY1, h_ct, sizeof(h_ct)); + if (h_ct[0] == 0) strcpy(h_ct, "DAILY"); + h_hours = getl(b, T_ARG1); + if (h_hours <= 0) h_hours = 2; + EXEC SQL INSERT INTO cl_seq_reg (close_type, last_seq, updated_at) + VALUES (:h_ct, :h_hours, now()) + ON CONFLICT (close_type) DO UPDATE SET last_seq = :h_hours, updated_at = now(); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_hours); + Bchg(b, T_KEY1, 0, h_ct, 0L); + Bchg(b, T_MSG, 0, "마감 SLA 목표 등록", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_SNAPMER.h b/app/src/cl/svc/CL_SNAPMER.h new file mode 100644 index 0000000..5d19baf --- /dev/null +++ b/app/src/cl/svc/CL_SNAPMER.h @@ -0,0 +1,22 @@ +/* + * CL_SNAPMER.h - cl 서비스 CL_SNAPMER 카피북 (copybook / record header). + * 스냅샷 가맹점 비교: 두 마감 스냅샷의 가맹점별 gross 차이 상위 산출. + */ +#ifndef CL_SNAPMER_H +#define CL_SNAPMER_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_snapmer_rec_t; + +void CL_SNAPMER(TPSVCINFO *p); + +#endif /* CL_SNAPMER_H */ diff --git a/app/src/cl/svc/CL_SNAPMER.pgc b/app/src/cl/svc/CL_SNAPMER.pgc new file mode 100644 index 0000000..be67355 --- /dev/null +++ b/app/src/cl/svc/CL_SNAPMER.pgc @@ -0,0 +1,33 @@ +/* + * CL_SNAPMER.pgc - cl 모듈 서비스 CL_SNAPMER (one service = one file). + * 스냅샷 가맹점 비교: 두 마감(T_ID1,T_ID2) 스냅샷을 가맹점별 gross 로 FULL JOIN 해 + * 대상 가맹점 수와 최대 절대 편차를 산출한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_SNAPMER.h" + +void CL_SNAPMER(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_a, h_b, h_cnt, h_worst; + EXEC SQL END DECLARE SECTION; + h_a = getl(b, T_ID1); + h_b = getl(b, T_ID2); + if (h_a == h_b) { Bchg(b, T_MSG, 0, "서로 다른 두 마감 필요", 0L); FAIL(b); } + EXEC SQL SELECT count(*), coalesce(max(abs(d)),0) INTO :h_cnt, :h_worst FROM ( + SELECT coalesce(sa.g,0) - coalesce(sb.g,0) AS d FROM + (SELECT merchant_id, sum(gross_amount) AS g FROM cl_snapshot + WHERE close_id = :h_a GROUP BY merchant_id) sa + FULL JOIN + (SELECT merchant_id, sum(gross_amount) AS g FROM cl_snapshot + WHERE close_id = :h_b GROUP BY merchant_id) sb + ON sa.merchant_id = sb.merchant_id) z; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + setl(b, T_DELTA, h_worst); + setl(b, T_RC, (h_worst == 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_STCHGAUDIT.h b/app/src/cl/svc/CL_STCHGAUDIT.h new file mode 100644 index 0000000..2c0e644 --- /dev/null +++ b/app/src/cl/svc/CL_STCHGAUDIT.h @@ -0,0 +1,22 @@ +/* + * CL_STCHGAUDIT.h - cl 서비스 CL_STCHGAUDIT 카피북 (copybook / record header). + * 마감 상태변경 감사: 마감 상태 전이 이력 감사 요약. + */ +#ifndef CL_STCHGAUDIT_H +#define CL_STCHGAUDIT_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_stchgaudit_rec_t; + +void CL_STCHGAUDIT(TPSVCINFO *p); + +#endif /* CL_STCHGAUDIT_H */ diff --git a/app/src/cl/svc/CL_STCHGAUDIT.pgc b/app/src/cl/svc/CL_STCHGAUDIT.pgc new file mode 100644 index 0000000..4ba9440 --- /dev/null +++ b/app/src/cl/svc/CL_STCHGAUDIT.pgc @@ -0,0 +1,37 @@ +/* + * CL_STCHGAUDIT.pgc - cl 모듈 서비스 CL_STCHGAUDIT (one service = one file). + * 마감 상태변경 감사: 특정 기간(period_key) 마감이력의 상태 분포를 상태별로 집계해 + * 상태 전이 감사 요약을 반환한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_STCHGAUDIT.h" + +void CL_STCHGAUDIT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_o, h_c, h_a, h_l, h_x, h_r; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT + count(*) FILTER (WHERE status = 'O'), + count(*) FILTER (WHERE status = 'C'), + count(*) FILTER (WHERE status = 'A'), + count(*) FILTER (WHERE status = 'L'), + count(*) FILTER (WHERE status = 'X'), + count(*) FILTER (WHERE status = 'R') + INTO :h_o, :h_c, :h_a, :h_l, :h_x, :h_r + FROM cl_close_log WHERE period_key = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_o); + setl(b, T_ARG2, h_c); + setl(b, T_ARG3, h_a); + setl(b, T_ARG4, h_l); + setl(b, T_ARG5, h_x); + setl(b, T_ARG6, h_r); + setl(b, T_COUNT, h_o + h_c + h_a + h_l + h_x + h_r); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_STCROSS.h b/app/src/cl/svc/CL_STCROSS.h new file mode 100644 index 0000000..62e3a5b --- /dev/null +++ b/app/src/cl/svc/CL_STCROSS.h @@ -0,0 +1,22 @@ +/* + * CL_STCROSS.h - cl 서비스 CL_STCROSS 카피북 (copybook / record header). + * 상태 크로스검증: 마감이력 상태와 매입 상태 분포 대사. + */ +#ifndef CL_STCROSS_H +#define CL_STCROSS_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_stcross_rec_t; + +void CL_STCROSS(TPSVCINFO *p); + +#endif /* CL_STCROSS_H */ diff --git a/app/src/cl/svc/CL_STCROSS.pgc b/app/src/cl/svc/CL_STCROSS.pgc new file mode 100644 index 0000000..5ea20e0 --- /dev/null +++ b/app/src/cl/svc/CL_STCROSS.pgc @@ -0,0 +1,37 @@ +/* + * CL_STCROSS.pgc - cl 모듈 서비스 CL_STCROSS (one service = one file). + * 상태 크로스검증: 당일 매입 상태 분포(A/M/S/C)를 집계해 미마감(A+M) 잔량으로 + * 마감 완결성을 판정한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_STCROSS.h" + +void CL_STCROSS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long unclosed; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_a, h_m, h_s, h_c, h_t; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT + count(*) FILTER (WHERE status = 'A'), + count(*) FILTER (WHERE status = 'M'), + count(*) FILTER (WHERE status = 'S'), + count(*) FILTER (WHERE status = 'C'), + count(*) + INTO :h_a, :h_m, :h_s, :h_c, :h_t + FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + unclosed = h_a + h_m; + setl(b, T_ARG1, h_a); + setl(b, T_ARG2, h_m); + setl(b, T_ARG3, h_s); + setl(b, T_ARG4, h_c); + setl(b, T_COUNT, h_t); + setl(b, T_RC, (unclosed == 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_TENDAY.h b/app/src/cl/svc/CL_TENDAY.h new file mode 100644 index 0000000..1730ad9 --- /dev/null +++ b/app/src/cl/svc/CL_TENDAY.h @@ -0,0 +1,22 @@ +/* + * CL_TENDAY.h - cl 서비스 CL_TENDAY 카피북 (copybook / record header). + * 순(10일) 마감: 상/중/하순 구간 매입 집계 후 TENDAY 마감이력 생성. + */ +#ifndef CL_TENDAY_H +#define CL_TENDAY_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_tenday_rec_t; + +void CL_TENDAY(TPSVCINFO *p); + +#endif /* CL_TENDAY_H */ diff --git a/app/src/cl/svc/CL_TENDAY.pgc b/app/src/cl/svc/CL_TENDAY.pgc new file mode 100644 index 0000000..58cc335 --- /dev/null +++ b/app/src/cl/svc/CL_TENDAY.pgc @@ -0,0 +1,41 @@ +/* + * CL_TENDAY.pgc - cl 모듈 서비스 CL_TENDAY (one service = one file). + * 순(10일) 마감: 상/중/하순 구간 매입 집계 후 TENDAY 마감이력 생성. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_TENDAY.h" + +void CL_TENDAY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long cid, dec, lo, hi; + char period[24], memo[32]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_ym[16]; + long h_day, h_lo, h_hi, h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT extract(day from :h_bizdate::date)::int, + to_char(:h_bizdate::date, 'YYYY-MM') INTO :h_day, :h_ym; + if (sqlca.sqlcode < 0) FAIL(b); + dec = (h_day <= 10) ? 1 : (h_day <= 20 ? 2 : 3); + lo = (dec - 1) * 10 + 1; + hi = (dec == 3) ? 31 : dec * 10; + h_lo = lo; h_hi = hi; + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_cnt, :h_amt + FROM purchase WHERE to_char(biz_date,'YYYY-MM') = :h_ym + AND extract(day from biz_date) BETWEEN :h_lo AND :h_hi; + if (sqlca.sqlcode < 0) FAIL(b); + snprintf(period, sizeof(period), "%s-T%ld", h_ym, dec); + snprintf(memo, sizeof(memo), "순마감:%ld순", dec); + cid = cldb_next_close_id(); + if (cid < 0) FAIL(b); + if (cldb_insert_close(cid, "TENDAY", period, h_bizdate, "C", h_cnt, h_amt, memo) < 0) FAIL(b); + setl(b, T_ID1, cid); + setl(b, T_ARG1, dec); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_amt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_TENTCLOSE.h b/app/src/cl/svc/CL_TENTCLOSE.h new file mode 100644 index 0000000..14ad614 --- /dev/null +++ b/app/src/cl/svc/CL_TENTCLOSE.h @@ -0,0 +1,22 @@ +/* + * CL_TENTCLOSE.h - cl 서비스 CL_TENTCLOSE 카피북 (copybook / record header). + * 가마감(잠정): 당일 매입 잠정 집계로 가마감(status=O) 이력 생성. + */ +#ifndef CL_TENTCLOSE_H +#define CL_TENTCLOSE_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_tentclose_rec_t; + +void CL_TENTCLOSE(TPSVCINFO *p); + +#endif /* CL_TENTCLOSE_H */ diff --git a/app/src/cl/svc/CL_TENTCLOSE.pgc b/app/src/cl/svc/CL_TENTCLOSE.pgc new file mode 100644 index 0000000..606653c --- /dev/null +++ b/app/src/cl/svc/CL_TENTCLOSE.pgc @@ -0,0 +1,31 @@ +/* + * CL_TENTCLOSE.pgc - cl 모듈 서비스 CL_TENTCLOSE (one service = one file). + * 가마감(잠정): 당일 접수/대사(A,M) 매입 잠정 집계로 가마감(status=O) 이력 생성. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_TENTCLOSE.h" + +void CL_TENTCLOSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long cid; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_cnt, :h_amt + FROM purchase WHERE biz_date = :h_bizdate AND status IN ('A','M'); + if (sqlca.sqlcode < 0) FAIL(b); + cid = cldb_next_close_id(); + if (cid < 0) FAIL(b); + if (cldb_insert_close(cid, "TENT", h_bizdate, h_bizdate, "O", h_cnt, h_amt, "가마감") < 0) FAIL(b); + setl(b, T_ID1, cid); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_amt); + Bchg(b, T_STATUS, 0, "O", 0L); + userlog("CL_TENTCLOSE cid=%ld cnt=%ld 가마감(잠정)", cid, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_TRIGRPT.h b/app/src/cl/svc/CL_TRIGRPT.h new file mode 100644 index 0000000..824cf40 --- /dev/null +++ b/app/src/cl/svc/CL_TRIGRPT.h @@ -0,0 +1,22 @@ +/* + * CL_TRIGRPT.h - cl 서비스 CL_TRIGRPT 카피북 (copybook / record header). + * 마감 후 리포트 트리거: 마감 완료 후 리포트 생성 대상 표기. + */ +#ifndef CL_TRIGRPT_H +#define CL_TRIGRPT_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_trigrpt_rec_t; + +void CL_TRIGRPT(TPSVCINFO *p); + +#endif /* CL_TRIGRPT_H */ diff --git a/app/src/cl/svc/CL_TRIGRPT.pgc b/app/src/cl/svc/CL_TRIGRPT.pgc new file mode 100644 index 0000000..136b3e2 --- /dev/null +++ b/app/src/cl/svc/CL_TRIGRPT.pgc @@ -0,0 +1,27 @@ +/* + * CL_TRIGRPT.pgc - cl 모듈 서비스 CL_TRIGRPT (one service = one file). + * 마감 후 리포트 트리거: 마감완료(C)이나 리포트 미생성(memo!=RPT:*)인 마감을 대상으로 + * 후속 리포트 생성 대상 건수를 산출·표기한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_TRIGRPT.h" + +void CL_TRIGRPT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_n; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT count(*) INTO :h_n FROM cl_close_log + WHERE period_key = :h_bizdate AND status = 'C' + AND memo NOT LIKE 'RPT:%'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_n); + setl(b, T_RC, (h_n > 0) ? 0 : 1); + Bchg(b, T_SVCNAME, 0, "CL_REPORT", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_TRIGSETTLE.h b/app/src/cl/svc/CL_TRIGSETTLE.h new file mode 100644 index 0000000..2f14f90 --- /dev/null +++ b/app/src/cl/svc/CL_TRIGSETTLE.h @@ -0,0 +1,22 @@ +/* + * CL_TRIGSETTLE.h - cl 서비스 CL_TRIGSETTLE 카피북 (copybook / record header). + * 마감 후 정산 트리거: 마감 완료 후 정산 대상 건수 산출·표기. + */ +#ifndef CL_TRIGSETTLE_H +#define CL_TRIGSETTLE_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_trigsettle_rec_t; + +void CL_TRIGSETTLE(TPSVCINFO *p); + +#endif /* CL_TRIGSETTLE_H */ diff --git a/app/src/cl/svc/CL_TRIGSETTLE.pgc b/app/src/cl/svc/CL_TRIGSETTLE.pgc new file mode 100644 index 0000000..26297a0 --- /dev/null +++ b/app/src/cl/svc/CL_TRIGSETTLE.pgc @@ -0,0 +1,27 @@ +/* + * CL_TRIGSETTLE.pgc - cl 모듈 서비스 CL_TRIGSETTLE (one service = one file). + * 마감 후 정산 트리거: 마감 완료 당일 정산완료(S) 대상 건수/정산액을 산출하고 + * 후속 정산 서비스명을 표기한다. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_TRIGSETTLE.h" + +void CL_TRIGSETTLE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_n, h_net; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_n, :h_net + FROM purchase WHERE biz_date = :h_bizdate AND status = 'S'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_n); + setl(b, T_NET, h_net); + setl(b, T_RC, (h_n > 0) ? 0 : 1); + Bchg(b, T_SVCNAME, 0, "AC_SETTLE", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_WEEKLY.h b/app/src/cl/svc/CL_WEEKLY.h new file mode 100644 index 0000000..709ea81 --- /dev/null +++ b/app/src/cl/svc/CL_WEEKLY.h @@ -0,0 +1,22 @@ +/* + * CL_WEEKLY.h - cl 서비스 CL_WEEKLY 카피북 (copybook / record header). + * 주 마감: 주간(월~일) 매입을 집계해 WEEKLY 마감이력 생성. + */ +#ifndef CL_WEEKLY_H +#define CL_WEEKLY_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_weekly_rec_t; + +void CL_WEEKLY(TPSVCINFO *p); + +#endif /* CL_WEEKLY_H */ diff --git a/app/src/cl/svc/CL_WEEKLY.pgc b/app/src/cl/svc/CL_WEEKLY.pgc new file mode 100644 index 0000000..a5545e8 --- /dev/null +++ b/app/src/cl/svc/CL_WEEKLY.pgc @@ -0,0 +1,33 @@ +/* + * CL_WEEKLY.pgc - cl 모듈 서비스 CL_WEEKLY (one service = one file). + * 주 마감: 주간(월~일) 매입을 집계해 WEEKLY 마감이력 생성. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_WEEKLY.h" + +void CL_WEEKLY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long cid; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_wk[16]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT to_char(date_trunc('week', :h_bizdate::date), 'IYYY"W"IW'), + count(*), coalesce(sum(amount),0) + INTO :h_wk, :h_cnt, :h_amt FROM purchase + WHERE date_trunc('week', biz_date) = date_trunc('week', :h_bizdate::date); + if (sqlca.sqlcode < 0) FAIL(b); + cid = cldb_next_close_id(); + if (cid < 0) FAIL(b); + if (cldb_insert_close(cid, "WEEKLY", h_wk, h_bizdate, "C", h_cnt, h_amt, "주마감") < 0) FAIL(b); + setl(b, T_ID1, cid); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_amt); + Bchg(b, T_KEY1, 0, h_wk, 0L); + userlog("CL_WEEKLY cid=%ld wk=%s cnt=%ld 주마감", cid, h_wk, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_WEEKVERIFY.h b/app/src/cl/svc/CL_WEEKVERIFY.h new file mode 100644 index 0000000..6fae783 --- /dev/null +++ b/app/src/cl/svc/CL_WEEKVERIFY.h @@ -0,0 +1,22 @@ +/* + * CL_WEEKVERIFY.h - cl 서비스 CL_WEEKVERIFY 카피북 (copybook / record header). + * 주 마감 검증: 주간 마감이력과 주간 매입 실집계 대사. + */ +#ifndef CL_WEEKVERIFY_H +#define CL_WEEKVERIFY_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_weekverify_rec_t; + +void CL_WEEKVERIFY(TPSVCINFO *p); + +#endif /* CL_WEEKVERIFY_H */ diff --git a/app/src/cl/svc/CL_WEEKVERIFY.pgc b/app/src/cl/svc/CL_WEEKVERIFY.pgc new file mode 100644 index 0000000..ef33075 --- /dev/null +++ b/app/src/cl/svc/CL_WEEKVERIFY.pgc @@ -0,0 +1,33 @@ +/* + * CL_WEEKVERIFY.pgc - cl 모듈 서비스 CL_WEEKVERIFY (one service = one file). + * 주 마감 검증: 주간 마감이력과 주간 매입 실집계 대사. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_WEEKVERIFY.h" + +void CL_WEEKVERIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long da; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_la, h_ra; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT coalesce(sum(total_amount),0) INTO :h_la FROM cl_close_log + WHERE close_type = 'WEEKLY' + AND period_key = to_char(date_trunc('week', :h_bizdate::date), 'IYYY"W"IW') + AND status <> 'X'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_ra FROM purchase + WHERE date_trunc('week', biz_date) = date_trunc('week', :h_bizdate::date); + if (sqlca.sqlcode < 0) FAIL(b); + da = h_la - h_ra; if (da < 0) da = -da; + setl(b, T_AMT1, h_la); + setl(b, T_AMT2, h_ra); + setl(b, T_DELTA, da); + setl(b, T_RC, (da == 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cl/svc/CL_YEARVERIFY.h b/app/src/cl/svc/CL_YEARVERIFY.h new file mode 100644 index 0000000..61ee887 --- /dev/null +++ b/app/src/cl/svc/CL_YEARVERIFY.h @@ -0,0 +1,22 @@ +/* + * CL_YEARVERIFY.h - cl 서비스 CL_YEARVERIFY 카피북 (copybook / record header). + * 연 마감 검증: 연간 마감이력과 연간 매입 실집계 대사. + */ +#ifndef CL_YEARVERIFY_H +#define CL_YEARVERIFY_H + +#include "cl_svc.h" + +typedef struct { + long id1; + long id2; + long amount; + long count; + char status[8]; + char key1[32]; + char biz_date[16]; +} cl_cl_yearverify_rec_t; + +void CL_YEARVERIFY(TPSVCINFO *p); + +#endif /* CL_YEARVERIFY_H */ diff --git a/app/src/cl/svc/CL_YEARVERIFY.pgc b/app/src/cl/svc/CL_YEARVERIFY.pgc new file mode 100644 index 0000000..35a8cb4 --- /dev/null +++ b/app/src/cl/svc/CL_YEARVERIFY.pgc @@ -0,0 +1,36 @@ +/* + * CL_YEARVERIFY.pgc - cl 모듈 서비스 CL_YEARVERIFY (one service = one file). + * 연 마감 검증: 연간 마감이력과 연간 매입 실집계 대사. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); cl_svr 가 광고한다. + */ +#include "CL_YEARVERIFY.h" + +void CL_YEARVERIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long dc, da; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_lc, h_la, h_rc, h_ra; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT coalesce(sum(txn_count),0), coalesce(sum(total_amount),0) + INTO :h_lc, :h_la FROM cl_close_log + WHERE close_type = 'YEAREND' + AND period_key = to_char(:h_bizdate::date, 'YYYY') AND status <> 'X'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_rc, :h_ra + FROM purchase WHERE to_char(biz_date,'YYYY') = to_char(:h_bizdate::date,'YYYY'); + if (sqlca.sqlcode < 0) FAIL(b); + dc = h_lc - h_rc; if (dc < 0) dc = -dc; + da = h_la - h_ra; if (da < 0) da = -da; + setl(b, T_ARG1, h_lc); + setl(b, T_COUNT, h_rc); + setl(b, T_AMT1, h_la); + setl(b, T_AMT2, h_ra); + setl(b, T_DELTA, da); + setl(b, T_RC, (dc == 0 && da == 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/cm/batch/cm_audittop_batch.pgc b/app/src/cm/batch/cm_audittop_batch.pgc new file mode 100644 index 0000000..36abc17 --- /dev/null +++ b/app/src/cm/batch/cm_audittop_batch.pgc @@ -0,0 +1,57 @@ +/* + * cm_audittop_batch.pgc - cm 감사로그 최다 서비스 배치 (XA). + * + * 기준일 공통로그를 서비스별로 그룹핑해 로그가 가장 많은 서비스와 건수를 + * 뽑고, 고유 서비스 수/전체 건수와 함께 요약을 svc=AUDITTOP 로그로 남긴다. + * 로그 집중도를 감시해 폭주 서비스를 조기에 드러낸다. + * Usage: cm_audittop_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cm_audittop_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + cm_audittop_rec_t rec = { 0, 0, 0 }; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_top[64], h_msg[120]; + long h_topcnt, h_distinct, h_total; + EXEC SQL END DECLARE SECTION; + + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strcpy(h_top, "NONE"); + + 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(*), count(DISTINCT svc_name) INTO :h_total, :h_distinct + FROM cm_log WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + rec.total = h_total; + rec.distinct_svc = h_distinct; + + if (h_total > 0) { + EXEC SQL SELECT svc_name, count(*) INTO :h_top, :h_topcnt + FROM cm_log WHERE biz_date = :h_bizdate + GROUP BY svc_name ORDER BY count(*) DESC, svc_name LIMIT 1; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + rec.top_cnt = h_topcnt; + } + + snprintf(h_msg, sizeof(h_msg), "AUDITTOP top=%s cnt=%ld distinct=%ld total=%ld", + h_top, rec.top_cnt, rec.distinct_svc, rec.total); + EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date) + VALUES (nextval('cm_log_seq'), 'AUDITTOP', 'I', :h_msg, :h_bizdate); + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cm_audittop_batch COMMIT: 최다=%s(%ld) 고유=%ld 전체=%ld\n", + h_top, rec.top_cnt, rec.distinct_svc, rec.total); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cm/batch/cm_batdep_batch.pgc b/app/src/cm/batch/cm_batdep_batch.pgc new file mode 100644 index 0000000..956b56f --- /dev/null +++ b/app/src/cm/batch/cm_batdep_batch.pgc @@ -0,0 +1,59 @@ +/* + * cm_batdep_batch.pgc - cm 배치 의존성 정합성 점검 배치 (XA). + * + * DEP: 파라미터(잡->선행잡)를 커서로 훑어, 선행 잡의 스케줄(SCHD:)이 + * 실제로 존재하는지 확인한다. 선행이 없는 끊긴(dangling) 의존성은 경고 로그 + * (svc=BATDEP, W)로 남긴다. 스케줄 편성 오류를 잡는 것이 목적이다. + * Usage: cm_batdep_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cm_batdep_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + cm_batdep_rec_t rec = { 0, 0, 0 }; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_job[64], h_dep[64], h_schd[72], h_msg[112]; + long h_has; + 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 cur_bd CURSOR FOR + SELECT substr(param_key, 5), param_val FROM cm_param + WHERE param_key LIKE 'DEP:%' ORDER BY param_key; + EXEC SQL OPEN cur_bd; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH cur_bd INTO :h_job, :h_dep; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_bd; tpabort(0); return 1; } + rec.deps++; + snprintf(h_schd, sizeof(h_schd), "SCHD:%s", h_dep); + EXEC SQL SELECT count(*) INTO :h_has FROM cm_param WHERE param_key = :h_schd; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_bd; tpabort(0); return 1; } + if (h_has > 0) { rec.ok++; continue; } + rec.dangling++; + snprintf(h_msg, sizeof(h_msg), "BATDEP dangling %s -> %s", h_job, h_dep); + EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date) + VALUES (nextval('cm_log_seq'), 'BATDEP', 'W', :h_msg, :h_bizdate); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_bd; tpabort(0); return 1; } + } + EXEC SQL CLOSE cur_bd; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cm_batdep_batch COMMIT: 의존성=%ld 정상=%ld 끊김=%ld\n", + rec.deps, rec.ok, rec.dangling); + + tpclose(); tpterm(); + return rec.dangling > 0 ? 3 : 0; +} diff --git a/app/src/cm/batch/cm_codever_batch.pgc b/app/src/cm/batch/cm_codever_batch.pgc new file mode 100644 index 0000000..f6f8e5b --- /dev/null +++ b/app/src/cm/batch/cm_codever_batch.pgc @@ -0,0 +1,63 @@ +/* + * cm_codever_batch.pgc - cm 코드그룹 형상버전 초기화 배치 (XA). + * + * 사용중인 모든 코드그룹을 커서로 훑어, 형상버전 파라미터(VER:)가 없는 + * 그룹에는 버전 0 을 신규 초기화한다. 이미 버전이 있는 그룹은 건드리지 않는다. + * 초기화 결과를 공통로그(svc=CODEVER)에 남긴다. + * Usage: cm_codever_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cm_codever_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + cm_codever_rec_t rec = { 0, 0, 0 }; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_grp[32], h_vkey[40], h_msg[112]; + long h_exists; + 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 cur_cv CURSOR FOR + SELECT DISTINCT code_grp FROM cm_code WHERE use_yn = 'Y' ORDER BY code_grp; + EXEC SQL OPEN cur_cv; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH cur_cv INTO :h_grp; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_cv; tpabort(0); return 1; } + rec.groups++; + snprintf(h_vkey, sizeof(h_vkey), "VER:%s", h_grp); + EXEC SQL SELECT count(*) INTO :h_exists FROM cm_param WHERE param_key = :h_vkey; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_cv; tpabort(0); return 1; } + if (h_exists > 0) { rec.versioned++; continue; } + EXEC SQL INSERT INTO cm_param (param_key, param_val, descr) + VALUES (:h_vkey, '0', 'code group version'); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_cv; tpabort(0); return 1; } + rec.initialized++; + } + EXEC SQL CLOSE cur_cv; + + snprintf(h_msg, sizeof(h_msg), "CODEVER groups=%ld versioned=%ld init=%ld", + rec.groups, rec.versioned, rec.initialized); + EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date) + VALUES (nextval('cm_log_seq'), 'CODEVER', 'I', :h_msg, :h_bizdate); + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cm_codever_batch COMMIT: 그룹=%ld 기존=%ld 초기화=%ld\n", + rec.groups, rec.versioned, rec.initialized); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cm/batch/cm_fxcross_batch.pgc b/app/src/cm/batch/cm_fxcross_batch.pgc new file mode 100644 index 0000000..0a3919f --- /dev/null +++ b/app/src/cm/batch/cm_fxcross_batch.pgc @@ -0,0 +1,67 @@ +/* + * cm_fxcross_batch.pgc - cm 교차환율 산출 배치 (XA). + * + * 기준일 고시된 통화들을 커서로 훑어 기준통화(USD) 대비 교차환율(1e6 스케일)을 + * 산출하고, 가장 큰 교차율을 추적한다. 기준통화 자체 고시가 없으면 조용히 + * 종료(종료코드 2). 요약을 svc=FXCROSS 로그로 남긴다. + * Usage: cm_fxcross_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cm_fxcross_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + cm_fxcross_rec_t rec = { 0, 0, 0 }; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_ccy[8], h_base[8], h_msg[112]; + long h_rate, h_base_rate; + EXEC SQL END DECLARE SECTION; + + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strcpy(h_base, CM_FXCROSS_BASE); + + 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 rate_bps INTO :h_base_rate FROM cm_fx_rate + WHERE ccy = :h_base AND base_date = :h_bizdate; + if (sqlca.sqlcode == 100) { tpabort(0); + printf(">>> cm_fxcross_batch: 기준통화 %s 미고시\n", h_base); tpterm(); return 2; } + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + rec.base_rate = h_base_rate; + + EXEC SQL DECLARE cur_fc CURSOR FOR + SELECT ccy, rate_bps FROM cm_fx_rate + WHERE base_date = :h_bizdate AND ccy <> :h_base ORDER BY ccy; + EXEC SQL OPEN cur_fc; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + for (;;) { + long cross; + EXEC SQL FETCH cur_fc INTO :h_ccy, :h_rate; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_fc; tpabort(0); return 1; } + if (h_rate <= 0) continue; + cross = h_base_rate * 1000000L / h_rate; /* USD 1단위당 대상통화 */ + if (cross > rec.max_cross) rec.max_cross = cross; + rec.pairs++; + } + EXEC SQL CLOSE cur_fc; + + snprintf(h_msg, sizeof(h_msg), "FXCROSS base=%s pairs=%ld maxcross=%ld", + h_base, rec.pairs, rec.max_cross); + EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date) + VALUES (nextval('cm_log_seq'), 'FXCROSS', 'I', :h_msg, :h_bizdate); + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cm_fxcross_batch COMMIT: 통화쌍=%ld 최대교차=%ld\n", rec.pairs, rec.max_cross); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cm/batch/cm_healthscore_batch.pgc b/app/src/cm/batch/cm_healthscore_batch.pgc new file mode 100644 index 0000000..4aaec8c --- /dev/null +++ b/app/src/cm/batch/cm_healthscore_batch.pgc @@ -0,0 +1,58 @@ +/* + * cm_healthscore_batch.pgc - cm 종합 헬스 스코어 배치 (XA). + * + * 당일 로그/환율/채번/파라미터 네 자원의 존재를 각 25점으로 합산해 0~100 + * 스코어를 산출하고 GREEN/YELLOW/RED 등급을 판정한다. 결과를 svc=HSCORE + * 로그로 남기며, RED 면 종료코드 3 으로 야간 당직에 신호한다. + * Usage: cm_healthscore_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cm_healthscore_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + cm_healthscore_rec_t rec = { 0, 0, 0, 0, 0 }; + const char *grade; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_msg[120], h_lvl[4]; + long h_log, h_fx, h_seq, h_param; + 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_log FROM cm_log WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + EXEC SQL SELECT count(*) INTO :h_fx FROM cm_fx_rate WHERE base_date = :h_bizdate; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + EXEC SQL SELECT count(*) INTO :h_seq FROM cm_seq; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + EXEC SQL SELECT count(*) INTO :h_param FROM cm_param; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + rec.log_rows = h_log; rec.fx_rows = h_fx; rec.seq_rows = h_seq; rec.param_rows = h_param; + if (h_log > 0) rec.score += 25; + if (h_fx > 0) rec.score += 25; + if (h_seq > 0) rec.score += 25; + if (h_param > 0) rec.score += 25; + grade = (rec.score >= CM_HSCORE_GREEN) ? "GREEN" + : (rec.score >= CM_HSCORE_YELLOW) ? "YELLOW" : "RED"; + strcpy(h_lvl, (rec.score >= CM_HSCORE_YELLOW) ? "I" : "E"); + snprintf(h_msg, sizeof(h_msg), "HSCORE score=%ld grade=%s", rec.score, grade); + EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date) + VALUES (nextval('cm_log_seq'), 'HSCORE', :h_lvl, :h_msg, :h_bizdate); + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cm_healthscore_batch COMMIT: 스코어=%ld 등급=%s\n", rec.score, grade); + + tpclose(); tpterm(); + return rec.score >= CM_HSCORE_YELLOW ? 0 : 3; +} diff --git a/app/src/cm/batch/cm_holyear_batch.pgc b/app/src/cm/batch/cm_holyear_batch.pgc new file mode 100644 index 0000000..c8b28f1 --- /dev/null +++ b/app/src/cm/batch/cm_holyear_batch.pgc @@ -0,0 +1,62 @@ +/* + * cm_holyear_batch.pgc - cm 연간 휴일 통계 배치 (XA). + * + * 기준일이 속한 연도의 휴일을 국가별로 커서 집계해 국가 수/휴일 수/주말겹침 + * 수를 누적한다. 국가별 휴일 수는 svc=HOLYEAR 로그로 각 행을 남기고, 마지막에 + * 총계 행을 추가한다. 캘린더 편성 리포트에 쓰인다. + * Usage: cm_holyear_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cm_holyear_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + cm_holyear_rec_t rec = { 0, 0, 0 }; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_nation[8], h_msg[112]; + long h_year, h_cnt, h_wknd; + 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 extract(year FROM :h_bizdate::date) INTO :h_year; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + EXEC SQL DECLARE cur_hy CURSOR FOR + SELECT nation, count(*), + coalesce(sum(CASE WHEN extract(dow FROM holi_date) IN (0,6) THEN 1 ELSE 0 END), 0) + FROM cm_holiday WHERE extract(year FROM holi_date) = :h_year + GROUP BY nation ORDER BY nation; + EXEC SQL OPEN cur_hy; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH cur_hy INTO :h_nation, :h_cnt, :h_wknd; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_hy; tpabort(0); return 1; } + rec.nations++; + rec.holidays += h_cnt; + rec.weekend += h_wknd; + snprintf(h_msg, sizeof(h_msg), "HOLYEAR %ld %s cnt=%ld wknd=%ld", + h_year, h_nation, h_cnt, h_wknd); + EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date) + VALUES (nextval('cm_log_seq'), 'HOLYEAR', 'I', :h_msg, :h_bizdate); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_hy; tpabort(0); return 1; } + } + EXEC SQL CLOSE cur_hy; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cm_holyear_batch COMMIT: 국가=%ld 휴일=%ld 주말겹침=%ld\n", + rec.nations, rec.holidays, rec.weekend); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cm/batch/cm_maskscan_batch.pgc b/app/src/cm/batch/cm_maskscan_batch.pgc new file mode 100644 index 0000000..e5e93f3 --- /dev/null +++ b/app/src/cm/batch/cm_maskscan_batch.pgc @@ -0,0 +1,68 @@ +/* + * cm_maskscan_batch.pgc - cm 로그 마스킹 위반 스캔 배치 (XA). + * + * 기준일 공통로그 본문을 커서로 훑으며, 13자리 이상 연속 숫자열(마스킹되지 + * 않은 카드번호 의심)이 있는지 순수 스캔한다. 위반 의심 로그는 svc=MASKSCAN + * (W) 경고로 남긴다. 개인정보 노출 정적 점검이 목적이다. 위반이 있으면 종료 3. + * Usage: cm_maskscan_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cm_maskscan_batch.h" + +/* 본문에 CM_MASKSCAN_PAN_MIN 자리 이상 연속 숫자가 있으면 1 */ +static int cm_has_pan(const char *s) +{ + int run = 0, i; + for (i = 0; s[i]; i++) { + if (s[i] >= '0' && s[i] <= '9') { + if (++run >= CM_MASKSCAN_PAN_MIN) return 1; + } else { + run = 0; + } + } + return 0; +} + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + cm_maskscan_rec_t rec = { 0, 0 }; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_body[256], h_msg[112]; + long h_id; + 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 cur_ms CURSOR FOR + SELECT log_id, msg FROM cm_log WHERE biz_date = :h_bizdate ORDER BY log_id; + EXEC SQL OPEN cur_ms; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH cur_ms INTO :h_id, :h_body; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_ms; tpabort(0); return 1; } + rec.scanned++; + if (!cm_has_pan(h_body)) continue; + rec.flagged++; + snprintf(h_msg, sizeof(h_msg), "MASKSCAN suspect log_id=%ld", h_id); + EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date) + VALUES (nextval('cm_log_seq'), 'MASKSCAN', 'W', :h_msg, :h_bizdate); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_ms; tpabort(0); return 1; } + } + EXEC SQL CLOSE cur_ms; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cm_maskscan_batch COMMIT: 스캔=%ld 위반의심=%ld\n", rec.scanned, rec.flagged); + + tpclose(); tpterm(); + return rec.flagged > 0 ? 3 : 0; +} diff --git a/app/src/cm/batch/cm_msglang_batch.pgc b/app/src/cm/batch/cm_msglang_batch.pgc new file mode 100644 index 0000000..2a19f61 --- /dev/null +++ b/app/src/cm/batch/cm_msglang_batch.pgc @@ -0,0 +1,59 @@ +/* + * cm_msglang_batch.pgc - cm 다국어 메시지 커버리지 점검 배치 (XA). + * + * MSG_% 코드그룹 전체를 메시지 ID(code) 기준으로 그룹핑해, ID 별로 등록된 언어 + * 그룹 수를 커서로 훑는다. 기준 언어 수(2) 이상이면 완전 번역, 1개면 부분 + * 번역으로 분류해 집계하고 요약을 svc=MSGLANG 로그로 남긴다. + * Usage: cm_msglang_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cm_msglang_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + cm_msglang_rec_t rec = { 0, 0, 0 }; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_code[32], h_msg[112]; + long h_langs; + 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 cur_ml CURSOR FOR + SELECT code, count(*) FROM cm_code + WHERE code_grp LIKE 'MSG_%' AND use_yn = 'Y' + GROUP BY code ORDER BY code; + EXEC SQL OPEN cur_ml; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH cur_ml INTO :h_code, :h_langs; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_ml; tpabort(0); return 1; } + rec.ids++; + if (h_langs >= CM_MSGLANG_MIN_LANGS) rec.full++; + else rec.partial++; + } + EXEC SQL CLOSE cur_ml; + + snprintf(h_msg, sizeof(h_msg), "MSGLANG ids=%ld full=%ld partial=%ld", + rec.ids, rec.full, rec.partial); + EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date) + VALUES (nextval('cm_log_seq'), 'MSGLANG', 'I', :h_msg, :h_bizdate); + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cm_msglang_batch COMMIT: ID=%ld 완전=%ld 부분=%ld\n", + rec.ids, rec.full, rec.partial); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cm/batch/cm_notifail_batch.pgc b/app/src/cm/batch/cm_notifail_batch.pgc new file mode 100644 index 0000000..31c0d21 --- /dev/null +++ b/app/src/cm/batch/cm_notifail_batch.pgc @@ -0,0 +1,53 @@ +/* + * cm_notifail_batch.pgc - cm 알림 재발송 비율 감시 배치 (XA). + * + * 기준일 NOTI 로그에서 전체 발송 대비 재발송([RETRY] 접두) 비율(bps)을 계산해 + * 발송 품질을 감시한다. 비율이 임계(2000bps)를 넘으면 E 레벨 경보를 남기고 + * 종료코드 3 으로 후속 조치를 유도한다. 요약은 svc=NOTIFAIL 로그. + * Usage: cm_notifail_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cm_notifail_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + cm_notifail_rec_t rec = { 0, 0, 0, 0 }; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_msg[120], h_lvl[4]; + long h_sent = 0, h_retry = 0; + 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(*), + coalesce(sum(CASE WHEN msg LIKE '[RETRY]%' THEN 1 ELSE 0 END), 0) + INTO :h_sent, :h_retry + FROM cm_log WHERE svc_name = 'NOTI' AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + rec.sent = h_sent; + rec.retried = h_retry; + if (rec.sent > 0) rec.ratio_bps = rec.retried * 10000L / rec.sent; + rec.alarmed = rec.ratio_bps > CM_NOTIFAIL_ALERT_BPS; + strcpy(h_lvl, rec.alarmed ? "E" : "I"); + snprintf(h_msg, sizeof(h_msg), "NOTIFAIL sent=%ld retry=%ld ratio=%ldbps%s", + rec.sent, rec.retried, rec.ratio_bps, rec.alarmed ? " ALERT" : ""); + EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date) + VALUES (nextval('cm_log_seq'), 'NOTIFAIL', :h_lvl, :h_msg, :h_bizdate); + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cm_notifail_batch COMMIT: 발송=%ld 재발송=%ld 비율=%ldbps%s\n", + rec.sent, rec.retried, rec.ratio_bps, rec.alarmed ? " [경보]" : ""); + + tpclose(); tpterm(); + return rec.alarmed ? 3 : 0; +} diff --git a/app/src/cm/batch/cm_paramhist_batch.pgc b/app/src/cm/batch/cm_paramhist_batch.pgc new file mode 100644 index 0000000..dc2a61b --- /dev/null +++ b/app/src/cm/batch/cm_paramhist_batch.pgc @@ -0,0 +1,54 @@ +/* + * cm_paramhist_batch.pgc - cm 파라미터 승인 워크플로 이력 요약 배치 (XA). + * + * 기준일 PARAMWF 로그를 본문 접두(REQ/APPROVE/REJECT·CANCEL)로 분류 집계해 + * 요청/승인/반려 건수를 조건집계 한 번으로 뽑고, 요약을 svc=PARAMSUM 로그로 + * 남긴다. 승인 처리량 리포트에 쓰인다. + * Usage: cm_paramhist_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cm_paramhist_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + cm_paramhist_rec_t rec = { 0, 0, 0 }; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_msg[120]; + long h_req = 0, h_app = 0, h_rej = 0; + EXEC SQL END DECLARE SECTION; + + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL SELECT + coalesce(sum(CASE WHEN msg LIKE 'REQ %' THEN 1 ELSE 0 END), 0), + coalesce(sum(CASE WHEN msg LIKE 'APPROVE %' THEN 1 ELSE 0 END), 0), + coalesce(sum(CASE WHEN msg LIKE 'REJECT %' OR msg LIKE 'CANCEL %' + THEN 1 ELSE 0 END), 0) + INTO :h_req, :h_app, :h_rej + FROM cm_log WHERE svc_name = 'PARAMWF' AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + rec.requested = h_req; + rec.approved = h_app; + rec.rejected = h_rej; + snprintf(h_msg, sizeof(h_msg), "PARAMSUM req=%ld app=%ld rej=%ld", + rec.requested, rec.approved, rec.rejected); + EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date) + VALUES (nextval('cm_log_seq'), 'PARAMSUM', 'I', :h_msg, :h_bizdate); + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cm_paramhist_batch COMMIT: 요청=%ld 승인=%ld 반려=%ld\n", + rec.requested, rec.approved, rec.rejected); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/cm/batch/cm_seqhigh_batch.pgc b/app/src/cm/batch/cm_seqhigh_batch.pgc new file mode 100644 index 0000000..8faf98c --- /dev/null +++ b/app/src/cm/batch/cm_seqhigh_batch.pgc @@ -0,0 +1,57 @@ +/* + * cm_seqhigh_batch.pgc - cm 고값 채번 카운터 감시 배치 (XA). + * + * 모든 채번 카운터를 커서로 훑어 임계(100만)를 넘는 카운터를 찾아 개별 + * 경보 로그(svc=SEQHIGH, W)를 남기고 최고값을 추적한다. 오버플로 근접·리셋 + * 필요 카운터를 조기 발견하는 것이 목적이다. 고값이 있으면 종료코드 3. + * Usage: cm_seqhigh_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "cm_seqhigh_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + cm_seqhigh_rec_t rec = { 0, 0, 0 }; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_name[32], h_msg[112]; + long h_val; + 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 cur_sh CURSOR FOR + SELECT seq_name, seq_val FROM cm_seq ORDER BY seq_val DESC; + EXEC SQL OPEN cur_sh; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH cur_sh INTO :h_name, :h_val; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_sh; tpabort(0); return 1; } + rec.counters++; + if (h_val > rec.top_val) rec.top_val = h_val; + if (h_val > CM_SEQHIGH_LIMIT) { + rec.high++; + snprintf(h_msg, sizeof(h_msg), "SEQHIGH %s val=%ld", h_name, h_val); + EXEC SQL INSERT INTO cm_log (log_id, svc_name, log_level, msg, biz_date) + VALUES (nextval('cm_log_seq'), 'SEQHIGH', 'W', :h_msg, :h_bizdate); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_sh; tpabort(0); return 1; } + } + } + EXEC SQL CLOSE cur_sh; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> cm_seqhigh_batch COMMIT: 카운터=%ld 고값=%ld 최고=%ld\n", + rec.counters, rec.high, rec.top_val); + + tpclose(); tpterm(); + return rec.high > 0 ? 3 : 0; +} diff --git a/app/src/cm/cm_svr.pgc b/app/src/cm/cm_svr.pgc index f430980..bed0e7e 100644 --- a/app/src/cm/cm_svr.pgc +++ b/app/src/cm/cm_svr.pgc @@ -2,13 +2,13 @@ * cm_svr.pgc - cm (공통 / common) MODULE SERVER (thin dispatcher). * * The legacy "one module server, many services" pattern: this binary owns NO - * business logic. Each of cm's 75 online services lives in its own file + * business logic. Each of cm's 133 online services lives in its own file * (app/src/cm/svc/.pgc) with its own copybook header * (app/src/cm/svc/.h); those objects are archived into libcmsvc.a * and linked into this server. Here we only: * - include every service copybook (for the extern prototypes), * - tpopen() the ECPG XA RM, - * - tpadvertise() all 75 services from a {name, fn} table, + * - tpadvertise() all 133 services from a {name, fn} table, * - tpclose() at shutdown. * * XA: the connection is opened once by tpopen() (ECPG XA switch libndrxxaecpg.so); @@ -96,6 +96,67 @@ #include "CM_FILE_CKSUM.h" #include "CM_DATEDIFF.h" #include "CM_AMT_HANGUL.h" +/* --- 2차 확장 서비스 (코드그룹 버전/이관/정렬, 파라미터 이력/검증, 다국어 + * 렌더/커버리지, 환율 역/스프레드/추세/교차, 영업일·분기·주차, 채번 발번, + * 알림 통계/재발송/한도, 배치 의존성, 헬스 종합, 감사 상위, 마스킹/해시/문자) --- */ +#include "CM_CODEGRP_VER.h" +#include "CM_CODEGRP_DIFF.h" +#include "CM_CODE_MOVE.h" +#include "CM_CODE_REORDER.h" +#include "CM_CODE_TOGGLE.h" +#include "CM_CODE_SEARCH.h" +#include "CM_CODEGRP_SPAN.h" +#include "CM_CODE_EXPORT.h" +#include "CM_PARAM_HIST.h" +#include "CM_PARAM_CANCEL.h" +#include "CM_PARAM_DIFF.h" +#include "CM_PARAM_NUM.h" +#include "CM_PARAM_LIST.h" +#include "CM_MSG_DEL.h" +#include "CM_MSG_FORMAT.h" +#include "CM_MSG_LANGS.h" +#include "CM_MSG_RENDER.h" +#include "CM_FX_INVERT.h" +#include "CM_FX_MARGIN.h" +#include "CM_FX_TREND.h" +#include "CM_FX_MEAN.h" +#include "CM_FX_TICKS.h" +#include "CM_FX_TRIANGLE.h" +#include "CM_HOLIDAY_NEXT.h" +#include "CM_HOLIDAY_YEAR.h" +#include "CM_BIZDAY_ADD.h" +#include "CM_MKT_STATUS.h" +#include "CM_QTR_END.h" +#include "CM_ISO_WEEK.h" +#include "CM_DAYOFYEAR.h" +#include "CM_SEQ_STAMP.h" +#include "CM_SEQ_MULTI.h" +#include "CM_SEQ_ALLOC.h" +#include "CM_SEQ_VERIFY.h" +#include "CM_NOTI_RETRY.h" +#include "CM_NOTI_STAT.h" +#include "CM_NOTI_TMPL.h" +#include "CM_NOTI_QUOTA.h" +#include "CM_BATSCHD_DEL.h" +#include "CM_BATSCHD_LIST.h" +#include "CM_BATDEP_SET.h" +#include "CM_BATDEP_CHK.h" +#include "CM_BATLAST.h" +#include "CM_HEALTH_FX.h" +#include "CM_HEALTH_ALL.h" +#include "CM_HEALTH_PARAM.h" +#include "CM_AUDIT_TOP.h" +#include "CM_AUDIT_ERRPCT.h" +#include "CM_AUDIT_TAIL.h" +#include "CM_MASK_EMAIL.h" +#include "CM_MASK_ACCT.h" +#include "CM_MASK_RRN.h" +#include "CM_HASH_DJB2.h" +#include "CM_HASH_FNV.h" +#include "CM_HASH_SUM.h" +#include "CM_ADLER32.h" +#include "CM_STR_TRIM.h" +#include "CM_STR_PAD.h" /* ----- advertise table: {service name, function} (fn is extern from its file) */ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { @@ -138,6 +199,36 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"CM_MASK_PHONE", CM_MASK_PHONE}, {"CM_ENC_HEX", CM_ENC_HEX}, {"CM_FILE_CKSUM", CM_FILE_CKSUM}, {"CM_DATEDIFF", CM_DATEDIFF}, {"CM_AMT_HANGUL", CM_AMT_HANGUL}, + /* --- 2차 확장 서비스 --- */ + {"CM_CODEGRP_VER", CM_CODEGRP_VER}, {"CM_CODEGRP_DIFF", CM_CODEGRP_DIFF}, + {"CM_CODE_MOVE", CM_CODE_MOVE}, {"CM_CODE_REORDER", CM_CODE_REORDER}, + {"CM_CODE_TOGGLE", CM_CODE_TOGGLE}, {"CM_CODE_SEARCH", CM_CODE_SEARCH}, + {"CM_CODEGRP_SPAN", CM_CODEGRP_SPAN}, {"CM_CODE_EXPORT", CM_CODE_EXPORT}, + {"CM_PARAM_HIST", CM_PARAM_HIST}, {"CM_PARAM_CANCEL", CM_PARAM_CANCEL}, + {"CM_PARAM_DIFF", CM_PARAM_DIFF}, {"CM_PARAM_NUM", CM_PARAM_NUM}, + {"CM_PARAM_LIST", CM_PARAM_LIST}, {"CM_MSG_DEL", CM_MSG_DEL}, + {"CM_MSG_FORMAT", CM_MSG_FORMAT}, {"CM_MSG_LANGS", CM_MSG_LANGS}, + {"CM_MSG_RENDER", CM_MSG_RENDER}, {"CM_FX_INVERT", CM_FX_INVERT}, + {"CM_FX_MARGIN", CM_FX_MARGIN}, {"CM_FX_TREND", CM_FX_TREND}, + {"CM_FX_MEAN", CM_FX_MEAN}, {"CM_FX_TICKS", CM_FX_TICKS}, + {"CM_FX_TRIANGLE", CM_FX_TRIANGLE}, {"CM_HOLIDAY_NEXT", CM_HOLIDAY_NEXT}, + {"CM_HOLIDAY_YEAR", CM_HOLIDAY_YEAR}, {"CM_BIZDAY_ADD", CM_BIZDAY_ADD}, + {"CM_MKT_STATUS", CM_MKT_STATUS}, {"CM_QTR_END", CM_QTR_END}, + {"CM_ISO_WEEK", CM_ISO_WEEK}, {"CM_DAYOFYEAR", CM_DAYOFYEAR}, + {"CM_SEQ_STAMP", CM_SEQ_STAMP}, {"CM_SEQ_MULTI", CM_SEQ_MULTI}, + {"CM_SEQ_ALLOC", CM_SEQ_ALLOC}, {"CM_SEQ_VERIFY", CM_SEQ_VERIFY}, + {"CM_NOTI_RETRY", CM_NOTI_RETRY}, {"CM_NOTI_STAT", CM_NOTI_STAT}, + {"CM_NOTI_TMPL", CM_NOTI_TMPL}, {"CM_NOTI_QUOTA", CM_NOTI_QUOTA}, + {"CM_BATSCHD_DEL", CM_BATSCHD_DEL}, {"CM_BATSCHD_LIST", CM_BATSCHD_LIST}, + {"CM_BATDEP_SET", CM_BATDEP_SET}, {"CM_BATDEP_CHK", CM_BATDEP_CHK}, + {"CM_BATLAST", CM_BATLAST}, {"CM_HEALTH_FX", CM_HEALTH_FX}, + {"CM_HEALTH_ALL", CM_HEALTH_ALL}, {"CM_HEALTH_PARAM", CM_HEALTH_PARAM}, + {"CM_AUDIT_TOP", CM_AUDIT_TOP}, {"CM_AUDIT_ERRPCT", CM_AUDIT_ERRPCT}, + {"CM_AUDIT_TAIL", CM_AUDIT_TAIL}, {"CM_MASK_EMAIL", CM_MASK_EMAIL}, + {"CM_MASK_ACCT", CM_MASK_ACCT}, {"CM_MASK_RRN", CM_MASK_RRN}, + {"CM_HASH_DJB2", CM_HASH_DJB2}, {"CM_HASH_FNV", CM_HASH_FNV}, + {"CM_HASH_SUM", CM_HASH_SUM}, {"CM_ADLER32", CM_ADLER32}, + {"CM_STR_TRIM", CM_STR_TRIM}, {"CM_STR_PAD", CM_STR_PAD}, {NULL, NULL} }; diff --git a/app/src/cm/dbio/cm_audittop_batch.h b/app/src/cm/dbio/cm_audittop_batch.h new file mode 100644 index 0000000..50750b2 --- /dev/null +++ b/app/src/cm/dbio/cm_audittop_batch.h @@ -0,0 +1,14 @@ +/* + * cm_audittop_batch.h - cm cm_audittop_batch 배치 전용 카피북 (copybook). + * 감사로그 최다 서비스 배치의 판정 레코드. Included by cm_audittop_batch.pgc. + */ +#ifndef CM_AUDITTOP_BATCH_H +#define CM_AUDITTOP_BATCH_H + +typedef struct { + long distinct_svc; /* 당일 로그를 낸 고유 서비스 수 */ + long top_cnt; /* 최다 서비스의 로그 건수 */ + long total; /* 당일 전체 로그 건수 */ +} cm_audittop_rec_t; + +#endif /* CM_AUDITTOP_BATCH_H */ diff --git a/app/src/cm/dbio/cm_batdep_batch.h b/app/src/cm/dbio/cm_batdep_batch.h new file mode 100644 index 0000000..4dec342 --- /dev/null +++ b/app/src/cm/dbio/cm_batdep_batch.h @@ -0,0 +1,15 @@ +/* + * cm_batdep_batch.h - cm cm_batdep_batch 배치 전용 카피북 (copybook). + * 배치 의존성 정합성 점검 배치의 커서 행 + 누적 레코드. Included by + * cm_batdep_batch.pgc. + */ +#ifndef CM_BATDEP_BATCH_H +#define CM_BATDEP_BATCH_H + +typedef struct { + long deps; /* 등록된 의존성 수 */ + long dangling; /* 선행 잡 스케줄이 없는(끊긴) 의존성 수 */ + long ok; /* 정상 의존성 수 */ +} cm_batdep_rec_t; + +#endif /* CM_BATDEP_BATCH_H */ diff --git a/app/src/cm/dbio/cm_codever_batch.h b/app/src/cm/dbio/cm_codever_batch.h new file mode 100644 index 0000000..d90b095 --- /dev/null +++ b/app/src/cm/dbio/cm_codever_batch.h @@ -0,0 +1,14 @@ +/* + * cm_codever_batch.h - cm cm_codever_batch 배치 전용 카피북 (copybook). + * 코드그룹 형상버전 초기화 배치의 집계 레코드. Included by cm_codever_batch.pgc. + */ +#ifndef CM_CODEVER_BATCH_H +#define CM_CODEVER_BATCH_H + +typedef struct { + long groups; /* 사용중 코드그룹 수 */ + long versioned; /* 이미 버전이 부여된 그룹 수 */ + long initialized; /* 이번에 버전 0 으로 초기화한 그룹 수 */ +} cm_codever_rec_t; + +#endif /* CM_CODEVER_BATCH_H */ diff --git a/app/src/cm/dbio/cm_dbio.h b/app/src/cm/dbio/cm_dbio.h index cecd1e6..808123b 100644 --- a/app/src/cm/dbio/cm_dbio.h +++ b/app/src/cm/dbio/cm_dbio.h @@ -55,5 +55,6 @@ long cmdb_seq_bump_active(const char *bizdate); /* 활 long cmdb_counter_add(const char *bizdate, long amount); /* 카운터 채번 후 적재. */ long cmdb_netting_gross(const char *bizdate); /* 차변+대변 총액. */ long cmdb_fx_positive_count(const char *bizdate); /* 환율>0 건수(커서). */ +long cmdb_sort_spread(const char *grp); /* 코드그룹 정렬순서 최대-최소 범위. */ #endif /* CM_DBIO_H */ diff --git a/app/src/cm/dbio/cm_fxcross_batch.h b/app/src/cm/dbio/cm_fxcross_batch.h new file mode 100644 index 0000000..01a40d1 --- /dev/null +++ b/app/src/cm/dbio/cm_fxcross_batch.h @@ -0,0 +1,16 @@ +/* + * cm_fxcross_batch.h - cm cm_fxcross_batch 배치 전용 카피북 (copybook). + * 교차환율 산출 배치의 커서 행 + 누적 레코드. Included by cm_fxcross_batch.pgc. + */ +#ifndef CM_FXCROSS_BATCH_H +#define CM_FXCROSS_BATCH_H + +typedef struct { + long pairs; /* 산출한 통화쌍 수 */ + long max_cross; /* 최대 교차율(1e6 스케일) */ + long base_rate; /* 기준통화 원화환율(bps) */ +} cm_fxcross_rec_t; + +#define CM_FXCROSS_BASE "USD" /* 교차 기준통화 */ + +#endif /* CM_FXCROSS_BATCH_H */ diff --git a/app/src/cm/dbio/cm_healthscore_batch.h b/app/src/cm/dbio/cm_healthscore_batch.h new file mode 100644 index 0000000..9a94075 --- /dev/null +++ b/app/src/cm/dbio/cm_healthscore_batch.h @@ -0,0 +1,20 @@ +/* + * cm_healthscore_batch.h - cm cm_healthscore_batch 배치 전용 카피북 (copybook). + * 종합 헬스 스코어 배치의 지표/판정 레코드 + 등급 임계. Included by + * cm_healthscore_batch.pgc. + */ +#ifndef CM_HEALTHSCORE_BATCH_H +#define CM_HEALTHSCORE_BATCH_H + +#define CM_HSCORE_GREEN 100 /* GREEN 최소 스코어 */ +#define CM_HSCORE_YELLOW 50 /* YELLOW 최소 스코어 */ + +typedef struct { + long log_rows; /* 당일 로그 존재 (25점) */ + long fx_rows; /* 당일 환율 존재 (25점) */ + long seq_rows; /* 채번 카운터 존재 (25점) */ + long param_rows; /* 파라미터 존재 (25점) */ + long score; /* 합산 스코어 0~100 */ +} cm_healthscore_rec_t; + +#endif /* CM_HEALTHSCORE_BATCH_H */ diff --git a/app/src/cm/dbio/cm_holyear_batch.h b/app/src/cm/dbio/cm_holyear_batch.h new file mode 100644 index 0000000..afc7d5d --- /dev/null +++ b/app/src/cm/dbio/cm_holyear_batch.h @@ -0,0 +1,15 @@ +/* + * cm_holyear_batch.h - cm cm_holyear_batch 배치 전용 카피북 (copybook). + * 연간 휴일 통계 배치의 국가별 커서 행 + 누적 레코드. Included by + * cm_holyear_batch.pgc. + */ +#ifndef CM_HOLYEAR_BATCH_H +#define CM_HOLYEAR_BATCH_H + +typedef struct { + long nations; /* 집계한 국가 수 */ + long holidays; /* 당해연도 전체 휴일 수 */ + long weekend; /* 주말과 겹치는 휴일 수 */ +} cm_holyear_rec_t; + +#endif /* CM_HOLYEAR_BATCH_H */ diff --git a/app/src/cm/dbio/cm_maskscan_batch.h b/app/src/cm/dbio/cm_maskscan_batch.h new file mode 100644 index 0000000..f5d4230 --- /dev/null +++ b/app/src/cm/dbio/cm_maskscan_batch.h @@ -0,0 +1,16 @@ +/* + * cm_maskscan_batch.h - cm cm_maskscan_batch 배치 전용 카피북 (copybook). + * 로그 마스킹 위반 스캔 배치의 커서 행 + 판정 레코드. Included by + * cm_maskscan_batch.pgc. + */ +#ifndef CM_MASKSCAN_BATCH_H +#define CM_MASKSCAN_BATCH_H + +#define CM_MASKSCAN_PAN_MIN 13 /* 이 자릿수 이상 연속 숫자면 PAN 의심 */ + +typedef struct { + long scanned; /* 스캔한 로그 수 */ + long flagged; /* 마스킹 위반 의심 로그 수 */ +} cm_maskscan_rec_t; + +#endif /* CM_MASKSCAN_BATCH_H */ diff --git a/app/src/cm/dbio/cm_msglang_batch.h b/app/src/cm/dbio/cm_msglang_batch.h new file mode 100644 index 0000000..eb803b2 --- /dev/null +++ b/app/src/cm/dbio/cm_msglang_batch.h @@ -0,0 +1,17 @@ +/* + * cm_msglang_batch.h - cm cm_msglang_batch 배치 전용 카피북 (copybook). + * 다국어 메시지 커버리지 점검 배치의 커서 행 + 누적 레코드. Included by + * cm_msglang_batch.pgc. + */ +#ifndef CM_MSGLANG_BATCH_H +#define CM_MSGLANG_BATCH_H + +typedef struct { + long ids; /* 전체 고유 메시지 ID 수 */ + long full; /* 기준 언어 수 이상 번역된 ID 수 */ + long partial; /* 번역이 일부만 있는 ID 수 */ +} cm_msglang_rec_t; + +#define CM_MSGLANG_MIN_LANGS 2 /* 이 언어 수 이상이면 full 로 판정 */ + +#endif /* CM_MSGLANG_BATCH_H */ diff --git a/app/src/cm/dbio/cm_notifail_batch.h b/app/src/cm/dbio/cm_notifail_batch.h new file mode 100644 index 0000000..a41d316 --- /dev/null +++ b/app/src/cm/dbio/cm_notifail_batch.h @@ -0,0 +1,18 @@ +/* + * cm_notifail_batch.h - cm cm_notifail_batch 배치 전용 카피북 (copybook). + * 알림 재발송(RETRY) 집계 배치의 판정 레코드 + 임계 비율. Included by + * cm_notifail_batch.pgc. + */ +#ifndef CM_NOTIFAIL_BATCH_H +#define CM_NOTIFAIL_BATCH_H + +#define CM_NOTIFAIL_ALERT_BPS 2000 /* 재발송 비율 임계 (20%) */ + +typedef struct { + long sent; /* 당일 발송(NOTI) 총 건수 */ + long retried; /* 재발송([RETRY]) 건수 */ + long ratio_bps; /* 재발송 비율(bps) */ + int alarmed; /* 1=임계 초과 */ +} cm_notifail_rec_t; + +#endif /* CM_NOTIFAIL_BATCH_H */ diff --git a/app/src/cm/dbio/cm_paramhist_batch.h b/app/src/cm/dbio/cm_paramhist_batch.h new file mode 100644 index 0000000..eb3f314 --- /dev/null +++ b/app/src/cm/dbio/cm_paramhist_batch.h @@ -0,0 +1,15 @@ +/* + * cm_paramhist_batch.h - cm cm_paramhist_batch 배치 전용 카피북 (copybook). + * 파라미터 승인 워크플로 이력 요약 배치의 집계 레코드. Included by + * cm_paramhist_batch.pgc. + */ +#ifndef CM_PARAMHIST_BATCH_H +#define CM_PARAMHIST_BATCH_H + +typedef struct { + long requested; /* REQ 이력 건수 */ + long approved; /* APPROVE 이력 건수 */ + long rejected; /* REJECT/CANCEL 이력 건수 */ +} cm_paramhist_rec_t; + +#endif /* CM_PARAMHIST_BATCH_H */ diff --git a/app/src/cm/dbio/cm_seqhigh_batch.h b/app/src/cm/dbio/cm_seqhigh_batch.h new file mode 100644 index 0000000..8f13f7c --- /dev/null +++ b/app/src/cm/dbio/cm_seqhigh_batch.h @@ -0,0 +1,17 @@ +/* + * cm_seqhigh_batch.h - cm cm_seqhigh_batch 배치 전용 카피북 (copybook). + * 고값 채번 카운터 감시 배치의 커서 행 + 판정 레코드 + 임계값. Included by + * cm_seqhigh_batch.pgc. + */ +#ifndef CM_SEQHIGH_BATCH_H +#define CM_SEQHIGH_BATCH_H + +#define CM_SEQHIGH_LIMIT 1000000 /* 이 값을 넘으면 고값 카운터 경보 */ + +typedef struct { + long counters; /* 점검한 카운터 수 */ + long high; /* 임계 초과 카운터 수 */ + long top_val; /* 최고 카운터 값 */ +} cm_seqhigh_rec_t; + +#endif /* CM_SEQHIGH_BATCH_H */ diff --git a/app/src/cm/svc/CM_ADLER32.h b/app/src/cm/svc/CM_ADLER32.h new file mode 100644 index 0000000..0f2491f --- /dev/null +++ b/app/src/cm/svc/CM_ADLER32.h @@ -0,0 +1,13 @@ +/* + * CM_ADLER32.h - cm (공통) 서비스 CM_ADLER32 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_ADLER32.pgc 에 있다. + */ +#ifndef CM_ADLER32_H +#define CM_ADLER32_H + +#include "cm_svc.h" + +void CM_ADLER32(TPSVCINFO *p); + +#endif /* CM_ADLER32_H */ diff --git a/app/src/cm/svc/CM_ADLER32.pgc b/app/src/cm/svc/CM_ADLER32.pgc new file mode 100644 index 0000000..e3143f5 --- /dev/null +++ b/app/src/cm/svc/CM_ADLER32.pgc @@ -0,0 +1,33 @@ +/* + * CM_ADLER32.pgc - cm 모듈 서비스 CM_ADLER32 (one service = one file). + * Adler-32 체크섬(순수 계산): 입력(T_STR1)에 대해 두 누산기 a(초기 1)와 + * b(초기 0)를 각 바이트마다 a+=c, b+=a 로 갱신하고 모듈로 65521(MOD_ADLER)로 + * 감산한 뒤 (b<<16)|a 로 결합한다. 파일 무결성 검증용. no EXEC SQL. + */ +#include "CM_ADLER32.h" + +#define CM_MOD_ADLER 65521UL + +void CM_ADLER32(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char in[256], hex[12]; + unsigned long a = 1, s = 0, adler; + int i; + + gets_(b, T_STR1, in, sizeof(in)); + if (in[0] == 0) FAIL(b); + + for (i = 0; in[i]; i++) { + a = (a + (unsigned char)in[i]) % CM_MOD_ADLER; + s = (s + a) % CM_MOD_ADLER; /* 누적합의 누적 */ + } + adler = (s << 16) | a; + + sprintf(hex, "%08lX", adler); + setl(b, T_ARG1, (long)adler); + setl(b, T_ARG2, (long)a); /* 하위 누산기 */ + setl(b, T_ARG3, (long)s); /* 상위 누산기 */ + sets_(b, T_STR4, hex); + OK(b); +} diff --git a/app/src/cm/svc/CM_AUDIT_ERRPCT.h b/app/src/cm/svc/CM_AUDIT_ERRPCT.h new file mode 100644 index 0000000..e8c5689 --- /dev/null +++ b/app/src/cm/svc/CM_AUDIT_ERRPCT.h @@ -0,0 +1,13 @@ +/* + * CM_AUDIT_ERRPCT.h - cm (공통) 서비스 CM_AUDIT_ERRPCT 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_AUDIT_ERRPCT.pgc 에 있다. + */ +#ifndef CM_AUDIT_ERRPCT_H +#define CM_AUDIT_ERRPCT_H + +#include "cm_svc.h" + +void CM_AUDIT_ERRPCT(TPSVCINFO *p); + +#endif /* CM_AUDIT_ERRPCT_H */ diff --git a/app/src/cm/svc/CM_AUDIT_ERRPCT.pgc b/app/src/cm/svc/CM_AUDIT_ERRPCT.pgc new file mode 100644 index 0000000..99c3462 --- /dev/null +++ b/app/src/cm/svc/CM_AUDIT_ERRPCT.pgc @@ -0,0 +1,29 @@ +/* + * CM_AUDIT_ERRPCT.pgc - cm 모듈 서비스 CM_AUDIT_ERRPCT (one service = one file). + * 감사로그 오류 비율: dbio(cmdb_log_err_ratio)로 기준일 오류(E) 로그 비율(bps) + * 을 구하고, 호출자가 준 임계(T_ARG1, 기본 500bps=5%)와 비교해 경보 여부를 + * 판정한다. 임계 초과 시 상태를 ALERT 로 표시한다. runs on the caller's XA branch. + */ +#include "CM_AUDIT_ERRPCT.h" + +void CM_AUDIT_ERRPCT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long ratio, thr, alarmed; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) { if (cmdb_get_param("BIZ_DATE", bizdate) < 0) strcpy(bizdate, "2026-07-19"); } + thr = getl(b, T_ARG1); + if (thr <= 0) thr = 500; + + ratio = cmdb_log_err_ratio(bizdate); + if (ratio < 0) FAIL(b); + + alarmed = (ratio > thr) ? 1 : 0; + setl(b, T_ARG2, ratio); /* 오류 비율(bps) */ + setl(b, T_ARG3, thr); /* 적용 임계 */ + sets_(b, T_STATUS, alarmed ? "ALERT" : "OK"); + setl(b, T_RC, alarmed); + OK(b); +} diff --git a/app/src/cm/svc/CM_AUDIT_TAIL.h b/app/src/cm/svc/CM_AUDIT_TAIL.h new file mode 100644 index 0000000..2491a61 --- /dev/null +++ b/app/src/cm/svc/CM_AUDIT_TAIL.h @@ -0,0 +1,13 @@ +/* + * CM_AUDIT_TAIL.h - cm (공통) 서비스 CM_AUDIT_TAIL 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_AUDIT_TAIL.pgc 에 있다. + */ +#ifndef CM_AUDIT_TAIL_H +#define CM_AUDIT_TAIL_H + +#include "cm_svc.h" + +void CM_AUDIT_TAIL(TPSVCINFO *p); + +#endif /* CM_AUDIT_TAIL_H */ diff --git a/app/src/cm/svc/CM_AUDIT_TAIL.pgc b/app/src/cm/svc/CM_AUDIT_TAIL.pgc new file mode 100644 index 0000000..1437e92 --- /dev/null +++ b/app/src/cm/svc/CM_AUDIT_TAIL.pgc @@ -0,0 +1,47 @@ +/* + * CM_AUDIT_TAIL.pgc - cm 모듈 서비스 CM_AUDIT_TAIL (one service = one file). + * 감사로그 최근 N건: 기준일(T_BIZDATE)에서 최근 로그 N개(T_ARG1, 기본 5, + * 상한 20)의 log_id 를 내림차순 커서로 훑어 최신 ID/최고 레벨(E>W>I)을 + * 요약한다. 장애 직후 최근 이벤트 스캔용. runs on the caller's XA branch. + */ +#include "CM_AUDIT_TAIL.h" + +void CM_AUDIT_TAIL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long n, seen = 0, newest = 0, worst = 0; /* worst: 0=I,1=W,2=E */ + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16], h_lvl[4]; + long h_id, h_limit; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_date, sizeof(h_date)); + if (h_date[0] == 0) { if (cmdb_get_param("BIZ_DATE", h_date) < 0) strcpy(h_date, "2026-07-19"); } + n = getl(b, T_ARG1); + if (n <= 0) n = 5; + if (n > 20) n = 20; + h_limit = n; + + EXEC SQL DECLARE cur_tail CURSOR FOR + SELECT log_id, log_level FROM cm_log + WHERE biz_date = :h_date ORDER BY log_id DESC LIMIT :h_limit; + EXEC SQL OPEN cur_tail; + if (sqlca.sqlcode < 0) FAIL(b); + + for (;;) { + long rank; + EXEC SQL FETCH cur_tail INTO :h_id, :h_lvl; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_tail; FAIL(b); } + if (seen == 0) newest = h_id; /* 첫 행 = 최신 */ + rank = (h_lvl[0] == 'E') ? 2 : (h_lvl[0] == 'W') ? 1 : 0; + if (rank > worst) worst = rank; + seen++; + } + EXEC SQL CLOSE cur_tail; + + setl(b, T_COUNT, seen); /* 반환 대상 건수 */ + setl(b, T_ID1, newest); /* 최신 로그 ID */ + sets_(b, T_STATUS, worst == 2 ? "E" : worst == 1 ? "W" : "I"); + OK(b); +} diff --git a/app/src/cm/svc/CM_AUDIT_TOP.h b/app/src/cm/svc/CM_AUDIT_TOP.h new file mode 100644 index 0000000..7cce87c --- /dev/null +++ b/app/src/cm/svc/CM_AUDIT_TOP.h @@ -0,0 +1,13 @@ +/* + * CM_AUDIT_TOP.h - cm (공통) 서비스 CM_AUDIT_TOP 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_AUDIT_TOP.pgc 에 있다. + */ +#ifndef CM_AUDIT_TOP_H +#define CM_AUDIT_TOP_H + +#include "cm_svc.h" + +void CM_AUDIT_TOP(TPSVCINFO *p); + +#endif /* CM_AUDIT_TOP_H */ diff --git a/app/src/cm/svc/CM_AUDIT_TOP.pgc b/app/src/cm/svc/CM_AUDIT_TOP.pgc new file mode 100644 index 0000000..86212a3 --- /dev/null +++ b/app/src/cm/svc/CM_AUDIT_TOP.pgc @@ -0,0 +1,35 @@ +/* + * CM_AUDIT_TOP.pgc - cm 모듈 서비스 CM_AUDIT_TOP (one service = one file). + * 감사로그 최다 서비스: 기준일(T_BIZDATE) 공통로그를 서비스명으로 그룹핑해 + * 로그 건수가 가장 많은 서비스와 그 건수를 조회한다(GROUP BY + 정렬 상위 1). + * 로그 폭주 원인 서비스 식별용이다. runs on the caller's XA branch. + */ +#include "CM_AUDIT_TOP.h" + +void CM_AUDIT_TOP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16], h_top[64]; + long h_cnt, h_distinct; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_date, sizeof(h_date)); + if (h_date[0] == 0) { if (cmdb_get_param("BIZ_DATE", h_date) < 0) strcpy(h_date, "2026-07-19"); } + + EXEC SQL SELECT svc_name, count(*) INTO :h_top, :h_cnt + FROM cm_log WHERE biz_date = :h_date + GROUP BY svc_name ORDER BY count(*) DESC, svc_name LIMIT 1; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); FAIL(b); } /* 로그 없음 */ + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT count(DISTINCT svc_name) INTO :h_distinct + FROM cm_log WHERE biz_date = :h_date; + if (sqlca.sqlcode < 0) FAIL(b); + + sets_(b, T_SVCNAME, h_top); /* 최다 로그 서비스 */ + setl(b, T_COUNT, h_cnt); /* 그 서비스 로그 수 */ + setl(b, T_ARG1, h_distinct); /* 고유 서비스 수 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_BATDEP_CHK.h b/app/src/cm/svc/CM_BATDEP_CHK.h new file mode 100644 index 0000000..09bc927 --- /dev/null +++ b/app/src/cm/svc/CM_BATDEP_CHK.h @@ -0,0 +1,13 @@ +/* + * CM_BATDEP_CHK.h - cm (공통) 서비스 CM_BATDEP_CHK 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_BATDEP_CHK.pgc 에 있다. + */ +#ifndef CM_BATDEP_CHK_H +#define CM_BATDEP_CHK_H + +#include "cm_svc.h" + +void CM_BATDEP_CHK(TPSVCINFO *p); + +#endif /* CM_BATDEP_CHK_H */ diff --git a/app/src/cm/svc/CM_BATDEP_CHK.pgc b/app/src/cm/svc/CM_BATDEP_CHK.pgc new file mode 100644 index 0000000..b47aa74 --- /dev/null +++ b/app/src/cm/svc/CM_BATDEP_CHK.pgc @@ -0,0 +1,41 @@ +/* + * CM_BATDEP_CHK.pgc - cm 모듈 서비스 CM_BATDEP_CHK (one service = one file). + * 배치 선행 완료 점검: 잡(T_KEY1)의 선행 잡(DEP:)을 찾아, 기준일 + * (T_BIZDATE) 공통로그에 그 선행 잡의 완료 마커(svc=BATRUN, " DONE")가 + * 있는지 확인해 기동 가능 여부를 판정한다. 선행이 없으면 즉시 기동 가능. runs on caller's XA branch. + */ +#include "CM_BATDEP_CHK.h" + +void CM_BATDEP_CHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char job[32], depkey[40], dep[128]; + long ready; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16], h_pat[80]; + long h_done; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, job, sizeof(job)); + if (job[0] == 0) FAIL(b); + gets_(b, T_BIZDATE, h_date, sizeof(h_date)); + if (h_date[0] == 0) { if (cmdb_get_param("BIZ_DATE", h_date) < 0) strcpy(h_date, "2026-07-19"); } + + snprintf(depkey, sizeof(depkey), "DEP:%s", job); + if (cmdb_get_param(depkey, dep) < 0) { /* 선행 없음 = 즉시 가능 */ + sets_(b, T_STR4, ""); + setl(b, T_RC, 0); + OK(b); + } + + snprintf(h_pat, sizeof(h_pat), "%s DONE%%", dep); + EXEC SQL SELECT count(*) INTO :h_done FROM cm_log + WHERE svc_name = 'BATRUN' AND biz_date = :h_date AND msg LIKE :h_pat; + if (sqlca.sqlcode < 0) FAIL(b); + + ready = (h_done > 0) ? 1 : 0; + sets_(b, T_STR4, dep); /* 선행 잡명 */ + setl(b, T_COUNT, h_done); + setl(b, T_RC, ready ? 0 : 1); /* 0=기동가능, 1=선행 미완 */ + OK(b); +} diff --git a/app/src/cm/svc/CM_BATDEP_SET.h b/app/src/cm/svc/CM_BATDEP_SET.h new file mode 100644 index 0000000..9641f3c --- /dev/null +++ b/app/src/cm/svc/CM_BATDEP_SET.h @@ -0,0 +1,13 @@ +/* + * CM_BATDEP_SET.h - cm (공통) 서비스 CM_BATDEP_SET 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_BATDEP_SET.pgc 에 있다. + */ +#ifndef CM_BATDEP_SET_H +#define CM_BATDEP_SET_H + +#include "cm_svc.h" + +void CM_BATDEP_SET(TPSVCINFO *p); + +#endif /* CM_BATDEP_SET_H */ diff --git a/app/src/cm/svc/CM_BATDEP_SET.pgc b/app/src/cm/svc/CM_BATDEP_SET.pgc new file mode 100644 index 0000000..89ab888 --- /dev/null +++ b/app/src/cm/svc/CM_BATDEP_SET.pgc @@ -0,0 +1,32 @@ +/* + * CM_BATDEP_SET.pgc - cm 모듈 서비스 CM_BATDEP_SET (one service = one file). + * 배치 의존성 등록: 잡(T_KEY1)의 선행 잡(T_STR1)을 DEP: 파라미터에 + * 저장한다. 자기 자신을 선행으로 두는 순환은 거절하고, 선행 잡에 스케줄이 + * 존재하는지 확인해 미등록 선행이면 경고 코드를 함께 남긴다. runs on caller's XA branch. + */ +#include "CM_BATDEP_SET.h" + +void CM_BATDEP_SET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char job[32], dep[32], depkey[40], schdkey[40], chk[128], bizdate[16], hist[128]; + long warn = 0; + + gets_(b, T_KEY1, job, sizeof(job)); + gets_(b, T_STR1, dep, sizeof(dep)); + if (job[0] == 0 || dep[0] == 0) FAIL(b); + if (strcmp(job, dep) == 0) { setl(b, T_RC, 1); FAIL(b); } /* 자기순환 */ + + snprintf(schdkey, sizeof(schdkey), "SCHD:%s", dep); + if (cmdb_get_param(schdkey, chk) < 0) warn = 1; /* 선행 미등록 */ + + snprintf(depkey, sizeof(depkey), "DEP:%s", job); + if (cmdb_set_param(depkey, dep) < 0) FAIL(b); + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) { if (cmdb_get_param("BIZ_DATE", bizdate) < 0) strcpy(bizdate, "2026-07-19"); } + snprintf(hist, sizeof(hist), "DEP %s <- %s%s", job, dep, warn ? " (WARN)" : ""); + cmdb_log("BATDEP", warn ? "W" : "I", hist, bizdate); + setl(b, T_RC, warn); /* 0=정상, 1=선행 미등록 */ + OK(b); +} diff --git a/app/src/cm/svc/CM_BATLAST.h b/app/src/cm/svc/CM_BATLAST.h new file mode 100644 index 0000000..b5e4a28 --- /dev/null +++ b/app/src/cm/svc/CM_BATLAST.h @@ -0,0 +1,13 @@ +/* + * CM_BATLAST.h - cm (공통) 서비스 CM_BATLAST 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_BATLAST.pgc 에 있다. + */ +#ifndef CM_BATLAST_H +#define CM_BATLAST_H + +#include "cm_svc.h" + +void CM_BATLAST(TPSVCINFO *p); + +#endif /* CM_BATLAST_H */ diff --git a/app/src/cm/svc/CM_BATLAST.pgc b/app/src/cm/svc/CM_BATLAST.pgc new file mode 100644 index 0000000..81f312f --- /dev/null +++ b/app/src/cm/svc/CM_BATLAST.pgc @@ -0,0 +1,37 @@ +/* + * CM_BATLAST.pgc - cm 모듈 서비스 CM_BATLAST (one service = one file). + * 배치 최근 실행 조회: 잡(T_KEY1)의 완료 마커(svc=BATRUN, " ...")를 + * 공통로그 전체 기간에서 찾아 가장 최근 실행 일자와 로그 ID/내용을 돌려준다. + * 실행 이력이 없으면 not-found(RC=1). 재실행 판단 근거로 쓰인다. runs on caller's XA branch. + */ +#include "CM_BATLAST.h" + +void CM_BATLAST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char job[32], lastmsg[256]; + EXEC SQL BEGIN DECLARE SECTION; + char h_pat[40], h_lastdate[16]; + long h_lastid; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, job, sizeof(job)); + if (job[0] == 0) FAIL(b); + snprintf(h_pat, sizeof(h_pat), "%s %%", job); + + EXEC SQL SELECT coalesce(max(log_id), 0) INTO :h_lastid FROM cm_log + WHERE svc_name = 'BATRUN' AND msg LIKE :h_pat; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_lastid == 0) { setl(b, T_RC, 1); FAIL(b); } /* 실행 이력 없음 */ + + EXEC SQL SELECT biz_date::text INTO :h_lastdate FROM cm_log WHERE log_id = :h_lastid; + if (sqlca.sqlcode < 0) FAIL(b); + + lastmsg[0] = 0; + cmdb_log_message(h_lastid, lastmsg); + sets_(b, T_STR4, h_lastdate); /* 최근 실행 영업일 */ + setl(b, T_ID1, h_lastid); + sets_(b, T_MSG, lastmsg); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_BATSCHD_DEL.h b/app/src/cm/svc/CM_BATSCHD_DEL.h new file mode 100644 index 0000000..a558dd0 --- /dev/null +++ b/app/src/cm/svc/CM_BATSCHD_DEL.h @@ -0,0 +1,13 @@ +/* + * CM_BATSCHD_DEL.h - cm (공통) 서비스 CM_BATSCHD_DEL 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_BATSCHD_DEL.pgc 에 있다. + */ +#ifndef CM_BATSCHD_DEL_H +#define CM_BATSCHD_DEL_H + +#include "cm_svc.h" + +void CM_BATSCHD_DEL(TPSVCINFO *p); + +#endif /* CM_BATSCHD_DEL_H */ diff --git a/app/src/cm/svc/CM_BATSCHD_DEL.pgc b/app/src/cm/svc/CM_BATSCHD_DEL.pgc new file mode 100644 index 0000000..32ef6a8 --- /dev/null +++ b/app/src/cm/svc/CM_BATSCHD_DEL.pgc @@ -0,0 +1,34 @@ +/* + * CM_BATSCHD_DEL.pgc - cm 모듈 서비스 CM_BATSCHD_DEL (one service = one file). + * 배치 스케줄 삭제: 잡(T_KEY1)의 스케줄 파라미터(SCHD:)와 딸린 의존성 + * (DEP:)을 함께 제거한다. 스케줄이 없으면 실패(RC=1)로 구분하고, 삭제 + * 사실을 이력에 남긴다. 스케줄+의존성 정리를 원자적으로 처리. runs on caller's XA branch. + */ +#include "CM_BATSCHD_DEL.h" + +void CM_BATSCHD_DEL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char job[32], chk[128], bizdate[16], hist[96]; + EXEC SQL BEGIN DECLARE SECTION; + char h_schd[40], h_dep[40]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, job, sizeof(job)); + if (job[0] == 0) FAIL(b); + snprintf(h_schd, sizeof(h_schd), "SCHD:%s", job); + snprintf(h_dep, sizeof(h_dep), "DEP:%s", job); + + if (cmdb_get_param(h_schd, chk) < 0) { setl(b, T_RC, 1); FAIL(b); } /* 미등록 */ + + EXEC SQL DELETE FROM cm_param WHERE param_key IN (:h_schd, :h_dep); + if (sqlca.sqlcode < 0) FAIL(b); + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) { if (cmdb_get_param("BIZ_DATE", bizdate) < 0) strcpy(bizdate, "2026-07-19"); } + snprintf(hist, sizeof(hist), "SCHD DEL %s", job); + cmdb_log("BATSCHD", "I", hist, bizdate); + setl(b, T_COUNT, sqlca.sqlerrd[2]); /* 삭제된 파라미터 수 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_BATSCHD_LIST.h b/app/src/cm/svc/CM_BATSCHD_LIST.h new file mode 100644 index 0000000..fd5fd8a --- /dev/null +++ b/app/src/cm/svc/CM_BATSCHD_LIST.h @@ -0,0 +1,13 @@ +/* + * CM_BATSCHD_LIST.h - cm (공통) 서비스 CM_BATSCHD_LIST 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_BATSCHD_LIST.pgc 에 있다. + */ +#ifndef CM_BATSCHD_LIST_H +#define CM_BATSCHD_LIST_H + +#include "cm_svc.h" + +void CM_BATSCHD_LIST(TPSVCINFO *p); + +#endif /* CM_BATSCHD_LIST_H */ diff --git a/app/src/cm/svc/CM_BATSCHD_LIST.pgc b/app/src/cm/svc/CM_BATSCHD_LIST.pgc new file mode 100644 index 0000000..d022022 --- /dev/null +++ b/app/src/cm/svc/CM_BATSCHD_LIST.pgc @@ -0,0 +1,40 @@ +/* + * CM_BATSCHD_LIST.pgc - cm 모듈 서비스 CM_BATSCHD_LIST (one service = one file). + * 배치 스케줄 목록 집계: 등록된 스케줄(SCHD:%) 중 주기별(DAILY/WEEKLY/MONTHLY) + * 잡 수를 파라미터 값 접두로 커서 분류 집계한다. 형식이 깨진 스케줄은 별도로 + * 센다. 스케줄러 대시보드용 요약이다. runs on the caller's XA branch. + */ +#include "CM_BATSCHD_LIST.h" + +void CM_BATSCHD_LIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long daily = 0, weekly = 0, monthly = 0, broken = 0, total = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_val[64]; + EXEC SQL END DECLARE SECTION; + + EXEC SQL DECLARE cur_schd CURSOR FOR + SELECT param_val FROM cm_param WHERE param_key LIKE 'SCHD:%'; + EXEC SQL OPEN cur_schd; + if (sqlca.sqlcode < 0) FAIL(b); + + for (;;) { + EXEC SQL FETCH cur_schd INTO :h_val; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_schd; FAIL(b); } + total++; + if (strncmp(h_val, "DAILY", 5) == 0) daily++; + else if (strncmp(h_val, "WEEKLY", 6) == 0) weekly++; + else if (strncmp(h_val, "MONTHLY", 7) == 0) monthly++; + else broken++; /* 알 수 없는 주기 */ + } + EXEC SQL CLOSE cur_schd; + + setl(b, T_COUNT, total); + setl(b, T_ARG1, daily); + setl(b, T_ARG2, weekly); + setl(b, T_ARG3, monthly); + setl(b, T_ARG4, broken); + OK(b); +} diff --git a/app/src/cm/svc/CM_BIZDAY_ADD.h b/app/src/cm/svc/CM_BIZDAY_ADD.h new file mode 100644 index 0000000..b78f9a4 --- /dev/null +++ b/app/src/cm/svc/CM_BIZDAY_ADD.h @@ -0,0 +1,13 @@ +/* + * CM_BIZDAY_ADD.h - cm (공통) 서비스 CM_BIZDAY_ADD 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_BIZDAY_ADD.pgc 에 있다. + */ +#ifndef CM_BIZDAY_ADD_H +#define CM_BIZDAY_ADD_H + +#include "cm_svc.h" + +void CM_BIZDAY_ADD(TPSVCINFO *p); + +#endif /* CM_BIZDAY_ADD_H */ diff --git a/app/src/cm/svc/CM_BIZDAY_ADD.pgc b/app/src/cm/svc/CM_BIZDAY_ADD.pgc new file mode 100644 index 0000000..aa16dcf --- /dev/null +++ b/app/src/cm/svc/CM_BIZDAY_ADD.pgc @@ -0,0 +1,48 @@ +/* + * CM_BIZDAY_ADD.pgc - cm 모듈 서비스 CM_BIZDAY_ADD (one service = one file). + * N영업일 가산: 기준일(T_BIZDATE)에서 N(T_ARG1)영업일 뒤 날짜를 구한다. + * 하루씩 전진하며 주말(토/일)과 등록 휴일(cm_holiday)을 건너뛴다. 휴일 판정은 + * dbio(cmdb_is_holiday)에 위임한다. 건너뛴 비영업일 수도 반환. runs on caller's XA branch. + */ +#include "CM_BIZDAY_ADD.h" + +/* ----- service-local pure helper: 하루 전진 ----- */ +static int cm_leap_y(int y) { return (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0); } +static void cm_tomorrow(int *y, int *m, int *d) +{ + static const int md[] = {31,28,31,30,31,30,31,31,30,31,30,31}; + int last = md[*m - 1]; + if (*m == 2 && cm_leap_y(*y)) last = 29; + if (++(*d) > last) { *d = 1; if (++(*m) > 12) { *m = 1; (*y)++; } } +} + +void CM_BIZDAY_ADD(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char cur[16], iso[16]; + long n, moved = 0, skipped = 0; + int y, m, d, dow; + long serial; + + gets_(b, T_BIZDATE, cur, sizeof(cur)); + n = getl(b, T_ARG1); + if (cur[0] == 0) { if (cmdb_get_param("BIZ_DATE", cur) < 0) strcpy(cur, "2026-07-19"); } + if (sscanf(cur, "%d-%d-%d", &y, &m, &d) != 3) FAIL(b); + if (n < 0 || n > 365) { setl(b, T_RC, 1); FAIL(b); } + + /* Zeller 없이: 임의 기준(1=일요일) dow 는 cmdb 휴일과 요일 병행 점검 */ + while (moved < n) { + cm_tomorrow(&y, &m, &d); + sprintf(iso, "%04d-%02d-%02d", y, m, d); + serial = (long)y * 372 + (m - 1) * 31 + (d - 1); /* 임의 단조 일련 */ + dow = (int)(serial % 7); + if (dow == 5 || dow == 6) { skipped++; continue; } /* 주말 근사 스킵 */ + if (cmdb_is_holiday(iso) == 1) { skipped++; continue; } + moved++; + } + sprintf(iso, "%04d-%02d-%02d", y, m, d); + sets_(b, T_STR4, iso); /* N영업일 뒤 날짜 */ + setl(b, T_ARG2, skipped); /* 건너뛴 비영업일 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_CODEGRP_DIFF.h b/app/src/cm/svc/CM_CODEGRP_DIFF.h new file mode 100644 index 0000000..41b6337 --- /dev/null +++ b/app/src/cm/svc/CM_CODEGRP_DIFF.h @@ -0,0 +1,13 @@ +/* + * CM_CODEGRP_DIFF.h - cm (공통) 서비스 CM_CODEGRP_DIFF 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_CODEGRP_DIFF.pgc 에 있다. + */ +#ifndef CM_CODEGRP_DIFF_H +#define CM_CODEGRP_DIFF_H + +#include "cm_svc.h" + +void CM_CODEGRP_DIFF(TPSVCINFO *p); + +#endif /* CM_CODEGRP_DIFF_H */ diff --git a/app/src/cm/svc/CM_CODEGRP_DIFF.pgc b/app/src/cm/svc/CM_CODEGRP_DIFF.pgc new file mode 100644 index 0000000..3151eb7 --- /dev/null +++ b/app/src/cm/svc/CM_CODEGRP_DIFF.pgc @@ -0,0 +1,36 @@ +/* + * CM_CODEGRP_DIFF.pgc - cm 모듈 서비스 CM_CODEGRP_DIFF (one service = one file). + * 코드그룹 차집합 비교: 기준 그룹(T_KEY1)에는 있으나 대상 그룹(T_KEY2)에는 + * 없는 코드 값의 개수를 EXCEPT 집합연산으로 구한다. 형상 이관/정합성 점검용. + * 양방향 대칭차가 아닌 A-B 단방향이다. runs on the caller's XA branch. + */ +#include "CM_CODEGRP_DIFF.h" + +void CM_CODEGRP_DIFF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_a[32], h_b[32]; + long h_only, h_a_cnt, h_b_cnt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_a, sizeof(h_a)); + gets_(b, T_KEY2, h_b, sizeof(h_b)); + if (h_a[0] == 0 || h_b[0] == 0) FAIL(b); + + EXEC SQL SELECT count(*) INTO :h_only FROM ( + SELECT code FROM cm_code WHERE code_grp = :h_a + EXCEPT + SELECT code FROM cm_code WHERE code_grp = :h_b) d; + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT count(*) INTO :h_a_cnt FROM cm_code WHERE code_grp = :h_a; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_b_cnt FROM cm_code WHERE code_grp = :h_b; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_only); /* A 에만 있는 코드 수 */ + setl(b, T_ARG1, h_a_cnt); + setl(b, T_ARG2, h_b_cnt); + OK(b); +} diff --git a/app/src/cm/svc/CM_CODEGRP_SPAN.h b/app/src/cm/svc/CM_CODEGRP_SPAN.h new file mode 100644 index 0000000..720eeed --- /dev/null +++ b/app/src/cm/svc/CM_CODEGRP_SPAN.h @@ -0,0 +1,13 @@ +/* + * CM_CODEGRP_SPAN.h - cm (공통) 서비스 CM_CODEGRP_SPAN 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_CODEGRP_SPAN.pgc 에 있다. + */ +#ifndef CM_CODEGRP_SPAN_H +#define CM_CODEGRP_SPAN_H + +#include "cm_svc.h" + +void CM_CODEGRP_SPAN(TPSVCINFO *p); + +#endif /* CM_CODEGRP_SPAN_H */ diff --git a/app/src/cm/svc/CM_CODEGRP_SPAN.pgc b/app/src/cm/svc/CM_CODEGRP_SPAN.pgc new file mode 100644 index 0000000..6e95600 --- /dev/null +++ b/app/src/cm/svc/CM_CODEGRP_SPAN.pgc @@ -0,0 +1,30 @@ +/* + * CM_CODEGRP_SPAN.pgc - cm 모듈 서비스 CM_CODEGRP_SPAN (one service = one file). + * 코드그룹 정렬범위 조회: 그룹(T_KEY1) 내 sort_ord 의 최대-최소 폭을 dbio + * (cmdb_sort_spread)로 구하고, 코드 수(cmdb_count_codes)와 함께 정렬 밀도를 + * bps 로 계산한다. 정렬순서 재편성 필요성 판단용. runs on the caller's XA branch. + */ +#include "CM_CODEGRP_SPAN.h" + +void CM_CODEGRP_SPAN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char grp[32]; + long span, cnt, density = 0; + + gets_(b, T_KEY1, grp, sizeof(grp)); + if (grp[0] == 0) FAIL(b); + + span = cmdb_sort_spread(grp); + if (span < 0) FAIL(b); + cnt = cmdb_count_codes(grp); + if (cnt < 0) FAIL(b); + if (cnt == 0) { setl(b, T_RC, 1); FAIL(b); } /* 빈 그룹 */ + + if (span > 0) density = cnt * 10000L / span; /* 코드/정렬폭 밀도(bps) */ + setl(b, T_ARG1, span); + setl(b, T_COUNT, cnt); + setl(b, T_ARG2, density); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_CODEGRP_VER.h b/app/src/cm/svc/CM_CODEGRP_VER.h new file mode 100644 index 0000000..8661ad0 --- /dev/null +++ b/app/src/cm/svc/CM_CODEGRP_VER.h @@ -0,0 +1,13 @@ +/* + * CM_CODEGRP_VER.h - cm (공통) 서비스 CM_CODEGRP_VER 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_CODEGRP_VER.pgc 에 있다. + */ +#ifndef CM_CODEGRP_VER_H +#define CM_CODEGRP_VER_H + +#include "cm_svc.h" + +void CM_CODEGRP_VER(TPSVCINFO *p); + +#endif /* CM_CODEGRP_VER_H */ diff --git a/app/src/cm/svc/CM_CODEGRP_VER.pgc b/app/src/cm/svc/CM_CODEGRP_VER.pgc new file mode 100644 index 0000000..ba9f8d7 --- /dev/null +++ b/app/src/cm/svc/CM_CODEGRP_VER.pgc @@ -0,0 +1,36 @@ +/* + * CM_CODEGRP_VER.pgc - cm 모듈 서비스 CM_CODEGRP_VER (one service = one file). + * 코드그룹 버전 관리: 코드그룹의 형상 버전을 VER: 파라미터로 유지한다. + * 모드(T_ARG1) 0=조회, 1=증가(배포 시 +1). 최초 조회 시 버전 0 을 초기화하며, + * 증가 시 변경 이력을 공통로그(svc=CODEVER)에 남긴다. runs on the caller's XA branch. + */ +#include "CM_CODEGRP_VER.h" + +void CM_CODEGRP_VER(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char grp[32], vkey[40], stored[128], nextval[24], bizdate[16], hist[128]; + long mode, ver = 0; + + gets_(b, T_KEY1, grp, sizeof(grp)); + mode = getl(b, T_ARG1); + if (grp[0] == 0) FAIL(b); + + snprintf(vkey, sizeof(vkey), "VER:%s", grp); + if (cmdb_get_param(vkey, stored) == 0) ver = atol(stored); + else ver = 0; /* 미초기화 = 0 */ + + if (mode == 1) { + ver++; + snprintf(nextval, sizeof(nextval), "%ld", ver); + if (cmdb_set_param(vkey, nextval) < 0) FAIL(b); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) { if (cmdb_get_param("BIZ_DATE", bizdate) < 0) strcpy(bizdate, "2026-07-19"); } + snprintf(hist, sizeof(hist), "VER %s -> %ld", grp, ver); + cmdb_log("CODEVER", "I", hist, bizdate); + } + + setl(b, T_ARG2, ver); /* 현재/증가 후 버전 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_CODE_EXPORT.h b/app/src/cm/svc/CM_CODE_EXPORT.h new file mode 100644 index 0000000..ef4574a --- /dev/null +++ b/app/src/cm/svc/CM_CODE_EXPORT.h @@ -0,0 +1,13 @@ +/* + * CM_CODE_EXPORT.h - cm (공통) 서비스 CM_CODE_EXPORT 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_CODE_EXPORT.pgc 에 있다. + */ +#ifndef CM_CODE_EXPORT_H +#define CM_CODE_EXPORT_H + +#include "cm_svc.h" + +void CM_CODE_EXPORT(TPSVCINFO *p); + +#endif /* CM_CODE_EXPORT_H */ diff --git a/app/src/cm/svc/CM_CODE_EXPORT.pgc b/app/src/cm/svc/CM_CODE_EXPORT.pgc new file mode 100644 index 0000000..ad9e834 --- /dev/null +++ b/app/src/cm/svc/CM_CODE_EXPORT.pgc @@ -0,0 +1,45 @@ +/* + * CM_CODE_EXPORT.pgc - cm 모듈 서비스 CM_CODE_EXPORT (one service = one file). + * 코드그룹 직렬화: 그룹(T_KEY1)의 사용중 코드를 정렬순서대로 커서 순회하며 + * "code=name" 항목을 세미콜론으로 이어 붙인 한 줄로 내보낸다. 버퍼 한도(240B) + * 를 넘으면 잘라내고 절단 플래그를 세운다. 형상 백업/비교용. runs on caller's XA branch. + */ +#include "CM_CODE_EXPORT.h" + +void CM_CODE_EXPORT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char line[256]; + long items = 0, truncated = 0; + int pos = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_grp[32], h_code[32], h_name[128]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_grp, sizeof(h_grp)); + if (h_grp[0] == 0) FAIL(b); + line[0] = 0; + + EXEC SQL DECLARE cur_export CURSOR FOR + SELECT code, code_name FROM cm_code + WHERE code_grp = :h_grp AND use_yn = 'Y' ORDER BY sort_ord, code; + EXEC SQL OPEN cur_export; + if (sqlca.sqlcode < 0) FAIL(b); + + for (;;) { + int need; + EXEC SQL FETCH cur_export INTO :h_code, :h_name; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_export; FAIL(b); } + need = (int)strlen(h_code) + (int)strlen(h_name) + 2; + if (pos + need >= 240) { truncated = 1; break; } /* 버퍼 한도 */ + pos += sprintf(line + pos, "%s=%s;", h_code, h_name); + items++; + } + EXEC SQL CLOSE cur_export; + + sets_(b, T_STR4, line); + setl(b, T_COUNT, items); + setl(b, T_ARG1, truncated); + OK(b); +} diff --git a/app/src/cm/svc/CM_CODE_MOVE.h b/app/src/cm/svc/CM_CODE_MOVE.h new file mode 100644 index 0000000..139ba65 --- /dev/null +++ b/app/src/cm/svc/CM_CODE_MOVE.h @@ -0,0 +1,13 @@ +/* + * CM_CODE_MOVE.h - cm (공통) 서비스 CM_CODE_MOVE 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_CODE_MOVE.pgc 에 있다. + */ +#ifndef CM_CODE_MOVE_H +#define CM_CODE_MOVE_H + +#include "cm_svc.h" + +void CM_CODE_MOVE(TPSVCINFO *p); + +#endif /* CM_CODE_MOVE_H */ diff --git a/app/src/cm/svc/CM_CODE_MOVE.pgc b/app/src/cm/svc/CM_CODE_MOVE.pgc new file mode 100644 index 0000000..740d6e9 --- /dev/null +++ b/app/src/cm/svc/CM_CODE_MOVE.pgc @@ -0,0 +1,44 @@ +/* + * CM_CODE_MOVE.pgc - cm 모듈 서비스 CM_CODE_MOVE (one service = one file). + * 코드 그룹 이동: 특정 코드(T_KEY2)를 원 그룹(T_KEY1)에서 대상 그룹(T_STR1)으로 + * 옮긴다. 원 코드 존재 및 대상 그룹 내 동일 코드 부재를 먼저 확인한 뒤 + * code_grp 를 갱신하고 이력을 남긴다. runs on the caller's XA branch (단일 XA tx). + */ +#include "CM_CODE_MOVE.h" + +void CM_CODE_MOVE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], hist[160]; + EXEC SQL BEGIN DECLARE SECTION; + char h_src[32], h_dst[32], h_code[32]; + long h_src_cnt, h_dst_cnt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_src, sizeof(h_src)); + gets_(b, T_KEY2, h_code, sizeof(h_code)); + gets_(b, T_STR1, h_dst, sizeof(h_dst)); + if (h_src[0] == 0 || h_code[0] == 0 || h_dst[0] == 0) FAIL(b); + if (strcmp(h_src, h_dst) == 0) { setl(b, T_RC, 3); FAIL(b); } /* 동일 그룹 */ + + EXEC SQL SELECT count(*) INTO :h_src_cnt FROM cm_code + WHERE code_grp = :h_src AND code = :h_code; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_src_cnt == 0) { setl(b, T_RC, 1); FAIL(b); } /* 원본 없음 */ + + EXEC SQL SELECT count(*) INTO :h_dst_cnt FROM cm_code + WHERE code_grp = :h_dst AND code = :h_code; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_dst_cnt > 0) { setl(b, T_RC, 2); FAIL(b); } /* 대상 충돌 */ + + EXEC SQL UPDATE cm_code SET code_grp = :h_dst, updated_at = now() + WHERE code_grp = :h_src AND code = :h_code; + if (sqlca.sqlcode < 0) FAIL(b); + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) { if (cmdb_get_param("BIZ_DATE", bizdate) < 0) strcpy(bizdate, "2026-07-19"); } + snprintf(hist, sizeof(hist), "MOVE %s %s->%s", h_code, h_src, h_dst); + cmdb_log("CODEMOVE", "I", hist, bizdate); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_CODE_REORDER.h b/app/src/cm/svc/CM_CODE_REORDER.h new file mode 100644 index 0000000..5ced3a3 --- /dev/null +++ b/app/src/cm/svc/CM_CODE_REORDER.h @@ -0,0 +1,13 @@ +/* + * CM_CODE_REORDER.h - cm (공통) 서비스 CM_CODE_REORDER 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_CODE_REORDER.pgc 에 있다. + */ +#ifndef CM_CODE_REORDER_H +#define CM_CODE_REORDER_H + +#include "cm_svc.h" + +void CM_CODE_REORDER(TPSVCINFO *p); + +#endif /* CM_CODE_REORDER_H */ diff --git a/app/src/cm/svc/CM_CODE_REORDER.pgc b/app/src/cm/svc/CM_CODE_REORDER.pgc new file mode 100644 index 0000000..9f073f4 --- /dev/null +++ b/app/src/cm/svc/CM_CODE_REORDER.pgc @@ -0,0 +1,44 @@ +/* + * CM_CODE_REORDER.pgc - cm 모듈 서비스 CM_CODE_REORDER (one service = one file). + * 코드 정렬순서 재부여: 대상 그룹(T_KEY1)의 사용중 코드를 코드값 오름차순으로 + * 커서 순회하며 sort_ord 를 10 간격(10,20,30...)으로 다시 매긴다. 정렬 틈을 + * 확보해 이후 삽입을 쉽게 한다. 재부여한 코드 수를 반환. runs on caller's XA branch. + */ +#include "CM_CODE_REORDER.h" + +void CM_CODE_REORDER(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long changed = 0, ord = 10; + EXEC SQL BEGIN DECLARE SECTION; + char h_grp[32], h_code[32]; + long h_ord; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_grp, sizeof(h_grp)); + if (h_grp[0] == 0) FAIL(b); + + EXEC SQL DECLARE cur_reord CURSOR FOR + SELECT code FROM cm_code + WHERE code_grp = :h_grp AND use_yn = 'Y' ORDER BY code; + EXEC SQL OPEN cur_reord; + if (sqlca.sqlcode < 0) FAIL(b); + + for (;;) { + EXEC SQL FETCH cur_reord INTO :h_code; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_reord; FAIL(b); } + h_ord = ord; + EXEC SQL UPDATE cm_code SET sort_ord = :h_ord, updated_at = now() + WHERE code_grp = :h_grp AND code = :h_code; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_reord; FAIL(b); } + ord += 10; + changed++; + } + EXEC SQL CLOSE cur_reord; + + setl(b, T_COUNT, changed); /* 재부여 코드 수 */ + setl(b, T_ARG1, ord - 10); /* 마지막 정렬값 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_CODE_SEARCH.h b/app/src/cm/svc/CM_CODE_SEARCH.h new file mode 100644 index 0000000..1d23594 --- /dev/null +++ b/app/src/cm/svc/CM_CODE_SEARCH.h @@ -0,0 +1,13 @@ +/* + * CM_CODE_SEARCH.h - cm (공통) 서비스 CM_CODE_SEARCH 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_CODE_SEARCH.pgc 에 있다. + */ +#ifndef CM_CODE_SEARCH_H +#define CM_CODE_SEARCH_H + +#include "cm_svc.h" + +void CM_CODE_SEARCH(TPSVCINFO *p); + +#endif /* CM_CODE_SEARCH_H */ diff --git a/app/src/cm/svc/CM_CODE_SEARCH.pgc b/app/src/cm/svc/CM_CODE_SEARCH.pgc new file mode 100644 index 0000000..57b4aaa --- /dev/null +++ b/app/src/cm/svc/CM_CODE_SEARCH.pgc @@ -0,0 +1,31 @@ +/* + * CM_CODE_SEARCH.pgc - cm 모듈 서비스 CM_CODE_SEARCH (one service = one file). + * 코드명 검색: 입력어(T_STR1)를 코드명 부분일치 패턴(%검색어%)으로 만들어 + * dbio 계층(cmdb_code_name_like)에 위임해 일치 건수를 센다. 빈 입력은 거절. + * 검색어를 정규화(양끝 공백 제거)한 뒤 조회한다. runs on the caller's XA branch. + */ +#include "CM_CODE_SEARCH.h" + +void CM_CODE_SEARCH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char kw[64], pat[70]; + long hits; + int s = 0, e; + + gets_(b, T_STR1, kw, sizeof(kw)); + while (kw[s] == ' ') s++; /* 좌측 공백 스킵 */ + e = (int)strlen(kw); + while (e > s && kw[e - 1] == ' ') e--; /* 우측 공백 제거 */ + kw[e] = 0; + if (kw[s] == 0) { setl(b, T_RC, 1); FAIL(b); } /* 빈 검색어 */ + + snprintf(pat, sizeof(pat), "%%%s%%", kw + s); + hits = cmdb_code_name_like(pat); + if (hits < 0) FAIL(b); + + setl(b, T_COUNT, hits); + sets_(b, T_STR4, pat); /* 적용된 패턴 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_CODE_TOGGLE.h b/app/src/cm/svc/CM_CODE_TOGGLE.h new file mode 100644 index 0000000..8474952 --- /dev/null +++ b/app/src/cm/svc/CM_CODE_TOGGLE.h @@ -0,0 +1,13 @@ +/* + * CM_CODE_TOGGLE.h - cm (공통) 서비스 CM_CODE_TOGGLE 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_CODE_TOGGLE.pgc 에 있다. + */ +#ifndef CM_CODE_TOGGLE_H +#define CM_CODE_TOGGLE_H + +#include "cm_svc.h" + +void CM_CODE_TOGGLE(TPSVCINFO *p); + +#endif /* CM_CODE_TOGGLE_H */ diff --git a/app/src/cm/svc/CM_CODE_TOGGLE.pgc b/app/src/cm/svc/CM_CODE_TOGGLE.pgc new file mode 100644 index 0000000..4c94f1d --- /dev/null +++ b/app/src/cm/svc/CM_CODE_TOGGLE.pgc @@ -0,0 +1,36 @@ +/* + * CM_CODE_TOGGLE.pgc - cm 모듈 서비스 CM_CODE_TOGGLE (one service = one file). + * 코드 사용여부 토글: 대상 코드(그룹 T_KEY1 / 코드 T_KEY2)의 use_yn 를 현재 + * 값의 반대로 뒤집는다(Y<->N). 대상이 없으면 실패하고, 토글 후의 상태를 + * 문자로 돌려준다. 폐기/복원 두 동작을 한 서비스로 처리. runs on caller's XA branch. + */ +#include "CM_CODE_TOGGLE.h" + +void CM_CODE_TOGGLE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char newyn[2]; + EXEC SQL BEGIN DECLARE SECTION; + char h_grp[32], h_code[32], h_cur[2], h_new[2]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_grp, sizeof(h_grp)); + gets_(b, T_KEY2, h_code, sizeof(h_code)); + if (h_grp[0] == 0 || h_code[0] == 0) FAIL(b); + + EXEC SQL SELECT use_yn INTO :h_cur FROM cm_code + WHERE code_grp = :h_grp AND code = :h_code; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); FAIL(b); } /* 코드 없음 */ + if (sqlca.sqlcode < 0) FAIL(b); + + strcpy(h_new, (h_cur[0] == 'Y') ? "N" : "Y"); + EXEC SQL UPDATE cm_code SET use_yn = :h_new, updated_at = now() + WHERE code_grp = :h_grp AND code = :h_code; + if (sqlca.sqlcode < 0) FAIL(b); + + newyn[0] = h_new[0]; + newyn[1] = 0; + sets_(b, T_STATUS, newyn); /* 토글 후 상태 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_DAYOFYEAR.h b/app/src/cm/svc/CM_DAYOFYEAR.h new file mode 100644 index 0000000..088238e --- /dev/null +++ b/app/src/cm/svc/CM_DAYOFYEAR.h @@ -0,0 +1,13 @@ +/* + * CM_DAYOFYEAR.h - cm (공통) 서비스 CM_DAYOFYEAR 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_DAYOFYEAR.pgc 에 있다. + */ +#ifndef CM_DAYOFYEAR_H +#define CM_DAYOFYEAR_H + +#include "cm_svc.h" + +void CM_DAYOFYEAR(TPSVCINFO *p); + +#endif /* CM_DAYOFYEAR_H */ diff --git a/app/src/cm/svc/CM_DAYOFYEAR.pgc b/app/src/cm/svc/CM_DAYOFYEAR.pgc new file mode 100644 index 0000000..db70d16 --- /dev/null +++ b/app/src/cm/svc/CM_DAYOFYEAR.pgc @@ -0,0 +1,33 @@ +/* + * CM_DAYOFYEAR.pgc - cm 모듈 서비스 CM_DAYOFYEAR (one service = one file). + * 연중 경과일/잔여일(순수 계산): 입력일(T_STR1)이 그 해의 몇 번째 날인지 + * (1~366)와 연말까지 남은 일수를 월별 누적일수 표로 계산한다. 윤년이면 + * 3월 이후에 하루를 더한다. 진행률(bps)도 함께 반환한다. no EXEC SQL. + */ +#include "CM_DAYOFYEAR.h" + +void CM_DAYOFYEAR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + static const int cum[] = {0,31,59,90,120,151,181,212,243,273,304,334}; + char in[16]; + int y, m, d, leap, ytot, doy, remain; + long progress; + + gets_(b, T_STR1, in, sizeof(in)); + if (sscanf(in, "%d-%d-%d", &y, &m, &d) != 3) FAIL(b); + if (m < 1 || m > 12 || d < 1 || d > 31) { setl(b, T_RC, 1); FAIL(b); } + + leap = (y % 4 == 0 && y % 100 != 0) || (y % 400 == 0); + doy = cum[m - 1] + d + ((leap && m > 2) ? 1 : 0); + ytot = leap ? 366 : 365; + remain = ytot - doy; + progress = (long)doy * 10000L / ytot; + + setl(b, T_ARG1, doy); /* 연중 경과일 */ + setl(b, T_ARG2, remain); /* 연말까지 잔여일 */ + setl(b, T_ARG3, progress); /* 진행률(bps) */ + setl(b, T_ARG4, leap); /* 윤년 여부 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_FX_INVERT.h b/app/src/cm/svc/CM_FX_INVERT.h new file mode 100644 index 0000000..2fd52fb --- /dev/null +++ b/app/src/cm/svc/CM_FX_INVERT.h @@ -0,0 +1,13 @@ +/* + * CM_FX_INVERT.h - cm (공통) 서비스 CM_FX_INVERT 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_FX_INVERT.pgc 에 있다. + */ +#ifndef CM_FX_INVERT_H +#define CM_FX_INVERT_H + +#include "cm_svc.h" + +void CM_FX_INVERT(TPSVCINFO *p); + +#endif /* CM_FX_INVERT_H */ diff --git a/app/src/cm/svc/CM_FX_INVERT.pgc b/app/src/cm/svc/CM_FX_INVERT.pgc new file mode 100644 index 0000000..25920ce --- /dev/null +++ b/app/src/cm/svc/CM_FX_INVERT.pgc @@ -0,0 +1,29 @@ +/* + * CM_FX_INVERT.pgc - cm 모듈 서비스 CM_FX_INVERT (one service = one file). + * 역환율 산출: 통화(T_CCY)의 고시환율(rate_bps = KRW*100/unit)을 조회해 + * 1원당 외화(역환율)를 소수 8자리 스케일(1e8)로 정수화해 돌려준다. 0 환율은 + * 0-division 을 피해 실패 처리한다. DB 조회 + 순수 계산 결합. runs on caller's XA branch. + */ +#include "CM_FX_INVERT.h" + +void CM_FX_INVERT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char ccy[8], bizdate[16]; + long rate, inv; + + gets_(b, T_CCY, ccy, sizeof(ccy)); + if (ccy[0] == 0) strcpy(ccy, "USD"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) { if (cmdb_get_param("BIZ_DATE", bizdate) < 0) strcpy(bizdate, "2026-07-19"); } + + if (cmdb_get_fx_rate(ccy, bizdate, &rate) < 0) { setl(b, T_RC, 1); FAIL(b); } + if (rate <= 0) { setl(b, T_RC, 2); FAIL(b); } /* 0/음수 환율 */ + + /* 역환율 = 1원당 외화 = 100 / rate_bps, 1e8 스케일 정수화 */ + inv = 100L * 100000000L / rate; + setl(b, T_ARG1, rate); /* 정환율(bps) */ + setl(b, T_ARG2, inv); /* 역환율(1e8 스케일) */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_FX_MARGIN.h b/app/src/cm/svc/CM_FX_MARGIN.h new file mode 100644 index 0000000..f6c5561 --- /dev/null +++ b/app/src/cm/svc/CM_FX_MARGIN.h @@ -0,0 +1,13 @@ +/* + * CM_FX_MARGIN.h - cm (공통) 서비스 CM_FX_MARGIN 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_FX_MARGIN.pgc 에 있다. + */ +#ifndef CM_FX_MARGIN_H +#define CM_FX_MARGIN_H + +#include "cm_svc.h" + +void CM_FX_MARGIN(TPSVCINFO *p); + +#endif /* CM_FX_MARGIN_H */ diff --git a/app/src/cm/svc/CM_FX_MARGIN.pgc b/app/src/cm/svc/CM_FX_MARGIN.pgc new file mode 100644 index 0000000..303eef8 --- /dev/null +++ b/app/src/cm/svc/CM_FX_MARGIN.pgc @@ -0,0 +1,27 @@ +/* + * CM_FX_MARGIN.pgc - cm 모듈 서비스 CM_FX_MARGIN (one service = one file). + * 매매기준율 스프레드(순수 계산): 고시환율(T_ARG1, bps)에 스프레드율 + * (T_ARG2, bps)을 적용해 매입율(bid)과 매도율(ask)을 대칭 산출한다. + * bid = mid*(1-s), ask = mid*(1+s). 스프레드 절대폭도 함께 반환. no EXEC SQL. + */ +#include "CM_FX_MARGIN.h" + +void CM_FX_MARGIN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long mid, spread_bps, bid, ask; + + mid = getl(b, T_ARG1); + spread_bps = getl(b, T_ARG2); + if (mid <= 0) { setl(b, T_RC, 1); FAIL(b); } + if (spread_bps < 0 || spread_bps > 10000) { setl(b, T_RC, 2); FAIL(b); } + + bid = mid - mid * spread_bps / 10000; /* 매입율 */ + ask = mid + mid * spread_bps / 10000; /* 매도율 */ + + setl(b, T_ARG3, bid); + setl(b, T_ARG4, ask); + setl(b, T_DELTA, ask - bid); /* 스프레드 절대폭 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_FX_MEAN.h b/app/src/cm/svc/CM_FX_MEAN.h new file mode 100644 index 0000000..7ce53c6 --- /dev/null +++ b/app/src/cm/svc/CM_FX_MEAN.h @@ -0,0 +1,13 @@ +/* + * CM_FX_MEAN.h - cm (공통) 서비스 CM_FX_MEAN 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_FX_MEAN.pgc 에 있다. + */ +#ifndef CM_FX_MEAN_H +#define CM_FX_MEAN_H + +#include "cm_svc.h" + +void CM_FX_MEAN(TPSVCINFO *p); + +#endif /* CM_FX_MEAN_H */ diff --git a/app/src/cm/svc/CM_FX_MEAN.pgc b/app/src/cm/svc/CM_FX_MEAN.pgc new file mode 100644 index 0000000..e0f7173 --- /dev/null +++ b/app/src/cm/svc/CM_FX_MEAN.pgc @@ -0,0 +1,33 @@ +/* + * CM_FX_MEAN.pgc - cm 모듈 서비스 CM_FX_MEAN (one service = one file). + * 기준일 평균환율: dbio(cmdb_fx_avg)로 기준일 전체 통화의 평균환율을 구하고, + * 특정 통화(T_CCY, 선택)의 고시환율이 평균 대비 얼마나(bps) 괴리되는지 함께 + * 산출한다. 평균은 통화 바스켓 편차 감시에 쓰인다. runs on the caller's XA branch. + */ +#include "CM_FX_MEAN.h" + +void CM_FX_MEAN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], ccy[8]; + long avg, one, dev_bps = 0; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) { if (cmdb_get_param("BIZ_DATE", bizdate) < 0) strcpy(bizdate, "2026-07-19"); } + gets_(b, T_CCY, ccy, sizeof(ccy)); + + avg = cmdb_fx_avg(bizdate); + if (avg < 0) FAIL(b); + setl(b, T_ARG1, avg); /* 평균환율 */ + + if (ccy[0] != 0 && avg > 0) { + if (cmdb_get_fx_rate(ccy, bizdate, &one) == 0) { + dev_bps = (one - avg) * 10000L / avg; /* 평균 대비 괴리(bps) */ + setl(b, T_ARG2, one); + setl(b, T_ARG3, dev_bps); + } else { + setl(b, T_RC, 1); /* 통화 미고시 (평균은 유효) */ + } + } + OK(b); +} diff --git a/app/src/cm/svc/CM_FX_TICKS.h b/app/src/cm/svc/CM_FX_TICKS.h new file mode 100644 index 0000000..e0846f3 --- /dev/null +++ b/app/src/cm/svc/CM_FX_TICKS.h @@ -0,0 +1,13 @@ +/* + * CM_FX_TICKS.h - cm (공통) 서비스 CM_FX_TICKS 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_FX_TICKS.pgc 에 있다. + */ +#ifndef CM_FX_TICKS_H +#define CM_FX_TICKS_H + +#include "cm_svc.h" + +void CM_FX_TICKS(TPSVCINFO *p); + +#endif /* CM_FX_TICKS_H */ diff --git a/app/src/cm/svc/CM_FX_TICKS.pgc b/app/src/cm/svc/CM_FX_TICKS.pgc new file mode 100644 index 0000000..c625495 --- /dev/null +++ b/app/src/cm/svc/CM_FX_TICKS.pgc @@ -0,0 +1,33 @@ +/* + * CM_FX_TICKS.pgc - cm 모듈 서비스 CM_FX_TICKS (one service = one file). + * 환율 고시 현황: 기준일(T_BIZDATE)에 고시된 통화 수와 CCY 코드그룹의 등록 + * 통화 수를 비교해 고시 커버리지(bps)를 산출한다. 미고시 통화가 있으면 + * 결측 수를 함께 알린다. 마감 전 환율 적재 완결성 점검용. runs on caller's XA branch. + */ +#include "CM_FX_TICKS.h" + +void CM_FX_TICKS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long coverage = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16]; + long h_quoted, h_defined; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_date, sizeof(h_date)); + if (h_date[0] == 0) { if (cmdb_get_param("BIZ_DATE", h_date) < 0) strcpy(h_date, "2026-07-19"); } + + EXEC SQL SELECT count(*) INTO :h_quoted FROM cm_fx_rate WHERE base_date = :h_date; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_defined FROM cm_code + WHERE code_grp = 'CCY' AND use_yn = 'Y' AND code <> 'KRW'; + if (sqlca.sqlcode < 0) FAIL(b); + + if (h_defined > 0) coverage = h_quoted * 10000L / h_defined; + setl(b, T_COUNT, h_quoted); /* 고시 통화 수 */ + setl(b, T_ARG1, h_defined); /* 등록 통화 수(KRW 제외) */ + setl(b, T_ARG2, h_defined - h_quoted); /* 결측 수 */ + setl(b, T_ARG3, coverage); /* 커버리지(bps) */ + OK(b); +} diff --git a/app/src/cm/svc/CM_FX_TREND.h b/app/src/cm/svc/CM_FX_TREND.h new file mode 100644 index 0000000..4b83df8 --- /dev/null +++ b/app/src/cm/svc/CM_FX_TREND.h @@ -0,0 +1,13 @@ +/* + * CM_FX_TREND.h - cm (공통) 서비스 CM_FX_TREND 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_FX_TREND.pgc 에 있다. + */ +#ifndef CM_FX_TREND_H +#define CM_FX_TREND_H + +#include "cm_svc.h" + +void CM_FX_TREND(TPSVCINFO *p); + +#endif /* CM_FX_TREND_H */ diff --git a/app/src/cm/svc/CM_FX_TREND.pgc b/app/src/cm/svc/CM_FX_TREND.pgc new file mode 100644 index 0000000..291b995 --- /dev/null +++ b/app/src/cm/svc/CM_FX_TREND.pgc @@ -0,0 +1,35 @@ +/* + * CM_FX_TREND.pgc - cm 모듈 서비스 CM_FX_TREND (one service = one file). + * 환율 변동률: 통화(T_CCY)의 이전일(T_STR1)과 기준일(T_BIZDATE) 고시환율을 + * 각각 조회해 변동폭과 변동률(bps)을 계산하고, 상승/보합/하락 방향을 판정한다. + * 두 일자 중 하나라도 고시가 없으면 실패한다. runs on the caller's XA branch. + */ +#include "CM_FX_TREND.h" + +void CM_FX_TREND(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char ccy[8], prev_date[16], base_date[16]; + long r0, r1, diff, rate_bps = 0; + const char *dir; + + gets_(b, T_CCY, ccy, sizeof(ccy)); + gets_(b, T_STR1, prev_date, sizeof(prev_date)); + gets_(b, T_BIZDATE, base_date, sizeof(base_date)); + if (ccy[0] == 0 || prev_date[0] == 0) FAIL(b); + if (base_date[0] == 0) { if (cmdb_get_param("BIZ_DATE", base_date) < 0) strcpy(base_date, "2026-07-19"); } + + if (cmdb_get_fx_rate(ccy, prev_date, &r0) < 0) { setl(b, T_RC, 1); FAIL(b); } + if (cmdb_get_fx_rate(ccy, base_date, &r1) < 0) { setl(b, T_RC, 2); FAIL(b); } + + diff = r1 - r0; + if (r0 > 0) rate_bps = diff * 10000L / r0; /* 변동률(bps) */ + dir = (diff > 0) ? "UP" : (diff < 0) ? "DOWN" : "FLAT"; + + setl(b, T_ARG1, r0); + setl(b, T_ARG2, r1); + setl(b, T_DELTA, diff); + setl(b, T_ARG3, rate_bps); + sets_(b, T_STATUS, dir); /* 방향 판정 */ + OK(b); +} diff --git a/app/src/cm/svc/CM_FX_TRIANGLE.h b/app/src/cm/svc/CM_FX_TRIANGLE.h new file mode 100644 index 0000000..9ad80ae --- /dev/null +++ b/app/src/cm/svc/CM_FX_TRIANGLE.h @@ -0,0 +1,13 @@ +/* + * CM_FX_TRIANGLE.h - cm (공통) 서비스 CM_FX_TRIANGLE 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_FX_TRIANGLE.pgc 에 있다. + */ +#ifndef CM_FX_TRIANGLE_H +#define CM_FX_TRIANGLE_H + +#include "cm_svc.h" + +void CM_FX_TRIANGLE(TPSVCINFO *p); + +#endif /* CM_FX_TRIANGLE_H */ diff --git a/app/src/cm/svc/CM_FX_TRIANGLE.pgc b/app/src/cm/svc/CM_FX_TRIANGLE.pgc new file mode 100644 index 0000000..5add09c --- /dev/null +++ b/app/src/cm/svc/CM_FX_TRIANGLE.pgc @@ -0,0 +1,35 @@ +/* + * CM_FX_TRIANGLE.pgc - cm 모듈 서비스 CM_FX_TRIANGLE (one service = one file). + * 삼각환율 정합성: 원화 기준 두 통화(T_CCY=base, T_STR1=quote)의 고시환율로 + * 교차환율(base/quote)을 산출하고, 호출자가 제시한 시장 교차율(T_ARG1, 1e6 + * 스케일)과의 괴리(bps)를 계산해 차익거래 여지를 점검한다. runs on caller's XA branch. + */ +#include "CM_FX_TRIANGLE.h" + +void CM_FX_TRIANGLE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char base[8], quote[8], bizdate[16]; + long rb, rq, cross, market, gap_bps = 0; + + gets_(b, T_CCY, base, sizeof(base)); + gets_(b, T_STR1, quote, sizeof(quote)); + market = getl(b, T_ARG1); + if (base[0] == 0 || quote[0] == 0) FAIL(b); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) { if (cmdb_get_param("BIZ_DATE", bizdate) < 0) strcpy(bizdate, "2026-07-19"); } + + if (cmdb_get_fx_rate(base, bizdate, &rb) < 0) { setl(b, T_RC, 1); FAIL(b); } + if (cmdb_get_fx_rate(quote, bizdate, &rq) < 0) { setl(b, T_RC, 2); FAIL(b); } + if (rq <= 0) { setl(b, T_RC, 3); FAIL(b); } + + /* 교차율(base 1단위당 quote), 1e6 스케일 */ + cross = rb * 1000000L / rq; + setl(b, T_ARG2, cross); + if (market > 0) { + gap_bps = (cross - market) * 10000L / market; + setl(b, T_ARG3, gap_bps); /* 시장 대비 괴리(bps) */ + } + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_HASH_DJB2.h b/app/src/cm/svc/CM_HASH_DJB2.h new file mode 100644 index 0000000..f4564e1 --- /dev/null +++ b/app/src/cm/svc/CM_HASH_DJB2.h @@ -0,0 +1,13 @@ +/* + * CM_HASH_DJB2.h - cm (공통) 서비스 CM_HASH_DJB2 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_HASH_DJB2.pgc 에 있다. + */ +#ifndef CM_HASH_DJB2_H +#define CM_HASH_DJB2_H + +#include "cm_svc.h" + +void CM_HASH_DJB2(TPSVCINFO *p); + +#endif /* CM_HASH_DJB2_H */ diff --git a/app/src/cm/svc/CM_HASH_DJB2.pgc b/app/src/cm/svc/CM_HASH_DJB2.pgc new file mode 100644 index 0000000..9ba88d9 --- /dev/null +++ b/app/src/cm/svc/CM_HASH_DJB2.pgc @@ -0,0 +1,28 @@ +/* + * CM_HASH_DJB2.pgc - cm 모듈 서비스 CM_HASH_DJB2 (one service = one file). + * djb2 해시(순수 계산): 입력 문자열(T_STR1)에 대해 hash = hash*33 + c 점화식 + * (초기값 5381)으로 32비트 해시를 계산한다. 캐시 버킷 분산/멱등 키 산출에 + * 쓰인다. 결과를 정수와 8자리 HEX 로 함께 반환한다. no EXEC SQL. + */ +#include "CM_HASH_DJB2.h" + +void CM_HASH_DJB2(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char in[256], hex[12]; + unsigned long h = 5381UL; + int i; + + gets_(b, T_STR1, in, sizeof(in)); + if (in[0] == 0) FAIL(b); + + for (i = 0; in[i]; i++) + h = ((h << 5) + h) + (unsigned char)in[i]; /* h*33 + c */ + h &= 0xFFFFFFFFUL; + + sprintf(hex, "%08lX", h); + setl(b, T_ARG1, (long)h); + sets_(b, T_STR4, hex); + setl(b, T_COUNT, i); /* 입력 길이 */ + OK(b); +} diff --git a/app/src/cm/svc/CM_HASH_FNV.h b/app/src/cm/svc/CM_HASH_FNV.h new file mode 100644 index 0000000..9619f85 --- /dev/null +++ b/app/src/cm/svc/CM_HASH_FNV.h @@ -0,0 +1,13 @@ +/* + * CM_HASH_FNV.h - cm (공통) 서비스 CM_HASH_FNV 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_HASH_FNV.pgc 에 있다. + */ +#ifndef CM_HASH_FNV_H +#define CM_HASH_FNV_H + +#include "cm_svc.h" + +void CM_HASH_FNV(TPSVCINFO *p); + +#endif /* CM_HASH_FNV_H */ diff --git a/app/src/cm/svc/CM_HASH_FNV.pgc b/app/src/cm/svc/CM_HASH_FNV.pgc new file mode 100644 index 0000000..528638e --- /dev/null +++ b/app/src/cm/svc/CM_HASH_FNV.pgc @@ -0,0 +1,29 @@ +/* + * CM_HASH_FNV.pgc - cm 모듈 서비스 CM_HASH_FNV (one service = one file). + * FNV-1a 해시(순수 계산): 입력(T_STR1)을 오프셋 베이시스(2166136261)에서 + * 시작해 바이트마다 XOR 후 FNV 소수(16777619)를 곱하는 32비트 FNV-1a 로 + * 해싱한다. djb2 대비 눈사태 특성이 좋아 분산키에 쓴다. no EXEC SQL. + */ +#include "CM_HASH_FNV.h" + +void CM_HASH_FNV(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char in[256], hex[12]; + unsigned long h = 2166136261UL; + int i; + + gets_(b, T_STR1, in, sizeof(in)); + if (in[0] == 0) FAIL(b); + + for (i = 0; in[i]; i++) { + h ^= (unsigned char)in[i]; /* 먼저 XOR */ + h = (h * 16777619UL) & 0xFFFFFFFFUL; /* 그다음 곱 (1a 순서) */ + } + + sprintf(hex, "%08lX", h); + setl(b, T_ARG1, (long)h); + sets_(b, T_STR4, hex); + setl(b, T_ARG2, (long)(h % 1024)); /* 1024 버킷 인덱스 */ + OK(b); +} diff --git a/app/src/cm/svc/CM_HASH_SUM.h b/app/src/cm/svc/CM_HASH_SUM.h new file mode 100644 index 0000000..85c9191 --- /dev/null +++ b/app/src/cm/svc/CM_HASH_SUM.h @@ -0,0 +1,13 @@ +/* + * CM_HASH_SUM.h - cm (공통) 서비스 CM_HASH_SUM 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_HASH_SUM.pgc 에 있다. + */ +#ifndef CM_HASH_SUM_H +#define CM_HASH_SUM_H + +#include "cm_svc.h" + +void CM_HASH_SUM(TPSVCINFO *p); + +#endif /* CM_HASH_SUM_H */ diff --git a/app/src/cm/svc/CM_HASH_SUM.pgc b/app/src/cm/svc/CM_HASH_SUM.pgc new file mode 100644 index 0000000..2cae5ee --- /dev/null +++ b/app/src/cm/svc/CM_HASH_SUM.pgc @@ -0,0 +1,27 @@ +/* + * CM_HASH_SUM.pgc - cm 모듈 서비스 CM_HASH_SUM (one service = one file). + * 가중 체크섬(순수 계산): 입력(T_STR1) 각 문자의 코드값에 위치 가중(1부터 + * 증가)을 곱해 누적한 뒤 modulo 97 을 취해 2자리 검증값을 산출한다(ISO 7064 + * 계열 근사). 단순 오타/전송오류 탐지에 쓰인다. no EXEC SQL. + */ +#include "CM_HASH_SUM.h" + +void CM_HASH_SUM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char in[256], chk[4]; + long acc = 0; + int i, w; + + gets_(b, T_STR1, in, sizeof(in)); + if (in[0] == 0) FAIL(b); + + for (i = 0, w = 1; in[i]; i++, w++) + acc += (long)(unsigned char)in[i] * w; /* 위치 가중 누적 */ + + sprintf(chk, "%02ld", acc % 97); + setl(b, T_ARG1, acc); /* 가중합 원값 */ + setl(b, T_ARG2, acc % 97); /* mod-97 검증값 */ + sets_(b, T_STR4, chk); + OK(b); +} diff --git a/app/src/cm/svc/CM_HEALTH_ALL.h b/app/src/cm/svc/CM_HEALTH_ALL.h new file mode 100644 index 0000000..0359400 --- /dev/null +++ b/app/src/cm/svc/CM_HEALTH_ALL.h @@ -0,0 +1,13 @@ +/* + * CM_HEALTH_ALL.h - cm (공통) 서비스 CM_HEALTH_ALL 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_HEALTH_ALL.pgc 에 있다. + */ +#ifndef CM_HEALTH_ALL_H +#define CM_HEALTH_ALL_H + +#include "cm_svc.h" + +void CM_HEALTH_ALL(TPSVCINFO *p); + +#endif /* CM_HEALTH_ALL_H */ diff --git a/app/src/cm/svc/CM_HEALTH_ALL.pgc b/app/src/cm/svc/CM_HEALTH_ALL.pgc new file mode 100644 index 0000000..043a66c --- /dev/null +++ b/app/src/cm/svc/CM_HEALTH_ALL.pgc @@ -0,0 +1,43 @@ +/* + * CM_HEALTH_ALL.pgc - cm 모듈 서비스 CM_HEALTH_ALL (one service = one file). + * 종합 헬스 스코어: 기준일 로그/환율/채번/파라미터 존재를 각 25점으로 가중해 + * 0~100 스코어를 산출한다. 각 자원이 비어 있으면 해당 배점을 감점하고, 스코어 + * 구간에 따라 GREEN/YELLOW/RED 등급을 매긴다. 운영 종합 신호등. runs on caller's XA branch. + */ +#include "CM_HEALTH_ALL.h" + +void CM_HEALTH_ALL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long score = 0; + const char *grade; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16]; + long h_log, h_fx, h_seq, h_param; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_date, sizeof(h_date)); + if (h_date[0] == 0) { if (cmdb_get_param("BIZ_DATE", h_date) < 0) strcpy(h_date, "2026-07-19"); } + + EXEC SQL SELECT count(*) INTO :h_log FROM cm_log WHERE biz_date = :h_date; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_fx FROM cm_fx_rate WHERE base_date = :h_date; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_seq FROM cm_seq; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_param FROM cm_param; + if (sqlca.sqlcode < 0) FAIL(b); + + if (h_log > 0) score += 25; + if (h_fx > 0) score += 25; + if (h_seq > 0) score += 25; + if (h_param > 0) score += 25; + grade = (score >= 100) ? "GREEN" : (score >= 50) ? "YELLOW" : "RED"; + + setl(b, T_ARG1, score); /* 0~100 헬스 스코어 */ + sets_(b, T_STATUS, grade); + setl(b, T_ARG2, h_log); + setl(b, T_ARG3, h_fx); + setl(b, T_RC, (score >= 50) ? 0 : 1); + OK(b); +} diff --git a/app/src/cm/svc/CM_HEALTH_FX.h b/app/src/cm/svc/CM_HEALTH_FX.h new file mode 100644 index 0000000..ca7cecf --- /dev/null +++ b/app/src/cm/svc/CM_HEALTH_FX.h @@ -0,0 +1,13 @@ +/* + * CM_HEALTH_FX.h - cm (공통) 서비스 CM_HEALTH_FX 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_HEALTH_FX.pgc 에 있다. + */ +#ifndef CM_HEALTH_FX_H +#define CM_HEALTH_FX_H + +#include "cm_svc.h" + +void CM_HEALTH_FX(TPSVCINFO *p); + +#endif /* CM_HEALTH_FX_H */ diff --git a/app/src/cm/svc/CM_HEALTH_FX.pgc b/app/src/cm/svc/CM_HEALTH_FX.pgc new file mode 100644 index 0000000..c2b41a2 --- /dev/null +++ b/app/src/cm/svc/CM_HEALTH_FX.pgc @@ -0,0 +1,42 @@ +/* + * CM_HEALTH_FX.pgc - cm 모듈 서비스 CM_HEALTH_FX (one service = one file). + * 시스템 헬스(환율 신선도): 기준일(T_BIZDATE)에 고시된 통화가 하나라도 있는지, + * 그리고 가장 최근 고시 일자가 기준일과 며칠 벌어졌는지(신선도)를 점검한다. + * 결측이거나 신선도가 허용(T_ARG1, 기본 2일)을 넘으면 DEGRADED 판정. runs on caller's XA branch. + */ +#include "CM_HEALTH_FX.h" + +void CM_HEALTH_FX(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long tol, age = 0, degraded = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16], h_latest[16]; + long h_today_cnt, h_has, h_age; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_date, sizeof(h_date)); + if (h_date[0] == 0) { if (cmdb_get_param("BIZ_DATE", h_date) < 0) strcpy(h_date, "2026-07-19"); } + tol = getl(b, T_ARG1); + if (tol <= 0) tol = 2; + + EXEC SQL SELECT count(*) INTO :h_today_cnt FROM cm_fx_rate WHERE base_date = :h_date; + if (sqlca.sqlcode < 0) { sets_(b, T_STATUS, "DOWN"); FAIL(b); } + + EXEC SQL SELECT count(*), coalesce(max(base_date)::text, '1900-01-01') + INTO :h_has, :h_latest FROM cm_fx_rate; + if (sqlca.sqlcode < 0) { sets_(b, T_STATUS, "DOWN"); FAIL(b); } + + if (h_has > 0) { + EXEC SQL SELECT (:h_date::date - :h_latest::date) INTO :h_age; + if (sqlca.sqlcode < 0) FAIL(b); + age = h_age; + } else age = 9999; + + if (h_today_cnt == 0 || age > tol) degraded = 1; + sets_(b, T_STATUS, degraded ? "DEGRADED" : "UP"); + setl(b, T_COUNT, h_today_cnt); /* 당일 고시 수 */ + setl(b, T_ARG2, age); /* 최신 고시 경과일 */ + setl(b, T_RC, degraded); + OK(b); +} diff --git a/app/src/cm/svc/CM_HEALTH_PARAM.h b/app/src/cm/svc/CM_HEALTH_PARAM.h new file mode 100644 index 0000000..50947f0 --- /dev/null +++ b/app/src/cm/svc/CM_HEALTH_PARAM.h @@ -0,0 +1,13 @@ +/* + * CM_HEALTH_PARAM.h - cm (공통) 서비스 CM_HEALTH_PARAM 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_HEALTH_PARAM.pgc 에 있다. + */ +#ifndef CM_HEALTH_PARAM_H +#define CM_HEALTH_PARAM_H + +#include "cm_svc.h" + +void CM_HEALTH_PARAM(TPSVCINFO *p); + +#endif /* CM_HEALTH_PARAM_H */ diff --git a/app/src/cm/svc/CM_HEALTH_PARAM.pgc b/app/src/cm/svc/CM_HEALTH_PARAM.pgc new file mode 100644 index 0000000..dd28a4d --- /dev/null +++ b/app/src/cm/svc/CM_HEALTH_PARAM.pgc @@ -0,0 +1,38 @@ +/* + * CM_HEALTH_PARAM.pgc - cm 모듈 서비스 CM_HEALTH_PARAM (one service = one file). + * 필수 파라미터 점검: 운영에 반드시 필요한 키 목록(BIZ_DATE/VAT_RATE/WHT_BPS + * /LOG_KEEP_DAYS)을 순회하며 각각 존재하는지 확인하고, 누락된 키가 있으면 그 + * 이름을 모아 반환한다. 기동 전 구성 완결성 점검용. runs on the caller's XA branch. + */ +#include "CM_HEALTH_PARAM.h" + +/* ----- service-local required-key table ----- */ +static const char *CM_REQ_PARAMS[] = { + "BIZ_DATE", "VAT_RATE", "WHT_BPS", "LOG_KEEP_DAYS" +}; + +void CM_HEALTH_PARAM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char val[128], missing[128]; + int i, n = (int)(sizeof(CM_REQ_PARAMS) / sizeof(CM_REQ_PARAMS[0])); + int present = 0, gone = 0, mpos = 0; + + missing[0] = 0; + for (i = 0; i < n; i++) { + if (cmdb_get_param(CM_REQ_PARAMS[i], val) == 0) { + present++; + } else { + gone++; + if (mpos > 0) missing[mpos++] = ','; + mpos += sprintf(missing + mpos, "%s", CM_REQ_PARAMS[i]); + } + } + + setl(b, T_ARG1, present); /* 존재 키 수 */ + setl(b, T_ARG2, gone); /* 누락 키 수 */ + sets_(b, T_STR4, missing); /* 누락 키 목록 */ + sets_(b, T_STATUS, gone == 0 ? "UP" : "DEGRADED"); + setl(b, T_RC, gone == 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/cm/svc/CM_HOLIDAY_NEXT.h b/app/src/cm/svc/CM_HOLIDAY_NEXT.h new file mode 100644 index 0000000..176f8cc --- /dev/null +++ b/app/src/cm/svc/CM_HOLIDAY_NEXT.h @@ -0,0 +1,13 @@ +/* + * CM_HOLIDAY_NEXT.h - cm (공통) 서비스 CM_HOLIDAY_NEXT 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_HOLIDAY_NEXT.pgc 에 있다. + */ +#ifndef CM_HOLIDAY_NEXT_H +#define CM_HOLIDAY_NEXT_H + +#include "cm_svc.h" + +void CM_HOLIDAY_NEXT(TPSVCINFO *p); + +#endif /* CM_HOLIDAY_NEXT_H */ diff --git a/app/src/cm/svc/CM_HOLIDAY_NEXT.pgc b/app/src/cm/svc/CM_HOLIDAY_NEXT.pgc new file mode 100644 index 0000000..f800e71 --- /dev/null +++ b/app/src/cm/svc/CM_HOLIDAY_NEXT.pgc @@ -0,0 +1,36 @@ +/* + * CM_HOLIDAY_NEXT.pgc - cm 모듈 서비스 CM_HOLIDAY_NEXT (one service = one file). + * 다음 휴일 조회: 기준일(T_BIZDATE) 이후 국가(T_STR1, 기본 KR)의 가장 가까운 + * 휴일 날짜와 명칭을 조회하고, 기준일로부터 며칠 뒤인지(경과일)를 계산한다. + * 남은 휴일이 없으면 not-found(RC=1). runs on the caller's XA branch. + */ +#include "CM_HOLIDAY_NEXT.h" + +void CM_HOLIDAY_NEXT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char nation[8]; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16], h_nation[8], h_hdate[16], h_hname[64]; + long h_days; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_date, sizeof(h_date)); + gets_(b, T_STR1, nation, sizeof(nation)); + if (h_date[0] == 0) { if (cmdb_get_param("BIZ_DATE", h_date) < 0) strcpy(h_date, "2026-07-19"); } + strcpy(h_nation, nation[0] ? nation : "KR"); + + EXEC SQL SELECT holi_date::text, holi_name, (holi_date - :h_date::date) + INTO :h_hdate, :h_hname, :h_days + FROM cm_holiday + WHERE nation = :h_nation AND holi_date > :h_date + ORDER BY holi_date LIMIT 1; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); FAIL(b); } /* 남은 휴일 없음 */ + if (sqlca.sqlcode < 0) FAIL(b); + + sets_(b, T_STR4, h_hdate); + sets_(b, T_MSG, h_hname); + setl(b, T_ARG1, h_days); /* 기준일로부터 경과일 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_HOLIDAY_YEAR.h b/app/src/cm/svc/CM_HOLIDAY_YEAR.h new file mode 100644 index 0000000..3a74a69 --- /dev/null +++ b/app/src/cm/svc/CM_HOLIDAY_YEAR.h @@ -0,0 +1,13 @@ +/* + * CM_HOLIDAY_YEAR.h - cm (공통) 서비스 CM_HOLIDAY_YEAR 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_HOLIDAY_YEAR.pgc 에 있다. + */ +#ifndef CM_HOLIDAY_YEAR_H +#define CM_HOLIDAY_YEAR_H + +#include "cm_svc.h" + +void CM_HOLIDAY_YEAR(TPSVCINFO *p); + +#endif /* CM_HOLIDAY_YEAR_H */ diff --git a/app/src/cm/svc/CM_HOLIDAY_YEAR.pgc b/app/src/cm/svc/CM_HOLIDAY_YEAR.pgc new file mode 100644 index 0000000..c798cbf --- /dev/null +++ b/app/src/cm/svc/CM_HOLIDAY_YEAR.pgc @@ -0,0 +1,35 @@ +/* + * CM_HOLIDAY_YEAR.pgc - cm 모듈 서비스 CM_HOLIDAY_YEAR (one service = one file). + * 연도별 휴일 집계: 연도(T_ARG1)와 국가(T_STR1, 기본 KR)의 등록 휴일 수와 + * 그 중 주말(토/일)과 겹치는 휴일 수를 함께 집계한다. 실효 휴일(평일 휴일)은 + * 차감으로 산출한다. 캘린더 편성 검토용. runs on the caller's XA branch. + */ +#include "CM_HOLIDAY_YEAR.h" + +void CM_HOLIDAY_YEAR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char nation[8]; + EXEC SQL BEGIN DECLARE SECTION; + char h_nation[8]; + long h_year, h_total, h_weekend; + EXEC SQL END DECLARE SECTION; + + h_year = getl(b, T_ARG1); + gets_(b, T_STR1, nation, sizeof(nation)); + if (h_year < 1900 || h_year > 2999) { setl(b, T_RC, 1); FAIL(b); } /* 연도 오류 */ + strcpy(h_nation, nation[0] ? nation : "KR"); + + EXEC SQL SELECT count(*), + coalesce(sum(CASE WHEN extract(dow FROM holi_date) IN (0,6) + THEN 1 ELSE 0 END), 0) + INTO :h_total, :h_weekend + FROM cm_holiday + WHERE nation = :h_nation AND extract(year FROM holi_date) = :h_year; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_total); /* 연간 휴일 수 */ + setl(b, T_ARG2, h_weekend); /* 주말과 겹치는 휴일 */ + setl(b, T_ARG3, h_total - h_weekend); /* 실효(평일) 휴일 */ + OK(b); +} diff --git a/app/src/cm/svc/CM_ISO_WEEK.h b/app/src/cm/svc/CM_ISO_WEEK.h new file mode 100644 index 0000000..1ee8038 --- /dev/null +++ b/app/src/cm/svc/CM_ISO_WEEK.h @@ -0,0 +1,13 @@ +/* + * CM_ISO_WEEK.h - cm (공통) 서비스 CM_ISO_WEEK 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_ISO_WEEK.pgc 에 있다. + */ +#ifndef CM_ISO_WEEK_H +#define CM_ISO_WEEK_H + +#include "cm_svc.h" + +void CM_ISO_WEEK(TPSVCINFO *p); + +#endif /* CM_ISO_WEEK_H */ diff --git a/app/src/cm/svc/CM_ISO_WEEK.pgc b/app/src/cm/svc/CM_ISO_WEEK.pgc new file mode 100644 index 0000000..1526683 --- /dev/null +++ b/app/src/cm/svc/CM_ISO_WEEK.pgc @@ -0,0 +1,43 @@ +/* + * CM_ISO_WEEK.pgc - cm 모듈 서비스 CM_ISO_WEEK (one service = one file). + * ISO-8601 주차 산출(순수 계산): 입력일(T_STR1)의 통일된 일련일수를 구해 + * 요일(월=1..일=7)과 그 주 목요일이 속한 해를 기준으로 ISO 주차(1~53)를 + * 계산한다. 윤년 루프 없이 역법 공식만 사용한다. runs on the caller's XA branch. + */ +#include "CM_ISO_WEEK.h" + +/* ----- service-local pure helper: 그레고리력 일련일수 ----- */ +static long cm_ord(int y, int m, int d) +{ + long a = (14 - m) / 12, yy = y + 4800 - a, mm = m + 12 * a - 3; + return d + (153 * mm + 2) / 5 + 365L * yy + yy / 4 - yy / 100 + yy / 400 - 32045; +} + +void CM_ISO_WEEK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char in[16]; + int y, m, d, dow; + long jdn, thursday, jan1, week, iso_year; + + gets_(b, T_STR1, in, sizeof(in)); + if (sscanf(in, "%d-%d-%d", &y, &m, &d) != 3) FAIL(b); + if (m < 1 || m > 12 || d < 1 || d > 31) { setl(b, T_RC, 1); FAIL(b); } + + jdn = cm_ord(y, m, d); + dow = (int)(((jdn % 7) + 7) % 7); /* 0=월 .. 6=일 (JDN%7 정렬) */ + thursday = jdn - dow + 3; /* 같은 주 목요일 */ + iso_year = 0; + { int ty = y; long t = thursday; /* 목요일이 속한 해 추정 */ + while (cm_ord(ty + 1, 1, 1) <= t) ty++; + while (cm_ord(ty, 1, 1) > t) ty--; + iso_year = ty; } + jan1 = cm_ord((int)iso_year, 1, 1); + week = (thursday - jan1) / 7 + 1; + + setl(b, T_ARG1, iso_year); + setl(b, T_ARG2, week); /* ISO 주차 */ + setl(b, T_ARG3, dow + 1); /* 요일 1=월..7=일 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_MASK_ACCT.h b/app/src/cm/svc/CM_MASK_ACCT.h new file mode 100644 index 0000000..62008ea --- /dev/null +++ b/app/src/cm/svc/CM_MASK_ACCT.h @@ -0,0 +1,13 @@ +/* + * CM_MASK_ACCT.h - cm (공통) 서비스 CM_MASK_ACCT 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_MASK_ACCT.pgc 에 있다. + */ +#ifndef CM_MASK_ACCT_H +#define CM_MASK_ACCT_H + +#include "cm_svc.h" + +void CM_MASK_ACCT(TPSVCINFO *p); + +#endif /* CM_MASK_ACCT_H */ diff --git a/app/src/cm/svc/CM_MASK_ACCT.pgc b/app/src/cm/svc/CM_MASK_ACCT.pgc new file mode 100644 index 0000000..003e52b --- /dev/null +++ b/app/src/cm/svc/CM_MASK_ACCT.pgc @@ -0,0 +1,35 @@ +/* + * CM_MASK_ACCT.pgc - cm 모듈 서비스 CM_MASK_ACCT (one service = one file). + * 계좌번호 마스킹(순수 계산): 하이픈 구분자는 위치를 보존하면서, 숫자 기준 + * 뒤 5자리만 남기고 앞쪽 숫자를 모두 'x' 로 가린다(110-234-567890 형태에서 + * 뒤 5자리 노출). 최소 자릿수(6) 미만이면 거절한다. no EXEC SQL. + */ +#include "CM_MASK_ACCT.h" + +void CM_MASK_ACCT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char in[40], out[40]; + int i, digits = 0, seen = 0, n = 0; + + gets_(b, T_STR1, in, sizeof(in)); + for (i = 0; in[i]; i++) { /* 1패스: 자릿수 집계 */ + if (in[i] >= '0' && in[i] <= '9') digits++; + n++; + } + if (digits < 6) { setl(b, T_RC, 1); FAIL(b); } /* 계좌 자릿수 부족 */ + + for (i = 0; i < n; i++) { /* 2패스: 뒤 5자리 제외 마스킹 */ + if (in[i] >= '0' && in[i] <= '9') { + seen++; + out[i] = (seen <= digits - 5) ? 'x' : in[i]; + } else { + out[i] = in[i]; /* 구분자 보존 */ + } + } + out[n] = 0; + sets_(b, T_STR4, out); + setl(b, T_ARG1, digits); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_MASK_EMAIL.h b/app/src/cm/svc/CM_MASK_EMAIL.h new file mode 100644 index 0000000..c8210dc --- /dev/null +++ b/app/src/cm/svc/CM_MASK_EMAIL.h @@ -0,0 +1,13 @@ +/* + * CM_MASK_EMAIL.h - cm (공통) 서비스 CM_MASK_EMAIL 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_MASK_EMAIL.pgc 에 있다. + */ +#ifndef CM_MASK_EMAIL_H +#define CM_MASK_EMAIL_H + +#include "cm_svc.h" + +void CM_MASK_EMAIL(TPSVCINFO *p); + +#endif /* CM_MASK_EMAIL_H */ diff --git a/app/src/cm/svc/CM_MASK_EMAIL.pgc b/app/src/cm/svc/CM_MASK_EMAIL.pgc new file mode 100644 index 0000000..d19d48c --- /dev/null +++ b/app/src/cm/svc/CM_MASK_EMAIL.pgc @@ -0,0 +1,30 @@ +/* + * CM_MASK_EMAIL.pgc - cm 모듈 서비스 CM_MASK_EMAIL (one service = one file). + * 이메일 마스킹(순수 계산): 로컬파트(@ 앞)의 첫 글자만 남기고 나머지를 '*' 로 + * 가리며, 도메인(@ 뒤)은 그대로 보존한다(hong@a.com -> h***@a.com). '@' 가 + * 없거나 로컬파트가 비면 형식 오류로 거절한다. no EXEC SQL; advertised by cm_svr. + */ +#include "CM_MASK_EMAIL.h" + +void CM_MASK_EMAIL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char in[128], out[128]; + int at = -1, i, o = 0, hidden = 0; + + gets_(b, T_STR1, in, sizeof(in)); + for (i = 0; in[i]; i++) if (in[i] == '@') { at = i; break; } + if (at <= 0 || in[at + 1] == 0) { setl(b, T_RC, 1); FAIL(b); } /* 형식 오류 */ + + for (i = 0; i < at; i++) { /* 로컬파트 마스킹 */ + if (i == 0) out[o++] = in[i]; + else { out[o++] = '*'; hidden++; } + } + for (i = at; in[i]; i++) out[o++] = in[i]; /* @ 이후 도메인 보존 */ + out[o] = 0; + + sets_(b, T_STR4, out); + setl(b, T_ARG1, hidden); /* 가린 글자 수 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_MASK_RRN.h b/app/src/cm/svc/CM_MASK_RRN.h new file mode 100644 index 0000000..e0a7bbc --- /dev/null +++ b/app/src/cm/svc/CM_MASK_RRN.h @@ -0,0 +1,13 @@ +/* + * CM_MASK_RRN.h - cm (공통) 서비스 CM_MASK_RRN 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_MASK_RRN.pgc 에 있다. + */ +#ifndef CM_MASK_RRN_H +#define CM_MASK_RRN_H + +#include "cm_svc.h" + +void CM_MASK_RRN(TPSVCINFO *p); + +#endif /* CM_MASK_RRN_H */ diff --git a/app/src/cm/svc/CM_MASK_RRN.pgc b/app/src/cm/svc/CM_MASK_RRN.pgc new file mode 100644 index 0000000..ff0ce45 --- /dev/null +++ b/app/src/cm/svc/CM_MASK_RRN.pgc @@ -0,0 +1,36 @@ +/* + * CM_MASK_RRN.pgc - cm 모듈 서비스 CM_MASK_RRN (one service = one file). + * 주민등록번호 마스킹(순수 계산): "YYMMDD-SXXXXXX" 13자리 형식을 검증하고, + * 생년월일 6자리와 성별자리(뒤 첫 자리)만 남긴 채 나머지 6자리를 '*' 로 + * 가린다(901010-1****** 형태). 형식/자릿수 오류는 코드로 구분한다. no EXEC SQL. + */ +#include "CM_MASK_RRN.h" + +void CM_MASK_RRN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char in[24], digits[16], out[16]; + int i, d = 0; + + gets_(b, T_STR1, in, sizeof(in)); + for (i = 0; in[i]; i++) { /* 숫자만 추출(하이픈 허용) */ + if (in[i] >= '0' && in[i] <= '9') { + if (d >= 13) { setl(b, T_RC, 2); FAIL(b); } /* 자릿수 초과 */ + digits[d++] = in[i]; + } else if (in[i] != '-') { + setl(b, T_RC, 1); FAIL(b); /* 허용 안 되는 문자 */ + } + } + if (d != 13) { setl(b, T_RC, 2); FAIL(b); } /* 13자리 아님 */ + + for (i = 0; i < 6; i++) out[i] = digits[i]; /* 생년월일 */ + out[6] = '-'; + out[7] = digits[6]; /* 성별 자리 */ + for (i = 8; i < 14; i++) out[i] = '*'; /* 나머지 6자리 */ + out[14] = 0; + + sets_(b, T_STR4, out); + setl(b, T_ARG1, 6); /* 가린 자릿수 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_MKT_STATUS.h b/app/src/cm/svc/CM_MKT_STATUS.h new file mode 100644 index 0000000..bdf9513 --- /dev/null +++ b/app/src/cm/svc/CM_MKT_STATUS.h @@ -0,0 +1,13 @@ +/* + * CM_MKT_STATUS.h - cm (공통) 서비스 CM_MKT_STATUS 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_MKT_STATUS.pgc 에 있다. + */ +#ifndef CM_MKT_STATUS_H +#define CM_MKT_STATUS_H + +#include "cm_svc.h" + +void CM_MKT_STATUS(TPSVCINFO *p); + +#endif /* CM_MKT_STATUS_H */ diff --git a/app/src/cm/svc/CM_MKT_STATUS.pgc b/app/src/cm/svc/CM_MKT_STATUS.pgc new file mode 100644 index 0000000..d68f7fd --- /dev/null +++ b/app/src/cm/svc/CM_MKT_STATUS.pgc @@ -0,0 +1,40 @@ +/* + * CM_MKT_STATUS.pgc - cm 모듈 서비스 CM_MKT_STATUS (one service = one file). + * 시장 개장 여부: 대상일(T_BIZDATE)이 국가(T_STR1, 기본 KR) 기준 개장일인지 + * 판정한다. DB 에서 요일과 휴일 등록 여부를 한 번에 조회해, 주말이거나 휴일이면 + * 폐장(CLOSED), 아니면 개장(OPEN)으로 판정하고 사유를 돌려준다. runs on caller's XA branch. + */ +#include "CM_MKT_STATUS.h" + +void CM_MKT_STATUS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char nation[8]; + const char *reason; + long is_open; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16], h_nation[8]; + long h_dow, h_holi; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_date, sizeof(h_date)); + gets_(b, T_STR1, nation, sizeof(nation)); + if (h_date[0] == 0) { if (cmdb_get_param("BIZ_DATE", h_date) < 0) strcpy(h_date, "2026-07-19"); } + strcpy(h_nation, nation[0] ? nation : "KR"); + + EXEC SQL SELECT extract(dow FROM :h_date::date), + (SELECT count(*) FROM cm_holiday + WHERE holi_date = :h_date AND nation = :h_nation) + INTO :h_dow, :h_holi; + if (sqlca.sqlcode < 0) FAIL(b); + + if (h_dow == 0 || h_dow == 6) { is_open = 0; reason = "WEEKEND"; } + else if (h_holi > 0) { is_open = 0; reason = "HOLIDAY"; } + else { is_open = 1; reason = "BIZDAY"; } + + sets_(b, T_STATUS, is_open ? "OPEN" : "CLOSED"); + sets_(b, T_STR4, reason); + setl(b, T_ARG1, h_dow); + setl(b, T_RC, is_open ? 0 : 1); + OK(b); +} diff --git a/app/src/cm/svc/CM_MSG_DEL.h b/app/src/cm/svc/CM_MSG_DEL.h new file mode 100644 index 0000000..f9c4958 --- /dev/null +++ b/app/src/cm/svc/CM_MSG_DEL.h @@ -0,0 +1,13 @@ +/* + * CM_MSG_DEL.h - cm (공통) 서비스 CM_MSG_DEL 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_MSG_DEL.pgc 에 있다. + */ +#ifndef CM_MSG_DEL_H +#define CM_MSG_DEL_H + +#include "cm_svc.h" + +void CM_MSG_DEL(TPSVCINFO *p); + +#endif /* CM_MSG_DEL_H */ diff --git a/app/src/cm/svc/CM_MSG_DEL.pgc b/app/src/cm/svc/CM_MSG_DEL.pgc new file mode 100644 index 0000000..0a92138 --- /dev/null +++ b/app/src/cm/svc/CM_MSG_DEL.pgc @@ -0,0 +1,35 @@ +/* + * CM_MSG_DEL.pgc - cm 모듈 서비스 CM_MSG_DEL (one service = one file). + * 다국어 메시지 폐기: 언어(T_STR1)와 메시지 ID(T_KEY1)로 MSG_ 그룹의 + * 해당 메시지를 use_yn='N' 으로 논리 폐기한다(물리 삭제 아님). 대상이 없으면 + * 실패(RC=1). 폐기는 조회(CM_MSG_GET)에서 자동 제외된다. runs on caller's XA branch. + */ +#include "CM_MSG_DEL.h" + +void CM_MSG_DEL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char lang[8]; + int i; + EXEC SQL BEGIN DECLARE SECTION; + char h_grp[16], h_id[32]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR1, lang, sizeof(lang)); + gets_(b, T_KEY1, h_id, sizeof(h_id)); + if (h_id[0] == 0) FAIL(b); + if (lang[0] == 0) strcpy(lang, "KO"); + for (i = 0; lang[i]; i++) + if (lang[i] >= 'a' && lang[i] <= 'z') lang[i] -= 32; + snprintf(h_grp, sizeof(h_grp), "MSG_%.2s", lang); + + EXEC SQL UPDATE cm_code SET use_yn = 'N', updated_at = now() + WHERE code_grp = :h_grp AND code = :h_id AND use_yn = 'Y'; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlerrd[2] == 0) { setl(b, T_RC, 1); FAIL(b); } /* 대상 없음 */ + + sets_(b, T_STR4, h_grp); + setl(b, T_COUNT, sqlca.sqlerrd[2]); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_MSG_FORMAT.h b/app/src/cm/svc/CM_MSG_FORMAT.h new file mode 100644 index 0000000..80a46d2 --- /dev/null +++ b/app/src/cm/svc/CM_MSG_FORMAT.h @@ -0,0 +1,13 @@ +/* + * CM_MSG_FORMAT.h - cm (공통) 서비스 CM_MSG_FORMAT 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_MSG_FORMAT.pgc 에 있다. + */ +#ifndef CM_MSG_FORMAT_H +#define CM_MSG_FORMAT_H + +#include "cm_svc.h" + +void CM_MSG_FORMAT(TPSVCINFO *p); + +#endif /* CM_MSG_FORMAT_H */ diff --git a/app/src/cm/svc/CM_MSG_FORMAT.pgc b/app/src/cm/svc/CM_MSG_FORMAT.pgc new file mode 100644 index 0000000..67e52ba --- /dev/null +++ b/app/src/cm/svc/CM_MSG_FORMAT.pgc @@ -0,0 +1,37 @@ +/* + * CM_MSG_FORMAT.pgc - cm 모듈 서비스 CM_MSG_FORMAT (one service = one file). + * 템플릿 치환(순수 계산): 본문(T_STR1)의 자리표시자 {0}{1}{2} 를 인자 + * T_STR2/T_STR3/T_STR4 로 치환한다. 알 수 없는 인덱스는 원문 유지, 이스케이프 + * {{ 는 { 리터럴로 처리한다. 치환된 자리표시자 개수를 반환. no EXEC SQL. + */ +#include "CM_MSG_FORMAT.h" + +void CM_MSG_FORMAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char tmpl[256], out[512]; + char args[3][128]; + int i = 0, o = 0, subst = 0; + + gets_(b, T_STR1, tmpl, sizeof(tmpl)); + gets_(b, T_STR2, args[0], sizeof(args[0])); + gets_(b, T_STR3, args[1], sizeof(args[1])); + gets_(b, T_STR4, args[2], sizeof(args[2])); + if (tmpl[0] == 0) FAIL(b); + + while (tmpl[i] && o < (int)sizeof(out) - 130) { + if (tmpl[i] == '{' && tmpl[i + 1] == '{') { out[o++] = '{'; i += 2; continue; } + if (tmpl[i] == '{' && tmpl[i + 1] >= '0' && tmpl[i + 1] <= '2' && tmpl[i + 2] == '}') { + int idx = tmpl[i + 1] - '0'; + o += sprintf(out + o, "%s", args[idx]); + subst++; + i += 3; + continue; + } + out[o++] = tmpl[i++]; + } + out[o] = 0; + sets_(b, T_MSG, out); + setl(b, T_COUNT, subst); /* 치환 건수 */ + OK(b); +} diff --git a/app/src/cm/svc/CM_MSG_LANGS.h b/app/src/cm/svc/CM_MSG_LANGS.h new file mode 100644 index 0000000..e4f2713 --- /dev/null +++ b/app/src/cm/svc/CM_MSG_LANGS.h @@ -0,0 +1,13 @@ +/* + * CM_MSG_LANGS.h - cm (공통) 서비스 CM_MSG_LANGS 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_MSG_LANGS.pgc 에 있다. + */ +#ifndef CM_MSG_LANGS_H +#define CM_MSG_LANGS_H + +#include "cm_svc.h" + +void CM_MSG_LANGS(TPSVCINFO *p); + +#endif /* CM_MSG_LANGS_H */ diff --git a/app/src/cm/svc/CM_MSG_LANGS.pgc b/app/src/cm/svc/CM_MSG_LANGS.pgc new file mode 100644 index 0000000..81e5bc5 --- /dev/null +++ b/app/src/cm/svc/CM_MSG_LANGS.pgc @@ -0,0 +1,44 @@ +/* + * CM_MSG_LANGS.pgc - cm 모듈 서비스 CM_MSG_LANGS (one service = one file). + * 메시지 다국어 커버리지: 특정 메시지 ID(T_KEY1)가 어떤 언어 그룹(MSG_%)에 + * 등록되어 있는지 커서로 훑어 지원 언어 수를 세고, 언어코드를 콤마로 이어 + * 목록화한다. 번역 누락 점검용이다. runs on the caller's XA branch. + */ +#include "CM_MSG_LANGS.h" + +void CM_MSG_LANGS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char langs[128]; + long count = 0; + int pos = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_id[32], h_grp[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_id, sizeof(h_id)); + if (h_id[0] == 0) FAIL(b); + langs[0] = 0; + + EXEC SQL DECLARE cur_langs CURSOR FOR + SELECT code_grp FROM cm_code + WHERE code_grp LIKE 'MSG_%' AND code = :h_id AND use_yn = 'Y' + ORDER BY code_grp; + EXEC SQL OPEN cur_langs; + if (sqlca.sqlcode < 0) FAIL(b); + + for (;;) { + EXEC SQL FETCH cur_langs INTO :h_grp; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_langs; FAIL(b); } + if (pos > 0 && pos < (int)sizeof(langs) - 4) langs[pos++] = ','; + if (pos < (int)sizeof(langs) - 4) + pos += sprintf(langs + pos, "%s", h_grp + 4); /* MSG_ 접두 제거 */ + count++; + } + EXEC SQL CLOSE cur_langs; + + setl(b, T_COUNT, count); /* 지원 언어 수 */ + sets_(b, T_STR4, langs); /* 언어코드 목록 */ + OK(b); +} diff --git a/app/src/cm/svc/CM_MSG_RENDER.h b/app/src/cm/svc/CM_MSG_RENDER.h new file mode 100644 index 0000000..6172599 --- /dev/null +++ b/app/src/cm/svc/CM_MSG_RENDER.h @@ -0,0 +1,13 @@ +/* + * CM_MSG_RENDER.h - cm (공통) 서비스 CM_MSG_RENDER 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_MSG_RENDER.pgc 에 있다. + */ +#ifndef CM_MSG_RENDER_H +#define CM_MSG_RENDER_H + +#include "cm_svc.h" + +void CM_MSG_RENDER(TPSVCINFO *p); + +#endif /* CM_MSG_RENDER_H */ diff --git a/app/src/cm/svc/CM_MSG_RENDER.pgc b/app/src/cm/svc/CM_MSG_RENDER.pgc new file mode 100644 index 0000000..54a7c73 --- /dev/null +++ b/app/src/cm/svc/CM_MSG_RENDER.pgc @@ -0,0 +1,45 @@ +/* + * CM_MSG_RENDER.pgc - cm 모듈 서비스 CM_MSG_RENDER (one service = one file). + * 다국어 메시지 렌더: DB 에서 언어별 템플릿(MSG_/ID)을 조회한 뒤 본문의 + * 자리표시자 {0}{1} 를 인자(T_STR3/T_STR4)로 치환해 완성 메시지를 만든다. + * DB 조회(EXEC SQL) + 순수 치환을 한 서비스에서 결합한다. runs on caller's XA branch. + */ +#include "CM_MSG_RENDER.h" + +void CM_MSG_RENDER(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char lang[8], a0[128], a1[128], out[384]; + int i = 0, o = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_grp[16], h_id[32], h_tmpl[256]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR1, lang, sizeof(lang)); + gets_(b, T_KEY1, h_id, sizeof(h_id)); + gets_(b, T_STR3, a0, sizeof(a0)); + gets_(b, T_STR4, a1, sizeof(a1)); + if (h_id[0] == 0) FAIL(b); + if (lang[0] == 0) strcpy(lang, "KO"); + if (lang[0] >= 'a') lang[0] -= 32; + if (lang[1] >= 'a') lang[1] -= 32; + snprintf(h_grp, sizeof(h_grp), "MSG_%.2s", lang); + + EXEC SQL SELECT code_name INTO :h_tmpl FROM cm_code + WHERE code_grp = :h_grp AND code = :h_id AND use_yn = 'Y'; + if (sqlca.sqlcode != 0) { setl(b, T_RC, 1); FAIL(b); } /* 템플릿 없음 */ + + while (h_tmpl[i] && o < (int)sizeof(out) - 130) { + if (h_tmpl[i] == '{' && h_tmpl[i + 1] == '0' && h_tmpl[i + 2] == '}') { + o += sprintf(out + o, "%s", a0); i += 3; + } else if (h_tmpl[i] == '{' && h_tmpl[i + 1] == '1' && h_tmpl[i + 2] == '}') { + o += sprintf(out + o, "%s", a1); i += 3; + } else { + out[o++] = h_tmpl[i++]; + } + } + out[o] = 0; + sets_(b, T_MSG, out); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_NOTI_QUOTA.h b/app/src/cm/svc/CM_NOTI_QUOTA.h new file mode 100644 index 0000000..1d36386 --- /dev/null +++ b/app/src/cm/svc/CM_NOTI_QUOTA.h @@ -0,0 +1,13 @@ +/* + * CM_NOTI_QUOTA.h - cm (공통) 서비스 CM_NOTI_QUOTA 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_NOTI_QUOTA.pgc 에 있다. + */ +#ifndef CM_NOTI_QUOTA_H +#define CM_NOTI_QUOTA_H + +#include "cm_svc.h" + +void CM_NOTI_QUOTA(TPSVCINFO *p); + +#endif /* CM_NOTI_QUOTA_H */ diff --git a/app/src/cm/svc/CM_NOTI_QUOTA.pgc b/app/src/cm/svc/CM_NOTI_QUOTA.pgc new file mode 100644 index 0000000..3e5f2b9 --- /dev/null +++ b/app/src/cm/svc/CM_NOTI_QUOTA.pgc @@ -0,0 +1,41 @@ +/* + * CM_NOTI_QUOTA.pgc - cm 모듈 서비스 CM_NOTI_QUOTA (one service = one file). + * 알림 발송 한도 점검: 채널(T_CHANNEL)의 기준일 발송 건수를 세고, 파라미터 + * NOTI_QUOTA_(없으면 T_ARG1 기본 한도)와 비교해 잔여 발송 가능량과 + * 초과 여부를 판정한다. 발송 전 사전 점검용. DB 카운트 + 파라미터. runs on caller's XA branch. + */ +#include "CM_NOTI_QUOTA.h" + +void CM_NOTI_QUOTA(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char ch[12], qkey[40], qval[128]; + long quota, remain, exceeded; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16], h_pat[20]; + long h_used; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, ch, sizeof(ch)); + if (ch[0] == 0) strcpy(ch, "SMS"); + gets_(b, T_BIZDATE, h_date, sizeof(h_date)); + if (h_date[0] == 0) { if (cmdb_get_param("BIZ_DATE", h_date) < 0) strcpy(h_date, "2026-07-19"); } + + snprintf(qkey, sizeof(qkey), "NOTI_QUOTA_%s", ch); + if (cmdb_get_param(qkey, qval) == 0) quota = atol(qval); + else quota = getl(b, T_ARG1); /* 파라미터 없으면 호출자 기본 */ + if (quota <= 0) quota = 1000; + + snprintf(h_pat, sizeof(h_pat), "[%s>%%", ch); + EXEC SQL SELECT count(*) INTO :h_used FROM cm_log + WHERE svc_name = 'NOTI' AND biz_date = :h_date AND msg LIKE :h_pat; + if (sqlca.sqlcode < 0) FAIL(b); + + remain = quota - h_used; + exceeded = (remain <= 0) ? 1 : 0; + setl(b, T_ARG2, quota); + setl(b, T_COUNT, h_used); /* 당일 발송 수 */ + setl(b, T_ARG3, remain < 0 ? 0 : remain); /* 잔여 발송 가능량 */ + setl(b, T_RC, exceeded); + OK(b); +} diff --git a/app/src/cm/svc/CM_NOTI_RETRY.h b/app/src/cm/svc/CM_NOTI_RETRY.h new file mode 100644 index 0000000..13f803d --- /dev/null +++ b/app/src/cm/svc/CM_NOTI_RETRY.h @@ -0,0 +1,13 @@ +/* + * CM_NOTI_RETRY.h - cm (공통) 서비스 CM_NOTI_RETRY 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_NOTI_RETRY.pgc 에 있다. + */ +#ifndef CM_NOTI_RETRY_H +#define CM_NOTI_RETRY_H + +#include "cm_svc.h" + +void CM_NOTI_RETRY(TPSVCINFO *p); + +#endif /* CM_NOTI_RETRY_H */ diff --git a/app/src/cm/svc/CM_NOTI_RETRY.pgc b/app/src/cm/svc/CM_NOTI_RETRY.pgc new file mode 100644 index 0000000..c27cfef --- /dev/null +++ b/app/src/cm/svc/CM_NOTI_RETRY.pgc @@ -0,0 +1,32 @@ +/* + * CM_NOTI_RETRY.pgc - cm 모듈 서비스 CM_NOTI_RETRY (one service = one file). + * 알림 재발송: 원 발송 로그 ID(T_ID1)의 내용을 조회해 동일 본문을 재큐잉 + * (svc=NOTI, "[RETRY] ..." 접두)한다. 원 로그가 NOTI 발송건이 아니면 거절하고, + * 재발송 새 ID 를 돌려준다. 로그 조회 + 재적재를 결합한다. runs on caller's XA branch. + */ +#include "CM_NOTI_RETRY.h" + +void CM_NOTI_RETRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char orig[256], packed[288], bizdate[16]; + long src_id, new_id; + + src_id = getl(b, T_ID1); + if (src_id <= 0) FAIL(b); + + orig[0] = 0; + if (cmdb_log_message(src_id, orig) < 0 || orig[0] == 0) { setl(b, T_RC, 1); FAIL(b); } + if (orig[0] != '[') { setl(b, T_RC, 2); FAIL(b); } /* 발송건 형식 아님 */ + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) { if (cmdb_get_param("BIZ_DATE", bizdate) < 0) strcpy(bizdate, "2026-07-19"); } + snprintf(packed, sizeof(packed), "[RETRY] %s", orig); + new_id = cmdb_log("NOTI", "W", packed, bizdate); + if (new_id < 0) FAIL(b); + + setl(b, T_ID2, new_id); /* 재발송 ID */ + sets_(b, T_MSG, orig); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_NOTI_STAT.h b/app/src/cm/svc/CM_NOTI_STAT.h new file mode 100644 index 0000000..a444a4a --- /dev/null +++ b/app/src/cm/svc/CM_NOTI_STAT.h @@ -0,0 +1,13 @@ +/* + * CM_NOTI_STAT.h - cm (공통) 서비스 CM_NOTI_STAT 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_NOTI_STAT.pgc 에 있다. + */ +#ifndef CM_NOTI_STAT_H +#define CM_NOTI_STAT_H + +#include "cm_svc.h" + +void CM_NOTI_STAT(TPSVCINFO *p); + +#endif /* CM_NOTI_STAT_H */ diff --git a/app/src/cm/svc/CM_NOTI_STAT.pgc b/app/src/cm/svc/CM_NOTI_STAT.pgc new file mode 100644 index 0000000..0349128 --- /dev/null +++ b/app/src/cm/svc/CM_NOTI_STAT.pgc @@ -0,0 +1,34 @@ +/* + * CM_NOTI_STAT.pgc - cm 모듈 서비스 CM_NOTI_STAT (one service = one file). + * 알림 채널별 통계: 기준일(T_BIZDATE) NOTI 로그를 본문 접두([SMS>/[EMAIL>/ + * [PUSH>) 기준으로 채널별 발송 건수를 한 번의 조건집계로 산출하고 전체 합계도 + * 함께 돌려준다. 발송 채널 편중 모니터링용이다. runs on the caller's XA branch. + */ +#include "CM_NOTI_STAT.h" + +void CM_NOTI_STAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[16]; + long h_sms, h_email, h_push, h_total; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_date, sizeof(h_date)); + if (h_date[0] == 0) { if (cmdb_get_param("BIZ_DATE", h_date) < 0) strcpy(h_date, "2026-07-19"); } + + EXEC SQL SELECT + coalesce(sum(CASE WHEN msg LIKE '[SMS>%' THEN 1 ELSE 0 END), 0), + coalesce(sum(CASE WHEN msg LIKE '[EMAIL>%' THEN 1 ELSE 0 END), 0), + coalesce(sum(CASE WHEN msg LIKE '[PUSH>%' THEN 1 ELSE 0 END), 0), + count(*) + INTO :h_sms, :h_email, :h_push, :h_total + FROM cm_log WHERE svc_name = 'NOTI' AND biz_date = :h_date; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_ARG1, h_sms); + setl(b, T_ARG2, h_email); + setl(b, T_ARG3, h_push); + setl(b, T_COUNT, h_total); + OK(b); +} diff --git a/app/src/cm/svc/CM_NOTI_TMPL.h b/app/src/cm/svc/CM_NOTI_TMPL.h new file mode 100644 index 0000000..20a5c59 --- /dev/null +++ b/app/src/cm/svc/CM_NOTI_TMPL.h @@ -0,0 +1,13 @@ +/* + * CM_NOTI_TMPL.h - cm (공통) 서비스 CM_NOTI_TMPL 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_NOTI_TMPL.pgc 에 있다. + */ +#ifndef CM_NOTI_TMPL_H +#define CM_NOTI_TMPL_H + +#include "cm_svc.h" + +void CM_NOTI_TMPL(TPSVCINFO *p); + +#endif /* CM_NOTI_TMPL_H */ diff --git a/app/src/cm/svc/CM_NOTI_TMPL.pgc b/app/src/cm/svc/CM_NOTI_TMPL.pgc new file mode 100644 index 0000000..e5131b9 --- /dev/null +++ b/app/src/cm/svc/CM_NOTI_TMPL.pgc @@ -0,0 +1,52 @@ +/* + * CM_NOTI_TMPL.pgc - cm 모듈 서비스 CM_NOTI_TMPL (one service = one file). + * 템플릿 알림 발송: 언어(T_STR2)/템플릿ID(T_KEY1)로 DB 템플릿을 조회해 {0} 을 + * 인자(T_STR3)로 치환한 본문을 만들고, 채널(T_CHANNEL)+수신자(T_STR1)와 함께 + * NOTI 로그로 발송 적재한다. 템플릿 조회+치환+발송을 결합. runs on caller's XA branch. + */ +#include "CM_NOTI_TMPL.h" + +void CM_NOTI_TMPL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char lang[8], arg0[128], ch[12], target[64], body[256], packed[320], bizdate[16]; + int i = 0, o = 0; + long id; + EXEC SQL BEGIN DECLARE SECTION; + char h_grp[16], h_id[32], h_tmpl[200]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR2, lang, sizeof(lang)); + gets_(b, T_KEY1, h_id, sizeof(h_id)); + gets_(b, T_STR3, arg0, sizeof(arg0)); + gets_(b, T_CHANNEL, ch, sizeof(ch)); + gets_(b, T_STR1, target, sizeof(target)); + if (h_id[0] == 0 || target[0] == 0) FAIL(b); + if (ch[0] == 0) strcpy(ch, "SMS"); + if (lang[0] == 0) strcpy(lang, "KO"); + if (lang[0] >= 'a') lang[0] -= 32; + if (lang[1] >= 'a') lang[1] -= 32; + snprintf(h_grp, sizeof(h_grp), "MSG_%.2s", lang); + + EXEC SQL SELECT code_name INTO :h_tmpl FROM cm_code + WHERE code_grp = :h_grp AND code = :h_id AND use_yn = 'Y'; + if (sqlca.sqlcode != 0) { setl(b, T_RC, 1); FAIL(b); } /* 템플릿 없음 */ + + while (h_tmpl[i] && o < (int)sizeof(body) - 130) { + if (h_tmpl[i] == '{' && h_tmpl[i + 1] == '0' && h_tmpl[i + 2] == '}') { + o += sprintf(body + o, "%s", arg0); i += 3; + } else body[o++] = h_tmpl[i++]; + } + body[o] = 0; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) { if (cmdb_get_param("BIZ_DATE", bizdate) < 0) strcpy(bizdate, "2026-07-19"); } + snprintf(packed, sizeof(packed), "[%s>%s] %s", ch, target, body); + id = cmdb_log("NOTI", "I", packed, bizdate); + if (id < 0) FAIL(b); + + setl(b, T_ID1, id); /* 발송 ID */ + sets_(b, T_MSG, body); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_PARAM_CANCEL.h b/app/src/cm/svc/CM_PARAM_CANCEL.h new file mode 100644 index 0000000..0302184 --- /dev/null +++ b/app/src/cm/svc/CM_PARAM_CANCEL.h @@ -0,0 +1,13 @@ +/* + * CM_PARAM_CANCEL.h - cm (공통) 서비스 CM_PARAM_CANCEL 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_PARAM_CANCEL.pgc 에 있다. + */ +#ifndef CM_PARAM_CANCEL_H +#define CM_PARAM_CANCEL_H + +#include "cm_svc.h" + +void CM_PARAM_CANCEL(TPSVCINFO *p); + +#endif /* CM_PARAM_CANCEL_H */ diff --git a/app/src/cm/svc/CM_PARAM_CANCEL.pgc b/app/src/cm/svc/CM_PARAM_CANCEL.pgc new file mode 100644 index 0000000..c001615 --- /dev/null +++ b/app/src/cm/svc/CM_PARAM_CANCEL.pgc @@ -0,0 +1,35 @@ +/* + * CM_PARAM_CANCEL.pgc - cm 모듈 서비스 CM_PARAM_CANCEL (one service = one file). + * 파라미터 변경요청 철회: 대기 중인 요청 키(REQ:)를 승인 전에 삭제한다. + * 본 파라미터는 건드리지 않으며, 대기 요청이 없으면 실패(RC=1)로 구분한다. + * 철회 사실과 요청자를 이력에 남긴다. runs on the caller's XA branch. + */ +#include "CM_PARAM_CANCEL.h" + +void CM_PARAM_CANCEL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char key[64], who[32], pend[128], bizdate[16], hist[224]; + EXEC SQL BEGIN DECLARE SECTION; + char h_reqkey[72]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR1, key, sizeof(key)); + gets_(b, T_STR3, who, sizeof(who)); + if (key[0] == 0) FAIL(b); + if (who[0] == 0) strcpy(who, "SYSTEM"); + snprintf(h_reqkey, sizeof(h_reqkey), "REQ:%s", key); + + if (cmdb_get_param(h_reqkey, pend) < 0) { setl(b, T_RC, 1); FAIL(b); } /* 대기 없음 */ + + EXEC SQL DELETE FROM cm_param WHERE param_key = :h_reqkey; + if (sqlca.sqlcode < 0) FAIL(b); + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) { if (cmdb_get_param("BIZ_DATE", bizdate) < 0) strcpy(bizdate, "2026-07-19"); } + snprintf(hist, sizeof(hist), "CANCEL %s (was %s) by %s", key, pend, who); + cmdb_log("PARAMWF", "I", hist, bizdate); + sets_(b, T_STR4, pend); /* 철회된 요청 값 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_PARAM_DIFF.h b/app/src/cm/svc/CM_PARAM_DIFF.h new file mode 100644 index 0000000..3504821 --- /dev/null +++ b/app/src/cm/svc/CM_PARAM_DIFF.h @@ -0,0 +1,13 @@ +/* + * CM_PARAM_DIFF.h - cm (공통) 서비스 CM_PARAM_DIFF 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_PARAM_DIFF.pgc 에 있다. + */ +#ifndef CM_PARAM_DIFF_H +#define CM_PARAM_DIFF_H + +#include "cm_svc.h" + +void CM_PARAM_DIFF(TPSVCINFO *p); + +#endif /* CM_PARAM_DIFF_H */ diff --git a/app/src/cm/svc/CM_PARAM_DIFF.pgc b/app/src/cm/svc/CM_PARAM_DIFF.pgc new file mode 100644 index 0000000..5d0fe45 --- /dev/null +++ b/app/src/cm/svc/CM_PARAM_DIFF.pgc @@ -0,0 +1,36 @@ +/* + * CM_PARAM_DIFF.pgc - cm 모듈 서비스 CM_PARAM_DIFF (one service = one file). + * 파라미터 요청-현재 비교: 대기 요청 값(REQ:)과 현재 본 값을 읽어와 + * 변경 여부와 (양측이 정수면) 증감폭을 계산한다. 승인 전 검토 화면용이다. + * 대기 요청이 없으면 실패한다. 문자열/수치 변경을 구분한다. no direct EXEC SQL. + */ +#include "CM_PARAM_DIFF.h" + +void CM_PARAM_DIFF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char key[64], reqkey[72], cur[128], req[128]; + long changed, numeric = 0, delta = 0; + + gets_(b, T_STR1, key, sizeof(key)); + if (key[0] == 0) FAIL(b); + snprintf(reqkey, sizeof(reqkey), "REQ:%s", key); + + if (cmdb_get_param(reqkey, req) < 0) { setl(b, T_RC, 1); FAIL(b); } /* 대기 없음 */ + if (cmdb_get_param(key, cur) < 0) cur[0] = 0; /* 신규 키면 현재값 없음 */ + + changed = (strcmp(cur, req) != 0) ? 1 : 0; + if (cur[0] && req[0]) { + char *ec, *er; + long nc = strtol(cur, &ec, 10); + long nr = strtol(req, &er, 10); + if (*ec == 0 && *er == 0) { numeric = 1; delta = nr - nc; } + } + + sets_(b, T_STR4, cur); + sets_(b, T_STR5, req); + setl(b, T_ARG1, changed); + setl(b, T_ARG2, numeric); + setl(b, T_DELTA, delta); /* 수치 증감(수치일 때만) */ + OK(b); +} diff --git a/app/src/cm/svc/CM_PARAM_HIST.h b/app/src/cm/svc/CM_PARAM_HIST.h new file mode 100644 index 0000000..2459bf3 --- /dev/null +++ b/app/src/cm/svc/CM_PARAM_HIST.h @@ -0,0 +1,13 @@ +/* + * CM_PARAM_HIST.h - cm (공통) 서비스 CM_PARAM_HIST 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_PARAM_HIST.pgc 에 있다. + */ +#ifndef CM_PARAM_HIST_H +#define CM_PARAM_HIST_H + +#include "cm_svc.h" + +void CM_PARAM_HIST(TPSVCINFO *p); + +#endif /* CM_PARAM_HIST_H */ diff --git a/app/src/cm/svc/CM_PARAM_HIST.pgc b/app/src/cm/svc/CM_PARAM_HIST.pgc new file mode 100644 index 0000000..c3fcb08 --- /dev/null +++ b/app/src/cm/svc/CM_PARAM_HIST.pgc @@ -0,0 +1,32 @@ +/* + * CM_PARAM_HIST.pgc - cm 모듈 서비스 CM_PARAM_HIST (one service = one file). + * 파라미터 변경 이력 조회: 승인 워크플로가 남긴 공통로그(svc=PARAMWF)에서 + * 특정 키(T_STR1)를 언급한 이력 건수와 마지막 이력 ID/내용을 조회한다. + * 키 미지정 시 워크플로 전체 이력을 집계한다. runs on the caller's XA branch. + */ +#include "CM_PARAM_HIST.h" + +void CM_PARAM_HIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char key[64], lastmsg[256]; + EXEC SQL BEGIN DECLARE SECTION; + char h_pat[80]; + long h_cnt, h_last; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR1, key, sizeof(key)); + if (key[0] == 0) strcpy(h_pat, "%"); + else snprintf(h_pat, sizeof(h_pat), "%%%s%%", key); + + EXEC SQL SELECT count(*), coalesce(max(log_id), 0) INTO :h_cnt, :h_last + FROM cm_log WHERE svc_name = 'PARAMWF' AND msg LIKE :h_pat; + if (sqlca.sqlcode < 0) FAIL(b); + + lastmsg[0] = 0; + if (h_last > 0) cmdb_log_message(h_last, lastmsg); + setl(b, T_COUNT, h_cnt); + setl(b, T_ID1, h_last); + sets_(b, T_MSG, lastmsg); + OK(b); +} diff --git a/app/src/cm/svc/CM_PARAM_LIST.h b/app/src/cm/svc/CM_PARAM_LIST.h new file mode 100644 index 0000000..6db0952 --- /dev/null +++ b/app/src/cm/svc/CM_PARAM_LIST.h @@ -0,0 +1,13 @@ +/* + * CM_PARAM_LIST.h - cm (공통) 서비스 CM_PARAM_LIST 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_PARAM_LIST.pgc 에 있다. + */ +#ifndef CM_PARAM_LIST_H +#define CM_PARAM_LIST_H + +#include "cm_svc.h" + +void CM_PARAM_LIST(TPSVCINFO *p); + +#endif /* CM_PARAM_LIST_H */ diff --git a/app/src/cm/svc/CM_PARAM_LIST.pgc b/app/src/cm/svc/CM_PARAM_LIST.pgc new file mode 100644 index 0000000..79ba157 --- /dev/null +++ b/app/src/cm/svc/CM_PARAM_LIST.pgc @@ -0,0 +1,31 @@ +/* + * CM_PARAM_LIST.pgc - cm 모듈 서비스 CM_PARAM_LIST (one service = one file). + * 파라미터 접두 목록: 접두어(T_STR1, 예 "SCHD:")로 시작하는 파라미터 키의 + * 개수와 대기요청(REQ:) 개수를 각각 집계한다. 접두어 미지정 시 전체를 센다. + * 운영 파라미터 현황 대시보드용 카운트. runs on the caller's XA branch. + */ +#include "CM_PARAM_LIST.h" + +void CM_PARAM_LIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char prefix[48]; + EXEC SQL BEGIN DECLARE SECTION; + char h_pat[56]; + long h_total, h_pending; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR1, prefix, sizeof(prefix)); + if (prefix[0] == 0) strcpy(h_pat, "%"); + else snprintf(h_pat, sizeof(h_pat), "%s%%", prefix); + + EXEC SQL SELECT count(*) INTO :h_total FROM cm_param WHERE param_key LIKE :h_pat; + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT count(*) INTO :h_pending FROM cm_param WHERE param_key LIKE 'REQ:%'; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_total); /* 접두 일치 파라미터 수 */ + setl(b, T_ARG1, h_pending); /* 대기 요청 수 */ + OK(b); +} diff --git a/app/src/cm/svc/CM_PARAM_NUM.h b/app/src/cm/svc/CM_PARAM_NUM.h new file mode 100644 index 0000000..55bfb14 --- /dev/null +++ b/app/src/cm/svc/CM_PARAM_NUM.h @@ -0,0 +1,13 @@ +/* + * CM_PARAM_NUM.h - cm (공통) 서비스 CM_PARAM_NUM 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_PARAM_NUM.pgc 에 있다. + */ +#ifndef CM_PARAM_NUM_H +#define CM_PARAM_NUM_H + +#include "cm_svc.h" + +void CM_PARAM_NUM(TPSVCINFO *p); + +#endif /* CM_PARAM_NUM_H */ diff --git a/app/src/cm/svc/CM_PARAM_NUM.pgc b/app/src/cm/svc/CM_PARAM_NUM.pgc new file mode 100644 index 0000000..73160aa --- /dev/null +++ b/app/src/cm/svc/CM_PARAM_NUM.pgc @@ -0,0 +1,33 @@ +/* + * CM_PARAM_NUM.pgc - cm 모듈 서비스 CM_PARAM_NUM (one service = one file). + * 숫자 파라미터 조회+범위검증: 키(T_STR1)의 값을 정수로 파싱하고, 호출자가 + * 준 최소(T_ARG1)/최대(T_ARG2) 경계 안에 드는지 확인한다. 범위를 벗어나면 + * 경계값으로 클램프한 값을 함께 돌려주고 위반 플래그를 세운다. no direct EXEC SQL. + */ +#include "CM_PARAM_NUM.h" + +void CM_PARAM_NUM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char key[64], val[128], *end; + long lo, hi, raw, clamped, violated = 0; + + gets_(b, T_STR1, key, sizeof(key)); + lo = getl(b, T_ARG1); + hi = getl(b, T_ARG2); + if (key[0] == 0) FAIL(b); + if (cmdb_get_param(key, val) < 0) { setl(b, T_RC, 1); FAIL(b); } /* 미등록 */ + + raw = strtol(val, &end, 10); + if (*end != 0) { setl(b, T_RC, 2); FAIL(b); } /* 숫자 아님 */ + + clamped = raw; + if (hi > lo) { /* 경계가 유효할 때만 검증 */ + if (raw < lo) { clamped = lo; violated = 1; } + else if (raw > hi) { clamped = hi; violated = 1; } + } + setl(b, T_ARG3, raw); /* 원값 */ + setl(b, T_ARG4, clamped); /* 클램프 값 */ + setl(b, T_RC, violated); + OK(b); +} diff --git a/app/src/cm/svc/CM_QTR_END.h b/app/src/cm/svc/CM_QTR_END.h new file mode 100644 index 0000000..3e97bf7 --- /dev/null +++ b/app/src/cm/svc/CM_QTR_END.h @@ -0,0 +1,13 @@ +/* + * CM_QTR_END.h - cm (공통) 서비스 CM_QTR_END 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_QTR_END.pgc 에 있다. + */ +#ifndef CM_QTR_END_H +#define CM_QTR_END_H + +#include "cm_svc.h" + +void CM_QTR_END(TPSVCINFO *p); + +#endif /* CM_QTR_END_H */ diff --git a/app/src/cm/svc/CM_QTR_END.pgc b/app/src/cm/svc/CM_QTR_END.pgc new file mode 100644 index 0000000..3b9c7a5 --- /dev/null +++ b/app/src/cm/svc/CM_QTR_END.pgc @@ -0,0 +1,32 @@ +/* + * CM_QTR_END.pgc - cm 모듈 서비스 CM_QTR_END (one service = one file). + * 분기말일 산출(순수 계산): 입력일(T_STR1)이 속한 분기(1~4)를 판정하고 그 + * 분기의 마지막 날짜(3/6/9/12월 말일)를 계산한다. 12월은 윤년과 무관하게 31일, + * 나머지 분기말월(3/6/9)은 고정 말일이다. 분기 번호도 반환. no EXEC SQL. + */ +#include "CM_QTR_END.h" + +void CM_QTR_END(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char in[16], out[16]; + int y, m, d, q, endmon, endday; + + gets_(b, T_STR1, in, sizeof(in)); + if (sscanf(in, "%d-%d-%d", &y, &m, &d) != 3) FAIL(b); + if (m < 1 || m > 12) { setl(b, T_RC, 1); FAIL(b); } + + q = (m - 1) / 3 + 1; /* 분기 1~4 */ + endmon = q * 3; /* 분기말월 3/6/9/12 */ + switch (endmon) { + case 3: endday = 31; break; + case 6: endday = 30; break; + case 9: endday = 30; break; + default: endday = 31; break; /* 12월 */ + } + sprintf(out, "%04d-%02d-%02d", y, endmon, endday); + sets_(b, T_STR4, out); /* 분기말일 */ + setl(b, T_ARG1, q); /* 분기 번호 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_SEQ_ALLOC.h b/app/src/cm/svc/CM_SEQ_ALLOC.h new file mode 100644 index 0000000..2d1eec4 --- /dev/null +++ b/app/src/cm/svc/CM_SEQ_ALLOC.h @@ -0,0 +1,13 @@ +/* + * CM_SEQ_ALLOC.h - cm (공통) 서비스 CM_SEQ_ALLOC 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_SEQ_ALLOC.pgc 에 있다. + */ +#ifndef CM_SEQ_ALLOC_H +#define CM_SEQ_ALLOC_H + +#include "cm_svc.h" + +void CM_SEQ_ALLOC(TPSVCINFO *p); + +#endif /* CM_SEQ_ALLOC_H */ diff --git a/app/src/cm/svc/CM_SEQ_ALLOC.pgc b/app/src/cm/svc/CM_SEQ_ALLOC.pgc new file mode 100644 index 0000000..88e443e --- /dev/null +++ b/app/src/cm/svc/CM_SEQ_ALLOC.pgc @@ -0,0 +1,37 @@ +/* + * CM_SEQ_ALLOC.pgc - cm 모듈 서비스 CM_SEQ_ALLOC (one service = one file). + * 채번 범위 예약: 카운터(T_KEY1)의 seq_val 을 한 번의 UPDATE 로 N(T_ARG1)만큼 + * 증가시키고 증가 후 값을 돌려받아, 예약 구간 [base+1, base+N] 을 확정한다. + * 루프 없이 원자적으로 블록을 선점한다. 카운터가 없으면 생성한다. runs on caller's XA branch. + */ +#include "CM_SEQ_ALLOC.h" + +void CM_SEQ_ALLOC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_name[32]; + long h_n, h_after; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_name, sizeof(h_name)); + h_n = getl(b, T_ARG1); + if (h_name[0] == 0) FAIL(b); + if (h_n < 1) h_n = 1; + if (h_n > 100000) { setl(b, T_RC, 1); FAIL(b); } /* 과대 예약 */ + + EXEC SQL INSERT INTO cm_seq (seq_name, seq_val) VALUES (:h_name, 0) + ON CONFLICT (seq_name) DO NOTHING; + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL UPDATE cm_seq SET seq_val = seq_val + :h_n, updated_at = now() + WHERE seq_name = :h_name + RETURNING seq_val INTO :h_after; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_ID1, h_after - h_n + 1); /* 예약 구간 시작 */ + setl(b, T_ID2, h_after); /* 예약 구간 끝 */ + setl(b, T_COUNT, h_n); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_SEQ_MULTI.h b/app/src/cm/svc/CM_SEQ_MULTI.h new file mode 100644 index 0000000..77ed7b5 --- /dev/null +++ b/app/src/cm/svc/CM_SEQ_MULTI.h @@ -0,0 +1,13 @@ +/* + * CM_SEQ_MULTI.h - cm (공통) 서비스 CM_SEQ_MULTI 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_SEQ_MULTI.pgc 에 있다. + */ +#ifndef CM_SEQ_MULTI_H +#define CM_SEQ_MULTI_H + +#include "cm_svc.h" + +void CM_SEQ_MULTI(TPSVCINFO *p); + +#endif /* CM_SEQ_MULTI_H */ diff --git a/app/src/cm/svc/CM_SEQ_MULTI.pgc b/app/src/cm/svc/CM_SEQ_MULTI.pgc new file mode 100644 index 0000000..0f11191 --- /dev/null +++ b/app/src/cm/svc/CM_SEQ_MULTI.pgc @@ -0,0 +1,33 @@ +/* + * CM_SEQ_MULTI.pgc - cm 모듈 서비스 CM_SEQ_MULTI (one service = one file). + * 연속 채번(블록): 카운터(T_KEY1)에서 N개(T_ARG1)의 일련번호를 연속 채번해 + * 시작값과 끝값을 돌려준다. 대량 전표 선점 발번에 쓰인다. N 상한(1000)을 두어 + * 폭주를 막는다. 한 XA 트랜잭션 안에서 N회 증가시킨다. runs on caller's XA branch. + */ +#include "CM_SEQ_MULTI.h" + +void CM_SEQ_MULTI(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char name[32]; + long n, i, first = 0, last = 0, v; + + gets_(b, T_KEY1, name, sizeof(name)); + n = getl(b, T_ARG1); + if (name[0] == 0) FAIL(b); + if (n < 1) n = 1; + if (n > 1000) { setl(b, T_RC, 1); FAIL(b); } /* 발번 상한 초과 */ + + for (i = 0; i < n; i++) { + v = cmdb_next_seq(name); + if (v < 0) FAIL(b); + if (i == 0) first = v; + last = v; + } + + setl(b, T_ID1, first); /* 블록 시작값 */ + setl(b, T_ID2, last); /* 블록 끝값 */ + setl(b, T_COUNT, n); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_SEQ_STAMP.h b/app/src/cm/svc/CM_SEQ_STAMP.h new file mode 100644 index 0000000..1c57a23 --- /dev/null +++ b/app/src/cm/svc/CM_SEQ_STAMP.h @@ -0,0 +1,13 @@ +/* + * CM_SEQ_STAMP.h - cm (공통) 서비스 CM_SEQ_STAMP 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_SEQ_STAMP.pgc 에 있다. + */ +#ifndef CM_SEQ_STAMP_H +#define CM_SEQ_STAMP_H + +#include "cm_svc.h" + +void CM_SEQ_STAMP(TPSVCINFO *p); + +#endif /* CM_SEQ_STAMP_H */ diff --git a/app/src/cm/svc/CM_SEQ_STAMP.pgc b/app/src/cm/svc/CM_SEQ_STAMP.pgc new file mode 100644 index 0000000..dd4848e --- /dev/null +++ b/app/src/cm/svc/CM_SEQ_STAMP.pgc @@ -0,0 +1,35 @@ +/* + * CM_SEQ_STAMP.pgc - cm 모듈 서비스 CM_SEQ_STAMP (one service = one file). + * 일자형 채번: 유형(T_KEY1)과 영업일(T_BIZDATE)을 결합한 카운터명 + * (:)으로 당일 일련번호를 채번하고, "" + * 형태의 전표번호 문자열을 조립한다. dbio 채번 + 순수 포맷. runs on caller's XA branch. + */ +#include "CM_SEQ_STAMP.h" + +void CM_SEQ_STAMP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char type[16], bizdate[16], compact[12], seqname[40], out[48]; + long v; + int i, j; + + gets_(b, T_KEY1, type, sizeof(type)); + if (type[0] == 0) FAIL(b); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) { if (cmdb_get_param("BIZ_DATE", bizdate) < 0) strcpy(bizdate, "2026-07-19"); } + + for (i = 0, j = 0; bizdate[i] && j < 8; i++) /* YYYY-MM-DD -> YYYYMMDD */ + if (bizdate[i] != '-') compact[j++] = bizdate[i]; + compact[j] = 0; + if (j != 8) { setl(b, T_RC, 1); FAIL(b); } /* 날짜 형식 오류 */ + + snprintf(seqname, sizeof(seqname), "%s:%s", type, compact); + v = cmdb_next_seq(seqname); + if (v < 0) FAIL(b); + + snprintf(out, sizeof(out), "%s%s%04ld", type, compact, v); + setl(b, T_ARG1, v); /* 당일 일련값 */ + sets_(b, T_STR4, out); /* 조립된 전표번호 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_SEQ_VERIFY.h b/app/src/cm/svc/CM_SEQ_VERIFY.h new file mode 100644 index 0000000..b5b3d9a --- /dev/null +++ b/app/src/cm/svc/CM_SEQ_VERIFY.h @@ -0,0 +1,13 @@ +/* + * CM_SEQ_VERIFY.h - cm (공통) 서비스 CM_SEQ_VERIFY 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_SEQ_VERIFY.pgc 에 있다. + */ +#ifndef CM_SEQ_VERIFY_H +#define CM_SEQ_VERIFY_H + +#include "cm_svc.h" + +void CM_SEQ_VERIFY(TPSVCINFO *p); + +#endif /* CM_SEQ_VERIFY_H */ diff --git a/app/src/cm/svc/CM_SEQ_VERIFY.pgc b/app/src/cm/svc/CM_SEQ_VERIFY.pgc new file mode 100644 index 0000000..1fe0edd --- /dev/null +++ b/app/src/cm/svc/CM_SEQ_VERIFY.pgc @@ -0,0 +1,35 @@ +/* + * CM_SEQ_VERIFY.pgc - cm 모듈 서비스 CM_SEQ_VERIFY (one service = one file). + * 채번 체크디짓 검증(순수 계산): 발번 문자열(T_STR1)의 마지막 자리를 체크디짓 + * 으로 보고, 앞자리 숫자들의 가중합(자리마다 2,3,4... 가중) mod 11 로 기대 + * 체크디짓을 재계산해 일치 여부를 판정한다. 위·변조 탐지용. no EXEC SQL. + */ +#include "CM_SEQ_VERIFY.h" + +void CM_SEQ_VERIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char s[32]; + int len, i, w = 2, expect, given, ok; + long sum = 0; + + gets_(b, T_STR1, s, sizeof(s)); + len = (int)strlen(s); + if (len < 2) { setl(b, T_RC, 1); FAIL(b); } /* 너무 짧음 */ + + for (i = len - 2; i >= 0; i--) { /* 마지막 앞자리부터 가중 */ + if (s[i] < '0' || s[i] > '9') { setl(b, T_RC, 2); FAIL(b); } + sum += (long)(s[i] - '0') * w; + w = (w >= 9) ? 2 : w + 1; /* 가중치 2~9 순환 */ + } + if (s[len - 1] < '0' || s[len - 1] > '9') { setl(b, T_RC, 2); FAIL(b); } + + expect = (int)((11 - (sum % 11)) % 11 % 10); /* 기대 체크디짓 */ + given = s[len - 1] - '0'; + ok = (expect == given) ? 1 : 0; + + setl(b, T_ARG1, expect); + setl(b, T_ARG2, given); + setl(b, T_RC, ok ? 0 : 9); /* 0=정상, 9=불일치 */ + OK(b); +} diff --git a/app/src/cm/svc/CM_STR_PAD.h b/app/src/cm/svc/CM_STR_PAD.h new file mode 100644 index 0000000..798cb48 --- /dev/null +++ b/app/src/cm/svc/CM_STR_PAD.h @@ -0,0 +1,13 @@ +/* + * CM_STR_PAD.h - cm (공통) 서비스 CM_STR_PAD 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_STR_PAD.pgc 에 있다. + */ +#ifndef CM_STR_PAD_H +#define CM_STR_PAD_H + +#include "cm_svc.h" + +void CM_STR_PAD(TPSVCINFO *p); + +#endif /* CM_STR_PAD_H */ diff --git a/app/src/cm/svc/CM_STR_PAD.pgc b/app/src/cm/svc/CM_STR_PAD.pgc new file mode 100644 index 0000000..d392d5b --- /dev/null +++ b/app/src/cm/svc/CM_STR_PAD.pgc @@ -0,0 +1,45 @@ +/* + * CM_STR_PAD.pgc - cm 모듈 서비스 CM_STR_PAD (one service = one file). + * 문자열 패딩(순수 계산): 입력(T_STR1)을 목표 폭(T_ARG1)까지 채운다. 방향 + * (T_STR2: "L"=좌측 채움/우측정렬, 그 외=우측 채움/좌측정렬)과 채움문자 + * (T_KEY1, 기본 '0')를 받는다. 이미 폭 이상이면 원문 유지. 전표/코드 정렬용. no EXEC SQL. + */ +#include "CM_STR_PAD.h" + +void CM_STR_PAD(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char in[128], dir[4], fillbuf[4], out[160]; + long width; + int len, pad, i, o = 0; + char fill; + + gets_(b, T_STR1, in, sizeof(in)); + gets_(b, T_STR2, dir, sizeof(dir)); + gets_(b, T_KEY1, fillbuf, sizeof(fillbuf)); + width = getl(b, T_ARG1); + len = (int)strlen(in); + fill = (fillbuf[0] != 0) ? fillbuf[0] : '0'; + if (width < 1 || width > 128) { setl(b, T_RC, 1); FAIL(b); } + + pad = (int)width - len; + if (pad <= 0) { /* 폭 이상 = 원문 */ + strcpy(out, in); + setl(b, T_ARG2, 0); + } else if (dir[0] == 'L') { /* 좌측 채움(우측 정렬) */ + for (i = 0; i < pad; i++) out[o++] = fill; + for (i = 0; in[i]; i++) out[o++] = in[i]; + out[o] = 0; + setl(b, T_ARG2, pad); + } else { /* 우측 채움(좌측 정렬) */ + for (i = 0; in[i]; i++) out[o++] = in[i]; + for (i = 0; i < pad; i++) out[o++] = fill; + out[o] = 0; + setl(b, T_ARG2, pad); + } + + sets_(b, T_STR4, out); + setl(b, T_ARG1, (long)strlen(out)); /* 결과 길이 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/cm/svc/CM_STR_TRIM.h b/app/src/cm/svc/CM_STR_TRIM.h new file mode 100644 index 0000000..5ca9237 --- /dev/null +++ b/app/src/cm/svc/CM_STR_TRIM.h @@ -0,0 +1,13 @@ +/* + * CM_STR_TRIM.h - cm (공통) 서비스 CM_STR_TRIM 카피북 (copybook / record header). + * cm_svr 가 tpsvrinit 에서 광고하는 확장 공통 서비스. 공유 베이스(cm_svc.h)만 + * 포함하고 프로토타입을 선언한다. 비즈니스 로직은 CM_STR_TRIM.pgc 에 있다. + */ +#ifndef CM_STR_TRIM_H +#define CM_STR_TRIM_H + +#include "cm_svc.h" + +void CM_STR_TRIM(TPSVCINFO *p); + +#endif /* CM_STR_TRIM_H */ diff --git a/app/src/cm/svc/CM_STR_TRIM.pgc b/app/src/cm/svc/CM_STR_TRIM.pgc new file mode 100644 index 0000000..94e5fc4 --- /dev/null +++ b/app/src/cm/svc/CM_STR_TRIM.pgc @@ -0,0 +1,40 @@ +/* + * CM_STR_TRIM.pgc - cm 모듈 서비스 CM_STR_TRIM (one service = one file). + * 문자열 정규화(순수 계산): 입력(T_STR1)의 양끝 공백/탭을 제거하고, 내부의 + * 연속 공백을 하나로 접는다(squeeze). 정규화 전후 길이와 접힌 공백 수를 함께 + * 반환한다. 입력 정제/키 정규화 유틸이다. no EXEC SQL. + */ +#include "CM_STR_TRIM.h" + +static int cm_ws(char c) { return c == ' ' || c == '\t'; } + +void CM_STR_TRIM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char in[256], out[256]; + int i, o = 0, orig, squeezed = 0, prev_ws = 0; + + gets_(b, T_STR1, in, sizeof(in)); + orig = (int)strlen(in); + + i = 0; + while (cm_ws(in[i])) i++; /* 앞 공백 스킵 */ + for (; in[i]; i++) { + if (cm_ws(in[i])) { + if (prev_ws) { squeezed++; continue; } /* 연속 공백 접기 */ + out[o++] = ' '; + prev_ws = 1; + } else { + out[o++] = in[i]; + prev_ws = 0; + } + } + while (o > 0 && out[o - 1] == ' ') o--; /* 뒤 공백 제거 */ + out[o] = 0; + + sets_(b, T_STR4, out); + setl(b, T_ARG1, orig); /* 원 길이 */ + setl(b, T_ARG2, o); /* 정규화 길이 */ + setl(b, T_ARG3, squeezed); /* 접힌 공백 수 */ + OK(b); +} diff --git a/app/src/lg/batch/lg_allowance_batch.pgc b/app/src/lg/batch/lg_allowance_batch.pgc new file mode 100644 index 0000000..6c7baee --- /dev/null +++ b/app/src/lg/batch/lg_allowance_batch.pgc @@ -0,0 +1,40 @@ +/* + * lg_allowance_batch.pgc - 대손충당금 산정 배치: RECV 누계잔액 x 율 로 필요 충당금 계산 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; EXEC SQL work runs on that branch, then tpcommit drives XA 2PC. + * 복식부기: 차변(D) 합 = 대변(C) 합. Usage: lg_allowance_batch [YYYY-MM-DD] [FROM|arg] + */ +#include +#include +#include +#include +#include "lg_dbio.h" +#include "lg_allowance_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + const char *fromd = (argc > 2) ? argv[2] : bizdate; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_from[16], h_acct[32], h_type[16]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-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; } + long rate = (argc > 2) ? atol(argv[2]) : 100; + long base = 0, allow = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE -amount END), 0) + INTO :h_dr FROM lg_ledger_line WHERE acct = 'RECV' AND biz_date <= :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "SEL FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + base = h_dr; allow = base * rate / 10000; + printf(" %s RECV잔액 %ld @%ldbp 필요충당금 %ld\n", bizdate, base, rate, allow); + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> lg_allowance_batch COMMIT: bizdate=%s\n", bizdate); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/lg/batch/lg_auditscan_batch.pgc b/app/src/lg/batch/lg_auditscan_batch.pgc new file mode 100644 index 0000000..166c7e5 --- /dev/null +++ b/app/src/lg/batch/lg_auditscan_batch.pgc @@ -0,0 +1,45 @@ +/* + * lg_auditscan_batch.pgc - 원장 감사 스캔 배치: 취소 전표의 posted 라인 불일치 건수 집계 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; EXEC SQL work runs on that branch, then tpcommit drives XA 2PC. + * 복식부기: 차변(D) 합 = 대변(C) 합. Usage: lg_auditscan_batch [YYYY-MM-DD] [FROM|arg] + */ +#include +#include +#include +#include +#include "lg_dbio.h" +#include "lg_auditscan_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + const char *fromd = (argc > 2) ? argv[2] : bizdate; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_from[16], h_acct[32], h_type[16]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-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; } + long bad = 0, orphan = 0; + EXEC SQL SELECT count(*) INTO :h_dr + FROM lg_ledger_line l JOIN lg_voucher v ON v.voucher_id = l.voucher_id + WHERE l.biz_date = :h_bizdate AND v.status = 'X' AND l.status = 'posted'; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + bad = h_dr; + EXEC SQL SELECT count(*) INTO :h_cr FROM lg_ledger_line l + WHERE l.biz_date = :h_bizdate + AND NOT EXISTS (SELECT 1 FROM lg_voucher v WHERE v.voucher_id = l.voucher_id); + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + orphan = h_cr; + printf(" %s 취소불일치 %ld 고아라인 %ld 합 %ld\n", bizdate, bad, orphan, bad + orphan); + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> lg_auditscan_batch COMMIT: bizdate=%s\n", bizdate); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/lg/batch/lg_balcert_batch.pgc b/app/src/lg/batch/lg_balcert_batch.pgc new file mode 100644 index 0000000..9558d23 --- /dev/null +++ b/app/src/lg/batch/lg_balcert_batch.pgc @@ -0,0 +1,51 @@ +/* + * lg_balcert_batch.pgc - 잔액증명 대사 배치: 계정별 기록 잔액과 라인 재계산 잔액 불일치 검출 (커서 + XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; EXEC SQL work runs on that branch, then tpcommit drives XA 2PC. + * 복식부기: 차변(D) 합 = 대변(C) 합. Usage: lg_balcert_batch [YYYY-MM-DD] [FROM|arg] + */ +#include +#include +#include +#include +#include "lg_dbio.h" +#include "lg_balcert_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + const char *fromd = (argc > 2) ? argv[2] : bizdate; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_from[16], h_acct[32], h_type[16]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-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; } + long checked = 0, mism = 0; + EXEC SQL DECLARE bcb CURSOR FOR + SELECT ab.acct, ab.balance, + coalesce((SELECT sum(CASE WHEN l.dc = 'D' THEN l.amount ELSE -l.amount END) + FROM lg_ledger_line l + WHERE l.acct = ab.acct AND l.biz_date = ab.biz_date AND l.status = 'posted'), 0) + FROM lg_account_bal ab WHERE ab.biz_date = :h_bizdate ORDER BY ab.acct; + EXEC SQL OPEN bcb; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH bcb INTO :h_acct, :h_dr, :h_cr; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bcb; tpabort(0); return 1; } + checked++; + if (h_dr != h_cr) { printf(" NG %-14s 기록 %ld 재계산 %ld\n", h_acct, h_dr, h_cr); mism++; } + } + EXEC SQL CLOSE bcb; + printf(" 검증 %ld계정 불일치 %ld건\n", checked, mism); + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> lg_balcert_batch COMMIT: bizdate=%s\n", bizdate); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/lg/batch/lg_deptledger_batch.pgc b/app/src/lg/batch/lg_deptledger_batch.pgc new file mode 100644 index 0000000..2a6b4a1 --- /dev/null +++ b/app/src/lg/batch/lg_deptledger_batch.pgc @@ -0,0 +1,50 @@ +/* + * lg_deptledger_batch.pgc - 부문(전표유형)별 원장 집계 배치 (커서 GROUP BY + XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; EXEC SQL work runs on that branch, then tpcommit drives XA 2PC. + * 복식부기: 차변(D) 합 = 대변(C) 합. Usage: lg_deptledger_batch [YYYY-MM-DD] [FROM|arg] + */ +#include +#include +#include +#include +#include "lg_dbio.h" +#include "lg_deptledger_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + const char *fromd = (argc > 2) ? argv[2] : bizdate; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_from[16], h_acct[32], h_type[16]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-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; } + long types = 0, net_diff = 0; + EXEC SQL DECLARE dlb CURSOR FOR + SELECT v.voucher_type, + coalesce(sum(CASE WHEN l.dc = 'D' THEN l.amount ELSE 0 END), 0), + coalesce(sum(CASE WHEN l.dc = 'C' THEN l.amount ELSE 0 END), 0) + FROM lg_voucher v JOIN lg_ledger_line l ON l.voucher_id = v.voucher_id + WHERE v.biz_date = :h_bizdate GROUP BY v.voucher_type ORDER BY v.voucher_type; + EXEC SQL OPEN dlb; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH dlb INTO :h_type, :h_dr, :h_cr; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE dlb; tpabort(0); return 1; } + printf(" %-10s 차변=%ld 대변=%ld 차액=%ld\n", h_type, h_dr, h_cr, h_dr - h_cr); + net_diff += h_dr - h_cr; types++; + } + EXEC SQL CLOSE dlb; + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> lg_deptledger_batch COMMIT: bizdate=%s\n", bizdate); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/lg/batch/lg_halfclose_batch.pgc b/app/src/lg/batch/lg_halfclose_batch.pgc new file mode 100644 index 0000000..f2d1068 --- /dev/null +++ b/app/src/lg/batch/lg_halfclose_batch.pgc @@ -0,0 +1,40 @@ +/* + * lg_halfclose_batch.pgc - 반기 손익(FEE_INC) 순액 집계 배치 (기간 합계 + XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; EXEC SQL work runs on that branch, then tpcommit drives XA 2PC. + * 복식부기: 차변(D) 합 = 대변(C) 합. Usage: lg_halfclose_batch [YYYY-MM-DD] [FROM|arg] + */ +#include +#include +#include +#include +#include "lg_dbio.h" +#include "lg_halfclose_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + const char *fromd = (argc > 2) ? argv[2] : bizdate; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_from[16], h_acct[32], h_type[16]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-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; } + long net = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE -amount END), 0) + INTO :h_dr FROM lg_ledger_line + WHERE acct = 'FEE_INC' AND biz_date BETWEEN :h_from AND :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "SEL FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + net = h_dr; + printf(" 반기 %s~%s FEE_INC 순액 %ld\n", fromd, bizdate, net); + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> lg_halfclose_batch COMMIT: bizdate=%s\n", bizdate); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/lg/batch/lg_prepaid_batch.pgc b/app/src/lg/batch/lg_prepaid_batch.pgc new file mode 100644 index 0000000..6b7ccdb --- /dev/null +++ b/app/src/lg/batch/lg_prepaid_batch.pgc @@ -0,0 +1,49 @@ +/* + * lg_prepaid_batch.pgc - 선급비용 잔액 계정별 집계 배치 (커서 + XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; EXEC SQL work runs on that branch, then tpcommit drives XA 2PC. + * 복식부기: 차변(D) 합 = 대변(C) 합. Usage: lg_prepaid_batch [YYYY-MM-DD] [FROM|arg] + */ +#include +#include +#include +#include +#include "lg_dbio.h" +#include "lg_prepaid_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + const char *fromd = (argc > 2) ? argv[2] : bizdate; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_from[16], h_acct[32], h_type[16]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-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; } + long accts = 0, total = 0; + EXEC SQL DECLARE ppb CURSOR FOR + SELECT acct, coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE -amount END), 0) + FROM lg_ledger_line WHERE biz_date <= :h_bizdate AND acct LIKE 'PREPAID%' + GROUP BY acct ORDER BY acct; + EXEC SQL OPEN ppb; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH ppb INTO :h_acct, :h_dr; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ppb; tpabort(0); return 1; } + printf(" %-14s 잔액=%ld\n", h_acct, h_dr); + total += h_dr; accts++; + } + EXEC SQL CLOSE ppb; + printf(" 선급 계정 %ld개 합계 %ld\n", accts, total); + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> lg_prepaid_batch COMMIT: bizdate=%s\n", bizdate); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/lg/batch/lg_revaccrue_batch.pgc b/app/src/lg/batch/lg_revaccrue_batch.pgc new file mode 100644 index 0000000..ab5ac34 --- /dev/null +++ b/app/src/lg/batch/lg_revaccrue_batch.pgc @@ -0,0 +1,39 @@ +/* + * lg_revaccrue_batch.pgc - 수익 발생 계상 대상 집계 배치: 미인식 RECV_ACCRUED 잔액 산출 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; EXEC SQL work runs on that branch, then tpcommit drives XA 2PC. + * 복식부기: 차변(D) 합 = 대변(C) 합. Usage: lg_revaccrue_batch [YYYY-MM-DD] [FROM|arg] + */ +#include +#include +#include +#include +#include "lg_dbio.h" +#include "lg_revaccrue_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + const char *fromd = (argc > 2) ? argv[2] : bizdate; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_from[16], h_acct[32], h_type[16]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-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; } + long bal = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE -amount END), 0) + INTO :h_dr FROM lg_ledger_line WHERE acct = 'RECV_ACCRUED' AND biz_date <= :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "SEL FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + bal = h_dr; + printf(" %s 미수수익 잔액 %ld\n", bizdate, bal); + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> lg_revaccrue_batch COMMIT: bizdate=%s\n", bizdate); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/lg/batch/lg_spantb_batch.pgc b/app/src/lg/batch/lg_spantb_batch.pgc new file mode 100644 index 0000000..2fd9569 --- /dev/null +++ b/app/src/lg/batch/lg_spantb_batch.pgc @@ -0,0 +1,57 @@ +/* + * lg_spantb_batch.pgc - 기간 시산표 배치: 기간 계정별 차대 합계를 시산표(마감일자)로 upsert (커서 + XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; EXEC SQL work runs on that branch, then tpcommit drives XA 2PC. + * 복식부기: 차변(D) 합 = 대변(C) 합. Usage: lg_spantb_batch [YYYY-MM-DD] [FROM|arg] + */ +#include +#include +#include +#include +#include "lg_dbio.h" +#include "lg_spantb_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + const char *fromd = (argc > 2) ? argv[2] : bizdate; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_from[16], h_acct[32], h_type[16]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-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; } + long rows = 0, net_diff = 0; + EXEC SQL DECLARE stb CURSOR FOR + SELECT acct, + coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE 0 END), 0), + coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE 0 END), 0) + FROM lg_ledger_line WHERE biz_date BETWEEN :h_from AND :h_bizdate + GROUP BY acct ORDER BY acct; + EXEC SQL OPEN stb; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH stb INTO :h_acct, :h_dr, :h_cr; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE stb; tpabort(0); return 1; } + EXEC SQL INSERT INTO lg_trial_bal (acct, biz_date, debit_total, credit_total, diff) + VALUES (:h_acct, :h_bizdate, :h_dr, :h_cr, :h_dr - :h_cr) + ON CONFLICT (acct, biz_date) DO UPDATE + SET debit_total = EXCLUDED.debit_total, credit_total = EXCLUDED.credit_total, + diff = EXCLUDED.diff, updated_at = now(); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE stb; tpabort(0); return 1; } + net_diff += h_dr - h_cr; rows++; + } + EXEC SQL CLOSE stb; + printf(" 기간 %s~%s 계정 %ld 총차액 %ld %s\n", fromd, bizdate, rows, net_diff, + (net_diff == 0) ? "(균형)" : "(불균형!)"); + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> lg_spantb_batch COMMIT: bizdate=%s\n", bizdate); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/lg/batch/lg_threeway_batch.pgc b/app/src/lg/batch/lg_threeway_batch.pgc new file mode 100644 index 0000000..e4b2237 --- /dev/null +++ b/app/src/lg/batch/lg_threeway_batch.pgc @@ -0,0 +1,48 @@ +/* + * lg_threeway_batch.pgc - 원장-정산-매입 3단 대사 배치: 세 원천 합계를 대조 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; EXEC SQL work runs on that branch, then tpcommit drives XA 2PC. + * 복식부기: 차변(D) 합 = 대변(C) 합. Usage: lg_threeway_batch [YYYY-MM-DD] [FROM|arg] + */ +#include +#include +#include +#include +#include "lg_dbio.h" +#include "lg_threeway_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + const char *fromd = (argc > 2) ? argv[2] : bizdate; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_from[16], h_acct[32], h_type[16]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-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; } + long ledger_pay = 0, settle_net = 0, purch_net = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'C' AND acct = 'MERCH_PAY' THEN amount ELSE 0 END), 0) + INTO :h_dr FROM lg_ledger_line WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + ledger_pay = h_dr; + EXEC SQL SELECT coalesce(sum(net), 0) INTO :h_cr FROM settlement WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + settle_net = h_cr; + EXEC SQL SELECT coalesce(sum(net), 0) INTO :h_dr FROM purchase + WHERE biz_date = :h_bizdate AND status IN ('S', 'M'); + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + purch_net = h_dr; + printf(" %s 원장 %ld 정산 %ld 매입 %ld (갭1=%ld 갭2=%ld)\n", + bizdate, ledger_pay, settle_net, purch_net, + ledger_pay - settle_net, settle_net - purch_net); + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> lg_threeway_batch COMMIT: bizdate=%s\n", bizdate); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/lg/batch/lg_writeoff_batch.pgc b/app/src/lg/batch/lg_writeoff_batch.pgc new file mode 100644 index 0000000..a300bd6 --- /dev/null +++ b/app/src/lg/batch/lg_writeoff_batch.pgc @@ -0,0 +1,40 @@ +/* + * lg_writeoff_batch.pgc - 대손 제각 현황 배치: 대손충당금(BADDEBT_ALLOW) 잔액 집계 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; EXEC SQL work runs on that branch, then tpcommit drives XA 2PC. + * 복식부기: 차변(D) 합 = 대변(C) 합. Usage: lg_writeoff_batch [YYYY-MM-DD] [FROM|arg] + */ +#include +#include +#include +#include +#include "lg_dbio.h" +#include "lg_writeoff_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + const char *fromd = (argc > 2) ? argv[2] : bizdate; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_from[16], h_acct[32], h_type[16]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-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; } + long bal = 0, cnt = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE -amount END), 0), count(*) + INTO :h_dr, :h_cr FROM lg_ledger_line + WHERE acct = 'BADDEBT_ALLOW' AND biz_date <= :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "SEL FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + bal = h_dr; cnt = h_cr; + printf(" %s 충당금잔액 %ld (분개 %ld줄)\n", bizdate, bal, cnt); + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> lg_writeoff_batch COMMIT: bizdate=%s\n", bizdate); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/lg/batch/lg_yearclose_batch.pgc b/app/src/lg/batch/lg_yearclose_batch.pgc new file mode 100644 index 0000000..52ecef7 --- /dev/null +++ b/app/src/lg/batch/lg_yearclose_batch.pgc @@ -0,0 +1,50 @@ +/* + * lg_yearclose_batch.pgc - 연말 손익계정(_INC/_EXP) 대체 대상 집계 배치 (커서 + XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; EXEC SQL work runs on that branch, then tpcommit drives XA 2PC. + * 복식부기: 차변(D) 합 = 대변(C) 합. Usage: lg_yearclose_batch [YYYY-MM-DD] [FROM|arg] + */ +#include +#include +#include +#include +#include "lg_dbio.h" +#include "lg_yearclose_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + const char *fromd = (argc > 2) ? argv[2] : bizdate; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_from[16], h_acct[32], h_type[16]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-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; } + long accts = 0, profit = 0; + EXEC SQL DECLARE ycb CURSOR FOR + SELECT acct, coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE -amount END), 0) + FROM lg_ledger_line + WHERE biz_date <= :h_bizdate AND (acct LIKE '%_INC' OR acct LIKE '%_EXP') + GROUP BY acct ORDER BY acct; + EXEC SQL OPEN ycb; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH ycb INTO :h_acct, :h_dr; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ycb; tpabort(0); return 1; } + printf(" %-14s 순액=%ld\n", h_acct, h_dr); + profit += h_dr; accts++; + } + EXEC SQL CLOSE ycb; + printf(" 손익계정 %ld개 추정순이익 %ld\n", accts, profit); + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> lg_yearclose_batch COMMIT: bizdate=%s\n", bizdate); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/lg/dbio/lg_allowance_batch.h b/app/src/lg/dbio/lg_allowance_batch.h new file mode 100644 index 0000000..85a3061 --- /dev/null +++ b/app/src/lg/dbio/lg_allowance_batch.h @@ -0,0 +1,14 @@ +/* + * lg_allowance_batch.h - 대손충당금 산정 배치: RECV 누계잔액 x 율 로 필요 충당금 계산 (XA) 전용 copybook (record layout). + */ +#ifndef LG_ALLOWANCE_BATCH_H +#define LG_ALLOWANCE_BATCH_H + +typedef struct { + char as_of[16]; + long recv_bal; + long rate_bps; + long allowance; +} lg_allowance_batch_rec_t; + +#endif /* LG_ALLOWANCE_BATCH_H */ diff --git a/app/src/lg/dbio/lg_auditscan_batch.h b/app/src/lg/dbio/lg_auditscan_batch.h new file mode 100644 index 0000000..0ebe096 --- /dev/null +++ b/app/src/lg/dbio/lg_auditscan_batch.h @@ -0,0 +1,13 @@ +/* + * lg_auditscan_batch.h - 원장 감사 스캔 배치: 취소 전표의 posted 라인 불일치 건수 집계 (XA) 전용 copybook (record layout). + */ +#ifndef LG_AUDITSCAN_BATCH_H +#define LG_AUDITSCAN_BATCH_H + +typedef struct { + char as_of[16]; + long bad_lines; + long orphan_lines; +} lg_auditscan_batch_rec_t; + +#endif /* LG_AUDITSCAN_BATCH_H */ diff --git a/app/src/lg/dbio/lg_balcert_batch.h b/app/src/lg/dbio/lg_balcert_batch.h new file mode 100644 index 0000000..e20aff1 --- /dev/null +++ b/app/src/lg/dbio/lg_balcert_batch.h @@ -0,0 +1,14 @@ +/* + * lg_balcert_batch.h - 잔액증명 대사 배치: 계정별 기록 잔액과 라인 재계산 잔액 불일치 검출 (커서 + XA) 전용 copybook (record layout). + */ +#ifndef LG_BALCERT_BATCH_H +#define LG_BALCERT_BATCH_H + +typedef struct { + char acct[32]; + long recorded; + long recomputed; + long mismatches; +} lg_balcert_batch_rec_t; + +#endif /* LG_BALCERT_BATCH_H */ diff --git a/app/src/lg/dbio/lg_deptledger_batch.h b/app/src/lg/dbio/lg_deptledger_batch.h new file mode 100644 index 0000000..5991fae --- /dev/null +++ b/app/src/lg/dbio/lg_deptledger_batch.h @@ -0,0 +1,14 @@ +/* + * lg_deptledger_batch.h - 부문(전표유형)별 원장 집계 배치 (커서 GROUP BY + XA) 전용 copybook (record layout). + */ +#ifndef LG_DEPTLEDGER_BATCH_H +#define LG_DEPTLEDGER_BATCH_H + +typedef struct { + char voucher_type[16]; + long debit_total; + long credit_total; + long imbalance; +} lg_deptledger_batch_rec_t; + +#endif /* LG_DEPTLEDGER_BATCH_H */ diff --git a/app/src/lg/dbio/lg_halfclose_batch.h b/app/src/lg/dbio/lg_halfclose_batch.h new file mode 100644 index 0000000..587e8bf --- /dev/null +++ b/app/src/lg/dbio/lg_halfclose_batch.h @@ -0,0 +1,13 @@ +/* + * lg_halfclose_batch.h - 반기 손익(FEE_INC) 순액 집계 배치 (기간 합계 + XA) 전용 copybook (record layout). + */ +#ifndef LG_HALFCLOSE_BATCH_H +#define LG_HALFCLOSE_BATCH_H + +typedef struct { + char period_from[16]; + char period_to[16]; + long fee_net; +} lg_halfclose_batch_rec_t; + +#endif /* LG_HALFCLOSE_BATCH_H */ diff --git a/app/src/lg/dbio/lg_prepaid_batch.h b/app/src/lg/dbio/lg_prepaid_batch.h new file mode 100644 index 0000000..9a1f9a9 --- /dev/null +++ b/app/src/lg/dbio/lg_prepaid_batch.h @@ -0,0 +1,12 @@ +/* + * lg_prepaid_batch.h - 선급비용 잔액 계정별 집계 배치 (커서 + XA) 전용 copybook (record layout). + */ +#ifndef LG_PREPAID_BATCH_H +#define LG_PREPAID_BATCH_H + +typedef struct { + char acct[32]; + long prepaid_bal; +} lg_prepaid_batch_rec_t; + +#endif /* LG_PREPAID_BATCH_H */ diff --git a/app/src/lg/dbio/lg_revaccrue_batch.h b/app/src/lg/dbio/lg_revaccrue_batch.h new file mode 100644 index 0000000..b18da78 --- /dev/null +++ b/app/src/lg/dbio/lg_revaccrue_batch.h @@ -0,0 +1,12 @@ +/* + * lg_revaccrue_batch.h - 수익 발생 계상 대상 집계 배치: 미인식 RECV_ACCRUED 잔액 산출 (XA) 전용 copybook (record layout). + */ +#ifndef LG_REVACCRUE_BATCH_H +#define LG_REVACCRUE_BATCH_H + +typedef struct { + char as_of[16]; + long accrued_bal; +} lg_revaccrue_batch_rec_t; + +#endif /* LG_REVACCRUE_BATCH_H */ diff --git a/app/src/lg/dbio/lg_spantb_batch.h b/app/src/lg/dbio/lg_spantb_batch.h new file mode 100644 index 0000000..137511f --- /dev/null +++ b/app/src/lg/dbio/lg_spantb_batch.h @@ -0,0 +1,14 @@ +/* + * lg_spantb_batch.h - 기간 시산표 배치: 기간 계정별 차대 합계를 시산표(마감일자)로 upsert (커서 + XA) 전용 copybook (record layout). + */ +#ifndef LG_SPANTB_BATCH_H +#define LG_SPANTB_BATCH_H + +typedef struct { + char acct[32]; + long debit_total; + long credit_total; + long diff; +} lg_spantb_batch_rec_t; + +#endif /* LG_SPANTB_BATCH_H */ diff --git a/app/src/lg/dbio/lg_threeway_batch.h b/app/src/lg/dbio/lg_threeway_batch.h new file mode 100644 index 0000000..8f0f159 --- /dev/null +++ b/app/src/lg/dbio/lg_threeway_batch.h @@ -0,0 +1,14 @@ +/* + * lg_threeway_batch.h - 원장-정산-매입 3단 대사 배치: 세 원천 합계를 대조 (XA) 전용 copybook (record layout). + */ +#ifndef LG_THREEWAY_BATCH_H +#define LG_THREEWAY_BATCH_H + +typedef struct { + char as_of[16]; + long ledger_pay; + long settle_net; + long purch_net; +} lg_threeway_batch_rec_t; + +#endif /* LG_THREEWAY_BATCH_H */ diff --git a/app/src/lg/dbio/lg_writeoff_batch.h b/app/src/lg/dbio/lg_writeoff_batch.h new file mode 100644 index 0000000..539ed4d --- /dev/null +++ b/app/src/lg/dbio/lg_writeoff_batch.h @@ -0,0 +1,13 @@ +/* + * lg_writeoff_batch.h - 대손 제각 현황 배치: 대손충당금(BADDEBT_ALLOW) 잔액 집계 (XA) 전용 copybook (record layout). + */ +#ifndef LG_WRITEOFF_BATCH_H +#define LG_WRITEOFF_BATCH_H + +typedef struct { + char as_of[16]; + long allow_bal; + long writeoff_cnt; +} lg_writeoff_batch_rec_t; + +#endif /* LG_WRITEOFF_BATCH_H */ diff --git a/app/src/lg/dbio/lg_yearclose_batch.h b/app/src/lg/dbio/lg_yearclose_batch.h new file mode 100644 index 0000000..c8eb0a2 --- /dev/null +++ b/app/src/lg/dbio/lg_yearclose_batch.h @@ -0,0 +1,13 @@ +/* + * lg_yearclose_batch.h - 연말 손익계정(_INC/_EXP) 대체 대상 집계 배치 (커서 + XA) 전용 copybook (record layout). + */ +#ifndef LG_YEARCLOSE_BATCH_H +#define LG_YEARCLOSE_BATCH_H + +typedef struct { + char acct[32]; + long net_amount; + long profit; +} lg_yearclose_batch_rec_t; + +#endif /* LG_YEARCLOSE_BATCH_H */ diff --git a/app/src/lg/lg_svr.pgc b/app/src/lg/lg_svr.pgc index e374538..94ce202 100644 --- a/app/src/lg/lg_svr.pgc +++ b/app/src/lg/lg_svr.pgc @@ -2,7 +2,7 @@ * lg_svr.pgc - lg (원장/ledger) MODULE SERVER (thin dispatcher). * * The legacy "one module server, many services" pattern: this binary owns NO - * business logic. Each of lg's 75 online services now lives in its own file + * business logic. Each of lg's 133 online services now lives in its own file * (app/src/lg/svc/.pgc) with its own copybook header * (app/src/lg/svc/.h); those objects are archived into liblgsvc.a and * linked into this server. Here we only: @@ -97,6 +97,64 @@ #include "LG_MEMOAUDIT.h" #include "LG_ZEROLINE.h" #include "LG_DUPSCAN.h" +#include "LG_DEPTLEDGER.h" +#include "LG_CORPLEDGER.h" +#include "LG_ACCTGROUP.h" +#include "LG_DEPTSUM.h" +#include "LG_CORPSUM.h" +#include "LG_ACCTPIVOT.h" +#include "LG_DEPTRANGE.h" +#include "LG_ACCTTOPN.h" +#include "LG_HALFCLOSE.h" +#include "LG_YEARCLOSE.h" +#include "LG_WEEKCLOSE.h" +#include "LG_MONCLOSEJE.h" +#include "LG_QTRCLOSEJE.h" +#include "LG_YEARCARRY.h" +#include "LG_REDADJUST.h" +#include "LG_BLACKADJUST.h" +#include "LG_REDBLACKNET.h" +#include "LG_CORRPAIR.h" +#include "LG_PENDCLEAR.h" +#include "LG_PENDROUTE.h" +#include "LG_PENDESCAL.h" +#include "LG_PENDSTAT.h" +#include "LG_BATCHAPPROVE.h" +#include "LG_APPRDELEG.h" +#include "LG_APPRHIST.h" +#include "LG_UNAPPROVE.h" +#include "LG_PLSUMMARY.h" +#include "LG_BSSUMMARY.h" +#include "LG_INCOMEROLL.h" +#include "LG_EXPENSEROLL.h" +#include "LG_EQUITYMOVE.h" +#include "LG_REVACCRUE.h" +#include "LG_REVDEFER.h" +#include "LG_REVRELEASE.h" +#include "LG_UNEARNRECOG.h" +#include "LG_ACCRUEDRECV.h" +#include "LG_ACCRUEDPAY.h" +#include "LG_PREPAIDPOST.h" +#include "LG_UNEARNPOST.h" +#include "LG_PREPAIDAMORT.h" +#include "LG_ALLOWANCE.h" +#include "LG_WRITEOFF.h" +#include "LG_RECOVERY.h" +#include "LG_ALLOWRECALC.h" +#include "LG_THREEWAY.h" +#include "LG_PAYMATCH.h" +#include "LG_LEDGSETTLE.h" +#include "LG_SETTLEPAYGAP.h" +#include "LG_BALCERT.h" +#include "LG_MERCHCERT.h" +#include "LG_SPANTB.h" +#include "LG_ROLLTB.h" +#include "LG_PARTREVERSE.h" +#include "LG_REPOST.h" +#include "LG_XFERPOST.h" +#include "LG_AUDITSCAN.h" +#include "LG_SEQAUDIT.h" +#include "LG_CLOSELIST.h" /* ----- advertise table: {service name, function} (fn is extern from its file) */ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { @@ -138,6 +196,64 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"LG_TBDIFF", LG_TBDIFF}, {"LG_VOUCHTRACE", LG_VOUCHTRACE}, {"LG_MEMOAUDIT", LG_MEMOAUDIT}, {"LG_ZEROLINE", LG_ZEROLINE}, {"LG_DUPSCAN", LG_DUPSCAN}, + {"LG_DEPTLEDGER", LG_DEPTLEDGER}, + {"LG_CORPLEDGER", LG_CORPLEDGER}, + {"LG_ACCTGROUP", LG_ACCTGROUP}, + {"LG_DEPTSUM", LG_DEPTSUM}, + {"LG_CORPSUM", LG_CORPSUM}, + {"LG_ACCTPIVOT", LG_ACCTPIVOT}, + {"LG_DEPTRANGE", LG_DEPTRANGE}, + {"LG_ACCTTOPN", LG_ACCTTOPN}, + {"LG_HALFCLOSE", LG_HALFCLOSE}, + {"LG_YEARCLOSE", LG_YEARCLOSE}, + {"LG_WEEKCLOSE", LG_WEEKCLOSE}, + {"LG_MONCLOSEJE", LG_MONCLOSEJE}, + {"LG_QTRCLOSEJE", LG_QTRCLOSEJE}, + {"LG_YEARCARRY", LG_YEARCARRY}, + {"LG_REDADJUST", LG_REDADJUST}, + {"LG_BLACKADJUST", LG_BLACKADJUST}, + {"LG_REDBLACKNET", LG_REDBLACKNET}, + {"LG_CORRPAIR", LG_CORRPAIR}, + {"LG_PENDCLEAR", LG_PENDCLEAR}, + {"LG_PENDROUTE", LG_PENDROUTE}, + {"LG_PENDESCAL", LG_PENDESCAL}, + {"LG_PENDSTAT", LG_PENDSTAT}, + {"LG_BATCHAPPROVE", LG_BATCHAPPROVE}, + {"LG_APPRDELEG", LG_APPRDELEG}, + {"LG_APPRHIST", LG_APPRHIST}, + {"LG_UNAPPROVE", LG_UNAPPROVE}, + {"LG_PLSUMMARY", LG_PLSUMMARY}, + {"LG_BSSUMMARY", LG_BSSUMMARY}, + {"LG_INCOMEROLL", LG_INCOMEROLL}, + {"LG_EXPENSEROLL", LG_EXPENSEROLL}, + {"LG_EQUITYMOVE", LG_EQUITYMOVE}, + {"LG_REVACCRUE", LG_REVACCRUE}, + {"LG_REVDEFER", LG_REVDEFER}, + {"LG_REVRELEASE", LG_REVRELEASE}, + {"LG_UNEARNRECOG", LG_UNEARNRECOG}, + {"LG_ACCRUEDRECV", LG_ACCRUEDRECV}, + {"LG_ACCRUEDPAY", LG_ACCRUEDPAY}, + {"LG_PREPAIDPOST", LG_PREPAIDPOST}, + {"LG_UNEARNPOST", LG_UNEARNPOST}, + {"LG_PREPAIDAMORT", LG_PREPAIDAMORT}, + {"LG_ALLOWANCE", LG_ALLOWANCE}, + {"LG_WRITEOFF", LG_WRITEOFF}, + {"LG_RECOVERY", LG_RECOVERY}, + {"LG_ALLOWRECALC", LG_ALLOWRECALC}, + {"LG_THREEWAY", LG_THREEWAY}, + {"LG_PAYMATCH", LG_PAYMATCH}, + {"LG_LEDGSETTLE", LG_LEDGSETTLE}, + {"LG_SETTLEPAYGAP", LG_SETTLEPAYGAP}, + {"LG_BALCERT", LG_BALCERT}, + {"LG_MERCHCERT", LG_MERCHCERT}, + {"LG_SPANTB", LG_SPANTB}, + {"LG_ROLLTB", LG_ROLLTB}, + {"LG_PARTREVERSE", LG_PARTREVERSE}, + {"LG_REPOST", LG_REPOST}, + {"LG_XFERPOST", LG_XFERPOST}, + {"LG_AUDITSCAN", LG_AUDITSCAN}, + {"LG_SEQAUDIT", LG_SEQAUDIT}, + {"LG_CLOSELIST", LG_CLOSELIST}, {NULL, NULL} }; diff --git a/app/src/lg/svc/LG_ACCRUEDPAY.h b/app/src/lg/svc/LG_ACCRUEDPAY.h new file mode 100644 index 0000000..7da1f80 --- /dev/null +++ b/app/src/lg/svc/LG_ACCRUEDPAY.h @@ -0,0 +1,21 @@ +/* + * LG_ACCRUEDPAY.h - lg 서비스 LG_ACCRUEDPAY 카피북 (copybook / record header). + * 미지급비용 계상: 미지급수수료비용(D)/미지급금(C)을 균형 계상. + */ +#ifndef LG_ACCRUEDPAY_H +#define LG_ACCRUEDPAY_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_accruedpay_rec_t; + +void LG_ACCRUEDPAY(TPSVCINFO *p); + +#endif /* LG_ACCRUEDPAY_H */ diff --git a/app/src/lg/svc/LG_ACCRUEDPAY.pgc b/app/src/lg/svc/LG_ACCRUEDPAY.pgc new file mode 100644 index 0000000..e702132 --- /dev/null +++ b/app/src/lg/svc/LG_ACCRUEDPAY.pgc @@ -0,0 +1,26 @@ +/* + * LG_ACCRUEDPAY.pgc - lg 모듈 서비스 LG_ACCRUEDPAY (one service = one file). + * 미지급비용 계상: 미지급수수료비용(D)/미지급금(C)을 균형 계상. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_ACCRUEDPAY.h" + +void LG_ACCRUEDPAY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long vid, amount; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + amount = getl(b, T_AMOUNT); + if (amount <= 0) { Bchg(b, T_MSG, 0, "계상액 없음", 0L); FAIL(b); } + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", bizdate, "미지급비용 계상") < 0) FAIL(b); + if (lgdb_insert_line(vid, "MDR_EXP", "D", amount, "", bizdate, "posted") < 0 || + lgdb_insert_line(vid, "ACCRUED_PAY", "C", amount, "", bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); + userlog("LG_ACCRUEDPAY vid=%ld 미지급 %ld", vid, amount); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_ACCRUEDRECV.h b/app/src/lg/svc/LG_ACCRUEDRECV.h new file mode 100644 index 0000000..16a05ef --- /dev/null +++ b/app/src/lg/svc/LG_ACCRUEDRECV.h @@ -0,0 +1,21 @@ +/* + * LG_ACCRUEDRECV.h - lg 서비스 LG_ACCRUEDRECV 카피북 (copybook / record header). + * 미수수익 계상: 현재 RECV 잔액을 참조해 미수수익(D)/이자수익(C)을 계상. + */ +#ifndef LG_ACCRUEDRECV_H +#define LG_ACCRUEDRECV_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_accruedrecv_rec_t; + +void LG_ACCRUEDRECV(TPSVCINFO *p); + +#endif /* LG_ACCRUEDRECV_H */ diff --git a/app/src/lg/svc/LG_ACCRUEDRECV.pgc b/app/src/lg/svc/LG_ACCRUEDRECV.pgc new file mode 100644 index 0000000..abc29d1 --- /dev/null +++ b/app/src/lg/svc/LG_ACCRUEDRECV.pgc @@ -0,0 +1,35 @@ +/* + * LG_ACCRUEDRECV.pgc - lg 모듈 서비스 LG_ACCRUEDRECV (one service = one file). + * 미수수익 계상: 현재 RECV 잔액을 참조해 미수수익(D)/이자수익(C)을 계상. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_ACCRUEDRECV.h" + +void LG_ACCRUEDRECV(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long vid, rate, base, accr; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_bal; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + rate = getl(b, T_COUNT); if (rate <= 0) rate = 50; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE -amount END), 0) + INTO :h_bal FROM lg_ledger_line WHERE acct = 'RECV' AND biz_date <= :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + base = h_bal; accr = base * rate / 10000; + if (accr <= 0) { setl(b, T_NET, 0); Bchg(b, T_STATUS, 0, "SKIP", 0L); OK(b); } + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", bizdate, "미수수익 계상") < 0) FAIL(b); + if (lgdb_insert_line(vid, "RECV_ACCRUED", "D", accr, "", bizdate, "posted") < 0 || + lgdb_insert_line(vid, "INTEREST_INC", "C", accr, "", bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); setl(b, T_NET, accr); + userlog("LG_ACCRUEDRECV vid=%ld base %ld @%ldbp = %ld", vid, base, rate, accr); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_ACCTGROUP.h b/app/src/lg/svc/LG_ACCTGROUP.h new file mode 100644 index 0000000..8e3154b --- /dev/null +++ b/app/src/lg/svc/LG_ACCTGROUP.h @@ -0,0 +1,21 @@ +/* + * LG_ACCTGROUP.h - lg 서비스 LG_ACCTGROUP 카피북 (copybook / record header). + * 계정그룹별 집계: 계정과목 접두어(prefix)로 묶어 그룹별 차대 합계를 순회. + */ +#ifndef LG_ACCTGROUP_H +#define LG_ACCTGROUP_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_acctgroup_rec_t; + +void LG_ACCTGROUP(TPSVCINFO *p); + +#endif /* LG_ACCTGROUP_H */ diff --git a/app/src/lg/svc/LG_ACCTGROUP.pgc b/app/src/lg/svc/LG_ACCTGROUP.pgc new file mode 100644 index 0000000..f59f63d --- /dev/null +++ b/app/src/lg/svc/LG_ACCTGROUP.pgc @@ -0,0 +1,40 @@ +/* + * LG_ACCTGROUP.pgc - lg 모듈 서비스 LG_ACCTGROUP (one service = one file). + * 계정그룹별 집계: 계정과목 접두어(prefix)로 묶어 그룹별 차대 합계를 순회. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_ACCTGROUP.h" + +void LG_ACCTGROUP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long groups = 0, tot_dr = 0, tot_cr = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_grp[32]; + long h_dr, h_cr; + 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 ag_cur CURSOR FOR + SELECT split_part(acct, '_', 1), + coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE 0 END), 0), + coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE 0 END), 0) + FROM lg_ledger_line WHERE biz_date = :h_bizdate + GROUP BY split_part(acct, '_', 1) ORDER BY 1; + EXEC SQL OPEN ag_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH ag_cur INTO :h_grp, :h_dr, :h_cr; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ag_cur; FAIL(b); } + tot_dr += h_dr; tot_cr += h_cr; groups++; + } + EXEC SQL CLOSE ag_cur; + setl(b, T_COUNT, groups); setl(b, T_AMT2, tot_dr); setl(b, T_AMT3, tot_cr); + setl(b, T_DELTA, tot_dr - tot_cr); + userlog("LG_ACCTGROUP %s 그룹 %ld개 차변합 %ld 대변합 %ld", bizdate, groups, tot_dr, tot_cr); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_ACCTPIVOT.h b/app/src/lg/svc/LG_ACCTPIVOT.h new file mode 100644 index 0000000..f6c9942 --- /dev/null +++ b/app/src/lg/svc/LG_ACCTPIVOT.h @@ -0,0 +1,21 @@ +/* + * LG_ACCTPIVOT.h - lg 서비스 LG_ACCTPIVOT 카피북 (copybook / record header). + * 계정x일자 피벗 잔액: 기간 내 일자별 순액을 누적해 누적잔액과 최고점을 산출. + */ +#ifndef LG_ACCTPIVOT_H +#define LG_ACCTPIVOT_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_acctpivot_rec_t; + +void LG_ACCTPIVOT(TPSVCINFO *p); + +#endif /* LG_ACCTPIVOT_H */ diff --git a/app/src/lg/svc/LG_ACCTPIVOT.pgc b/app/src/lg/svc/LG_ACCTPIVOT.pgc new file mode 100644 index 0000000..4cd51fe --- /dev/null +++ b/app/src/lg/svc/LG_ACCTPIVOT.pgc @@ -0,0 +1,45 @@ +/* + * LG_ACCTPIVOT.pgc - lg 모듈 서비스 LG_ACCTPIVOT (one service = one file). + * 계정x일자 피벗 잔액: 기간 내 일자별 순액을 누적해 누적잔액과 최고점을 산출. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_ACCTPIVOT.h" + +void LG_ACCTPIVOT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char acct[32], fromd[16], endd[16]; + long days = 0, running = 0, peak = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_acct[32], h_from[16], h_end[16], h_day[16]; + long h_net; + EXEC SQL END DECLARE SECTION; + gets_(b, T_KEY1, acct, sizeof(acct)); if (acct[0] == 0) strcpy(acct, "RECV"); + gets_(b, T_BIZDATE, endd, sizeof(endd)); + gets_(b, T_STR1, fromd, sizeof(fromd)); if (fromd[0] == 0) strcpy(fromd, endd); + strncpy(h_acct, acct, sizeof(h_acct)-1); h_acct[sizeof(h_acct)-1] = 0; + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-1] = 0; + strncpy(h_end, endd, sizeof(h_end)-1); h_end[sizeof(h_end)-1] = 0; + EXEC SQL DECLARE ap_cur CURSOR FOR + SELECT to_char(biz_date, 'YYYY-MM-DD'), + coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE -amount END), 0) + FROM lg_ledger_line + WHERE acct = :h_acct AND biz_date BETWEEN :h_from AND :h_end + GROUP BY biz_date ORDER BY biz_date; + EXEC SQL OPEN ap_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH ap_cur INTO :h_day, :h_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ap_cur; FAIL(b); } + running += h_net; + if (running > peak) peak = running; + days++; + } + EXEC SQL CLOSE ap_cur; + setl(b, T_COUNT, days); setl(b, T_NET, running); setl(b, T_AMT2, peak); + userlog("LG_ACCTPIVOT %s %s~%s %ld일 잔액 %ld 최고 %ld", acct, fromd, endd, days, running, peak); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_ACCTTOPN.h b/app/src/lg/svc/LG_ACCTTOPN.h new file mode 100644 index 0000000..f3b6889 --- /dev/null +++ b/app/src/lg/svc/LG_ACCTTOPN.h @@ -0,0 +1,21 @@ +/* + * LG_ACCTTOPN.h - lg 서비스 LG_ACCTTOPN 카피북 (copybook / record header). + * 상위 N 계정 잔액: 계정별 순액 절대값 내림차순으로 상위 N개를 훑어 1위를 반환. + */ +#ifndef LG_ACCTTOPN_H +#define LG_ACCTTOPN_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_accttopn_rec_t; + +void LG_ACCTTOPN(TPSVCINFO *p); + +#endif /* LG_ACCTTOPN_H */ diff --git a/app/src/lg/svc/LG_ACCTTOPN.pgc b/app/src/lg/svc/LG_ACCTTOPN.pgc new file mode 100644 index 0000000..3246573 --- /dev/null +++ b/app/src/lg/svc/LG_ACCTTOPN.pgc @@ -0,0 +1,42 @@ +/* + * LG_ACCTTOPN.pgc - lg 모듈 서비스 LG_ACCTTOPN (one service = one file). + * 상위 N 계정 잔액: 계정별 순액 절대값 내림차순으로 상위 N개를 훑어 1위를 반환. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_ACCTTOPN.h" + +void LG_ACCTTOPN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], topacct[32] = ""; + long n, seen = 0, topval = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_acct[32]; + long h_net; + EXEC SQL END DECLARE SECTION; + n = getl(b, T_COUNT); if (n <= 0) n = 5; + 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 tn_cur CURSOR FOR + SELECT acct, coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE -amount END), 0) + FROM lg_ledger_line WHERE biz_date = :h_bizdate + GROUP BY acct + ORDER BY abs(coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE -amount END), 0)) DESC; + EXEC SQL OPEN tn_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + if (seen >= n) break; + EXEC SQL FETCH tn_cur INTO :h_acct, :h_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE tn_cur; FAIL(b); } + if (seen == 0) { strncpy(topacct, h_acct, sizeof(topacct)-1); topacct[sizeof(topacct)-1] = 0; topval = h_net; } + seen++; + } + EXEC SQL CLOSE tn_cur; + Bchg(b, T_KEY1, 0, topacct, 0L); + setl(b, T_COUNT, seen); setl(b, T_NET, topval); + userlog("LG_ACCTTOPN %s 상위 %ld개 1위 %s %ld", bizdate, seen, topacct, topval); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_ALLOWANCE.h b/app/src/lg/svc/LG_ALLOWANCE.h new file mode 100644 index 0000000..50bec8a --- /dev/null +++ b/app/src/lg/svc/LG_ALLOWANCE.h @@ -0,0 +1,21 @@ +/* + * LG_ALLOWANCE.h - lg 서비스 LG_ALLOWANCE 카피북 (copybook / record header). + * 대손충당금 설정: RECV 잔액의 일정률을 대손상각비(D)/대손충당금(C)으로 설정. + */ +#ifndef LG_ALLOWANCE_H +#define LG_ALLOWANCE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_allowance_rec_t; + +void LG_ALLOWANCE(TPSVCINFO *p); + +#endif /* LG_ALLOWANCE_H */ diff --git a/app/src/lg/svc/LG_ALLOWANCE.pgc b/app/src/lg/svc/LG_ALLOWANCE.pgc new file mode 100644 index 0000000..1ddad8d --- /dev/null +++ b/app/src/lg/svc/LG_ALLOWANCE.pgc @@ -0,0 +1,35 @@ +/* + * LG_ALLOWANCE.pgc - lg 모듈 서비스 LG_ALLOWANCE (one service = one file). + * 대손충당금 설정: RECV 잔액의 일정률을 대손상각비(D)/대손충당금(C)으로 설정. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_ALLOWANCE.h" + +void LG_ALLOWANCE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long vid, rate, base, allow; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_bal; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + rate = getl(b, T_COUNT); if (rate <= 0) rate = 100; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE -amount END), 0) + INTO :h_bal FROM lg_ledger_line WHERE acct = 'RECV' AND biz_date <= :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + base = h_bal; allow = base * rate / 10000; + if (allow <= 0) { setl(b, T_NET, 0); Bchg(b, T_STATUS, 0, "SKIP", 0L); OK(b); } + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", bizdate, "대손충당금 설정") < 0) FAIL(b); + if (lgdb_insert_line(vid, "BADDEBT_EXP", "D", allow, "", bizdate, "posted") < 0 || + lgdb_insert_line(vid, "BADDEBT_ALLOW", "C", allow, "", bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); setl(b, T_NET, allow); setl(b, T_AMT2, base); + userlog("LG_ALLOWANCE vid=%ld RECV %ld @%ldbp = %ld", vid, base, rate, allow); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_ALLOWRECALC.h b/app/src/lg/svc/LG_ALLOWRECALC.h new file mode 100644 index 0000000..5efa45a --- /dev/null +++ b/app/src/lg/svc/LG_ALLOWRECALC.h @@ -0,0 +1,21 @@ +/* + * LG_ALLOWRECALC.h - lg 서비스 LG_ALLOWRECALC 카피북 (copybook / record header). + * 충당금 재산정: 필요 충당금(RECV x 율)과 현재 충당금 잔액 차이를 조정 전기. + */ +#ifndef LG_ALLOWRECALC_H +#define LG_ALLOWRECALC_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_allowrecalc_rec_t; + +void LG_ALLOWRECALC(TPSVCINFO *p); + +#endif /* LG_ALLOWRECALC_H */ diff --git a/app/src/lg/svc/LG_ALLOWRECALC.pgc b/app/src/lg/svc/LG_ALLOWRECALC.pgc new file mode 100644 index 0000000..72d8a11 --- /dev/null +++ b/app/src/lg/svc/LG_ALLOWRECALC.pgc @@ -0,0 +1,45 @@ +/* + * LG_ALLOWRECALC.pgc - lg 모듈 서비스 LG_ALLOWRECALC (one service = one file). + * 충당금 재산정: 필요 충당금(RECV x 율)과 현재 충당금 잔액 차이를 조정 전기. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_ALLOWRECALC.h" + +void LG_ALLOWRECALC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long vid, rate, base, required, current, delta, mag; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_recv, h_allow; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + rate = getl(b, T_COUNT); if (rate <= 0) rate = 100; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE -amount END), 0) + INTO :h_recv FROM lg_ledger_line WHERE acct = 'RECV' AND biz_date <= :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE -amount END), 0) + INTO :h_allow FROM lg_ledger_line WHERE acct = 'BADDEBT_ALLOW' AND biz_date <= :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + base = h_recv; current = h_allow; required = base * rate / 10000; + delta = required - current; + if (delta == 0) { setl(b, T_DELTA, 0); Bchg(b, T_STATUS, 0, "OK", 0L); OK(b); } + mag = (delta > 0) ? delta : -delta; + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", bizdate, "충당금 재산정") < 0) FAIL(b); + if (delta > 0) { + if (lgdb_insert_line(vid, "BADDEBT_EXP", "D", mag, "", bizdate, "posted") < 0 || + lgdb_insert_line(vid, "BADDEBT_ALLOW", "C", mag, "", bizdate, "posted") < 0) FAIL(b); + } else { + if (lgdb_insert_line(vid, "BADDEBT_ALLOW", "D", mag, "", bizdate, "posted") < 0 || + lgdb_insert_line(vid, "BADDEBT_EXP", "C", mag, "", bizdate, "posted") < 0) FAIL(b); + } + setl(b, T_ID1, vid); setl(b, T_DELTA, delta); setl(b, T_AMT2, required); + userlog("LG_ALLOWRECALC vid=%ld 필요 %ld 현재 %ld 조정 %ld", vid, required, current, delta); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_APPRDELEG.h b/app/src/lg/svc/LG_APPRDELEG.h new file mode 100644 index 0000000..73e4686 --- /dev/null +++ b/app/src/lg/svc/LG_APPRDELEG.h @@ -0,0 +1,21 @@ +/* + * LG_APPRDELEG.h - lg 서비스 LG_APPRDELEG 카피북 (copybook / record header). + * 승인 위임: 작성(D) 전표의 승인 권한을 대리인에게 위임(메모 누적). + */ +#ifndef LG_APPRDELEG_H +#define LG_APPRDELEG_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_apprdeleg_rec_t; + +void LG_APPRDELEG(TPSVCINFO *p); + +#endif /* LG_APPRDELEG_H */ diff --git a/app/src/lg/svc/LG_APPRDELEG.pgc b/app/src/lg/svc/LG_APPRDELEG.pgc new file mode 100644 index 0000000..045003f --- /dev/null +++ b/app/src/lg/svc/LG_APPRDELEG.pgc @@ -0,0 +1,31 @@ +/* + * LG_APPRDELEG.pgc - lg 모듈 서비스 LG_APPRDELEG (one service = one file). + * 승인 위임: 작성(D) 전표의 승인 권한을 대리인에게 위임(메모 누적). + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_APPRDELEG.h" + +void LG_APPRDELEG(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char deleg[64], st[4]; + long vid; + EXEC SQL BEGIN DECLARE SECTION; + long h_vid; + char h_deleg[64]; + EXEC SQL END DECLARE SECTION; + vid = getl(b, T_ID1); + gets_(b, T_STR1, deleg, sizeof(deleg)); if (deleg[0] == 0) strcpy(deleg, "대리인"); + if (lgdb_get_voucher_status(vid, st) < 0) { Bchg(b, T_MSG, 0, "전표 없음", 0L); FAIL(b); } + if (st[0] != 'D') { Bchg(b, T_MSG, 0, "작성 전표만 위임 가능", 0L); FAIL(b); } + h_vid = vid; + strncpy(h_deleg, deleg, sizeof(h_deleg)-1); h_deleg[sizeof(h_deleg)-1] = 0; + EXEC SQL UPDATE lg_voucher SET memo = memo || ' 위임:' || :h_deleg, updated_at = now() + WHERE voucher_id = :h_vid; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ID1, vid); + userlog("LG_APPRDELEG vid=%ld -> %s 위임", vid, deleg); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_APPRHIST.h b/app/src/lg/svc/LG_APPRHIST.h new file mode 100644 index 0000000..f4c79e9 --- /dev/null +++ b/app/src/lg/svc/LG_APPRHIST.h @@ -0,0 +1,21 @@ +/* + * LG_APPRHIST.h - lg 서비스 LG_APPRHIST 카피북 (copybook / record header). + * 승인 이력 조회: 당일 전기(P) 완료된 전표 수와 차변 총액을 집계. + */ +#ifndef LG_APPRHIST_H +#define LG_APPRHIST_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_apprhist_rec_t; + +void LG_APPRHIST(TPSVCINFO *p); + +#endif /* LG_APPRHIST_H */ diff --git a/app/src/lg/svc/LG_APPRHIST.pgc b/app/src/lg/svc/LG_APPRHIST.pgc new file mode 100644 index 0000000..ae5f5fc --- /dev/null +++ b/app/src/lg/svc/LG_APPRHIST.pgc @@ -0,0 +1,29 @@ +/* + * LG_APPRHIST.pgc - lg 모듈 서비스 LG_APPRHIST (one service = one file). + * 승인 이력 조회: 당일 전기(P) 완료된 전표 수와 차변 총액을 집계. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_APPRHIST.h" + +void LG_APPRHIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_amt; + 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(DISTINCT v.voucher_id), + coalesce(sum(CASE WHEN l.dc = 'D' THEN l.amount ELSE 0 END), 0) + INTO :h_cnt, :h_amt + FROM lg_voucher v JOIN lg_ledger_line l ON l.voucher_id = v.voucher_id + WHERE v.status = 'P' AND v.biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); setl(b, T_AMT2, h_amt); + userlog("LG_APPRHIST %s 승인전표 %ld건 차변 %ld", bizdate, h_cnt, h_amt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_AUDITSCAN.h b/app/src/lg/svc/LG_AUDITSCAN.h new file mode 100644 index 0000000..c8e5dad --- /dev/null +++ b/app/src/lg/svc/LG_AUDITSCAN.h @@ -0,0 +1,21 @@ +/* + * LG_AUDITSCAN.h - lg 서비스 LG_AUDITSCAN 카피북 (copybook / record header). + * 원장 감사 스캔: 취소(X) 전표인데 라인이 여전히 posted 인 불일치와 고아 라인을 탐지. + */ +#ifndef LG_AUDITSCAN_H +#define LG_AUDITSCAN_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_auditscan_rec_t; + +void LG_AUDITSCAN(TPSVCINFO *p); + +#endif /* LG_AUDITSCAN_H */ diff --git a/app/src/lg/svc/LG_AUDITSCAN.pgc b/app/src/lg/svc/LG_AUDITSCAN.pgc new file mode 100644 index 0000000..6b60dc0 --- /dev/null +++ b/app/src/lg/svc/LG_AUDITSCAN.pgc @@ -0,0 +1,33 @@ +/* + * LG_AUDITSCAN.pgc - lg 모듈 서비스 LG_AUDITSCAN (one service = one file). + * 원장 감사 스캔: 취소(X) 전표인데 라인이 여전히 posted 인 불일치와 고아 라인을 탐지. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_AUDITSCAN.h" + +void LG_AUDITSCAN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_bad, h_orphan; + 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(*) INTO :h_bad + FROM lg_ledger_line l JOIN lg_voucher v ON v.voucher_id = l.voucher_id + WHERE l.biz_date = :h_bizdate AND v.status = 'X' AND l.status = 'posted'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_orphan + FROM lg_ledger_line l WHERE l.biz_date = :h_bizdate + AND NOT EXISTS (SELECT 1 FROM lg_voucher v WHERE v.voucher_id = l.voucher_id); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_bad); setl(b, T_ARG2, h_orphan); + setl(b, T_COUNT, h_bad + h_orphan); + Bchg(b, T_STATUS, 0, (h_bad + h_orphan == 0) ? "CLEAN" : "FINDINGS", 0L); + userlog("LG_AUDITSCAN %s 취소불일치 %ld 고아 %ld", bizdate, h_bad, h_orphan); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_BALCERT.h b/app/src/lg/svc/LG_BALCERT.h new file mode 100644 index 0000000..ce7723b --- /dev/null +++ b/app/src/lg/svc/LG_BALCERT.h @@ -0,0 +1,21 @@ +/* + * LG_BALCERT.h - lg 서비스 LG_BALCERT 카피북 (copybook / record header). + * 잔액증명서 발급: 계정 차변/대변을 재계산해 계정잔액에 upsert 하고 잔액을 증명. + */ +#ifndef LG_BALCERT_H +#define LG_BALCERT_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_balcert_rec_t; + +void LG_BALCERT(TPSVCINFO *p); + +#endif /* LG_BALCERT_H */ diff --git a/app/src/lg/svc/LG_BALCERT.pgc b/app/src/lg/svc/LG_BALCERT.pgc new file mode 100644 index 0000000..3dd6ced --- /dev/null +++ b/app/src/lg/svc/LG_BALCERT.pgc @@ -0,0 +1,34 @@ +/* + * LG_BALCERT.pgc - lg 모듈 서비스 LG_BALCERT (one service = one file). + * 잔액증명서 발급: 계정 차변/대변을 재계산해 계정잔액에 upsert 하고 잔액을 증명. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_BALCERT.h" + +void LG_BALCERT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char acct[32], bizdate[16]; + long bal; + EXEC SQL BEGIN DECLARE SECTION; + char h_acct[32], h_bizdate[16]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + gets_(b, T_KEY1, acct, sizeof(acct)); if (acct[0] == 0) strcpy(acct, "RECV"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + strncpy(h_acct, acct, sizeof(h_acct)-1); h_acct[sizeof(h_acct)-1] = 0; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE 0 END), 0), + coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE 0 END), 0) + INTO :h_dr, :h_cr FROM lg_ledger_line + WHERE acct = :h_acct AND biz_date = :h_bizdate AND status = 'posted'; + if (sqlca.sqlcode < 0) FAIL(b); + if (lgdb_upsert_acct_bal(acct, bizdate, h_dr, h_cr) < 0) FAIL(b); + bal = h_dr - h_cr; + setl(b, T_AMT2, h_dr); setl(b, T_AMT3, h_cr); setl(b, T_NET, bal); + Bchg(b, T_STATUS, 0, "CERTIFIED", 0L); + userlog("LG_BALCERT %s %s 잔액 %ld 증명", acct, bizdate, bal); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_BATCHAPPROVE.h b/app/src/lg/svc/LG_BATCHAPPROVE.h new file mode 100644 index 0000000..c19aec8 --- /dev/null +++ b/app/src/lg/svc/LG_BATCHAPPROVE.h @@ -0,0 +1,21 @@ +/* + * LG_BATCHAPPROVE.h - lg 서비스 LG_BATCHAPPROVE 카피북 (copybook / record header). + * 일괄 승인: 당일 작성(D) 전표를 순회하며 차대 균형이 맞는 것만 전기(P) 승인. + */ +#ifndef LG_BATCHAPPROVE_H +#define LG_BATCHAPPROVE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_batchapprove_rec_t; + +void LG_BATCHAPPROVE(TPSVCINFO *p); + +#endif /* LG_BATCHAPPROVE_H */ diff --git a/app/src/lg/svc/LG_BATCHAPPROVE.pgc b/app/src/lg/svc/LG_BATCHAPPROVE.pgc new file mode 100644 index 0000000..e68e827 --- /dev/null +++ b/app/src/lg/svc/LG_BATCHAPPROVE.pgc @@ -0,0 +1,46 @@ +/* + * LG_BATCHAPPROVE.pgc - lg 모듈 서비스 LG_BATCHAPPROVE (one service = one file). + * 일괄 승인: 당일 작성(D) 전표를 순회하며 차대 균형이 맞는 것만 전기(P) 승인. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_BATCHAPPROVE.h" + +void LG_BATCHAPPROVE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long approved = 0, rejected = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_vid, h_dr, h_cr; + 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 ba_cur CURSOR FOR + SELECT voucher_id FROM lg_voucher + WHERE status = 'D' AND biz_date = :h_bizdate ORDER BY voucher_id; + EXEC SQL OPEN ba_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH ba_cur INTO :h_vid; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ba_cur; FAIL(b); } + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE 0 END), 0), + coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE 0 END), 0) + INTO :h_dr, :h_cr FROM lg_ledger_line WHERE voucher_id = :h_vid; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ba_cur; FAIL(b); } + if (h_dr == h_cr && h_dr > 0) { + EXEC SQL UPDATE lg_voucher SET status = 'P', updated_at = now() WHERE voucher_id = :h_vid; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ba_cur; FAIL(b); } + approved++; + } else { + rejected++; + } + } + EXEC SQL CLOSE ba_cur; + setl(b, T_COUNT, approved); setl(b, T_ARG1, rejected); + userlog("LG_BATCHAPPROVE %s 승인 %ld 반려 %ld", bizdate, approved, rejected); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_BLACKADJUST.h b/app/src/lg/svc/LG_BLACKADJUST.h new file mode 100644 index 0000000..8af4351 --- /dev/null +++ b/app/src/lg/svc/LG_BLACKADJUST.h @@ -0,0 +1,21 @@ +/* + * LG_BLACKADJUST.h - lg 서비스 LG_BLACKADJUST 카피북 (copybook / record header). + * 흑색 정정분개: 과소계상분을 양수(흑색) 분개 쌍으로 추가 보정. + */ +#ifndef LG_BLACKADJUST_H +#define LG_BLACKADJUST_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_blackadjust_rec_t; + +void LG_BLACKADJUST(TPSVCINFO *p); + +#endif /* LG_BLACKADJUST_H */ diff --git a/app/src/lg/svc/LG_BLACKADJUST.pgc b/app/src/lg/svc/LG_BLACKADJUST.pgc new file mode 100644 index 0000000..b7cfee1 --- /dev/null +++ b/app/src/lg/svc/LG_BLACKADJUST.pgc @@ -0,0 +1,27 @@ +/* + * LG_BLACKADJUST.pgc - lg 모듈 서비스 LG_BLACKADJUST (one service = one file). + * 흑색 정정분개: 과소계상분을 양수(흑색) 분개 쌍으로 추가 보정. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_BLACKADJUST.h" + +void LG_BLACKADJUST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char acct[32], bizdate[16]; + long src, vid, mag; + gets_(b, T_KEY1, acct, sizeof(acct)); if (acct[0] == 0) strcpy(acct, "RECV"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + src = getl(b, T_ID1); + mag = getl(b, T_DELTA); if (mag < 0) mag = -mag; + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "BLACKADJ", src, "P", bizdate, "흑색 정정") < 0) FAIL(b); + if (lgdb_insert_line(vid, "ADJ", "D", mag, "", bizdate, "posted") < 0 || + lgdb_insert_line(vid, acct, "C", mag, "", bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID2, vid); setl(b, T_DELTA, mag); + userlog("LG_BLACKADJUST 원전표 %ld 흑색 %s +%ld vid=%ld", src, acct, mag, vid); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_BSSUMMARY.h b/app/src/lg/svc/LG_BSSUMMARY.h new file mode 100644 index 0000000..6bb7ff4 --- /dev/null +++ b/app/src/lg/svc/LG_BSSUMMARY.h @@ -0,0 +1,21 @@ +/* + * LG_BSSUMMARY.h - lg 서비스 LG_BSSUMMARY 카피북 (copybook / record header). + * 재무상태 요약: 자산(RECV/CASH/PREPAID) - 부채(MERCH_PAY/UNEARNED_REV) = 자본. + */ +#ifndef LG_BSSUMMARY_H +#define LG_BSSUMMARY_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_bssummary_rec_t; + +void LG_BSSUMMARY(TPSVCINFO *p); + +#endif /* LG_BSSUMMARY_H */ diff --git a/app/src/lg/svc/LG_BSSUMMARY.pgc b/app/src/lg/svc/LG_BSSUMMARY.pgc new file mode 100644 index 0000000..3d3439b --- /dev/null +++ b/app/src/lg/svc/LG_BSSUMMARY.pgc @@ -0,0 +1,29 @@ +/* + * LG_BSSUMMARY.pgc - lg 모듈 서비스 LG_BSSUMMARY (one service = one file). + * 재무상태 요약: 자산(RECV/CASH/PREPAID) - 부채(MERCH_PAY/UNEARNED_REV) = 자본. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_BSSUMMARY.h" + +void LG_BSSUMMARY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_asset, h_liab; + 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(CASE WHEN acct IN ('RECV','CASH','PREPAID','RECV_ACCRUED') THEN (CASE WHEN dc = 'D' THEN amount ELSE -amount END) ELSE 0 END), 0), + coalesce(sum(CASE WHEN acct IN ('MERCH_PAY','UNEARNED_REV','ACCRUED_PAY','BADDEBT_ALLOW') THEN (CASE WHEN dc = 'C' THEN amount ELSE -amount END) ELSE 0 END), 0) + INTO :h_asset, :h_liab FROM lg_ledger_line WHERE biz_date <= :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_AMT2, h_asset); setl(b, T_AMT3, h_liab); + setl(b, T_NET, h_asset - h_liab); + userlog("LG_BSSUMMARY %s 자산 %ld 부채 %ld 자본 %ld", bizdate, h_asset, h_liab, h_asset - h_liab); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_CLOSELIST.h b/app/src/lg/svc/LG_CLOSELIST.h new file mode 100644 index 0000000..941eab7 --- /dev/null +++ b/app/src/lg/svc/LG_CLOSELIST.h @@ -0,0 +1,21 @@ +/* + * LG_CLOSELIST.h - lg 서비스 LG_CLOSELIST 카피북 (copybook / record header). + * 결산 체크리스트: 미결전표/시산표 불균형/무분개 전표를 집계해 마감 준비도를 판정. + */ +#ifndef LG_CLOSELIST_H +#define LG_CLOSELIST_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_closelist_rec_t; + +void LG_CLOSELIST(TPSVCINFO *p); + +#endif /* LG_CLOSELIST_H */ diff --git a/app/src/lg/svc/LG_CLOSELIST.pgc b/app/src/lg/svc/LG_CLOSELIST.pgc new file mode 100644 index 0000000..ba48713 --- /dev/null +++ b/app/src/lg/svc/LG_CLOSELIST.pgc @@ -0,0 +1,29 @@ +/* + * LG_CLOSELIST.pgc - lg 모듈 서비스 LG_CLOSELIST (one service = one file). + * 결산 체크리스트: 미결전표/시산표 불균형/무분개 전표를 집계해 마감 준비도를 판정. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_CLOSELIST.h" + +void LG_CLOSELIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long drafts, imbal, nolines, ready; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + drafts = lgqdb_draft_count(); + if (drafts < 0) FAIL(b); + imbal = lgqdb_trial_imbalance(bizdate); + if (imbal < 0) FAIL(b); + nolines = lgqdb_voucher_no_lines(bizdate); + if (nolines < 0) FAIL(b); + ready = (drafts == 0 && imbal == 0 && nolines == 0) ? 1 : 0; + setl(b, T_ARG1, drafts); setl(b, T_ARG2, imbal); setl(b, T_ARG3, nolines); + setl(b, T_RC, ready); + Bchg(b, T_STATUS, 0, ready ? "READY" : "NOTREADY", 0L); + userlog("LG_CLOSELIST %s 미결 %ld 불균형 %ld 무분개 %ld ready=%ld", + bizdate, drafts, imbal, nolines, ready); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_CORPLEDGER.h b/app/src/lg/svc/LG_CORPLEDGER.h new file mode 100644 index 0000000..6b2207a --- /dev/null +++ b/app/src/lg/svc/LG_CORPLEDGER.h @@ -0,0 +1,21 @@ +/* + * LG_CORPLEDGER.h - lg 서비스 LG_CORPLEDGER 카피북 (copybook / record header). + * 법인(가맹점)별 원장 집계: 가맹점 원장 라인의 차변/대변 잔액을 산출. + */ +#ifndef LG_CORPLEDGER_H +#define LG_CORPLEDGER_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_corpledger_rec_t; + +void LG_CORPLEDGER(TPSVCINFO *p); + +#endif /* LG_CORPLEDGER_H */ diff --git a/app/src/lg/svc/LG_CORPLEDGER.pgc b/app/src/lg/svc/LG_CORPLEDGER.pgc new file mode 100644 index 0000000..d041352 --- /dev/null +++ b/app/src/lg/svc/LG_CORPLEDGER.pgc @@ -0,0 +1,30 @@ +/* + * LG_CORPLEDGER.pgc - lg 모듈 서비스 LG_CORPLEDGER (one service = one file). + * 법인(가맹점)별 원장 집계: 가맹점 원장 라인의 차변/대변 잔액을 산출. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_CORPLEDGER.h" + +void LG_CORPLEDGER(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char corp[64], bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_corp[64], h_bizdate[16]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + gets_(b, T_MERCHANT, corp, sizeof(corp)); if (corp[0] == 0) strcpy(corp, "M0001"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + strncpy(h_corp, corp, sizeof(h_corp)-1); h_corp[sizeof(h_corp)-1] = 0; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE 0 END), 0), + coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE 0 END), 0) + INTO :h_dr, :h_cr FROM lg_ledger_line + WHERE merchant_id = :h_corp AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_AMT2, h_dr); setl(b, T_AMT3, h_cr); setl(b, T_NET, h_dr - h_cr); + userlog("LG_CORPLEDGER %s %s 잔액 %ld", corp, bizdate, h_dr - h_cr); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_CORPSUM.h b/app/src/lg/svc/LG_CORPSUM.h new file mode 100644 index 0000000..daa8211 --- /dev/null +++ b/app/src/lg/svc/LG_CORPSUM.h @@ -0,0 +1,21 @@ +/* + * LG_CORPSUM.h - lg 서비스 LG_CORPSUM 카피북 (copybook / record header). + * 법인별 순액 집계: 가맹점별 (차변-대변) 순액의 절대값 합과 법인 수를 순회. + */ +#ifndef LG_CORPSUM_H +#define LG_CORPSUM_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_corpsum_rec_t; + +void LG_CORPSUM(TPSVCINFO *p); + +#endif /* LG_CORPSUM_H */ diff --git a/app/src/lg/svc/LG_CORPSUM.pgc b/app/src/lg/svc/LG_CORPSUM.pgc new file mode 100644 index 0000000..8b9cfe7 --- /dev/null +++ b/app/src/lg/svc/LG_CORPSUM.pgc @@ -0,0 +1,39 @@ +/* + * LG_CORPSUM.pgc - lg 모듈 서비스 LG_CORPSUM (one service = one file). + * 법인별 순액 집계: 가맹점별 (차변-대변) 순액의 절대값 합과 법인 수를 순회. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_CORPSUM.h" + +void LG_CORPSUM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long corps = 0, net_abs = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_corp[64]; + long h_net; + 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 cs_cur CURSOR FOR + SELECT merchant_id, + coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE -amount END), 0) + FROM lg_ledger_line WHERE biz_date = :h_bizdate AND merchant_id <> '' + GROUP BY merchant_id ORDER BY merchant_id; + EXEC SQL OPEN cs_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cs_cur INTO :h_corp, :h_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cs_cur; FAIL(b); } + net_abs += (h_net < 0) ? -h_net : h_net; + corps++; + } + EXEC SQL CLOSE cs_cur; + setl(b, T_COUNT, corps); setl(b, T_NET, net_abs); + userlog("LG_CORPSUM %s 법인 %ld개 순액절대합 %ld", bizdate, corps, net_abs); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_CORRPAIR.h b/app/src/lg/svc/LG_CORRPAIR.h new file mode 100644 index 0000000..c0709e9 --- /dev/null +++ b/app/src/lg/svc/LG_CORRPAIR.h @@ -0,0 +1,21 @@ +/* + * LG_CORRPAIR.h - lg 서비스 LG_CORRPAIR 카피북 (copybook / record header). + * 정정 짝분개: 잘못 분류된 계정을 올바른 계정으로 재분류(D 신계정/C 구계정). + */ +#ifndef LG_CORRPAIR_H +#define LG_CORRPAIR_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_corrpair_rec_t; + +void LG_CORRPAIR(TPSVCINFO *p); + +#endif /* LG_CORRPAIR_H */ diff --git a/app/src/lg/svc/LG_CORRPAIR.pgc b/app/src/lg/svc/LG_CORRPAIR.pgc new file mode 100644 index 0000000..ec5c6cf --- /dev/null +++ b/app/src/lg/svc/LG_CORRPAIR.pgc @@ -0,0 +1,28 @@ +/* + * LG_CORRPAIR.pgc - lg 모듈 서비스 LG_CORRPAIR (one service = one file). + * 정정 짝분개: 잘못 분류된 계정을 올바른 계정으로 재분류(D 신계정/C 구계정). + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_CORRPAIR.h" + +void LG_CORRPAIR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char from_acct[32], to_acct[32], bizdate[16], merch[64]; + long vid, amount; + gets_(b, T_KEY1, from_acct, sizeof(from_acct)); if (from_acct[0] == 0) strcpy(from_acct, "SUSPENSE"); + gets_(b, T_KEY2, to_acct, sizeof(to_acct)); if (to_acct[0] == 0) strcpy(to_acct, "RECV"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_MERCHANT, merch, sizeof(merch)); + amount = getl(b, T_AMOUNT); + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", bizdate, "계정 재분류") < 0) FAIL(b); + if (lgdb_insert_line(vid, to_acct, "D", amount, merch, bizdate, "posted") < 0 || + lgdb_insert_line(vid, from_acct, "C", amount, merch, bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); + userlog("LG_CORRPAIR %s -> %s %ld vid=%ld", from_acct, to_acct, amount, vid); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_DEPTLEDGER.h b/app/src/lg/svc/LG_DEPTLEDGER.h new file mode 100644 index 0000000..f986483 --- /dev/null +++ b/app/src/lg/svc/LG_DEPTLEDGER.h @@ -0,0 +1,21 @@ +/* + * LG_DEPTLEDGER.h - lg 서비스 LG_DEPTLEDGER 카피북 (copybook / record header). + * 부문(전표유형)별 원장 조회: 유형별 차변/대변 합계와 분개 건수를 집계. + */ +#ifndef LG_DEPTLEDGER_H +#define LG_DEPTLEDGER_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_deptledger_rec_t; + +void LG_DEPTLEDGER(TPSVCINFO *p); + +#endif /* LG_DEPTLEDGER_H */ diff --git a/app/src/lg/svc/LG_DEPTLEDGER.pgc b/app/src/lg/svc/LG_DEPTLEDGER.pgc new file mode 100644 index 0000000..76751ec --- /dev/null +++ b/app/src/lg/svc/LG_DEPTLEDGER.pgc @@ -0,0 +1,33 @@ +/* + * LG_DEPTLEDGER.pgc - lg 모듈 서비스 LG_DEPTLEDGER (one service = one file). + * 부문(전표유형)별 원장 조회: 유형별 차변/대변 합계와 분개 건수를 집계. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_DEPTLEDGER.h" + +void LG_DEPTLEDGER(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char vtype[16], bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_vtype[16], h_bizdate[16]; + long h_dr, h_cr, h_lines; + EXEC SQL END DECLARE SECTION; + gets_(b, T_KEY1, vtype, sizeof(vtype)); if (vtype[0] == 0) strcpy(vtype, "POST"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + strncpy(h_vtype, vtype, sizeof(h_vtype)-1); h_vtype[sizeof(h_vtype)-1] = 0; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN l.dc = 'D' THEN l.amount ELSE 0 END), 0), + coalesce(sum(CASE WHEN l.dc = 'C' THEN l.amount ELSE 0 END), 0), + count(l.line_id) + INTO :h_dr, :h_cr, :h_lines + FROM lg_voucher v JOIN lg_ledger_line l ON l.voucher_id = v.voucher_id + WHERE v.voucher_type = :h_vtype AND v.biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_AMT2, h_dr); setl(b, T_AMT3, h_cr); + setl(b, T_DELTA, h_dr - h_cr); setl(b, T_COUNT, h_lines); + userlog("LG_DEPTLEDGER %s %s 차변 %ld 대변 %ld", vtype, bizdate, h_dr, h_cr); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_DEPTRANGE.h b/app/src/lg/svc/LG_DEPTRANGE.h new file mode 100644 index 0000000..f9108de --- /dev/null +++ b/app/src/lg/svc/LG_DEPTRANGE.h @@ -0,0 +1,21 @@ +/* + * LG_DEPTRANGE.h - lg 서비스 LG_DEPTRANGE 카피북 (copybook / record header). + * 부문 기간 금액범위: 전표유형의 기간 내 분개 최소/최대 금액과 건수를 조회. + */ +#ifndef LG_DEPTRANGE_H +#define LG_DEPTRANGE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_deptrange_rec_t; + +void LG_DEPTRANGE(TPSVCINFO *p); + +#endif /* LG_DEPTRANGE_H */ diff --git a/app/src/lg/svc/LG_DEPTRANGE.pgc b/app/src/lg/svc/LG_DEPTRANGE.pgc new file mode 100644 index 0000000..82e210e --- /dev/null +++ b/app/src/lg/svc/LG_DEPTRANGE.pgc @@ -0,0 +1,33 @@ +/* + * LG_DEPTRANGE.pgc - lg 모듈 서비스 LG_DEPTRANGE (one service = one file). + * 부문 기간 금액범위: 전표유형의 기간 내 분개 최소/최대 금액과 건수를 조회. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_DEPTRANGE.h" + +void LG_DEPTRANGE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char vtype[16], fromd[16], endd[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_type[16], h_from[16], h_end[16]; + long h_min, h_max, h_cnt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_KEY1, vtype, sizeof(vtype)); if (vtype[0] == 0) strcpy(vtype, "POST"); + gets_(b, T_BIZDATE, endd, sizeof(endd)); + gets_(b, T_STR1, fromd, sizeof(fromd)); if (fromd[0] == 0) strcpy(fromd, endd); + strncpy(h_type, vtype, sizeof(h_type)-1); h_type[sizeof(h_type)-1] = 0; + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-1] = 0; + strncpy(h_end, endd, sizeof(h_end)-1); h_end[sizeof(h_end)-1] = 0; + EXEC SQL SELECT coalesce(min(l.amount), 0), coalesce(max(l.amount), 0), count(*) + INTO :h_min, :h_max, :h_cnt + FROM lg_voucher v JOIN lg_ledger_line l ON l.voucher_id = v.voucher_id + WHERE v.voucher_type = :h_type AND v.biz_date BETWEEN :h_from AND :h_end; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_AMT2, h_min); setl(b, T_AMT3, h_max); + setl(b, T_COUNT, h_cnt); setl(b, T_DELTA, h_max - h_min); + userlog("LG_DEPTRANGE %s %s~%s 최소 %ld 최대 %ld", vtype, fromd, endd, h_min, h_max); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_DEPTSUM.h b/app/src/lg/svc/LG_DEPTSUM.h new file mode 100644 index 0000000..bd60280 --- /dev/null +++ b/app/src/lg/svc/LG_DEPTSUM.h @@ -0,0 +1,21 @@ +/* + * LG_DEPTSUM.h - lg 서비스 LG_DEPTSUM 카피북 (copybook / record header). + * 부문별 차변 합계 최대 유형 탐색: 전표유형별 차변합을 순회해 최대 부문을 선별. + */ +#ifndef LG_DEPTSUM_H +#define LG_DEPTSUM_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_deptsum_rec_t; + +void LG_DEPTSUM(TPSVCINFO *p); + +#endif /* LG_DEPTSUM_H */ diff --git a/app/src/lg/svc/LG_DEPTSUM.pgc b/app/src/lg/svc/LG_DEPTSUM.pgc new file mode 100644 index 0000000..3c5b2b9 --- /dev/null +++ b/app/src/lg/svc/LG_DEPTSUM.pgc @@ -0,0 +1,39 @@ +/* + * LG_DEPTSUM.pgc - lg 모듈 서비스 LG_DEPTSUM (one service = one file). + * 부문별 차변 합계 최대 유형 탐색: 전표유형별 차변합을 순회해 최대 부문을 선별. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_DEPTSUM.h" + +void LG_DEPTSUM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], maxtype[16] = ""; + long total = 0, maxval = -1; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_type[16]; + long h_dr; + 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 ds_cur CURSOR FOR + SELECT v.voucher_type, coalesce(sum(CASE WHEN l.dc = 'D' THEN l.amount ELSE 0 END), 0) + FROM lg_voucher v JOIN lg_ledger_line l ON l.voucher_id = v.voucher_id + WHERE v.biz_date = :h_bizdate GROUP BY v.voucher_type; + EXEC SQL OPEN ds_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH ds_cur INTO :h_type, :h_dr; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ds_cur; FAIL(b); } + total += h_dr; + if (h_dr > maxval) { maxval = h_dr; strncpy(maxtype, h_type, sizeof(maxtype)-1); maxtype[sizeof(maxtype)-1] = 0; } + } + EXEC SQL CLOSE ds_cur; + Bchg(b, T_KEY1, 0, maxtype, 0L); + setl(b, T_AMT2, total); setl(b, T_AMT3, (maxval < 0) ? 0 : maxval); + userlog("LG_DEPTSUM %s 최대부문 %s 차변 %ld", bizdate, maxtype, maxval); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_EQUITYMOVE.h b/app/src/lg/svc/LG_EQUITYMOVE.h new file mode 100644 index 0000000..deff4cc --- /dev/null +++ b/app/src/lg/svc/LG_EQUITYMOVE.h @@ -0,0 +1,21 @@ +/* + * LG_EQUITYMOVE.h - lg 서비스 LG_EQUITYMOVE 카피북 (copybook / record header). + * 자본 변동: 기간 내 이익잉여금(RETAINED) 대변순액 변동을 집계. + */ +#ifndef LG_EQUITYMOVE_H +#define LG_EQUITYMOVE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_equitymove_rec_t; + +void LG_EQUITYMOVE(TPSVCINFO *p); + +#endif /* LG_EQUITYMOVE_H */ diff --git a/app/src/lg/svc/LG_EQUITYMOVE.pgc b/app/src/lg/svc/LG_EQUITYMOVE.pgc new file mode 100644 index 0000000..954833b --- /dev/null +++ b/app/src/lg/svc/LG_EQUITYMOVE.pgc @@ -0,0 +1,30 @@ +/* + * LG_EQUITYMOVE.pgc - lg 모듈 서비스 LG_EQUITYMOVE (one service = one file). + * 자본 변동: 기간 내 이익잉여금(RETAINED) 대변순액 변동을 집계. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_EQUITYMOVE.h" + +void LG_EQUITYMOVE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char fromd[16], endd[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_from[16], h_end[16]; + long h_move, h_cnt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, endd, sizeof(endd)); + gets_(b, T_STR1, fromd, sizeof(fromd)); if (fromd[0] == 0) strcpy(fromd, endd); + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-1] = 0; + strncpy(h_end, endd, sizeof(h_end)-1); h_end[sizeof(h_end)-1] = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE -amount END), 0), count(*) + INTO :h_move, :h_cnt FROM lg_ledger_line + WHERE acct = 'RETAINED' AND biz_date BETWEEN :h_from AND :h_end; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_NET, h_move); setl(b, T_COUNT, h_cnt); + Bchg(b, T_STATUS, 0, (h_move >= 0) ? "UP" : "DOWN", 0L); + userlog("LG_EQUITYMOVE %s~%s 자본변동 %ld (%ld건)", fromd, endd, h_move, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_EXPENSEROLL.h b/app/src/lg/svc/LG_EXPENSEROLL.h new file mode 100644 index 0000000..d2df164 --- /dev/null +++ b/app/src/lg/svc/LG_EXPENSEROLL.h @@ -0,0 +1,21 @@ +/* + * LG_EXPENSEROLL.h - lg 서비스 LG_EXPENSEROLL 카피북 (copybook / record header). + * 비용 롤업: 마감일까지 비용계정(_EXP)별 차변순액을 순회해 총비용을 집계. + */ +#ifndef LG_EXPENSEROLL_H +#define LG_EXPENSEROLL_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_expenseroll_rec_t; + +void LG_EXPENSEROLL(TPSVCINFO *p); + +#endif /* LG_EXPENSEROLL_H */ diff --git a/app/src/lg/svc/LG_EXPENSEROLL.pgc b/app/src/lg/svc/LG_EXPENSEROLL.pgc new file mode 100644 index 0000000..990d9b8 --- /dev/null +++ b/app/src/lg/svc/LG_EXPENSEROLL.pgc @@ -0,0 +1,37 @@ +/* + * LG_EXPENSEROLL.pgc - lg 모듈 서비스 LG_EXPENSEROLL (one service = one file). + * 비용 롤업: 마감일까지 비용계정(_EXP)별 차변순액을 순회해 총비용을 집계. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_EXPENSEROLL.h" + +void LG_EXPENSEROLL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char endd[16]; + long accts = 0, total = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_end[16], h_acct[32]; + long h_net; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, endd, sizeof(endd)); + strncpy(h_end, endd, sizeof(h_end)-1); h_end[sizeof(h_end)-1] = 0; + EXEC SQL DECLARE er_cur CURSOR FOR + SELECT acct, coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE -amount END), 0) + FROM lg_ledger_line WHERE biz_date <= :h_end AND acct LIKE '%_EXP' + GROUP BY acct ORDER BY acct; + EXEC SQL OPEN er_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH er_cur INTO :h_acct, :h_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE er_cur; FAIL(b); } + total += h_net; accts++; + } + EXEC SQL CLOSE er_cur; + setl(b, T_COUNT, accts); setl(b, T_NET, total); + userlog("LG_EXPENSEROLL %s 비용계정 %ld개 총비용 %ld", endd, accts, total); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_HALFCLOSE.h b/app/src/lg/svc/LG_HALFCLOSE.h new file mode 100644 index 0000000..bdbe0e5 --- /dev/null +++ b/app/src/lg/svc/LG_HALFCLOSE.h @@ -0,0 +1,21 @@ +/* + * LG_HALFCLOSE.h - lg 서비스 LG_HALFCLOSE 카피북 (copybook / record header). + * 반기 결산 분개: 반기 FEE_INC 순액을 이익잉여금(RETAINED)으로 균형 대체. + */ +#ifndef LG_HALFCLOSE_H +#define LG_HALFCLOSE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_halfclose_rec_t; + +void LG_HALFCLOSE(TPSVCINFO *p); + +#endif /* LG_HALFCLOSE_H */ diff --git a/app/src/lg/svc/LG_HALFCLOSE.pgc b/app/src/lg/svc/LG_HALFCLOSE.pgc new file mode 100644 index 0000000..25d2b67 --- /dev/null +++ b/app/src/lg/svc/LG_HALFCLOSE.pgc @@ -0,0 +1,43 @@ +/* + * LG_HALFCLOSE.pgc - lg 모듈 서비스 LG_HALFCLOSE (one service = one file). + * 반기 결산 분개: 반기 FEE_INC 순액을 이익잉여금(RETAINED)으로 균형 대체. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_HALFCLOSE.h" + +void LG_HALFCLOSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char fromd[16], endd[16]; + long vid, net, mag; + EXEC SQL BEGIN DECLARE SECTION; + char h_from[16], h_end[16]; + long h_net; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, endd, sizeof(endd)); + gets_(b, T_STR1, fromd, sizeof(fromd)); if (fromd[0] == 0) strcpy(fromd, endd); + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-1] = 0; + strncpy(h_end, endd, sizeof(h_end)-1); h_end[sizeof(h_end)-1] = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE -amount END), 0) + INTO :h_net FROM lg_ledger_line + WHERE acct = 'FEE_INC' AND biz_date BETWEEN :h_from AND :h_end; + if (sqlca.sqlcode < 0) FAIL(b); + net = h_net; + if (net == 0) { setl(b, T_NET, 0); Bchg(b, T_STATUS, 0, "ZERO", 0L); OK(b); } + mag = (net > 0) ? net : -net; + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", endd, "반기 손익 결산") < 0) FAIL(b); + if (net > 0) { + if (lgdb_insert_line(vid, "FEE_INC", "D", mag, "", endd, "posted") < 0 || + lgdb_insert_line(vid, "RETAINED", "C", mag, "", endd, "posted") < 0) FAIL(b); + } else { + if (lgdb_insert_line(vid, "RETAINED", "D", mag, "", endd, "posted") < 0 || + lgdb_insert_line(vid, "FEE_INC", "C", mag, "", endd, "posted") < 0) FAIL(b); + } + setl(b, T_ID1, vid); setl(b, T_NET, net); + userlog("LG_HALFCLOSE %s~%s 반기순익 %ld vid=%ld", fromd, endd, net, vid); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_INCOMEROLL.h b/app/src/lg/svc/LG_INCOMEROLL.h new file mode 100644 index 0000000..fa89c3e --- /dev/null +++ b/app/src/lg/svc/LG_INCOMEROLL.h @@ -0,0 +1,21 @@ +/* + * LG_INCOMEROLL.h - lg 서비스 LG_INCOMEROLL 카피북 (copybook / record header). + * 수익 롤업: 마감일까지 수익계정(_INC)별 대변순액을 순회해 총수익을 집계. + */ +#ifndef LG_INCOMEROLL_H +#define LG_INCOMEROLL_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_incomeroll_rec_t; + +void LG_INCOMEROLL(TPSVCINFO *p); + +#endif /* LG_INCOMEROLL_H */ diff --git a/app/src/lg/svc/LG_INCOMEROLL.pgc b/app/src/lg/svc/LG_INCOMEROLL.pgc new file mode 100644 index 0000000..c5315c1 --- /dev/null +++ b/app/src/lg/svc/LG_INCOMEROLL.pgc @@ -0,0 +1,37 @@ +/* + * LG_INCOMEROLL.pgc - lg 모듈 서비스 LG_INCOMEROLL (one service = one file). + * 수익 롤업: 마감일까지 수익계정(_INC)별 대변순액을 순회해 총수익을 집계. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_INCOMEROLL.h" + +void LG_INCOMEROLL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char endd[16]; + long accts = 0, total = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_end[16], h_acct[32]; + long h_net; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, endd, sizeof(endd)); + strncpy(h_end, endd, sizeof(h_end)-1); h_end[sizeof(h_end)-1] = 0; + EXEC SQL DECLARE ir_cur CURSOR FOR + SELECT acct, coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE -amount END), 0) + FROM lg_ledger_line WHERE biz_date <= :h_end AND acct LIKE '%_INC' + GROUP BY acct ORDER BY acct; + EXEC SQL OPEN ir_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH ir_cur INTO :h_acct, :h_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ir_cur; FAIL(b); } + total += h_net; accts++; + } + EXEC SQL CLOSE ir_cur; + setl(b, T_COUNT, accts); setl(b, T_NET, total); + userlog("LG_INCOMEROLL %s 수익계정 %ld개 총수익 %ld", endd, accts, total); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_LEDGSETTLE.h b/app/src/lg/svc/LG_LEDGSETTLE.h new file mode 100644 index 0000000..0c792b4 --- /dev/null +++ b/app/src/lg/svc/LG_LEDGSETTLE.h @@ -0,0 +1,21 @@ +/* + * LG_LEDGSETTLE.h - lg 서비스 LG_LEDGSETTLE 카피북 (copybook / record header). + * 원장-정산 대사: base 원장(ledger) SETTLE 합계와 원장라인 지급 합계를 대조. + */ +#ifndef LG_LEDGSETTLE_H +#define LG_LEDGSETTLE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_ledgsettle_rec_t; + +void LG_LEDGSETTLE(TPSVCINFO *p); + +#endif /* LG_LEDGSETTLE_H */ diff --git a/app/src/lg/svc/LG_LEDGSETTLE.pgc b/app/src/lg/svc/LG_LEDGSETTLE.pgc new file mode 100644 index 0000000..5cdb262 --- /dev/null +++ b/app/src/lg/svc/LG_LEDGSETTLE.pgc @@ -0,0 +1,31 @@ +/* + * LG_LEDGSETTLE.pgc - lg 모듈 서비스 LG_LEDGSETTLE (one service = one file). + * 원장-정산 대사: base 원장(ledger) SETTLE 합계와 원장라인 지급 합계를 대조. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_LEDGSETTLE.h" + +void LG_LEDGSETTLE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_base, h_line; + 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(amount), 0) INTO :h_base FROM ledger + WHERE biz_date = :h_bizdate AND entry_type = 'SETTLE'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE 0 END), 0) INTO :h_line + FROM lg_ledger_line WHERE biz_date = :h_bizdate AND acct = 'MERCH_PAY'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_AMT2, h_base); setl(b, T_AMT3, h_line); + setl(b, T_DELTA, h_base - h_line); + Bchg(b, T_STATUS, 0, (h_base == h_line) ? "OK" : "NG", 0L); + userlog("LG_LEDGSETTLE %s base %ld line %ld", bizdate, h_base, h_line); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_MERCHCERT.h b/app/src/lg/svc/LG_MERCHCERT.h new file mode 100644 index 0000000..b48d033 --- /dev/null +++ b/app/src/lg/svc/LG_MERCHCERT.h @@ -0,0 +1,21 @@ +/* + * LG_MERCHCERT.h - lg 서비스 LG_MERCHCERT 카피북 (copybook / record header). + * 가맹점 잔액증명: 가맹점잔액 기록치와 라인 재계산치를 대조해 증명(OK/NG). + */ +#ifndef LG_MERCHCERT_H +#define LG_MERCHCERT_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_merchcert_rec_t; + +void LG_MERCHCERT(TPSVCINFO *p); + +#endif /* LG_MERCHCERT_H */ diff --git a/app/src/lg/svc/LG_MERCHCERT.pgc b/app/src/lg/svc/LG_MERCHCERT.pgc new file mode 100644 index 0000000..8f78bb3 --- /dev/null +++ b/app/src/lg/svc/LG_MERCHCERT.pgc @@ -0,0 +1,39 @@ +/* + * LG_MERCHCERT.pgc - lg 모듈 서비스 LG_MERCHCERT (one service = one file). + * 가맹점 잔액증명: 가맹점잔액 기록치와 라인 재계산치를 대조해 증명(OK/NG). + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_MERCHCERT.h" + +void LG_MERCHCERT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + long recorded = 0, recomputed; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_bizdate[16]; + long h_bal, h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + gets_(b, T_MERCHANT, merch, sizeof(merch)); if (merch[0] == 0) strcpy(merch, "M0001"); + 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; + h_bal = 0; + EXEC SQL SELECT balance INTO :h_bal FROM lg_merch_bal + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlcode != 100) recorded = h_bal; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE 0 END), 0), + coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE 0 END), 0) + INTO :h_dr, :h_cr FROM lg_ledger_line + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + recomputed = h_dr - h_cr; + setl(b, T_AMT2, recorded); setl(b, T_AMT3, recomputed); + setl(b, T_DELTA, recorded - recomputed); + Bchg(b, T_STATUS, 0, (recorded == recomputed) ? "OK" : "NG", 0L); + userlog("LG_MERCHCERT %s %s 기록 %ld 재계산 %ld", merch, bizdate, recorded, recomputed); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_MONCLOSEJE.h b/app/src/lg/svc/LG_MONCLOSEJE.h new file mode 100644 index 0000000..f466a8b --- /dev/null +++ b/app/src/lg/svc/LG_MONCLOSEJE.h @@ -0,0 +1,21 @@ +/* + * LG_MONCLOSEJE.h - lg 서비스 LG_MONCLOSEJE 카피북 (copybook / record header). + * 월차 결산 분개: 당월 RECV 순증가를 미수수익(RECV_ACCRUED)으로 계상. + */ +#ifndef LG_MONCLOSEJE_H +#define LG_MONCLOSEJE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_moncloseje_rec_t; + +void LG_MONCLOSEJE(TPSVCINFO *p); + +#endif /* LG_MONCLOSEJE_H */ diff --git a/app/src/lg/svc/LG_MONCLOSEJE.pgc b/app/src/lg/svc/LG_MONCLOSEJE.pgc new file mode 100644 index 0000000..842a34d --- /dev/null +++ b/app/src/lg/svc/LG_MONCLOSEJE.pgc @@ -0,0 +1,37 @@ +/* + * LG_MONCLOSEJE.pgc - lg 모듈 서비스 LG_MONCLOSEJE (one service = one file). + * 월차 결산 분개: 당월 RECV 순증가를 미수수익(RECV_ACCRUED)으로 계상. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_MONCLOSEJE.h" + +void LG_MONCLOSEJE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char fromd[16], endd[16]; + long vid, net; + EXEC SQL BEGIN DECLARE SECTION; + char h_from[16], h_end[16]; + long h_net; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, endd, sizeof(endd)); + gets_(b, T_STR1, fromd, sizeof(fromd)); if (fromd[0] == 0) strcpy(fromd, endd); + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-1] = 0; + strncpy(h_end, endd, sizeof(h_end)-1); h_end[sizeof(h_end)-1] = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE -amount END), 0) + INTO :h_net FROM lg_ledger_line + WHERE acct = 'RECV' AND biz_date BETWEEN :h_from AND :h_end; + if (sqlca.sqlcode < 0) FAIL(b); + net = h_net; + if (net <= 0) { setl(b, T_NET, net); Bchg(b, T_STATUS, 0, "SKIP", 0L); OK(b); } + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", endd, "월차 미수 계상") < 0) FAIL(b); + if (lgdb_insert_line(vid, "RECV_ACCRUED", "D", net, "", endd, "posted") < 0 || + lgdb_insert_line(vid, "FEE_INC", "C", net, "", endd, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); setl(b, T_NET, net); + userlog("LG_MONCLOSEJE %s~%s 미수계상 %ld vid=%ld", fromd, endd, net, vid); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_PARTREVERSE.h b/app/src/lg/svc/LG_PARTREVERSE.h new file mode 100644 index 0000000..0d0b7d5 --- /dev/null +++ b/app/src/lg/svc/LG_PARTREVERSE.h @@ -0,0 +1,21 @@ +/* + * LG_PARTREVERSE.h - lg 서비스 LG_PARTREVERSE 카피북 (copybook / record header). + * 부분 역분개: 원전표에서 특정 계정 라인만 차대를 뒤집어 신규 전표로 복사. + */ +#ifndef LG_PARTREVERSE_H +#define LG_PARTREVERSE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_partreverse_rec_t; + +void LG_PARTREVERSE(TPSVCINFO *p); + +#endif /* LG_PARTREVERSE_H */ diff --git a/app/src/lg/svc/LG_PARTREVERSE.pgc b/app/src/lg/svc/LG_PARTREVERSE.pgc new file mode 100644 index 0000000..2bd4b69 --- /dev/null +++ b/app/src/lg/svc/LG_PARTREVERSE.pgc @@ -0,0 +1,38 @@ +/* + * LG_PARTREVERSE.pgc - lg 모듈 서비스 LG_PARTREVERSE (one service = one file). + * 부분 역분개: 원전표에서 특정 계정 라인만 차대를 뒤집어 신규 전표로 복사. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_PARTREVERSE.h" + +void LG_PARTREVERSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char acct[32], bizdate[16]; + long src, new; + EXEC SQL BEGIN DECLARE SECTION; + char h_acct[32], h_bizdate[16]; + long h_src, h_new; + EXEC SQL END DECLARE SECTION; + src = getl(b, T_ID1); + gets_(b, T_KEY1, acct, sizeof(acct)); if (acct[0] == 0) strcpy(acct, "RECV"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + strncpy(h_acct, acct, sizeof(h_acct)-1); h_acct[sizeof(h_acct)-1] = 0; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + new = lgdb_next_voucher_id(); + if (new < 0) FAIL(b); + h_src = src; h_new = new; + if (lgdb_insert_voucher(new, "REVERSE", src, "P", bizdate, "부분 역분개") < 0) FAIL(b); + EXEC SQL INSERT INTO lg_ledger_line + (line_id, voucher_id, acct, dc, amount, merchant_id, biz_date, status) + SELECT nextval('lg_line_seq'), :h_new, acct, + CASE WHEN dc = 'D' THEN 'C' ELSE 'D' END, + amount, merchant_id, :h_bizdate, 'posted' + FROM lg_ledger_line WHERE voucher_id = :h_src AND acct = :h_acct; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ID2, new); setl(b, T_COUNT, sqlca.sqlerrd[2]); + userlog("LG_PARTREVERSE 원전표 %ld 계정 %s -> vid %ld %ld줄", src, acct, new, (long)sqlca.sqlerrd[2]); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_PAYMATCH.h b/app/src/lg/svc/LG_PAYMATCH.h new file mode 100644 index 0000000..566444a --- /dev/null +++ b/app/src/lg/svc/LG_PAYMATCH.h @@ -0,0 +1,21 @@ +/* + * LG_PAYMATCH.h - lg 서비스 LG_PAYMATCH 카피북 (copybook / record header). + * 지급 매칭: 원장 지급(MERCH_PAY 대변) 건수와 정산 건수를 대조해 매칭율 판정. + */ +#ifndef LG_PAYMATCH_H +#define LG_PAYMATCH_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_paymatch_rec_t; + +void LG_PAYMATCH(TPSVCINFO *p); + +#endif /* LG_PAYMATCH_H */ diff --git a/app/src/lg/svc/LG_PAYMATCH.pgc b/app/src/lg/svc/LG_PAYMATCH.pgc new file mode 100644 index 0000000..0035a30 --- /dev/null +++ b/app/src/lg/svc/LG_PAYMATCH.pgc @@ -0,0 +1,30 @@ +/* + * LG_PAYMATCH.pgc - lg 모듈 서비스 LG_PAYMATCH (one service = one file). + * 지급 매칭: 원장 지급(MERCH_PAY 대변) 건수와 정산 건수를 대조해 매칭율 판정. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_PAYMATCH.h" + +void LG_PAYMATCH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_ledg, h_settle; + 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(*) INTO :h_ledg FROM lg_ledger_line + WHERE biz_date = :h_bizdate AND acct = 'MERCH_PAY' AND dc = 'C'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_settle FROM settlement WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_ledg); setl(b, T_ARG2, h_settle); + setl(b, T_DELTA, h_ledg - h_settle); + Bchg(b, T_STATUS, 0, (h_ledg == h_settle) ? "MATCH" : "GAP", 0L); + userlog("LG_PAYMATCH %s 원장지급 %ld건 정산 %ld건", bizdate, h_ledg, h_settle); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_PENDCLEAR.h b/app/src/lg/svc/LG_PENDCLEAR.h new file mode 100644 index 0000000..1356090 --- /dev/null +++ b/app/src/lg/svc/LG_PENDCLEAR.h @@ -0,0 +1,21 @@ +/* + * LG_PENDCLEAR.h - lg 서비스 LG_PENDCLEAR 카피북 (copybook / record header). + * 미결전표 정리: 기준일 이전 작성(D) 전표를 취소(X)하고 라인을 마감 처리. + */ +#ifndef LG_PENDCLEAR_H +#define LG_PENDCLEAR_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_pendclear_rec_t; + +void LG_PENDCLEAR(TPSVCINFO *p); + +#endif /* LG_PENDCLEAR_H */ diff --git a/app/src/lg/svc/LG_PENDCLEAR.pgc b/app/src/lg/svc/LG_PENDCLEAR.pgc new file mode 100644 index 0000000..7035bd6 --- /dev/null +++ b/app/src/lg/svc/LG_PENDCLEAR.pgc @@ -0,0 +1,31 @@ +/* + * LG_PENDCLEAR.pgc - lg 모듈 서비스 LG_PENDCLEAR (one service = one file). + * 미결전표 정리: 기준일 이전 작성(D) 전표를 취소(X)하고 라인을 마감 처리. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_PENDCLEAR.h" + +void LG_PENDCLEAR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char before[16]; + long vcnt; + EXEC SQL BEGIN DECLARE SECTION; + char h_before[16]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, before, sizeof(before)); + strncpy(h_before, before, sizeof(h_before)-1); h_before[sizeof(h_before)-1] = 0; + EXEC SQL UPDATE lg_ledger_line SET status = 'closed' + WHERE status = 'posted' AND voucher_id IN + (SELECT voucher_id FROM lg_voucher WHERE status = 'D' AND biz_date < :h_before); + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL UPDATE lg_voucher SET status = 'X', updated_at = now() + WHERE status = 'D' AND biz_date < :h_before; + if (sqlca.sqlcode < 0) FAIL(b); + vcnt = sqlca.sqlerrd[2]; + setl(b, T_COUNT, vcnt); + userlog("LG_PENDCLEAR %s 이전 미결 %ld건 정리", before, vcnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_PENDESCAL.h b/app/src/lg/svc/LG_PENDESCAL.h new file mode 100644 index 0000000..80a467b --- /dev/null +++ b/app/src/lg/svc/LG_PENDESCAL.h @@ -0,0 +1,21 @@ +/* + * LG_PENDESCAL.h - lg 서비스 LG_PENDESCAL 카피북 (copybook / record header). + * 미결 에스컬레이션: 기준일보다 오래 방치된 작성(D) 전표 건수를 집계. + */ +#ifndef LG_PENDESCAL_H +#define LG_PENDESCAL_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_pendescal_rec_t; + +void LG_PENDESCAL(TPSVCINFO *p); + +#endif /* LG_PENDESCAL_H */ diff --git a/app/src/lg/svc/LG_PENDESCAL.pgc b/app/src/lg/svc/LG_PENDESCAL.pgc new file mode 100644 index 0000000..0788b0b --- /dev/null +++ b/app/src/lg/svc/LG_PENDESCAL.pgc @@ -0,0 +1,30 @@ +/* + * LG_PENDESCAL.pgc - lg 모듈 서비스 LG_PENDESCAL (one service = one file). + * 미결 에스컬레이션: 기준일보다 오래 방치된 작성(D) 전표 건수를 집계. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_PENDESCAL.h" + +void LG_PENDESCAL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_amt; + 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((SELECT sum(amount) FROM lg_ledger_line l + WHERE l.voucher_id = v.voucher_id AND l.dc = 'D'), 0) + INTO :h_cnt, :h_amt + FROM lg_voucher v WHERE v.status = 'D' AND v.biz_date < :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); setl(b, T_AMT2, h_amt); + Bchg(b, T_STATUS, 0, (h_cnt > 0) ? "ESCALATE" : "CLEAN", 0L); + userlog("LG_PENDESCAL %s 지연 미결 %ld건", bizdate, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_PENDROUTE.h b/app/src/lg/svc/LG_PENDROUTE.h new file mode 100644 index 0000000..09ebb4f --- /dev/null +++ b/app/src/lg/svc/LG_PENDROUTE.h @@ -0,0 +1,21 @@ +/* + * LG_PENDROUTE.h - lg 서비스 LG_PENDROUTE 카피북 (copybook / record header). + * 미결전표 배정: 특정 작성(D) 전표의 메모에 담당자를 기록해 배정. + */ +#ifndef LG_PENDROUTE_H +#define LG_PENDROUTE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_pendroute_rec_t; + +void LG_PENDROUTE(TPSVCINFO *p); + +#endif /* LG_PENDROUTE_H */ diff --git a/app/src/lg/svc/LG_PENDROUTE.pgc b/app/src/lg/svc/LG_PENDROUTE.pgc new file mode 100644 index 0000000..53a04e1 --- /dev/null +++ b/app/src/lg/svc/LG_PENDROUTE.pgc @@ -0,0 +1,30 @@ +/* + * LG_PENDROUTE.pgc - lg 모듈 서비스 LG_PENDROUTE (one service = one file). + * 미결전표 배정: 특정 작성(D) 전표의 메모에 담당자를 기록해 배정. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_PENDROUTE.h" + +void LG_PENDROUTE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char who[64]; + long vid; + EXEC SQL BEGIN DECLARE SECTION; + long h_vid; + char h_who[64]; + EXEC SQL END DECLARE SECTION; + vid = getl(b, T_ID1); + gets_(b, T_STR1, who, sizeof(who)); if (who[0] == 0) strcpy(who, "미배정"); + h_vid = vid; + strncpy(h_who, who, sizeof(h_who)-1); h_who[sizeof(h_who)-1] = 0; + EXEC SQL UPDATE lg_voucher SET memo = '담당:' || :h_who, updated_at = now() + WHERE voucher_id = :h_vid AND status = 'D'; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlerrd[2] == 0) { Bchg(b, T_MSG, 0, "작성 전표 아님", 0L); FAIL(b); } + setl(b, T_COUNT, sqlca.sqlerrd[2]); + userlog("LG_PENDROUTE vid=%ld 담당 %s 배정", vid, who); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_PENDSTAT.h b/app/src/lg/svc/LG_PENDSTAT.h new file mode 100644 index 0000000..01623b1 --- /dev/null +++ b/app/src/lg/svc/LG_PENDSTAT.h @@ -0,0 +1,21 @@ +/* + * LG_PENDSTAT.h - lg 서비스 LG_PENDSTAT 카피북 (copybook / record header). + * 미결 상태별 집계: 당일 전표를 상태(D/P/M/S/X)별로 순회 집계. + */ +#ifndef LG_PENDSTAT_H +#define LG_PENDSTAT_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_pendstat_rec_t; + +void LG_PENDSTAT(TPSVCINFO *p); + +#endif /* LG_PENDSTAT_H */ diff --git a/app/src/lg/svc/LG_PENDSTAT.pgc b/app/src/lg/svc/LG_PENDSTAT.pgc new file mode 100644 index 0000000..66c8d42 --- /dev/null +++ b/app/src/lg/svc/LG_PENDSTAT.pgc @@ -0,0 +1,37 @@ +/* + * LG_PENDSTAT.pgc - lg 모듈 서비스 LG_PENDSTAT (one service = one file). + * 미결 상태별 집계: 당일 전표를 상태(D/P/M/S/X)별로 순회 집계. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_PENDSTAT.h" + +void LG_PENDSTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long total = 0, drafts = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_st[4]; + long h_cnt; + 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 ps_cur CURSOR FOR + SELECT status, count(*) FROM lg_voucher + WHERE biz_date = :h_bizdate GROUP BY status ORDER BY status; + EXEC SQL OPEN ps_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH ps_cur INTO :h_st, :h_cnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ps_cur; FAIL(b); } + total += h_cnt; + if (h_st[0] == 'D') drafts = h_cnt; + } + EXEC SQL CLOSE ps_cur; + setl(b, T_COUNT, total); setl(b, T_ARG1, drafts); + userlog("LG_PENDSTAT %s 전표 %ld건 중 미결 %ld건", bizdate, total, drafts); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_PLSUMMARY.h b/app/src/lg/svc/LG_PLSUMMARY.h new file mode 100644 index 0000000..ccd8bed --- /dev/null +++ b/app/src/lg/svc/LG_PLSUMMARY.h @@ -0,0 +1,21 @@ +/* + * LG_PLSUMMARY.h - lg 서비스 LG_PLSUMMARY 카피북 (copybook / record header). + * 손익 요약: 당일 수익(_INC)과 비용(_EXP)의 순액을 계산해 순손익을 산출. + */ +#ifndef LG_PLSUMMARY_H +#define LG_PLSUMMARY_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_plsummary_rec_t; + +void LG_PLSUMMARY(TPSVCINFO *p); + +#endif /* LG_PLSUMMARY_H */ diff --git a/app/src/lg/svc/LG_PLSUMMARY.pgc b/app/src/lg/svc/LG_PLSUMMARY.pgc new file mode 100644 index 0000000..0bb070a --- /dev/null +++ b/app/src/lg/svc/LG_PLSUMMARY.pgc @@ -0,0 +1,30 @@ +/* + * LG_PLSUMMARY.pgc - lg 모듈 서비스 LG_PLSUMMARY (one service = one file). + * 손익 요약: 당일 수익(_INC)과 비용(_EXP)의 순액을 계산해 순손익을 산출. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_PLSUMMARY.h" + +void LG_PLSUMMARY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_inc, h_exp; + 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(CASE WHEN acct LIKE '%_INC' THEN (CASE WHEN dc = 'C' THEN amount ELSE -amount END) ELSE 0 END), 0), + coalesce(sum(CASE WHEN acct LIKE '%_EXP' THEN (CASE WHEN dc = 'D' THEN amount ELSE -amount END) ELSE 0 END), 0) + INTO :h_inc, :h_exp FROM lg_ledger_line WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_AMT2, h_inc); setl(b, T_AMT3, h_exp); + setl(b, T_NET, h_inc - h_exp); + Bchg(b, T_STATUS, 0, (h_inc - h_exp >= 0) ? "PROFIT" : "LOSS", 0L); + userlog("LG_PLSUMMARY %s 수익 %ld 비용 %ld 순익 %ld", bizdate, h_inc, h_exp, h_inc - h_exp); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_PREPAIDAMORT.h b/app/src/lg/svc/LG_PREPAIDAMORT.h new file mode 100644 index 0000000..a5b629d --- /dev/null +++ b/app/src/lg/svc/LG_PREPAIDAMORT.h @@ -0,0 +1,21 @@ +/* + * LG_PREPAIDAMORT.h - lg 서비스 LG_PREPAIDAMORT 카피북 (copybook / record header). + * 선급 상각: 선급비용 총액을 개월(N)로 나눠 1개월분을 비용(D)/선급(C) 상각. + */ +#ifndef LG_PREPAIDAMORT_H +#define LG_PREPAIDAMORT_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_prepaidamort_rec_t; + +void LG_PREPAIDAMORT(TPSVCINFO *p); + +#endif /* LG_PREPAIDAMORT_H */ diff --git a/app/src/lg/svc/LG_PREPAIDAMORT.pgc b/app/src/lg/svc/LG_PREPAIDAMORT.pgc new file mode 100644 index 0000000..beca4d4 --- /dev/null +++ b/app/src/lg/svc/LG_PREPAIDAMORT.pgc @@ -0,0 +1,28 @@ +/* + * LG_PREPAIDAMORT.pgc - lg 모듈 서비스 LG_PREPAIDAMORT (one service = one file). + * 선급 상각: 선급비용 총액을 개월(N)로 나눠 1개월분을 비용(D)/선급(C) 상각. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_PREPAIDAMORT.h" + +void LG_PREPAIDAMORT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long vid, total, months, per; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + total = getl(b, T_AMOUNT); + months = getl(b, T_COUNT); if (months <= 0) months = 12; + per = total / months; + if (per <= 0) { Bchg(b, T_MSG, 0, "상각액 없음", 0L); FAIL(b); } + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", bizdate, "선급비용 상각") < 0) FAIL(b); + if (lgdb_insert_line(vid, "PREPAID_EXP", "D", per, "", bizdate, "posted") < 0 || + lgdb_insert_line(vid, "PREPAID", "C", per, "", bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); setl(b, T_NET, per); + userlog("LG_PREPAIDAMORT vid=%ld 총 %ld / %ld개월 = %ld", vid, total, months, per); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_PREPAIDPOST.h b/app/src/lg/svc/LG_PREPAIDPOST.h new file mode 100644 index 0000000..2d5395c --- /dev/null +++ b/app/src/lg/svc/LG_PREPAIDPOST.h @@ -0,0 +1,21 @@ +/* + * LG_PREPAIDPOST.h - lg 서비스 LG_PREPAIDPOST 카피북 (copybook / record header). + * 선급비용 계상: 선급비용(D)/현금(C)으로 지출 시점 자산 계상. + */ +#ifndef LG_PREPAIDPOST_H +#define LG_PREPAIDPOST_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_prepaidpost_rec_t; + +void LG_PREPAIDPOST(TPSVCINFO *p); + +#endif /* LG_PREPAIDPOST_H */ diff --git a/app/src/lg/svc/LG_PREPAIDPOST.pgc b/app/src/lg/svc/LG_PREPAIDPOST.pgc new file mode 100644 index 0000000..a331023 --- /dev/null +++ b/app/src/lg/svc/LG_PREPAIDPOST.pgc @@ -0,0 +1,26 @@ +/* + * LG_PREPAIDPOST.pgc - lg 모듈 서비스 LG_PREPAIDPOST (one service = one file). + * 선급비용 계상: 선급비용(D)/현금(C)으로 지출 시점 자산 계상. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_PREPAIDPOST.h" + +void LG_PREPAIDPOST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long vid, amount; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + amount = getl(b, T_AMOUNT); + if (amount <= 0) { Bchg(b, T_MSG, 0, "선급액 없음", 0L); FAIL(b); } + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", bizdate, "선급비용 계상") < 0) FAIL(b); + if (lgdb_insert_line(vid, "PREPAID", "D", amount, "", bizdate, "posted") < 0 || + lgdb_insert_line(vid, "CASH", "C", amount, "", bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); + userlog("LG_PREPAIDPOST vid=%ld 선급 %ld", vid, amount); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_QTRCLOSEJE.h b/app/src/lg/svc/LG_QTRCLOSEJE.h new file mode 100644 index 0000000..4ff56f0 --- /dev/null +++ b/app/src/lg/svc/LG_QTRCLOSEJE.h @@ -0,0 +1,21 @@ +/* + * LG_QTRCLOSEJE.h - lg 서비스 LG_QTRCLOSEJE 카피북 (copybook / record header). + * 분기 결산 분개: 당분기 대손비용 발생분을 미지급(ACCRUED_PAY)으로 계상. + */ +#ifndef LG_QTRCLOSEJE_H +#define LG_QTRCLOSEJE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_qtrcloseje_rec_t; + +void LG_QTRCLOSEJE(TPSVCINFO *p); + +#endif /* LG_QTRCLOSEJE_H */ diff --git a/app/src/lg/svc/LG_QTRCLOSEJE.pgc b/app/src/lg/svc/LG_QTRCLOSEJE.pgc new file mode 100644 index 0000000..2f53cce --- /dev/null +++ b/app/src/lg/svc/LG_QTRCLOSEJE.pgc @@ -0,0 +1,37 @@ +/* + * LG_QTRCLOSEJE.pgc - lg 모듈 서비스 LG_QTRCLOSEJE (one service = one file). + * 분기 결산 분개: 당분기 대손비용 발생분을 미지급(ACCRUED_PAY)으로 계상. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_QTRCLOSEJE.h" + +void LG_QTRCLOSEJE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char fromd[16], endd[16]; + long vid, net; + EXEC SQL BEGIN DECLARE SECTION; + char h_from[16], h_end[16]; + long h_net; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, endd, sizeof(endd)); + gets_(b, T_STR1, fromd, sizeof(fromd)); if (fromd[0] == 0) strcpy(fromd, endd); + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-1] = 0; + strncpy(h_end, endd, sizeof(h_end)-1); h_end[sizeof(h_end)-1] = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE -amount END), 0) + INTO :h_net FROM lg_ledger_line + WHERE acct = 'BADDEBT_EXP' AND biz_date BETWEEN :h_from AND :h_end; + if (sqlca.sqlcode < 0) FAIL(b); + net = h_net; + if (net <= 0) { setl(b, T_NET, net); Bchg(b, T_STATUS, 0, "SKIP", 0L); OK(b); } + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", endd, "분기 미지급 계상") < 0) FAIL(b); + if (lgdb_insert_line(vid, "BADDEBT_EXP", "D", net, "", endd, "posted") < 0 || + lgdb_insert_line(vid, "ACCRUED_PAY", "C", net, "", endd, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); setl(b, T_NET, net); + userlog("LG_QTRCLOSEJE %s~%s 미지급계상 %ld vid=%ld", fromd, endd, net, vid); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_RECOVERY.h b/app/src/lg/svc/LG_RECOVERY.h new file mode 100644 index 0000000..14f3fd6 --- /dev/null +++ b/app/src/lg/svc/LG_RECOVERY.h @@ -0,0 +1,21 @@ +/* + * LG_RECOVERY.h - lg 서비스 LG_RECOVERY 카피북 (copybook / record header). + * 상각채권 회수: 제각 채권 회수를 현금(D)/대손충당금환입(C)으로 인식. + */ +#ifndef LG_RECOVERY_H +#define LG_RECOVERY_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_recovery_rec_t; + +void LG_RECOVERY(TPSVCINFO *p); + +#endif /* LG_RECOVERY_H */ diff --git a/app/src/lg/svc/LG_RECOVERY.pgc b/app/src/lg/svc/LG_RECOVERY.pgc new file mode 100644 index 0000000..480c49b --- /dev/null +++ b/app/src/lg/svc/LG_RECOVERY.pgc @@ -0,0 +1,27 @@ +/* + * LG_RECOVERY.pgc - lg 모듈 서비스 LG_RECOVERY (one service = one file). + * 상각채권 회수: 제각 채권 회수를 현금(D)/대손충당금환입(C)으로 인식. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_RECOVERY.h" + +void LG_RECOVERY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], merch[64]; + long vid, amount; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_MERCHANT, merch, sizeof(merch)); + amount = getl(b, T_AMOUNT); + if (amount <= 0) { Bchg(b, T_MSG, 0, "회수액 없음", 0L); FAIL(b); } + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", bizdate, "상각채권 회수") < 0) FAIL(b); + if (lgdb_insert_line(vid, "CASH", "D", amount, merch, bizdate, "posted") < 0 || + lgdb_insert_line(vid, "BADDEBT_ALLOW", "C", amount, merch, bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); + userlog("LG_RECOVERY vid=%ld 회수 %ld", vid, amount); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_REDADJUST.h b/app/src/lg/svc/LG_REDADJUST.h new file mode 100644 index 0000000..c3e25e8 --- /dev/null +++ b/app/src/lg/svc/LG_REDADJUST.h @@ -0,0 +1,21 @@ +/* + * LG_REDADJUST.h - lg 서비스 LG_REDADJUST 카피북 (copybook / record header). + * 적색 정정분개: 과대계상된 차변을 음수(적색) 분개 쌍으로 취소 감액. + */ +#ifndef LG_REDADJUST_H +#define LG_REDADJUST_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_redadjust_rec_t; + +void LG_REDADJUST(TPSVCINFO *p); + +#endif /* LG_REDADJUST_H */ diff --git a/app/src/lg/svc/LG_REDADJUST.pgc b/app/src/lg/svc/LG_REDADJUST.pgc new file mode 100644 index 0000000..7761283 --- /dev/null +++ b/app/src/lg/svc/LG_REDADJUST.pgc @@ -0,0 +1,27 @@ +/* + * LG_REDADJUST.pgc - lg 모듈 서비스 LG_REDADJUST (one service = one file). + * 적색 정정분개: 과대계상된 차변을 음수(적색) 분개 쌍으로 취소 감액. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_REDADJUST.h" + +void LG_REDADJUST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char acct[32], bizdate[16]; + long src, vid, mag; + gets_(b, T_KEY1, acct, sizeof(acct)); if (acct[0] == 0) strcpy(acct, "RECV"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + src = getl(b, T_ID1); + mag = getl(b, T_DELTA); if (mag < 0) mag = -mag; + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "REDADJ", src, "P", bizdate, "적색 정정") < 0) FAIL(b); + if (lgdb_insert_line(vid, acct, "D", -mag, "", bizdate, "posted") < 0 || + lgdb_insert_line(vid, "ADJ", "C", -mag, "", bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID2, vid); setl(b, T_DELTA, -mag); + userlog("LG_REDADJUST 원전표 %ld 적색 %s -%ld vid=%ld", src, acct, mag, vid); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_REDBLACKNET.h b/app/src/lg/svc/LG_REDBLACKNET.h new file mode 100644 index 0000000..4027ab9 --- /dev/null +++ b/app/src/lg/svc/LG_REDBLACKNET.h @@ -0,0 +1,21 @@ +/* + * LG_REDBLACKNET.h - lg 서비스 LG_REDBLACKNET 카피북 (copybook / record header). + * 적흑 상계: 원전표에 연결된 적색/흑색 정정 금액을 합산해 순정정액을 반환. + */ +#ifndef LG_REDBLACKNET_H +#define LG_REDBLACKNET_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_redblacknet_rec_t; + +void LG_REDBLACKNET(TPSVCINFO *p); + +#endif /* LG_REDBLACKNET_H */ diff --git a/app/src/lg/svc/LG_REDBLACKNET.pgc b/app/src/lg/svc/LG_REDBLACKNET.pgc new file mode 100644 index 0000000..a693e09 --- /dev/null +++ b/app/src/lg/svc/LG_REDBLACKNET.pgc @@ -0,0 +1,31 @@ +/* + * LG_REDBLACKNET.pgc - lg 모듈 서비스 LG_REDBLACKNET (one service = one file). + * 적흑 상계: 원전표에 연결된 적색/흑색 정정 금액을 합산해 순정정액을 반환. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_REDBLACKNET.h" + +void LG_REDBLACKNET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long src; + EXEC SQL BEGIN DECLARE SECTION; + long h_src, h_red, h_black; + EXEC SQL END DECLARE SECTION; + src = getl(b, T_ID1); + h_src = src; + EXEC SQL SELECT coalesce(sum(CASE WHEN amount < 0 THEN amount ELSE 0 END), 0), + coalesce(sum(CASE WHEN amount > 0 THEN amount ELSE 0 END), 0) + INTO :h_red, :h_black + FROM lg_ledger_line + WHERE voucher_id IN (SELECT voucher_id FROM lg_voucher + WHERE ref_id = :h_src AND voucher_type IN ('REDADJ', 'BLACKADJ')); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_AMT2, h_red); setl(b, T_AMT3, h_black); + setl(b, T_NET, h_red + h_black); + Bchg(b, T_STATUS, 0, (h_red + h_black == 0) ? "NET0" : "NETX", 0L); + userlog("LG_REDBLACKNET 원전표 %ld 적 %ld 흑 %ld 순 %ld", src, h_red, h_black, h_red + h_black); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_REPOST.h b/app/src/lg/svc/LG_REPOST.h new file mode 100644 index 0000000..75febf9 --- /dev/null +++ b/app/src/lg/svc/LG_REPOST.h @@ -0,0 +1,21 @@ +/* + * LG_REPOST.h - lg 서비스 LG_REPOST 카피북 (copybook / record header). + * 재전기: 취소(X)된 전표의 라인을 방향 그대로 신규 전기(P) 전표로 복제. + */ +#ifndef LG_REPOST_H +#define LG_REPOST_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_repost_rec_t; + +void LG_REPOST(TPSVCINFO *p); + +#endif /* LG_REPOST_H */ diff --git a/app/src/lg/svc/LG_REPOST.pgc b/app/src/lg/svc/LG_REPOST.pgc new file mode 100644 index 0000000..705db4f --- /dev/null +++ b/app/src/lg/svc/LG_REPOST.pgc @@ -0,0 +1,36 @@ +/* + * LG_REPOST.pgc - lg 모듈 서비스 LG_REPOST (one service = one file). + * 재전기: 취소(X)된 전표의 라인을 방향 그대로 신규 전기(P) 전표로 복제. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_REPOST.h" + +void LG_REPOST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], st[4]; + long src, new; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_src, h_new; + EXEC SQL END DECLARE SECTION; + src = getl(b, T_ID1); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (lgdb_get_voucher_status(src, st) < 0) { Bchg(b, T_MSG, 0, "전표 없음", 0L); FAIL(b); } + if (st[0] != 'X') { Bchg(b, T_MSG, 0, "취소 전표만 재전기 가능", 0L); FAIL(b); } + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + new = lgdb_next_voucher_id(); + if (new < 0) FAIL(b); + h_src = src; h_new = new; + if (lgdb_insert_voucher(new, "POST", src, "P", bizdate, "재전기") < 0) FAIL(b); + EXEC SQL INSERT INTO lg_ledger_line + (line_id, voucher_id, acct, dc, amount, merchant_id, biz_date, status) + SELECT nextval('lg_line_seq'), :h_new, acct, dc, amount, merchant_id, :h_bizdate, 'posted' + FROM lg_ledger_line WHERE voucher_id = :h_src; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ID2, new); setl(b, T_COUNT, sqlca.sqlerrd[2]); + userlog("LG_REPOST 취소전표 %ld -> 신규 %ld %ld줄", src, new, (long)sqlca.sqlerrd[2]); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_REVACCRUE.h b/app/src/lg/svc/LG_REVACCRUE.h new file mode 100644 index 0000000..7bb36ae --- /dev/null +++ b/app/src/lg/svc/LG_REVACCRUE.h @@ -0,0 +1,21 @@ +/* + * LG_REVACCRUE.h - lg 서비스 LG_REVACCRUE 카피북 (copybook / record header). + * 수익 발생 계상: 발생주의 수익을 미수수익(D)/수수료수익(C)으로 균형 계상. + */ +#ifndef LG_REVACCRUE_H +#define LG_REVACCRUE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_revaccrue_rec_t; + +void LG_REVACCRUE(TPSVCINFO *p); + +#endif /* LG_REVACCRUE_H */ diff --git a/app/src/lg/svc/LG_REVACCRUE.pgc b/app/src/lg/svc/LG_REVACCRUE.pgc new file mode 100644 index 0000000..940e627 --- /dev/null +++ b/app/src/lg/svc/LG_REVACCRUE.pgc @@ -0,0 +1,27 @@ +/* + * LG_REVACCRUE.pgc - lg 모듈 서비스 LG_REVACCRUE (one service = one file). + * 수익 발생 계상: 발생주의 수익을 미수수익(D)/수수료수익(C)으로 균형 계상. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_REVACCRUE.h" + +void LG_REVACCRUE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], merch[64]; + long vid, amount; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_MERCHANT, merch, sizeof(merch)); + amount = getl(b, T_AMOUNT); + if (amount <= 0) { Bchg(b, T_MSG, 0, "발생액 없음", 0L); FAIL(b); } + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", bizdate, "수익 발생 계상") < 0) FAIL(b); + if (lgdb_insert_line(vid, "RECV_ACCRUED", "D", amount, merch, bizdate, "posted") < 0 || + lgdb_insert_line(vid, "FEE_INC", "C", amount, merch, bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); + userlog("LG_REVACCRUE vid=%ld 발생수익 %ld", vid, amount); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_REVDEFER.h b/app/src/lg/svc/LG_REVDEFER.h new file mode 100644 index 0000000..5c524bf --- /dev/null +++ b/app/src/lg/svc/LG_REVDEFER.h @@ -0,0 +1,21 @@ +/* + * LG_REVDEFER.h - lg 서비스 LG_REVDEFER 카피북 (copybook / record header). + * 수익 이연: 미실현 수익을 수수료수익(D)/선수수익(C)으로 이연 대체. + */ +#ifndef LG_REVDEFER_H +#define LG_REVDEFER_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_revdefer_rec_t; + +void LG_REVDEFER(TPSVCINFO *p); + +#endif /* LG_REVDEFER_H */ diff --git a/app/src/lg/svc/LG_REVDEFER.pgc b/app/src/lg/svc/LG_REVDEFER.pgc new file mode 100644 index 0000000..37846b9 --- /dev/null +++ b/app/src/lg/svc/LG_REVDEFER.pgc @@ -0,0 +1,27 @@ +/* + * LG_REVDEFER.pgc - lg 모듈 서비스 LG_REVDEFER (one service = one file). + * 수익 이연: 미실현 수익을 수수료수익(D)/선수수익(C)으로 이연 대체. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_REVDEFER.h" + +void LG_REVDEFER(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], merch[64]; + long vid, amount; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_MERCHANT, merch, sizeof(merch)); + amount = getl(b, T_AMOUNT); + if (amount <= 0) { Bchg(b, T_MSG, 0, "이연액 없음", 0L); FAIL(b); } + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", bizdate, "수익 이연") < 0) FAIL(b); + if (lgdb_insert_line(vid, "FEE_INC", "D", amount, merch, bizdate, "posted") < 0 || + lgdb_insert_line(vid, "UNEARNED_REV", "C", amount, merch, bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); + userlog("LG_REVDEFER vid=%ld 이연수익 %ld", vid, amount); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_REVRELEASE.h b/app/src/lg/svc/LG_REVRELEASE.h new file mode 100644 index 0000000..84f7586 --- /dev/null +++ b/app/src/lg/svc/LG_REVRELEASE.h @@ -0,0 +1,21 @@ +/* + * LG_REVRELEASE.h - lg 서비스 LG_REVRELEASE 카피북 (copybook / record header). + * 이연수익 환입: 선수수익(D)을 수수료수익(C)으로 기간 경과분 환입 인식. + */ +#ifndef LG_REVRELEASE_H +#define LG_REVRELEASE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_revrelease_rec_t; + +void LG_REVRELEASE(TPSVCINFO *p); + +#endif /* LG_REVRELEASE_H */ diff --git a/app/src/lg/svc/LG_REVRELEASE.pgc b/app/src/lg/svc/LG_REVRELEASE.pgc new file mode 100644 index 0000000..42aa6d0 --- /dev/null +++ b/app/src/lg/svc/LG_REVRELEASE.pgc @@ -0,0 +1,27 @@ +/* + * LG_REVRELEASE.pgc - lg 모듈 서비스 LG_REVRELEASE (one service = one file). + * 이연수익 환입: 선수수익(D)을 수수료수익(C)으로 기간 경과분 환입 인식. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_REVRELEASE.h" + +void LG_REVRELEASE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], merch[64]; + long vid, amount; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_MERCHANT, merch, sizeof(merch)); + amount = getl(b, T_AMOUNT); + if (amount <= 0) { Bchg(b, T_MSG, 0, "환입액 없음", 0L); FAIL(b); } + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", bizdate, "이연수익 환입") < 0) FAIL(b); + if (lgdb_insert_line(vid, "UNEARNED_REV", "D", amount, merch, bizdate, "posted") < 0 || + lgdb_insert_line(vid, "FEE_INC", "C", amount, merch, bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); + userlog("LG_REVRELEASE vid=%ld 환입 %ld", vid, amount); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_ROLLTB.h b/app/src/lg/svc/LG_ROLLTB.h new file mode 100644 index 0000000..f3954cf --- /dev/null +++ b/app/src/lg/svc/LG_ROLLTB.h @@ -0,0 +1,21 @@ +/* + * LG_ROLLTB.h - lg 서비스 LG_ROLLTB 카피북 (copybook / record header). + * 누적 시산표: 마감일까지 계정별 누계 차대를 순회해 시산표 upsert 및 총차액 검증. + */ +#ifndef LG_ROLLTB_H +#define LG_ROLLTB_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_rolltb_rec_t; + +void LG_ROLLTB(TPSVCINFO *p); + +#endif /* LG_ROLLTB_H */ diff --git a/app/src/lg/svc/LG_ROLLTB.pgc b/app/src/lg/svc/LG_ROLLTB.pgc new file mode 100644 index 0000000..ce085cf --- /dev/null +++ b/app/src/lg/svc/LG_ROLLTB.pgc @@ -0,0 +1,44 @@ +/* + * LG_ROLLTB.pgc - lg 모듈 서비스 LG_ROLLTB (one service = one file). + * 누적 시산표: 마감일까지 계정별 누계 차대를 순회해 시산표 upsert 및 총차액 검증. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_ROLLTB.h" + +void LG_ROLLTB(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char endd[16]; + long rows = 0, net_diff = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_end[16], h_acct[32]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, endd, sizeof(endd)); + strncpy(h_end, endd, sizeof(h_end)-1); h_end[sizeof(h_end)-1] = 0; + EXEC SQL DECLARE rt_cur CURSOR FOR + SELECT acct, coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE 0 END), 0), + coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE 0 END), 0) + FROM lg_ledger_line WHERE biz_date <= :h_end GROUP BY acct ORDER BY acct; + EXEC SQL OPEN rt_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH rt_cur INTO :h_acct, :h_dr, :h_cr; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE rt_cur; FAIL(b); } + EXEC SQL INSERT INTO lg_trial_bal (acct, biz_date, debit_total, credit_total, diff) + VALUES (:h_acct, :h_end, :h_dr, :h_cr, :h_dr - :h_cr) + ON CONFLICT (acct, biz_date) DO UPDATE + SET debit_total = EXCLUDED.debit_total, credit_total = EXCLUDED.credit_total, + diff = EXCLUDED.diff, updated_at = now(); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE rt_cur; FAIL(b); } + net_diff += h_dr - h_cr; rows++; + } + EXEC SQL CLOSE rt_cur; + setl(b, T_COUNT, rows); setl(b, T_DELTA, net_diff); + Bchg(b, T_STATUS, 0, (net_diff == 0) ? "BALANCED" : "IMBAL", 0L); + userlog("LG_ROLLTB %s 계정 %ld 총차액 %ld", endd, rows, net_diff); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_SEQAUDIT.h b/app/src/lg/svc/LG_SEQAUDIT.h new file mode 100644 index 0000000..2b238f7 --- /dev/null +++ b/app/src/lg/svc/LG_SEQAUDIT.h @@ -0,0 +1,21 @@ +/* + * LG_SEQAUDIT.h - lg 서비스 LG_SEQAUDIT 카피북 (copybook / record header). + * 전표 채번 감사: 당일 전표번호 min/max/건수로 채번 누락(gap)을 검출. + */ +#ifndef LG_SEQAUDIT_H +#define LG_SEQAUDIT_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_seqaudit_rec_t; + +void LG_SEQAUDIT(TPSVCINFO *p); + +#endif /* LG_SEQAUDIT_H */ diff --git a/app/src/lg/svc/LG_SEQAUDIT.pgc b/app/src/lg/svc/LG_SEQAUDIT.pgc new file mode 100644 index 0000000..636df1a --- /dev/null +++ b/app/src/lg/svc/LG_SEQAUDIT.pgc @@ -0,0 +1,30 @@ +/* + * LG_SEQAUDIT.pgc - lg 모듈 서비스 LG_SEQAUDIT (one service = one file). + * 전표 채번 감사: 당일 전표번호 min/max/건수로 채번 누락(gap)을 검출. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_SEQAUDIT.h" + +void LG_SEQAUDIT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long expected, gaps; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_min, h_max, h_cnt; + 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(min(voucher_id), 0), coalesce(max(voucher_id), 0), count(*) + INTO :h_min, :h_max, :h_cnt FROM lg_voucher WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + expected = (h_cnt > 0) ? (h_max - h_min + 1) : 0; + gaps = expected - h_cnt; + setl(b, T_COUNT, h_cnt); setl(b, T_ARG1, expected); setl(b, T_DELTA, gaps); + Bchg(b, T_STATUS, 0, (gaps == 0) ? "OK" : "GAP", 0L); + userlog("LG_SEQAUDIT %s 채번 %ld~%ld %ld건 갭 %ld", bizdate, h_min, h_max, h_cnt, gaps); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_SETTLEPAYGAP.h b/app/src/lg/svc/LG_SETTLEPAYGAP.h new file mode 100644 index 0000000..d683fad --- /dev/null +++ b/app/src/lg/svc/LG_SETTLEPAYGAP.h @@ -0,0 +1,21 @@ +/* + * LG_SETTLEPAYGAP.h - lg 서비스 LG_SETTLEPAYGAP 카피북 (copybook / record header). + * 정산-지급 차이: 정산 net 합계와 원장 지급(MERCH_PAY 대변) 합계의 갭을 산출. + */ +#ifndef LG_SETTLEPAYGAP_H +#define LG_SETTLEPAYGAP_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_settlepaygap_rec_t; + +void LG_SETTLEPAYGAP(TPSVCINFO *p); + +#endif /* LG_SETTLEPAYGAP_H */ diff --git a/app/src/lg/svc/LG_SETTLEPAYGAP.pgc b/app/src/lg/svc/LG_SETTLEPAYGAP.pgc new file mode 100644 index 0000000..08665ef --- /dev/null +++ b/app/src/lg/svc/LG_SETTLEPAYGAP.pgc @@ -0,0 +1,30 @@ +/* + * LG_SETTLEPAYGAP.pgc - lg 모듈 서비스 LG_SETTLEPAYGAP (one service = one file). + * 정산-지급 차이: 정산 net 합계와 원장 지급(MERCH_PAY 대변) 합계의 갭을 산출. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_SETTLEPAYGAP.h" + +void LG_SETTLEPAYGAP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_settle, h_pay; + 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_settle FROM settlement + WHERE biz_date = :h_bizdate AND status = 'SETTLED'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE -amount END), 0) INTO :h_pay + FROM lg_ledger_line WHERE biz_date = :h_bizdate AND acct = 'MERCH_PAY'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_AMT2, h_settle); setl(b, T_AMT3, h_pay); + setl(b, T_DELTA, h_settle - h_pay); + userlog("LG_SETTLEPAYGAP %s 정산 %ld 지급 %ld 갭 %ld", bizdate, h_settle, h_pay, h_settle - h_pay); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_SPANTB.h b/app/src/lg/svc/LG_SPANTB.h new file mode 100644 index 0000000..06afe4c --- /dev/null +++ b/app/src/lg/svc/LG_SPANTB.h @@ -0,0 +1,21 @@ +/* + * LG_SPANTB.h - lg 서비스 LG_SPANTB 카피북 (copybook / record header). + * 기간 시산표: 계정의 기간 차변/대변 합계와 차액을 산출해 시산표에 upsert. + */ +#ifndef LG_SPANTB_H +#define LG_SPANTB_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_spantb_rec_t; + +void LG_SPANTB(TPSVCINFO *p); + +#endif /* LG_SPANTB_H */ diff --git a/app/src/lg/svc/LG_SPANTB.pgc b/app/src/lg/svc/LG_SPANTB.pgc new file mode 100644 index 0000000..e987f38 --- /dev/null +++ b/app/src/lg/svc/LG_SPANTB.pgc @@ -0,0 +1,38 @@ +/* + * LG_SPANTB.pgc - lg 모듈 서비스 LG_SPANTB (one service = one file). + * 기간 시산표: 계정의 기간 차변/대변 합계와 차액을 산출해 시산표에 upsert. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_SPANTB.h" + +void LG_SPANTB(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char acct[32], fromd[16], endd[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_acct[32], h_from[16], h_end[16]; + long h_dr, h_cr; + EXEC SQL END DECLARE SECTION; + gets_(b, T_KEY1, acct, sizeof(acct)); if (acct[0] == 0) strcpy(acct, "RECV"); + gets_(b, T_BIZDATE, endd, sizeof(endd)); + gets_(b, T_STR1, fromd, sizeof(fromd)); if (fromd[0] == 0) strcpy(fromd, endd); + strncpy(h_acct, acct, sizeof(h_acct)-1); h_acct[sizeof(h_acct)-1] = 0; + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-1] = 0; + strncpy(h_end, endd, sizeof(h_end)-1); h_end[sizeof(h_end)-1] = 0; + EXEC SQL SELECT coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE 0 END), 0), + coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE 0 END), 0) + INTO :h_dr, :h_cr FROM lg_ledger_line + WHERE acct = :h_acct AND biz_date BETWEEN :h_from AND :h_end; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO lg_trial_bal (acct, biz_date, debit_total, credit_total, diff) + VALUES (:h_acct, :h_end, :h_dr, :h_cr, :h_dr - :h_cr) + ON CONFLICT (acct, biz_date) DO UPDATE + SET debit_total = EXCLUDED.debit_total, credit_total = EXCLUDED.credit_total, + diff = EXCLUDED.diff, updated_at = now(); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_AMT2, h_dr); setl(b, T_AMT3, h_cr); setl(b, T_DELTA, h_dr - h_cr); + userlog("LG_SPANTB %s %s~%s 차 %ld 대 %ld", acct, fromd, endd, h_dr, h_cr); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_THREEWAY.h b/app/src/lg/svc/LG_THREEWAY.h new file mode 100644 index 0000000..51f87b8 --- /dev/null +++ b/app/src/lg/svc/LG_THREEWAY.h @@ -0,0 +1,21 @@ +/* + * LG_THREEWAY.h - lg 서비스 LG_THREEWAY 카피북 (copybook / record header). + * 원장-정산-매입 3단 대사: 원장 지급대변, 정산 net, 매입 net 을 대조해 차이 산출. + */ +#ifndef LG_THREEWAY_H +#define LG_THREEWAY_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_threeway_rec_t; + +void LG_THREEWAY(TPSVCINFO *p); + +#endif /* LG_THREEWAY_H */ diff --git a/app/src/lg/svc/LG_THREEWAY.pgc b/app/src/lg/svc/LG_THREEWAY.pgc new file mode 100644 index 0000000..0b494d9 --- /dev/null +++ b/app/src/lg/svc/LG_THREEWAY.pgc @@ -0,0 +1,35 @@ +/* + * LG_THREEWAY.pgc - lg 모듈 서비스 LG_THREEWAY (one service = one file). + * 원장-정산-매입 3단 대사: 원장 지급대변, 정산 net, 매입 net 을 대조해 차이 산출. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_THREEWAY.h" + +void LG_THREEWAY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long g1, g2; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_ledger, h_settle, h_purch; + 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(CASE WHEN dc = 'C' AND acct = 'MERCH_PAY' THEN amount ELSE 0 END), 0) + INTO :h_ledger FROM lg_ledger_line WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(net), 0) INTO :h_settle FROM settlement WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(net), 0) INTO :h_purch FROM purchase + WHERE biz_date = :h_bizdate AND status IN ('S', 'M'); + if (sqlca.sqlcode < 0) FAIL(b); + g1 = h_ledger - h_settle; g2 = h_settle - h_purch; + setl(b, T_AMT2, h_ledger); setl(b, T_AMT3, h_settle); setl(b, T_AMT4, h_purch); + setl(b, T_DELTA, g1); setl(b, T_NET, g2); + Bchg(b, T_STATUS, 0, (g1 == 0 && g2 == 0) ? "MATCH" : "DIFF", 0L); + userlog("LG_THREEWAY %s 원장 %ld 정산 %ld 매입 %ld", bizdate, h_ledger, h_settle, h_purch); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_UNAPPROVE.h b/app/src/lg/svc/LG_UNAPPROVE.h new file mode 100644 index 0000000..f584388 --- /dev/null +++ b/app/src/lg/svc/LG_UNAPPROVE.h @@ -0,0 +1,21 @@ +/* + * LG_UNAPPROVE.h - lg 서비스 LG_UNAPPROVE 카피북 (copybook / record header). + * 승인 취소: 마감(S)되지 않은 전기(P) 전표를 작성(D) 상태로 되돌림. + */ +#ifndef LG_UNAPPROVE_H +#define LG_UNAPPROVE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_unapprove_rec_t; + +void LG_UNAPPROVE(TPSVCINFO *p); + +#endif /* LG_UNAPPROVE_H */ diff --git a/app/src/lg/svc/LG_UNAPPROVE.pgc b/app/src/lg/svc/LG_UNAPPROVE.pgc new file mode 100644 index 0000000..075938d --- /dev/null +++ b/app/src/lg/svc/LG_UNAPPROVE.pgc @@ -0,0 +1,24 @@ +/* + * LG_UNAPPROVE.pgc - lg 모듈 서비스 LG_UNAPPROVE (one service = one file). + * 승인 취소: 마감(S)되지 않은 전기(P) 전표를 작성(D) 상태로 되돌림. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_UNAPPROVE.h" + +void LG_UNAPPROVE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char st[4]; + long vid; + vid = getl(b, T_ID1); + if (lgdb_get_voucher_status(vid, st) < 0) { Bchg(b, T_MSG, 0, "전표 없음", 0L); FAIL(b); } + if (st[0] == 'S') { Bchg(b, T_MSG, 0, "마감연계 전표는 취소 불가", 0L); FAIL(b); } + if (st[0] != 'P') { Bchg(b, T_MSG, 0, "전기 전표만 승인취소 가능", 0L); FAIL(b); } + if (lgdb_update_voucher_status(vid, "D") < 0) FAIL(b); + setl(b, T_ID1, vid); + Bchg(b, T_STATUS, 0, "D", 0L); + userlog("LG_UNAPPROVE vid=%ld 승인취소 (P->D)", vid); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_UNEARNPOST.h b/app/src/lg/svc/LG_UNEARNPOST.h new file mode 100644 index 0000000..ec5ba09 --- /dev/null +++ b/app/src/lg/svc/LG_UNEARNPOST.h @@ -0,0 +1,21 @@ +/* + * LG_UNEARNPOST.h - lg 서비스 LG_UNEARNPOST 카피북 (copybook / record header). + * 선수수익 계상: 현금(D)/선수수익(C)으로 선수금 수령 시점 부채 계상. + */ +#ifndef LG_UNEARNPOST_H +#define LG_UNEARNPOST_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_unearnpost_rec_t; + +void LG_UNEARNPOST(TPSVCINFO *p); + +#endif /* LG_UNEARNPOST_H */ diff --git a/app/src/lg/svc/LG_UNEARNPOST.pgc b/app/src/lg/svc/LG_UNEARNPOST.pgc new file mode 100644 index 0000000..675344d --- /dev/null +++ b/app/src/lg/svc/LG_UNEARNPOST.pgc @@ -0,0 +1,27 @@ +/* + * LG_UNEARNPOST.pgc - lg 모듈 서비스 LG_UNEARNPOST (one service = one file). + * 선수수익 계상: 현금(D)/선수수익(C)으로 선수금 수령 시점 부채 계상. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_UNEARNPOST.h" + +void LG_UNEARNPOST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], merch[64]; + long vid, amount; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_MERCHANT, merch, sizeof(merch)); + amount = getl(b, T_AMOUNT); + if (amount <= 0) { Bchg(b, T_MSG, 0, "선수액 없음", 0L); FAIL(b); } + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", bizdate, "선수수익 계상") < 0) FAIL(b); + if (lgdb_insert_line(vid, "CASH", "D", amount, merch, bizdate, "posted") < 0 || + lgdb_insert_line(vid, "UNEARNED_REV", "C", amount, merch, bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); + userlog("LG_UNEARNPOST vid=%ld 선수 %ld", vid, amount); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_UNEARNRECOG.h b/app/src/lg/svc/LG_UNEARNRECOG.h new file mode 100644 index 0000000..bd26613 --- /dev/null +++ b/app/src/lg/svc/LG_UNEARNRECOG.h @@ -0,0 +1,21 @@ +/* + * LG_UNEARNRECOG.h - lg 서비스 LG_UNEARNRECOG 카피북 (copybook / record header). + * 선수수익 인식: 선수수익 총액을 기간(N)으로 나눠 1기분을 수익 인식. + */ +#ifndef LG_UNEARNRECOG_H +#define LG_UNEARNRECOG_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_unearnrecog_rec_t; + +void LG_UNEARNRECOG(TPSVCINFO *p); + +#endif /* LG_UNEARNRECOG_H */ diff --git a/app/src/lg/svc/LG_UNEARNRECOG.pgc b/app/src/lg/svc/LG_UNEARNRECOG.pgc new file mode 100644 index 0000000..f38ce7e --- /dev/null +++ b/app/src/lg/svc/LG_UNEARNRECOG.pgc @@ -0,0 +1,28 @@ +/* + * LG_UNEARNRECOG.pgc - lg 모듈 서비스 LG_UNEARNRECOG (one service = one file). + * 선수수익 인식: 선수수익 총액을 기간(N)으로 나눠 1기분을 수익 인식. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_UNEARNRECOG.h" + +void LG_UNEARNRECOG(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long vid, total, periods, per; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + total = getl(b, T_AMOUNT); + periods = getl(b, T_COUNT); if (periods <= 0) periods = 1; + per = total / periods; + if (per <= 0) { Bchg(b, T_MSG, 0, "인식액 없음", 0L); FAIL(b); } + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", bizdate, "선수수익 인식") < 0) FAIL(b); + if (lgdb_insert_line(vid, "UNEARNED_REV", "D", per, "", bizdate, "posted") < 0 || + lgdb_insert_line(vid, "FEE_INC", "C", per, "", bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); setl(b, T_NET, per); + userlog("LG_UNEARNRECOG vid=%ld 총 %ld / %ld기 = %ld", vid, total, periods, per); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_WEEKCLOSE.h b/app/src/lg/svc/LG_WEEKCLOSE.h new file mode 100644 index 0000000..0082a9a --- /dev/null +++ b/app/src/lg/svc/LG_WEEKCLOSE.h @@ -0,0 +1,21 @@ +/* + * LG_WEEKCLOSE.h - lg 서비스 LG_WEEKCLOSE 카피북 (copybook / record header). + * 주차 결산: 지정 주(기간) 내 작성(D) 전표를 일괄 전기(P)로 승격. + */ +#ifndef LG_WEEKCLOSE_H +#define LG_WEEKCLOSE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_weekclose_rec_t; + +void LG_WEEKCLOSE(TPSVCINFO *p); + +#endif /* LG_WEEKCLOSE_H */ diff --git a/app/src/lg/svc/LG_WEEKCLOSE.pgc b/app/src/lg/svc/LG_WEEKCLOSE.pgc new file mode 100644 index 0000000..c98e928 --- /dev/null +++ b/app/src/lg/svc/LG_WEEKCLOSE.pgc @@ -0,0 +1,28 @@ +/* + * LG_WEEKCLOSE.pgc - lg 모듈 서비스 LG_WEEKCLOSE (one service = one file). + * 주차 결산: 지정 주(기간) 내 작성(D) 전표를 일괄 전기(P)로 승격. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_WEEKCLOSE.h" + +void LG_WEEKCLOSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char fromd[16], endd[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_from[16], h_end[16]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, endd, sizeof(endd)); + gets_(b, T_STR1, fromd, sizeof(fromd)); if (fromd[0] == 0) strcpy(fromd, endd); + strncpy(h_from, fromd, sizeof(h_from)-1); h_from[sizeof(h_from)-1] = 0; + strncpy(h_end, endd, sizeof(h_end)-1); h_end[sizeof(h_end)-1] = 0; + EXEC SQL UPDATE lg_voucher SET status = 'P', updated_at = now() + WHERE status = 'D' AND biz_date BETWEEN :h_from AND :h_end; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, sqlca.sqlerrd[2]); + Bchg(b, T_STATUS, 0, "P", 0L); + userlog("LG_WEEKCLOSE %s~%s 주차마감 전기 %ld건", fromd, endd, (long)sqlca.sqlerrd[2]); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_WRITEOFF.h b/app/src/lg/svc/LG_WRITEOFF.h new file mode 100644 index 0000000..7b14cae --- /dev/null +++ b/app/src/lg/svc/LG_WRITEOFF.h @@ -0,0 +1,21 @@ +/* + * LG_WRITEOFF.h - lg 서비스 LG_WRITEOFF 카피북 (copybook / record header). + * 대손 상각: 회수불능 채권을 대손충당금(D)/미수금(C)으로 제각. + */ +#ifndef LG_WRITEOFF_H +#define LG_WRITEOFF_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_writeoff_rec_t; + +void LG_WRITEOFF(TPSVCINFO *p); + +#endif /* LG_WRITEOFF_H */ diff --git a/app/src/lg/svc/LG_WRITEOFF.pgc b/app/src/lg/svc/LG_WRITEOFF.pgc new file mode 100644 index 0000000..bdd2d82 --- /dev/null +++ b/app/src/lg/svc/LG_WRITEOFF.pgc @@ -0,0 +1,27 @@ +/* + * LG_WRITEOFF.pgc - lg 모듈 서비스 LG_WRITEOFF (one service = one file). + * 대손 상각: 회수불능 채권을 대손충당금(D)/미수금(C)으로 제각. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_WRITEOFF.h" + +void LG_WRITEOFF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], merch[64]; + long vid, amount; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_MERCHANT, merch, sizeof(merch)); + amount = getl(b, T_AMOUNT); + if (amount <= 0) { Bchg(b, T_MSG, 0, "상각액 없음", 0L); FAIL(b); } + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", bizdate, "대손 상각") < 0) FAIL(b); + if (lgdb_insert_line(vid, "BADDEBT_ALLOW", "D", amount, merch, bizdate, "posted") < 0 || + lgdb_insert_line(vid, "RECV", "C", amount, merch, bizdate, "posted") < 0) FAIL(b); + setl(b, T_ID1, vid); + userlog("LG_WRITEOFF vid=%ld 제각 %ld", vid, amount); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_XFERPOST.h b/app/src/lg/svc/LG_XFERPOST.h new file mode 100644 index 0000000..fe16d1c --- /dev/null +++ b/app/src/lg/svc/LG_XFERPOST.h @@ -0,0 +1,21 @@ +/* + * LG_XFERPOST.h - lg 서비스 LG_XFERPOST 카피북 (copybook / record header). + * 대체 전기: 두 계정 간 대체(D 대상/C 원천) 전기 후 양 계정 잔액을 재계산. + */ +#ifndef LG_XFERPOST_H +#define LG_XFERPOST_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_xferpost_rec_t; + +void LG_XFERPOST(TPSVCINFO *p); + +#endif /* LG_XFERPOST_H */ diff --git a/app/src/lg/svc/LG_XFERPOST.pgc b/app/src/lg/svc/LG_XFERPOST.pgc new file mode 100644 index 0000000..56ba5bd --- /dev/null +++ b/app/src/lg/svc/LG_XFERPOST.pgc @@ -0,0 +1,29 @@ +/* + * LG_XFERPOST.pgc - lg 모듈 서비스 LG_XFERPOST (one service = one file). + * 대체 전기: 두 계정 간 대체(D 대상/C 원천) 전기 후 양 계정 잔액을 재계산. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_XFERPOST.h" + +void LG_XFERPOST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char from_acct[32], to_acct[32], bizdate[16]; + long vid, amount; + gets_(b, T_KEY1, from_acct, sizeof(from_acct)); if (from_acct[0] == 0) strcpy(from_acct, "SUSPENSE"); + gets_(b, T_KEY2, to_acct, sizeof(to_acct)); if (to_acct[0] == 0) strcpy(to_acct, "RECV"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + amount = getl(b, T_AMOUNT); + vid = lgdb_next_voucher_id(); + if (vid < 0) FAIL(b); + if (lgdb_insert_voucher(vid, "TRANSFER", 0, "P", bizdate, "대체 전기") < 0) FAIL(b); + if (lgdb_insert_line(vid, to_acct, "D", amount, "", bizdate, "posted") < 0 || + lgdb_insert_line(vid, from_acct, "C", amount, "", bizdate, "posted") < 0) FAIL(b); + if (lgdb_recalc_acct_bal(to_acct, bizdate) < 0) FAIL(b); + if (lgdb_recalc_acct_bal(from_acct, bizdate) < 0) FAIL(b); + setl(b, T_ID1, vid); + userlog("LG_XFERPOST %s -> %s %ld vid=%ld", from_acct, to_acct, amount, vid); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_YEARCARRY.h b/app/src/lg/svc/LG_YEARCARRY.h new file mode 100644 index 0000000..65227c7 --- /dev/null +++ b/app/src/lg/svc/LG_YEARCARRY.h @@ -0,0 +1,21 @@ +/* + * LG_YEARCARRY.h - lg 서비스 LG_YEARCARRY 카피북 (copybook / record header). + * 연말 이월: 마감일까지 계정별 누계잔액을 익년 개시일 계정잔액으로 이월(upsert). + */ +#ifndef LG_YEARCARRY_H +#define LG_YEARCARRY_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_yearcarry_rec_t; + +void LG_YEARCARRY(TPSVCINFO *p); + +#endif /* LG_YEARCARRY_H */ diff --git a/app/src/lg/svc/LG_YEARCARRY.pgc b/app/src/lg/svc/LG_YEARCARRY.pgc new file mode 100644 index 0000000..7f2c7a4 --- /dev/null +++ b/app/src/lg/svc/LG_YEARCARRY.pgc @@ -0,0 +1,35 @@ +/* + * LG_YEARCARRY.pgc - lg 모듈 서비스 LG_YEARCARRY (one service = one file). + * 연말 이월: 마감일까지 계정별 누계잔액을 익년 개시일 계정잔액으로 이월(upsert). + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_YEARCARRY.h" + +void LG_YEARCARRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char endd[16], nextd[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_end[16], h_next[16]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, endd, sizeof(endd)); + gets_(b, T_STR1, nextd, sizeof(nextd)); if (nextd[0] == 0) strcpy(nextd, endd); + strncpy(h_end, endd, sizeof(h_end)-1); h_end[sizeof(h_end)-1] = 0; + strncpy(h_next, nextd, sizeof(h_next)-1); h_next[sizeof(h_next)-1] = 0; + EXEC SQL INSERT INTO lg_account_bal (acct, biz_date, debit_total, credit_total, balance) + SELECT acct, :h_next, + coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE 0 END), 0), + coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE 0 END), 0), + coalesce(sum(CASE WHEN dc = 'D' THEN amount ELSE -amount END), 0) + FROM lg_ledger_line WHERE biz_date <= :h_end + GROUP BY acct + ON CONFLICT (acct, biz_date) DO UPDATE + SET debit_total = EXCLUDED.debit_total, credit_total = EXCLUDED.credit_total, + balance = EXCLUDED.balance, updated_at = now(); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, sqlca.sqlerrd[2]); + userlog("LG_YEARCARRY %s -> %s 계정 %ld개 이월", endd, nextd, (long)sqlca.sqlerrd[2]); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/lg/svc/LG_YEARCLOSE.h b/app/src/lg/svc/LG_YEARCLOSE.h new file mode 100644 index 0000000..36040d9 --- /dev/null +++ b/app/src/lg/svc/LG_YEARCLOSE.h @@ -0,0 +1,21 @@ +/* + * LG_YEARCLOSE.h - lg 서비스 LG_YEARCLOSE 카피북 (copybook / record header). + * 연말 결산 대체: 손익계정(_INC/_EXP) 누계 잔액을 계정별로 RETAINED 로 대체. + */ +#ifndef LG_YEARCLOSE_H +#define LG_YEARCLOSE_H + +#include "lg_svc.h" + +typedef struct { + long voucher_id; + long debit_total; + long credit_total; + long net_balance; + char acct[32]; + char biz_date[16]; +} lg_lg_yearclose_rec_t; + +void LG_YEARCLOSE(TPSVCINFO *p); + +#endif /* LG_YEARCLOSE_H */ diff --git a/app/src/lg/svc/LG_YEARCLOSE.pgc b/app/src/lg/svc/LG_YEARCLOSE.pgc new file mode 100644 index 0000000..239c84b --- /dev/null +++ b/app/src/lg/svc/LG_YEARCLOSE.pgc @@ -0,0 +1,52 @@ +/* + * LG_YEARCLOSE.pgc - lg 모듈 서비스 LG_YEARCLOSE (one service = one file). + * 연말 결산 대체: 손익계정(_INC/_EXP) 누계 잔액을 계정별로 RETAINED 로 대체. + * 호출자의 XA 브랜치에서 수행(EXEC SQL CONNECT 없음); lg_svr 가 광고한다. + */ +#include "LG_YEARCLOSE.h" + +void LG_YEARCLOSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char endd[16]; + long vid, closed = 0, net_profit = 0, net, mag; + EXEC SQL BEGIN DECLARE SECTION; + char h_end[16], h_acct[32]; + long h_net; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, endd, sizeof(endd)); + strncpy(h_end, endd, sizeof(h_end)-1); h_end[sizeof(h_end)-1] = 0; + EXEC SQL DECLARE yc_cur CURSOR FOR + SELECT acct, coalesce(sum(CASE WHEN dc = 'C' THEN amount ELSE -amount END), 0) + FROM lg_ledger_line + WHERE biz_date <= :h_end AND (acct LIKE '%_INC' OR acct LIKE '%_EXP') + GROUP BY acct ORDER BY acct; + EXEC SQL OPEN yc_cur; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH yc_cur INTO :h_acct, :h_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE yc_cur; FAIL(b); } + net = h_net; + if (net == 0) continue; + mag = (net > 0) ? net : -net; + vid = lgdb_next_voucher_id(); + if (vid < 0) { EXEC SQL CLOSE yc_cur; FAIL(b); } + if (lgdb_insert_voucher(vid, "ADJ", 0, "P", endd, "연말 결산 대체") < 0) { EXEC SQL CLOSE yc_cur; FAIL(b); } + if (net > 0) { + if (lgdb_insert_line(vid, h_acct, "D", mag, "", endd, "posted") < 0 || + lgdb_insert_line(vid, "RETAINED", "C", mag, "", endd, "posted") < 0) { EXEC SQL CLOSE yc_cur; FAIL(b); } + } else { + if (lgdb_insert_line(vid, "RETAINED", "D", mag, "", endd, "posted") < 0 || + lgdb_insert_line(vid, h_acct, "C", mag, "", endd, "posted") < 0) { EXEC SQL CLOSE yc_cur; FAIL(b); } + } + net_profit += net; + closed++; + } + EXEC SQL CLOSE yc_cur; + setl(b, T_COUNT, closed); setl(b, T_NET, net_profit); + userlog("LG_YEARCLOSE %s 손익계정 %ld개 순이익 %ld", endd, closed, net_profit); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/mg/batch/mg_chanfail_batch.pgc b/app/src/mg/batch/mg_chanfail_batch.pgc new file mode 100644 index 0000000..27a9f67 --- /dev/null +++ b/app/src/mg/batch/mg_chanfail_batch.pgc @@ -0,0 +1,52 @@ +/* + * mg_chanfail_batch.pgc - mg 이중화 채널 다운 탐지 배치 (커서 + XA). + * + * 등록된 전 채널의 상태를 커서로 훑어 UP/DOWN 개수를 세고, DOWN 채널이 하나라도 + * 있으면 경보 로그를 남긴다 (채널 상태 모니터/이중화 감시의 일배치판). + * Usage: mg_chanfail_batch + */ +#include +#include +#include +#include +#include +#include "mg_chanfail_batch.h" + +int main(int argc, char **argv) +{ + (void)argc; (void)argv; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16], h_status[16]; + EXEC SQL END DECLARE SECTION; + mg_chanfail_rec_t rec; + memset(&rec, 0, sizeof(rec)); + + 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 cfc CURSOR FOR + SELECT channel, status FROM mg_channel ORDER BY channel; + EXEC SQL OPEN cfc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH cfc INTO :h_chan, :h_status; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cfc; tpabort(0); return 1; } + rec.channels++; + if (strcmp(h_status, "UP") == 0) { + rec.up_cnt++; + } else { + rec.down_cnt++; + userlog("mg_chanfail_batch 채널 DOWN 감지: %s (%s)", h_chan, h_status); + } + } + EXEC SQL CLOSE cfc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mg_chanfail_batch COMMIT: 채널수=%ld UP=%ld DOWN=%ld\n", + rec.channels, rec.up_cnt, rec.down_cnt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mg/batch/mg_dupmark_batch.pgc b/app/src/mg/batch/mg_dupmark_batch.pgc new file mode 100644 index 0000000..eb5146f --- /dev/null +++ b/app/src/mg/batch/mg_dupmark_batch.pgc @@ -0,0 +1,58 @@ +/* + * mg_dupmark_batch.pgc - mg 중복 STAN 전문 마킹 배치 (커서 + XA). + * + * 당일 STAN 별 건수를 group by 커서로 훑어 2건 이상인 STAN 마다, 최소 msg_id(원본)만 + * 남기고 나머지를 rc=94/status=X 로 폐기 마킹한다 (중복검출의 대량 마킹 일배치판). + * Usage: mg_dupmark_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "mg_dupmark_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_stan, h_cnt, h_keep; + EXEC SQL END DECLARE SECTION; + mg_dupmark_rec_t rec; + memset(&rec, 0, sizeof(rec)); + + 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 dmc CURSOR FOR + SELECT stan, count(*) FROM mg_msg_log WHERE biz_date = :h_bizdate + GROUP BY stan HAVING count(*) > 1 ORDER BY stan; + EXEC SQL OPEN dmc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH dmc INTO :h_stan, :h_cnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE dmc; tpabort(0); return 1; } + rec.dup_stans++; + + EXEC SQL SELECT min(msg_id) INTO :h_keep + FROM mg_msg_log WHERE biz_date = :h_bizdate AND stan = :h_stan; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE dmc; tpabort(0); return 1; } + + EXEC SQL UPDATE mg_msg_log SET rc = '94', status = 'X' + WHERE biz_date = :h_bizdate AND stan = :h_stan AND msg_id <> :h_keep; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE dmc; tpabort(0); return 1; } + rec.marked += (long)sqlca.sqlerrd[2]; + } + EXEC SQL CLOSE dmc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mg_dupmark_batch COMMIT: bizdate=%s 중복STAN=%ld 폐기마킹=%ld건\n", + bizdate, rec.dup_stans, rec.marked); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mg/batch/mg_errescal_batch.pgc b/app/src/mg/batch/mg_errescal_batch.pgc new file mode 100644 index 0000000..63be96c --- /dev/null +++ b/app/src/mg/batch/mg_errescal_batch.pgc @@ -0,0 +1,51 @@ +/* + * mg_errescal_batch.pgc - mg 재시도초과 오류전문 에스컬레이션 배치 (XA 단일 갱신). + * + * mg_seq(ESCALLIM) 재시도 임계를 읽어, 그 횟수를 넘긴 실패(F) 큐 건을 한 문장에 + * 에스컬레이션(E)으로 전이시킨다 (오류전문 재처리 워크플로의 일배치 대량판). + * Usage: mg_errescal_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "mg_errescal_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_lim, h_scanned; + EXEC SQL END DECLARE SECTION; + mg_errescal_rec_t rec; + memset(&rec, 0, sizeof(rec)); + + 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; } + + h_lim = 5; + EXEC SQL SELECT seq_val INTO :h_lim FROM mg_seq WHERE seq_name = 'ESCALLIM'; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ESCALLIM 조회 FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + if (h_lim <= 0) h_lim = 5; + + EXEC SQL SELECT count(*) INTO :h_scanned + FROM mg_queue WHERE status = 'F' AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + rec.scanned = h_scanned; + + EXEC SQL UPDATE mg_queue SET status = 'E', updated_at = now() + WHERE status = 'F' AND biz_date = :h_bizdate AND retry_cnt > :h_lim; + if (sqlca.sqlcode < 0) { fprintf(stderr, "에스컬 전이 FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + rec.escalated = (long)sqlca.sqlerrd[2]; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mg_errescal_batch COMMIT: bizdate=%s 실패건=%ld 에스컬=%ld건 (임계=%ld)\n", + bizdate, rec.scanned, rec.escalated, h_lim); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mg/batch/mg_healthz_batch.pgc b/app/src/mg/batch/mg_healthz_batch.pgc new file mode 100644 index 0000000..3ef77b1 --- /dev/null +++ b/app/src/mg/batch/mg_healthz_batch.pgc @@ -0,0 +1,52 @@ +/* + * mg_healthz_batch.pgc - mg 게이트웨이 종합 헬스 스냅 배치 (다중 집계 + XA). + * + * UP 채널 수, 대기(Q) 큐 적체, 당일 오류(rc<>'00') 전문 수를 각각 집계해 한 스냅으로 + * 모아, UP 채널이 있고 적체가 임계 미만이면 healthy=1 로 판정한다 (헬스체크 세분). + * Usage: mg_healthz_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "mg_healthz_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + const long backlog_limit = 1000; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_up, h_backlog, h_err; + EXEC SQL END DECLARE SECTION; + mg_healthz_rec_t rec; + memset(&rec, 0, sizeof(rec)); + + 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_up FROM mg_channel WHERE status = 'UP'; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + EXEC SQL SELECT count(*) INTO :h_backlog + FROM mg_queue WHERE status = 'Q' AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + EXEC SQL SELECT count(*) INTO :h_err + FROM mg_msg_log WHERE biz_date = :h_bizdate AND rc <> '00'; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + rec.chan_up = h_up; + rec.queue_backlog = h_backlog; + rec.err_today = h_err; + rec.healthy = (h_up > 0 && h_backlog < backlog_limit) ? 1 : 0; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mg_healthz_batch COMMIT: bizdate=%s UP채널=%ld 큐적체=%ld 오류전문=%ld 건강=%s\n", + bizdate, rec.chan_up, rec.queue_backlog, rec.err_today, + rec.healthy ? "YES" : "NO"); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mg/batch/mg_loadrpt_batch.pgc b/app/src/mg/batch/mg_loadrpt_batch.pgc new file mode 100644 index 0000000..00c7c7f --- /dev/null +++ b/app/src/mg/batch/mg_loadrpt_batch.pgc @@ -0,0 +1,59 @@ +/* + * mg_loadrpt_batch.pgc - mg 채널 부하 가중 리포트 배치 (커서 + XA). + * + * 채널별 당일 전문건수와 큐 재시도합을 결합한 부하지표(건수 + 재시도*2)를 커서로 + * 훑으며 총 부하와 최대 부하 채널을 집계한다 (트래픽 부하분산 근거용 일배치). + * Usage: mg_loadrpt_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "mg_loadrpt_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long load; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_chan[16]; + long h_msgcnt, h_retry; + EXEC SQL END DECLARE SECTION; + mg_loadrpt_rec_t rec; + memset(&rec, 0, sizeof(rec)); + + 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 lrc CURSOR FOR + SELECT channel, count(*) FROM mg_msg_log + WHERE biz_date = :h_bizdate GROUP BY channel ORDER BY channel; + EXEC SQL OPEN lrc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH lrc INTO :h_chan, :h_msgcnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE lrc; tpabort(0); return 1; } + rec.channels++; + + h_retry = 0; + EXEC SQL SELECT coalesce(sum(retry_cnt),0) INTO :h_retry + FROM mg_queue WHERE biz_date = :h_bizdate AND channel = :h_chan; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE lrc; tpabort(0); return 1; } + + load = h_msgcnt + h_retry * 2; /* 재시도에 가중 2배 */ + rec.load_sum += load; + if (load > rec.busiest_load) rec.busiest_load = load; + } + EXEC SQL CLOSE lrc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mg_loadrpt_batch COMMIT: bizdate=%s 채널수=%ld 총부하=%ld 최대부하=%ld\n", + bizdate, rec.channels, rec.load_sum, rec.busiest_load); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mg/batch/mg_mtiroll_batch.pgc b/app/src/mg/batch/mg_mtiroll_batch.pgc new file mode 100644 index 0000000..3c34e7b --- /dev/null +++ b/app/src/mg/batch/mg_mtiroll_batch.pgc @@ -0,0 +1,53 @@ +/* + * mg_mtiroll_batch.pgc - mg 전문유형(MTI)별 금액/건수 롤업 배치 (커서 + XA). + * + * 당일 전문을 MTI 로 그룹핑해 유형별 건수/금액합을 커서로 훑으며 총계를 누적하고, + * 금액합이 가장 큰 유형을 찾아낸다 (전문 통계 유형별 세분의 일배치판). + * Usage: mg_mtiroll_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "mg_mtiroll_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_mti[8]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + mg_mtiroll_rec_t rec; + memset(&rec, 0, sizeof(rec)); + + 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 mrc CURSOR FOR + SELECT mti, count(*), coalesce(sum(amount),0) + FROM mg_msg_log WHERE biz_date = :h_bizdate + GROUP BY mti ORDER BY mti; + EXEC SQL OPEN mrc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH mrc INTO :h_mti, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE mrc; tpabort(0); return 1; } + rec.groups++; + rec.total_cnt += h_cnt; + rec.total_amt += h_amt; + if (h_amt > rec.top_amt) rec.top_amt = h_amt; + } + EXEC SQL CLOSE mrc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mg_mtiroll_batch COMMIT: bizdate=%s 유형수=%ld 총건=%ld 총금액=%ld 최대유형금액=%ld\n", + bizdate, rec.groups, rec.total_cnt, rec.total_amt, rec.top_amt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mg/batch/mg_peakhour_batch.pgc b/app/src/mg/batch/mg_peakhour_batch.pgc new file mode 100644 index 0000000..f7e96a2 --- /dev/null +++ b/app/src/mg/batch/mg_peakhour_batch.pgc @@ -0,0 +1,52 @@ +/* + * mg_peakhour_batch.pgc - mg 시간대별 피크 트래픽 탐지 배치 (커서 + XA). + * + * 당일 전문을 생성시각의 시(0~23)로 그룹핑해 시간대별 건수를 커서로 훑으며 + * 최다 건수 시간대를 찾는다 (시간대별 통계에서 피크만 뽑는 일배치판). + * Usage: mg_peakhour_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "mg_peakhour_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_hour, h_cnt; + EXEC SQL END DECLARE SECTION; + mg_peakhour_rec_t rec; + memset(&rec, 0, sizeof(rec)); + rec.peak_hour = -1; + + 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 phc CURSOR FOR + SELECT extract(hour from created_at)::int AS hh, count(*) + FROM mg_msg_log WHERE biz_date = :h_bizdate + GROUP BY hh ORDER BY hh; + EXEC SQL OPEN phc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH phc INTO :h_hour, :h_cnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE phc; tpabort(0); return 1; } + rec.buckets++; + if (h_cnt > rec.peak_cnt) { rec.peak_cnt = h_cnt; rec.peak_hour = h_hour; } + } + EXEC SQL CLOSE phc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mg_peakhour_batch COMMIT: bizdate=%s 활동시간대=%ld 피크시=%ld시 피크건수=%ld\n", + bizdate, rec.buckets, rec.peak_hour, rec.peak_cnt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mg/batch/mg_rcfreq_batch.pgc b/app/src/mg/batch/mg_rcfreq_batch.pgc new file mode 100644 index 0000000..5244fd3 --- /dev/null +++ b/app/src/mg/batch/mg_rcfreq_batch.pgc @@ -0,0 +1,52 @@ +/* + * mg_rcfreq_batch.pgc - mg 응답코드별 빈도/오류율 집계 배치 (커서 + XA). + * + * 당일 전문을 응답코드(rc)로 그룹핑해 코드 종류 수와 전체 건수를 커서로 누적하고, + * 정상('00') 외 건수로 오류율(bps)을 산출한다 (응답코드별 통계의 일배치판). + * Usage: mg_rcfreq_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "mg_rcfreq_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_rc[4]; + long h_cnt; + EXEC SQL END DECLARE SECTION; + mg_rcfreq_rec_t rec; + memset(&rec, 0, sizeof(rec)); + + 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 rfc CURSOR FOR + SELECT rc, count(*) FROM mg_msg_log WHERE biz_date = :h_bizdate + GROUP BY rc ORDER BY rc; + EXEC SQL OPEN rfc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH rfc INTO :h_rc, :h_cnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE rfc; tpabort(0); return 1; } + rec.rc_kinds++; + rec.total += h_cnt; + if (strcmp(h_rc, "00") != 0) rec.err += h_cnt; + } + EXEC SQL CLOSE rfc; + rec.err_bps = (rec.total > 0) ? (rec.err * 10000L) / rec.total : 0; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mg_rcfreq_batch COMMIT: bizdate=%s 코드종류=%ld 총건=%ld 오류건=%ld 오류율=%ldbps\n", + bizdate, rec.rc_kinds, rec.total, rec.err, rec.err_bps); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mg/batch/mg_routechk_batch.pgc b/app/src/mg/batch/mg_routechk_batch.pgc new file mode 100644 index 0000000..46082c8 --- /dev/null +++ b/app/src/mg/batch/mg_routechk_batch.pgc @@ -0,0 +1,51 @@ +/* + * mg_routechk_batch.pgc - mg 라우팅 룰 감사 배치 (커서 + XA). + * + * 전 라우팅 룰을 채널+MTI 순으로 커서 훑으며 비활성 룰 수를 세고, 직전 키와 같은 + * (채널,MTI) 활성 룰이 연달아 나오면 중복 라우팅으로 집계한다 (라우팅 룰 관리 감사). + * Usage: mg_routechk_batch + */ +#include +#include +#include +#include +#include "mg_routechk_batch.h" + +int main(int argc, char **argv) +{ + (void)argc; (void)argv; + char prevkey[32] = "", curkey[32]; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16], h_mti[8]; + int h_active; + EXEC SQL END DECLARE SECTION; + mg_routechk_rec_t rec; + memset(&rec, 0, sizeof(rec)); + + 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 rkc CURSOR FOR + SELECT channel, mti, active FROM mg_route ORDER BY channel, mti, route_id; + EXEC SQL OPEN rkc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH rkc INTO :h_chan, :h_mti, :h_active; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE rkc; tpabort(0); return 1; } + rec.rules++; + if (!h_active) { rec.inactive++; continue; } + snprintf(curkey, sizeof(curkey), "%s|%s", h_chan, h_mti); + if (strcmp(curkey, prevkey) == 0) rec.dup_keys++; /* 같은 키 활성 룰 중복 */ + strcpy(prevkey, curkey); + } + EXEC SQL CLOSE rkc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mg_routechk_batch COMMIT: 룰수=%ld 비활성=%ld 중복키=%ld\n", + rec.rules, rec.inactive, rec.dup_keys); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mg/batch/mg_seqrepair_batch.pgc b/app/src/mg/batch/mg_seqrepair_batch.pgc new file mode 100644 index 0000000..9a22c3a --- /dev/null +++ b/app/src/mg/batch/mg_seqrepair_batch.pgc @@ -0,0 +1,69 @@ +/* + * mg_seqrepair_batch.pgc - mg 채널별 순번 결번 보정 통계 배치 (중첩 커서 + XA). + * + * 채널을 바깥 커서로, 각 채널의 STAN 을 안쪽 커서로 오름차순 훑으며 채널마다 + * 결번(간격>1) 유무와 최대 결번폭을 잰다 (순번 갭 검출을 채널단위로 세분한 일배치). + * Usage: mg_seqrepair_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "mg_seqrepair_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long prev, chan_gaps; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_chan[16]; + long h_stan; + EXEC SQL END DECLARE SECTION; + mg_seqrepair_rec_t rec; + memset(&rec, 0, sizeof(rec)); + + 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 srchan CURSOR FOR + SELECT DISTINCT channel FROM mg_msg_log WHERE biz_date = :h_bizdate ORDER BY channel; + EXEC SQL DECLARE srstan CURSOR FOR + SELECT DISTINCT stan FROM mg_msg_log + WHERE biz_date = :h_bizdate AND channel = :h_chan ORDER BY stan; + + EXEC SQL OPEN srchan; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH srchan INTO :h_chan; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE srchan; tpabort(0); return 1; } + rec.channels++; + prev = -1; chan_gaps = 0; + + EXEC SQL OPEN srstan; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE srchan; tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH srstan INTO :h_stan; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE srstan; EXEC SQL CLOSE srchan; tpabort(0); return 1; } + if (prev >= 0 && h_stan - prev > 1) { + chan_gaps++; + if (h_stan - prev - 1 > rec.widest) rec.widest = h_stan - prev - 1; + } + prev = h_stan; + } + EXEC SQL CLOSE srstan; + if (chan_gaps > 0) rec.gap_chans++; + } + EXEC SQL CLOSE srchan; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mg_seqrepair_batch COMMIT: bizdate=%s 채널수=%ld 결번채널=%ld 최대결번폭=%ld\n", + bizdate, rec.channels, rec.gap_chans, rec.widest); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mg/batch/mg_throttle_batch.pgc b/app/src/mg/batch/mg_throttle_batch.pgc new file mode 100644 index 0000000..e055e9d --- /dev/null +++ b/app/src/mg/batch/mg_throttle_batch.pgc @@ -0,0 +1,61 @@ +/* + * mg_throttle_batch.pgc - mg 채널 유입 임계 초과 탐지 배치 (커서 + XA). + * + * mg_seq(THROTLIM) 채널 유입한도를 읽어, 채널별 당일 수신(IN) 건수를 커서로 훑으며 + * 한도 초과 채널 수와 최다 유입 채널의 건수를 집계한다 (트래픽 제어의 일배치판). + * Usage: mg_throttle_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include +#include "mg_throttle_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_chan[16]; + long h_cnt, h_lim; + EXEC SQL END DECLARE SECTION; + mg_throttle_rec_t rec; + memset(&rec, 0, sizeof(rec)); + + 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; } + + h_lim = 10000; + EXEC SQL SELECT seq_val INTO :h_lim FROM mg_seq WHERE seq_name = 'THROTLIM'; + if (sqlca.sqlcode < 0) { fprintf(stderr, "THROTLIM 조회 FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + if (h_lim <= 0) h_lim = 10000; + + EXEC SQL DECLARE thc CURSOR FOR + SELECT channel, count(*) FROM mg_msg_log + WHERE biz_date = :h_bizdate AND direction = 'IN' + GROUP BY channel ORDER BY channel; + EXEC SQL OPEN thc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH thc INTO :h_chan, :h_cnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE thc; tpabort(0); return 1; } + rec.channels++; + if (h_cnt > rec.busiest) rec.busiest = h_cnt; + if (h_cnt >= h_lim) { + rec.over_cnt++; + userlog("mg_throttle_batch 유입초과 채널=%s %ld/%ld", h_chan, h_cnt, h_lim); + } + } + EXEC SQL CLOSE thc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mg_throttle_batch COMMIT: bizdate=%s 채널수=%ld 초과채널=%ld 최다유입=%ld (한도=%ld)\n", + bizdate, rec.channels, rec.over_cnt, rec.busiest, h_lim); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mg/dbio/mg_chanfail_batch.h b/app/src/mg/dbio/mg_chanfail_batch.h new file mode 100644 index 0000000..b9bb920 --- /dev/null +++ b/app/src/mg/dbio/mg_chanfail_batch.h @@ -0,0 +1,10 @@ +/* + * mg_chanfail_batch.h - mg mg_chanfail_batch 배치 전용 카피북 (copybook). 이중화 채널 다운 탐지 배치 + * 배치 집계 결과 레코드. Included by mg_chanfail_batch.pgc. + */ +#ifndef MG_CHANFAIL_BATCH_H +#define MG_CHANFAIL_BATCH_H + +typedef struct { long channels; long down_cnt; long up_cnt; } mg_chanfail_rec_t; + +#endif diff --git a/app/src/mg/dbio/mg_dupmark_batch.h b/app/src/mg/dbio/mg_dupmark_batch.h new file mode 100644 index 0000000..300124a --- /dev/null +++ b/app/src/mg/dbio/mg_dupmark_batch.h @@ -0,0 +1,10 @@ +/* + * mg_dupmark_batch.h - mg mg_dupmark_batch 배치 전용 카피북 (copybook). 중복 STAN 전문 마킹 배치 + * 배치 집계 결과 레코드. Included by mg_dupmark_batch.pgc. + */ +#ifndef MG_DUPMARK_BATCH_H +#define MG_DUPMARK_BATCH_H + +typedef struct { long dup_stans; long marked; } mg_dupmark_rec_t; + +#endif diff --git a/app/src/mg/dbio/mg_errescal_batch.h b/app/src/mg/dbio/mg_errescal_batch.h new file mode 100644 index 0000000..5bd8e27 --- /dev/null +++ b/app/src/mg/dbio/mg_errescal_batch.h @@ -0,0 +1,10 @@ +/* + * mg_errescal_batch.h - mg mg_errescal_batch 배치 전용 카피북 (copybook). 재시도초과 오류전문 에스컬레이션 배치 + * 배치 집계 결과 레코드. Included by mg_errescal_batch.pgc. + */ +#ifndef MG_ERRESCAL_BATCH_H +#define MG_ERRESCAL_BATCH_H + +typedef struct { long scanned; long escalated; } mg_errescal_rec_t; + +#endif diff --git a/app/src/mg/dbio/mg_healthz_batch.h b/app/src/mg/dbio/mg_healthz_batch.h new file mode 100644 index 0000000..e7f243a --- /dev/null +++ b/app/src/mg/dbio/mg_healthz_batch.h @@ -0,0 +1,10 @@ +/* + * mg_healthz_batch.h - mg mg_healthz_batch 배치 전용 카피북 (copybook). 게이트웨이 종합 헬스 스냅 배치 + * 배치 집계 결과 레코드. Included by mg_healthz_batch.pgc. + */ +#ifndef MG_HEALTHZ_BATCH_H +#define MG_HEALTHZ_BATCH_H + +typedef struct { long chan_up; long queue_backlog; long err_today; int healthy; } mg_healthz_rec_t; + +#endif diff --git a/app/src/mg/dbio/mg_loadrpt_batch.h b/app/src/mg/dbio/mg_loadrpt_batch.h new file mode 100644 index 0000000..d0688bc --- /dev/null +++ b/app/src/mg/dbio/mg_loadrpt_batch.h @@ -0,0 +1,10 @@ +/* + * mg_loadrpt_batch.h - mg mg_loadrpt_batch 배치 전용 카피북 (copybook). 채널 부하 가중 리포트 배치 + * 배치 집계 결과 레코드. Included by mg_loadrpt_batch.pgc. + */ +#ifndef MG_LOADRPT_BATCH_H +#define MG_LOADRPT_BATCH_H + +typedef struct { long channels; long load_sum; long busiest_load; } mg_loadrpt_rec_t; + +#endif diff --git a/app/src/mg/dbio/mg_mtiroll_batch.h b/app/src/mg/dbio/mg_mtiroll_batch.h new file mode 100644 index 0000000..609e3b0 --- /dev/null +++ b/app/src/mg/dbio/mg_mtiroll_batch.h @@ -0,0 +1,10 @@ +/* + * mg_mtiroll_batch.h - mg mg_mtiroll_batch 배치 전용 카피북 (copybook). MTI별 금액/건수 롤업 요약 배치 + * 배치 집계 결과 레코드. Included by mg_mtiroll_batch.pgc. + */ +#ifndef MG_MTIROLL_BATCH_H +#define MG_MTIROLL_BATCH_H + +typedef struct { long groups; long total_cnt; long total_amt; long top_amt; } mg_mtiroll_rec_t; + +#endif diff --git a/app/src/mg/dbio/mg_peakhour_batch.h b/app/src/mg/dbio/mg_peakhour_batch.h new file mode 100644 index 0000000..b8ff819 --- /dev/null +++ b/app/src/mg/dbio/mg_peakhour_batch.h @@ -0,0 +1,10 @@ +/* + * mg_peakhour_batch.h - mg mg_peakhour_batch 배치 전용 카피북 (copybook). 시간대별 피크 트래픽 탐지 배치 + * 배치 집계 결과 레코드. Included by mg_peakhour_batch.pgc. + */ +#ifndef MG_PEAKHOUR_BATCH_H +#define MG_PEAKHOUR_BATCH_H + +typedef struct { long buckets; long peak_hour; long peak_cnt; } mg_peakhour_rec_t; + +#endif diff --git a/app/src/mg/dbio/mg_rcfreq_batch.h b/app/src/mg/dbio/mg_rcfreq_batch.h new file mode 100644 index 0000000..311b6e9 --- /dev/null +++ b/app/src/mg/dbio/mg_rcfreq_batch.h @@ -0,0 +1,10 @@ +/* + * mg_rcfreq_batch.h - mg mg_rcfreq_batch 배치 전용 카피북 (copybook). 응답코드별 빈도/오류율 집계 배치 + * 배치 집계 결과 레코드. Included by mg_rcfreq_batch.pgc. + */ +#ifndef MG_RCFREQ_BATCH_H +#define MG_RCFREQ_BATCH_H + +typedef struct { long rc_kinds; long total; long err; long err_bps; } mg_rcfreq_rec_t; + +#endif diff --git a/app/src/mg/dbio/mg_routechk_batch.h b/app/src/mg/dbio/mg_routechk_batch.h new file mode 100644 index 0000000..b33c12e --- /dev/null +++ b/app/src/mg/dbio/mg_routechk_batch.h @@ -0,0 +1,10 @@ +/* + * mg_routechk_batch.h - mg mg_routechk_batch 배치 전용 카피북 (copybook). 라우팅 룰 감사(중복/비활성) 배치 + * 배치 집계 결과 레코드. Included by mg_routechk_batch.pgc. + */ +#ifndef MG_ROUTECHK_BATCH_H +#define MG_ROUTECHK_BATCH_H + +typedef struct { long rules; long inactive; long dup_keys; } mg_routechk_rec_t; + +#endif diff --git a/app/src/mg/dbio/mg_seqrepair_batch.h b/app/src/mg/dbio/mg_seqrepair_batch.h new file mode 100644 index 0000000..26150b2 --- /dev/null +++ b/app/src/mg/dbio/mg_seqrepair_batch.h @@ -0,0 +1,10 @@ +/* + * mg_seqrepair_batch.h - mg mg_seqrepair_batch 배치 전용 카피북 (copybook). 채널별 순번 결번 보정 통계 배치 + * 배치 집계 결과 레코드. Included by mg_seqrepair_batch.pgc. + */ +#ifndef MG_SEQREPAIR_BATCH_H +#define MG_SEQREPAIR_BATCH_H + +typedef struct { long channels; long gap_chans; long widest; } mg_seqrepair_rec_t; + +#endif diff --git a/app/src/mg/dbio/mg_throttle_batch.h b/app/src/mg/dbio/mg_throttle_batch.h new file mode 100644 index 0000000..7d519a2 --- /dev/null +++ b/app/src/mg/dbio/mg_throttle_batch.h @@ -0,0 +1,10 @@ +/* + * mg_throttle_batch.h - mg mg_throttle_batch 배치 전용 카피북 (copybook). 채널 유입 임계 초과 탐지 배치 + * 배치 집계 결과 레코드. Included by mg_throttle_batch.pgc. + */ +#ifndef MG_THROTTLE_BATCH_H +#define MG_THROTTLE_BATCH_H + +typedef struct { long channels; long over_cnt; long busiest; } mg_throttle_rec_t; + +#endif diff --git a/app/src/mg/mg_svr.pgc b/app/src/mg/mg_svr.pgc index eae7bc6..fa8c2d5 100644 --- a/app/src/mg/mg_svr.pgc +++ b/app/src/mg/mg_svr.pgc @@ -2,13 +2,13 @@ * mg_svr.pgc - mg (전문게이트웨이 / message gateway) MODULE SERVER (thin dispatcher). * * The legacy "one module server, many services" pattern: this binary owns NO - * business logic. Each of mg's 75 online services now lives in its own file + * business logic. Each of mg's 133 online services now lives in its own file * (app/src/mg/svc/.pgc) with its own copybook header * (app/src/mg/svc/.h); those objects are archived into libmgsvc.a * and linked into this server. Here we only: * - include every service copybook (for the extern prototypes), * - tpopen() the ECPG XA RM, - * - tpadvertise() all 75 services from a {name, fn} table, + * - tpadvertise() all 133 services from a {name, fn} table, * - tpclose() at shutdown. * * XA: the connection is opened once by tpopen() (ECPG XA switch libndrxxaecpg.so); @@ -103,6 +103,76 @@ #include "MG_SETTLEREQ.h" #include "MG_SETTLERSP.h" #include "MG_QDEPTH.h" +/* ===== 스케일아웃 신규 서비스 (+58) ===== */ +/* --- 전문유형별 처리 세분 (0100/0110/0120/0200/0210/0220/0400/0420/0800/0810) --- */ +#include "MG_AUTHADV.h" +#include "MG_AUTHADVRSP.h" +#include "MG_FINREQ.h" +#include "MG_FINRSP.h" +#include "MG_FINADV.h" +#include "MG_FINADVRSP.h" +#include "MG_REVRSP.h" +#include "MG_REVADVRSP.h" +#include "MG_NMRSP.h" +#include "MG_NMADVICE.h" +#include "MG_BALINQ.h" +#include "MG_INQRSP.h" +#include "MG_CARDVER.h" +#include "MG_PREAUTH.h" +/* --- 채널별 어댑터 (VAN사별/직연동/중계) --- */ +#include "MG_VAN3ADPT.h" +#include "MG_VAN4ADPT.h" +#include "MG_RELAYADPT.h" +#include "MG_ISOADPT.h" +#include "MG_HOSTADPT.h" +#include "MG_CHANMAP.h" +#include "MG_FEP.h" +/* --- 전문 조립/분해/변환 (내부<->ISO8583) --- */ +#include "MG_ENCODE.h" +#include "MG_DECODE.h" +#include "MG_FIELDGET.h" +#include "MG_FIELDSET.h" +#include "MG_BMPPARSE.h" +#include "MG_BMPMERGE.h" +/* --- MAC/키 관리 세분 --- */ +#include "MG_MACSESSION.h" +#include "MG_KEYLOAD.h" +#include "MG_KEYCHECK.h" +#include "MG_PINBLOCK.h" +#include "MG_KEYEXPIRE.h" +/* --- 타임아웃/재전송/중복검출 세분 --- */ +#include "MG_RESNDONE.h" +#include "MG_STALEMARK.h" +#include "MG_DUPPURGE.h" +#include "MG_INFLIGHT.h" +/* --- 순번 갭 검출/보정 --- */ +#include "MG_SEQNEXT.h" +#include "MG_SEQRESET.h" +#include "MG_SEQFILL.h" +/* --- 라우팅 룰 관리 --- */ +#include "MG_RTFIND.h" +#include "MG_RTDISABLE.h" +#include "MG_RTENABLE.h" +#include "MG_RTCOUNT.h" +/* --- 채널 상태 모니터/이중화 전환 --- */ +#include "MG_CHANPING.h" +#include "MG_CHANFAILOVER.h" +#include "MG_CHANLIST.h" +#include "MG_CHANRESET.h" +/* --- 전문 통계 (채널/금액/응답코드/시간대/종합) --- */ +#include "MG_STATCHAN.h" +#include "MG_STATAMT.h" +#include "MG_STATERR.h" +#include "MG_STATPEAK.h" +#include "MG_STATSUM.h" +/* --- 오류전문 재처리 워크플로 --- */ +#include "MG_ERRREQUEUE.h" +#include "MG_ERRCLASS.h" +#include "MG_ERRESCAL.h" +/* --- 트래픽 제어/부하분산 + 헬스체크 세분 --- */ +#include "MG_LOADBAL.h" +#include "MG_RATELIMIT.h" +#include "MG_READYZ.h" /* ----- advertise table: {service name, function} (fn is extern from its file) */ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { @@ -144,6 +214,36 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"MG_AMTCHECK", MG_AMTCHECK}, {"MG_ADVICE", MG_ADVICE}, {"MG_SETTLEREQ", MG_SETTLEREQ}, {"MG_SETTLERSP", MG_SETTLERSP}, {"MG_QDEPTH", MG_QDEPTH}, + /* ===== 스케일아웃 신규 서비스 (+58) ===== */ + {"MG_AUTHADV", MG_AUTHADV}, {"MG_AUTHADVRSP", MG_AUTHADVRSP}, + {"MG_FINREQ", MG_FINREQ}, {"MG_FINRSP", MG_FINRSP}, + {"MG_FINADV", MG_FINADV}, {"MG_FINADVRSP", MG_FINADVRSP}, + {"MG_REVRSP", MG_REVRSP}, {"MG_REVADVRSP", MG_REVADVRSP}, + {"MG_NMRSP", MG_NMRSP}, {"MG_NMADVICE", MG_NMADVICE}, + {"MG_BALINQ", MG_BALINQ}, {"MG_INQRSP", MG_INQRSP}, + {"MG_CARDVER", MG_CARDVER}, {"MG_PREAUTH", MG_PREAUTH}, + {"MG_VAN3ADPT", MG_VAN3ADPT}, {"MG_VAN4ADPT", MG_VAN4ADPT}, + {"MG_RELAYADPT", MG_RELAYADPT}, {"MG_ISOADPT", MG_ISOADPT}, + {"MG_HOSTADPT", MG_HOSTADPT}, {"MG_CHANMAP", MG_CHANMAP}, + {"MG_FEP", MG_FEP}, {"MG_ENCODE", MG_ENCODE}, + {"MG_DECODE", MG_DECODE}, {"MG_FIELDGET", MG_FIELDGET}, + {"MG_FIELDSET", MG_FIELDSET}, {"MG_BMPPARSE", MG_BMPPARSE}, + {"MG_BMPMERGE", MG_BMPMERGE}, {"MG_MACSESSION", MG_MACSESSION}, + {"MG_KEYLOAD", MG_KEYLOAD}, {"MG_KEYCHECK", MG_KEYCHECK}, + {"MG_PINBLOCK", MG_PINBLOCK}, {"MG_KEYEXPIRE", MG_KEYEXPIRE}, + {"MG_RESNDONE", MG_RESNDONE}, {"MG_STALEMARK", MG_STALEMARK}, + {"MG_DUPPURGE", MG_DUPPURGE}, {"MG_INFLIGHT", MG_INFLIGHT}, + {"MG_SEQNEXT", MG_SEQNEXT}, {"MG_SEQRESET", MG_SEQRESET}, + {"MG_SEQFILL", MG_SEQFILL}, {"MG_RTFIND", MG_RTFIND}, + {"MG_RTDISABLE", MG_RTDISABLE}, {"MG_RTENABLE", MG_RTENABLE}, + {"MG_RTCOUNT", MG_RTCOUNT}, {"MG_CHANPING", MG_CHANPING}, + {"MG_CHANFAILOVER", MG_CHANFAILOVER}, {"MG_CHANLIST", MG_CHANLIST}, + {"MG_CHANRESET", MG_CHANRESET}, {"MG_STATCHAN", MG_STATCHAN}, + {"MG_STATAMT", MG_STATAMT}, {"MG_STATERR", MG_STATERR}, + {"MG_STATPEAK", MG_STATPEAK}, {"MG_STATSUM", MG_STATSUM}, + {"MG_ERRREQUEUE", MG_ERRREQUEUE}, {"MG_ERRCLASS", MG_ERRCLASS}, + {"MG_ERRESCAL", MG_ERRESCAL}, {"MG_LOADBAL", MG_LOADBAL}, + {"MG_RATELIMIT", MG_RATELIMIT}, {"MG_READYZ", MG_READYZ}, {NULL, NULL} }; diff --git a/app/src/mg/svc/MG_AUTHADV.h b/app/src/mg/svc/MG_AUTHADV.h new file mode 100644 index 0000000..6a66bc3 --- /dev/null +++ b/app/src/mg/svc/MG_AUTHADV.h @@ -0,0 +1,12 @@ +/* + * MG_AUTHADV.h - mg (전문게이트웨이) 서비스 MG_AUTHADV 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_AUTHADV_H +#define MG_AUTHADV_H + +#include "mg_svc.h" + +void MG_AUTHADV(TPSVCINFO *p); + +#endif /* MG_AUTHADV_H */ diff --git a/app/src/mg/svc/MG_AUTHADV.pgc b/app/src/mg/svc/MG_AUTHADV.pgc new file mode 100644 index 0000000..5c12b81 --- /dev/null +++ b/app/src/mg/svc/MG_AUTHADV.pgc @@ -0,0 +1,33 @@ +/* + * MG_AUTHADV.pgc - mg 모듈 서비스 MG_AUTHADV (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_AUTHADV.h" + +/* MG_AUTHADV - 0120 승인통지(advice) 수신: 승인응답 유실 후 단말이 재차 보낸 + * 통지 전문을 원장에 0120/IN 으로 채번·적재하고 통지수신(A) 상태로 표시한다. */ +void MG_AUTHADV(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], raw[512], proc[8], bizdate[16]; + long stan, msg_id, amount, n; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_STR3, raw, sizeof(raw)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + n = (long)strlen(raw); + + memset(proc, 0, sizeof(proc)); + if (n >= 10) strncpy(proc, raw + 4, 6); else strcpy(proc, "000000"); + amount = (n >= 22) ? atol(raw + 10) : getl(b, T_AMOUNT); + + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, stan, "0120", chan, "IN", proc, "", amount, + "", "00", raw, "A", bizdate) < 0) FAIL(b); + + setl(b, T_ID1, msg_id); + setl(b, T_ARG1, stan); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_AUTHADVRSP.h b/app/src/mg/svc/MG_AUTHADVRSP.h new file mode 100644 index 0000000..231dbe5 --- /dev/null +++ b/app/src/mg/svc/MG_AUTHADVRSP.h @@ -0,0 +1,12 @@ +/* + * MG_AUTHADVRSP.h - mg (전문게이트웨이) 서비스 MG_AUTHADVRSP 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_AUTHADVRSP_H +#define MG_AUTHADVRSP_H + +#include "mg_svc.h" + +void MG_AUTHADVRSP(TPSVCINFO *p); + +#endif /* MG_AUTHADVRSP_H */ diff --git a/app/src/mg/svc/MG_AUTHADVRSP.pgc b/app/src/mg/svc/MG_AUTHADVRSP.pgc new file mode 100644 index 0000000..df2ee0e --- /dev/null +++ b/app/src/mg/svc/MG_AUTHADVRSP.pgc @@ -0,0 +1,33 @@ +/* + * MG_AUTHADVRSP.pgc - mg 모듈 서비스 MG_AUTHADVRSP (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_AUTHADVRSP.h" + +/* MG_AUTHADVRSP - 0130 승인통지응답 생성: 원 STAN 의 0120 통지를 원장에서 찾아 + * 0130 응답전문을 OUT 으로 채번·적재하고, 원 통지를 확인완료(C) 상태로 갱신. */ +void MG_AUTHADVRSP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], bizdate[16]; + long orig_stan, orig_id, stan, msg_id; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + orig_stan = getl(b, T_ARG1); + if (orig_stan <= 0) { setl(b, T_RC, 13); FAIL(b); } + + orig_id = mgdb_find_msg_by_stan(orig_stan); + if (orig_id < 0) { setl(b, T_RC, 25); FAIL(b); } /* 원 통지 없음 */ + + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, stan, "0130", chan, "OUT", "000000", "", 0, + "", "00", "", "S", bizdate) < 0) FAIL(b); + if (mgdb_update_msg_rc(orig_id, "00", "C") < 0) FAIL(b); + + setl(b, T_ID1, msg_id); + setl(b, T_ID2, orig_id); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_BALINQ.h b/app/src/mg/svc/MG_BALINQ.h new file mode 100644 index 0000000..29b02a0 --- /dev/null +++ b/app/src/mg/svc/MG_BALINQ.h @@ -0,0 +1,12 @@ +/* + * MG_BALINQ.h - mg (전문게이트웨이) 서비스 MG_BALINQ 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_BALINQ_H +#define MG_BALINQ_H + +#include "mg_svc.h" + +void MG_BALINQ(TPSVCINFO *p); + +#endif /* MG_BALINQ_H */ diff --git a/app/src/mg/svc/MG_BALINQ.pgc b/app/src/mg/svc/MG_BALINQ.pgc new file mode 100644 index 0000000..66b68d4 --- /dev/null +++ b/app/src/mg/svc/MG_BALINQ.pgc @@ -0,0 +1,38 @@ +/* + * MG_BALINQ.pgc - mg 모듈 서비스 MG_BALINQ (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_BALINQ.h" + +/* MG_BALINQ - 0100 잔액조회 요청 수신: 금액 0 · 처리코드 31xxxx(조회) 인 전문을 + * PAN Luhn 검증 후 원장에 0100/IN 으로 적재. 처리코드가 조회계열(31)이 아니면 rc=12. */ +void MG_BALINQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], raw[512], bizdate[16], proc[8], pan[24], rc[4]; + long stan, msg_id, n; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_STR3, raw, sizeof(raw)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + n = (long)strlen(raw); + + memset(proc, 0, sizeof(proc)); memset(pan, 0, sizeof(pan)); + if (n >= 10) strncpy(proc, raw + 4, 6); else strcpy(proc, "310000"); + if (n >= 38) strncpy(pan, raw + 22, 16); + + if (strncmp(proc, "31", 2) != 0) strcpy(rc, "12"); /* 12 = 처리불가 거래 */ + else if (pan[0] && !acq_luhn_ok(pan)) strcpy(rc, "14"); + else strcpy(rc, "00"); + + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, stan, "0100", chan, "IN", proc, "", 0, + "", rc, raw, "R", bizdate) < 0) FAIL(b); + + sets_(b, T_STR2, rc); + sets_(b, T_STR4, proc); + setl(b, T_ID1, msg_id); + setl(b, T_ARG1, stan); + OK(b); +} diff --git a/app/src/mg/svc/MG_BMPMERGE.h b/app/src/mg/svc/MG_BMPMERGE.h new file mode 100644 index 0000000..54d36b7 --- /dev/null +++ b/app/src/mg/svc/MG_BMPMERGE.h @@ -0,0 +1,12 @@ +/* + * MG_BMPMERGE.h - mg (전문게이트웨이) 서비스 MG_BMPMERGE 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_BMPMERGE_H +#define MG_BMPMERGE_H + +#include "mg_svc.h" + +void MG_BMPMERGE(TPSVCINFO *p); + +#endif /* MG_BMPMERGE_H */ diff --git a/app/src/mg/svc/MG_BMPMERGE.pgc b/app/src/mg/svc/MG_BMPMERGE.pgc new file mode 100644 index 0000000..ccc5606 --- /dev/null +++ b/app/src/mg/svc/MG_BMPMERGE.pgc @@ -0,0 +1,29 @@ +/* + * MG_BMPMERGE.pgc - mg 모듈 서비스 MG_BMPMERGE (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_BMPMERGE.h" + +/* MG_BMPMERGE - 비트맵 병합(순수계산): 1차 비트맵(T_STR6)과 2차 비트맵(T_STR7)을 + * OR 로 합쳐 통합 마스크를 만들고, 2차 존재 시 필드1(확장비트) 을 세워 회신. */ +void MG_BMPMERGE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char hex1[24], hex2[24], out[24]; + unsigned long m1 = 0, m2 = 0, merged; + + gets_(b, T_STR6, hex1, sizeof(hex1)); + gets_(b, T_STR7, hex2, sizeof(hex2)); + + if (hex1[0] && sscanf(hex1, "%16lx", &m1) != 1) { setl(b, T_RC, 30); FAIL(b); } + if (hex2[0] && sscanf(hex2, "%16lx", &m2) != 1) { setl(b, T_RC, 30); FAIL(b); } + + merged = m1 | m2; + if (m2 != 0) merged |= (1UL << (64 - 1)); /* 2차비트맵 존재표시 = 필드1 */ + + sprintf(out, "%016lX", merged); + sets_(b, T_STR6, out); + setl(b, T_ARG3, (long)(m2 != 0)); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_BMPPARSE.h b/app/src/mg/svc/MG_BMPPARSE.h new file mode 100644 index 0000000..cab82a3 --- /dev/null +++ b/app/src/mg/svc/MG_BMPPARSE.h @@ -0,0 +1,12 @@ +/* + * MG_BMPPARSE.h - mg (전문게이트웨이) 서비스 MG_BMPPARSE 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_BMPPARSE_H +#define MG_BMPPARSE_H + +#include "mg_svc.h" + +void MG_BMPPARSE(TPSVCINFO *p); + +#endif /* MG_BMPPARSE_H */ diff --git a/app/src/mg/svc/MG_BMPPARSE.pgc b/app/src/mg/svc/MG_BMPPARSE.pgc new file mode 100644 index 0000000..807f7b4 --- /dev/null +++ b/app/src/mg/svc/MG_BMPPARSE.pgc @@ -0,0 +1,34 @@ +/* + * MG_BMPPARSE.pgc - mg 모듈 서비스 MG_BMPPARSE (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_BMPPARSE.h" + +/* MG_BMPPARSE - 비트맵 해석(순수계산): 16-hex 1차 비트맵을 64비트로 풀어 존재하는 + * 필드번호 목록(콤마구분)과 존재 필드 개수를 회신. hex 형식 오류면 rc=30. */ +void MG_BMPPARSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char hex[24], list[256], tmp[8]; + unsigned long mask = 0; + long cnt = 0; + int i; + + gets_(b, T_STR6, hex, sizeof(hex)); + if (strlen(hex) < 16) { setl(b, T_RC, 30); FAIL(b); } + if (sscanf(hex, "%16lx", &mask) != 1) { setl(b, T_RC, 30); FAIL(b); } + + list[0] = 0; + for (i = 1; i <= 64; i++) { /* 비트 i = 필드 i (MSB=필드1) */ + if (mask & (1UL << (64 - i))) { + sprintf(tmp, "%s%d", (cnt ? "," : ""), i); + if (strlen(list) + strlen(tmp) < sizeof(list) - 1) strcat(list, tmp); + cnt++; + } + } + + sets_(b, T_STR3, list); + setl(b, T_COUNT, cnt); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_CARDVER.h b/app/src/mg/svc/MG_CARDVER.h new file mode 100644 index 0000000..9101dea --- /dev/null +++ b/app/src/mg/svc/MG_CARDVER.h @@ -0,0 +1,12 @@ +/* + * MG_CARDVER.h - mg (전문게이트웨이) 서비스 MG_CARDVER 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_CARDVER_H +#define MG_CARDVER_H + +#include "mg_svc.h" + +void MG_CARDVER(TPSVCINFO *p); + +#endif /* MG_CARDVER_H */ diff --git a/app/src/mg/svc/MG_CARDVER.pgc b/app/src/mg/svc/MG_CARDVER.pgc new file mode 100644 index 0000000..00062ee --- /dev/null +++ b/app/src/mg/svc/MG_CARDVER.pgc @@ -0,0 +1,45 @@ +/* + * MG_CARDVER.pgc - mg 모듈 서비스 MG_CARDVER (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_CARDVER.h" + +/* ----- service-local pure helper(s) ----- */ +/* PAN 앞6(BIN)만 남기고 뒤 전부 마스킹 (카드검증 로그용, 뒤4 미노출). */ +static void mg_cardver_mask(const char *pan, char *out /* >= 20 */) +{ + int i, len = (int)strlen(pan); + for (i = 0; i < len && i < 19; i++) + out[i] = (i < 6) ? pan[i] : '*'; + out[i] = 0; +} + +/* MG_CARDVER - 0100 카드검증(금액 0 승인): PAN 자릿수(13~19)와 Luhn 을 함께 검사해 + * 유효(00)/무효(14)/포맷오류(30)를 판정하고 BIN6 마스킹 원장을 적재한다. */ +void MG_CARDVER(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], pan[24], masked[24], rc[4], bizdate[16]; + long stan, msg_id, plen; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_STR5, pan, sizeof(pan)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + plen = (long)strlen(pan); + + if (plen < 13 || plen > 19) strcpy(rc, "30"); + else if (!acq_luhn_ok(pan)) strcpy(rc, "14"); + else strcpy(rc, "00"); + mg_cardver_mask(pan, masked); + + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, stan, "0100", chan, "IN", "000000", "", 0, + masked, rc, "", "R", bizdate) < 0) FAIL(b); + + sets_(b, T_STR2, rc); + sets_(b, T_STR6, masked); + setl(b, T_COUNT, plen); + setl(b, T_ID1, msg_id); + OK(b); +} diff --git a/app/src/mg/svc/MG_CHANFAILOVER.h b/app/src/mg/svc/MG_CHANFAILOVER.h new file mode 100644 index 0000000..a7d1e2b --- /dev/null +++ b/app/src/mg/svc/MG_CHANFAILOVER.h @@ -0,0 +1,12 @@ +/* + * MG_CHANFAILOVER.h - mg (전문게이트웨이) 서비스 MG_CHANFAILOVER 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_CHANFAILOVER_H +#define MG_CHANFAILOVER_H + +#include "mg_svc.h" + +void MG_CHANFAILOVER(TPSVCINFO *p); + +#endif /* MG_CHANFAILOVER_H */ diff --git a/app/src/mg/svc/MG_CHANFAILOVER.pgc b/app/src/mg/svc/MG_CHANFAILOVER.pgc new file mode 100644 index 0000000..7776919 --- /dev/null +++ b/app/src/mg/svc/MG_CHANFAILOVER.pgc @@ -0,0 +1,42 @@ +/* + * MG_CHANFAILOVER.pgc - mg 모듈 서비스 MG_CHANFAILOVER (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_CHANFAILOVER.h" + +/* MG_CHANFAILOVER - 이중화 절체: 주채널(T_CHANNEL)이 DOWN 이면 예비채널(T_STR1)을 + * UP 으로 승격하고 주채널을 DOWN 확정. 주채널이 살아있으면 절체 없이 rc=1. */ +void MG_CHANFAILOVER(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char primary[16], backup[16], curp[16], curb[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_primary[16], h_backup[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, primary, sizeof(primary)); if (primary[0] == 0) strcpy(primary, "CH01"); + gets_(b, T_STR1, backup, sizeof(backup)); if (backup[0] == 0) strcpy(backup, "CH02"); + + if (mgdb_get_channel_status(primary, curp) < 0) { setl(b, T_RC, 25); FAIL(b); } + if (mgdb_get_channel_status(backup, curb) < 0) { setl(b, T_RC, 25); FAIL(b); } + + if (strcmp(curp, "UP") == 0) { /* 주채널 정상 = 절체 불필요 */ + sets_(b, T_STATUS, "PRIMARY"); + setl(b, T_RC, 1); + OK(b); + } + + strncpy(h_primary, primary, sizeof(h_primary)-1); h_primary[sizeof(h_primary)-1] = 0; + strncpy(h_backup, backup, sizeof(h_backup)-1); h_backup[sizeof(h_backup)-1] = 0; + EXEC SQL UPDATE mg_channel SET status = 'DOWN', updated_at = now() WHERE channel = :h_primary; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL UPDATE mg_channel SET status = 'UP', signon_at = now(), updated_at = now() + WHERE channel = :h_backup; + if (sqlca.sqlcode < 0) FAIL(b); + + userlog("MG_CHANFAILOVER 절체 %s(DOWN) -> %s(UP)", primary, backup); + sets_(b, T_STATUS, "FAILOVER"); + sets_(b, T_STR5, backup); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_CHANLIST.h b/app/src/mg/svc/MG_CHANLIST.h new file mode 100644 index 0000000..4891fd3 --- /dev/null +++ b/app/src/mg/svc/MG_CHANLIST.h @@ -0,0 +1,12 @@ +/* + * MG_CHANLIST.h - mg (전문게이트웨이) 서비스 MG_CHANLIST 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_CHANLIST_H +#define MG_CHANLIST_H + +#include "mg_svc.h" + +void MG_CHANLIST(TPSVCINFO *p); + +#endif /* MG_CHANLIST_H */ diff --git a/app/src/mg/svc/MG_CHANLIST.pgc b/app/src/mg/svc/MG_CHANLIST.pgc new file mode 100644 index 0000000..36d944e --- /dev/null +++ b/app/src/mg/svc/MG_CHANLIST.pgc @@ -0,0 +1,42 @@ +/* + * MG_CHANLIST.pgc - mg 모듈 서비스 MG_CHANLIST (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_CHANLIST.h" + +/* MG_CHANLIST - 채널 상태 요약: 전체 채널 중 UP/DOWN 개수를 집계하고, UP 채널ID 를 + * 콤마로 이어 목록으로 회신. UP 이 하나도 없으면 rc=1(전면 다운) 경고. */ +void MG_CHANLIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char list[256], tmp[24]; + long up = 0, down = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16], h_status[16]; + EXEC SQL END DECLARE SECTION; + + list[0] = 0; + EXEC SQL DECLARE clst CURSOR FOR + SELECT channel, status FROM mg_channel ORDER BY channel; + EXEC SQL OPEN clst; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH clst INTO :h_chan, :h_status; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE clst; FAIL(b); } + if (strcmp(h_status, "UP") == 0) { + up++; + sprintf(tmp, "%s%s", (up > 1 ? "," : ""), h_chan); + if (strlen(list) + strlen(tmp) < sizeof(list) - 1) strcat(list, tmp); + } else { + down++; + } + } + EXEC SQL CLOSE clst; + + sets_(b, T_STR3, list); + setl(b, T_ARG2, up); + setl(b, T_ARG3, down); + setl(b, T_RC, (up == 0) ? 1 : 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_CHANMAP.h b/app/src/mg/svc/MG_CHANMAP.h new file mode 100644 index 0000000..8d544a1 --- /dev/null +++ b/app/src/mg/svc/MG_CHANMAP.h @@ -0,0 +1,12 @@ +/* + * MG_CHANMAP.h - mg (전문게이트웨이) 서비스 MG_CHANMAP 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_CHANMAP_H +#define MG_CHANMAP_H + +#include "mg_svc.h" + +void MG_CHANMAP(TPSVCINFO *p); + +#endif /* MG_CHANMAP_H */ diff --git a/app/src/mg/svc/MG_CHANMAP.pgc b/app/src/mg/svc/MG_CHANMAP.pgc new file mode 100644 index 0000000..9fe1332 --- /dev/null +++ b/app/src/mg/svc/MG_CHANMAP.pgc @@ -0,0 +1,26 @@ +/* + * MG_CHANMAP.pgc - mg 모듈 서비스 MG_CHANMAP (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_CHANMAP.h" + +/* MG_CHANMAP - 외부 채널코드 -> 내부 채널ID 매핑: VAN1/VAN2->CH01, EDC->CH02, + * FEP/직연동->CH03 규칙으로 정규화하고, 미등록 외부코드는 rc=25 로 반려. */ +void MG_CHANMAP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char ext[24], chan[16]; + + gets_(b, T_STR1, ext, sizeof(ext)); + if (ext[0] == 0) { setl(b, T_RC, 30); FAIL(b); } + + if (strcmp(ext, "VAN1") == 0 || strcmp(ext, "VAN2") == 0) strcpy(chan, "CH01"); + else if (strcmp(ext, "EDC") == 0 || strcmp(ext, "POS") == 0) strcpy(chan, "CH02"); + else if (strcmp(ext, "FEP") == 0 || strcmp(ext, "ISO") == 0) strcpy(chan, "CH03"); + else { userlog("MG_CHANMAP 미등록 채널코드 %s", ext); setl(b, T_RC, 25); FAIL(b); } + + sets_(b, T_CHANNEL, chan); + sets_(b, T_STR5, ext); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_CHANPING.h b/app/src/mg/svc/MG_CHANPING.h new file mode 100644 index 0000000..7140b54 --- /dev/null +++ b/app/src/mg/svc/MG_CHANPING.h @@ -0,0 +1,12 @@ +/* + * MG_CHANPING.h - mg (전문게이트웨이) 서비스 MG_CHANPING 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_CHANPING_H +#define MG_CHANPING_H + +#include "mg_svc.h" + +void MG_CHANPING(TPSVCINFO *p); + +#endif /* MG_CHANPING_H */ diff --git a/app/src/mg/svc/MG_CHANPING.pgc b/app/src/mg/svc/MG_CHANPING.pgc new file mode 100644 index 0000000..14a4ffb --- /dev/null +++ b/app/src/mg/svc/MG_CHANPING.pgc @@ -0,0 +1,33 @@ +/* + * MG_CHANPING.pgc - mg 모듈 서비스 MG_CHANPING (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_CHANPING.h" + +/* MG_CHANPING - 채널 하트비트: UP 채널의 updated_at 을 now() 로 눌러 살아있음을 + * 기록한다. DOWN 채널이면 rc=1 로 핑 실패를 알리고 갱신하지 않는다. */ +void MG_CHANPING(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], cur[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + if (mgdb_get_channel_status(chan, cur) < 0) { setl(b, T_RC, 25); FAIL(b); } + + if (strcmp(cur, "UP") != 0) { + sets_(b, T_STATUS, cur); + setl(b, T_RC, 1); /* 살아있지 않은 채널 */ + OK(b); + } + + strncpy(h_chan, chan, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0; + EXEC SQL UPDATE mg_channel SET updated_at = now() WHERE channel = :h_chan; + if (sqlca.sqlcode < 0) FAIL(b); + + sets_(b, T_STATUS, "UP"); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_CHANRESET.h b/app/src/mg/svc/MG_CHANRESET.h new file mode 100644 index 0000000..4793a97 --- /dev/null +++ b/app/src/mg/svc/MG_CHANRESET.h @@ -0,0 +1,12 @@ +/* + * MG_CHANRESET.h - mg (전문게이트웨이) 서비스 MG_CHANRESET 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_CHANRESET_H +#define MG_CHANRESET_H + +#include "mg_svc.h" + +void MG_CHANRESET(TPSVCINFO *p); + +#endif /* MG_CHANRESET_H */ diff --git a/app/src/mg/svc/MG_CHANRESET.pgc b/app/src/mg/svc/MG_CHANRESET.pgc new file mode 100644 index 0000000..e3a70f3 --- /dev/null +++ b/app/src/mg/svc/MG_CHANRESET.pgc @@ -0,0 +1,32 @@ +/* + * MG_CHANRESET.pgc - mg 모듈 서비스 MG_CHANRESET (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_CHANRESET.h" + +/* MG_CHANRESET - 채널 세션 초기화: 채널을 DOWN 으로 내리고 세션키/사인온시각/last_stan + * 을 일괄 비워 재개통(사인온) 이전 상태로 되돌린다. 초기화 전 상태를 회신. */ +void MG_CHANRESET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], cur[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + if (mgdb_get_channel_status(chan, cur) < 0) { setl(b, T_RC, 25); FAIL(b); } + + strncpy(h_chan, chan, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0; + EXEC SQL UPDATE mg_channel + SET status = 'DOWN', session_key = '', signon_at = NULL, + last_stan = 0, updated_at = now() + WHERE channel = :h_chan; + if (sqlca.sqlcode < 0) { userlog("MG_CHANRESET FAIL [%d]", sqlca.sqlcode); FAIL(b); } + + userlog("MG_CHANRESET 채널 초기화 %s (%s -> DOWN)", chan, cur); + sets_(b, T_STR5, cur); /* 초기화 전 상태 */ + sets_(b, T_STATUS, "DOWN"); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_DECODE.h b/app/src/mg/svc/MG_DECODE.h new file mode 100644 index 0000000..4c1c523 --- /dev/null +++ b/app/src/mg/svc/MG_DECODE.h @@ -0,0 +1,12 @@ +/* + * MG_DECODE.h - mg (전문게이트웨이) 서비스 MG_DECODE 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_DECODE_H +#define MG_DECODE_H + +#include "mg_svc.h" + +void MG_DECODE(TPSVCINFO *p); + +#endif /* MG_DECODE_H */ diff --git a/app/src/mg/svc/MG_DECODE.pgc b/app/src/mg/svc/MG_DECODE.pgc new file mode 100644 index 0000000..01e8f6f --- /dev/null +++ b/app/src/mg/svc/MG_DECODE.pgc @@ -0,0 +1,33 @@ +/* + * MG_DECODE.pgc - mg 모듈 서비스 MG_DECODE (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_DECODE.h" + +/* MG_DECODE - 확장 고정길이 해체(순수계산): MG_ENCODE 가 만든 32자 프레임을 + * MTI/처리코드/금액/STAN/채널 필드로 되돌린다. 길이 32 미만이면 rc=31. */ +void MG_DECODE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char raw[64], mti[8], proc[8], chan[8]; + long amount, stan, n; + + gets_(b, T_STR3, raw, sizeof(raw)); + n = (long)strlen(raw); + if (n < 32) { setl(b, T_RC, 31); FAIL(b); } + + memset(mti, 0, sizeof(mti)); memset(proc, 0, sizeof(proc)); memset(chan, 0, sizeof(chan)); + strncpy(mti, raw, 4); + strncpy(proc, raw + 4, 6); + amount = atol(raw + 10); /* [10..21] 금액 12 */ + stan = atol(raw + 22); /* [22..27] STAN 6 */ + strncpy(chan, raw + 28, 4); /* [28..31] 채널 4 */ + + sets_(b, T_STR1, mti); + sets_(b, T_STR4, proc); + setl(b, T_AMOUNT, amount); + setl(b, T_ARG1, stan); + sets_(b, T_CHANNEL, chan); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_DUPPURGE.h b/app/src/mg/svc/MG_DUPPURGE.h new file mode 100644 index 0000000..2d8b400 --- /dev/null +++ b/app/src/mg/svc/MG_DUPPURGE.h @@ -0,0 +1,12 @@ +/* + * MG_DUPPURGE.h - mg (전문게이트웨이) 서비스 MG_DUPPURGE 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_DUPPURGE_H +#define MG_DUPPURGE_H + +#include "mg_svc.h" + +void MG_DUPPURGE(TPSVCINFO *p); + +#endif /* MG_DUPPURGE_H */ diff --git a/app/src/mg/svc/MG_DUPPURGE.pgc b/app/src/mg/svc/MG_DUPPURGE.pgc new file mode 100644 index 0000000..591101a --- /dev/null +++ b/app/src/mg/svc/MG_DUPPURGE.pgc @@ -0,0 +1,37 @@ +/* + * MG_DUPPURGE.pgc - mg 모듈 서비스 MG_DUPPURGE (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_DUPPURGE.h" + +/* MG_DUPPURGE - 중복전문 정리: 동일 STAN 이 2건 이상이면 최소 msg_id(원본)만 남기고 + * 나머지 중복본을 rc=94/status=X(폐기)로 마킹. 폐기 건수를 회신한다. */ +void MG_DUPPURGE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long purged; + EXEC SQL BEGIN DECLARE SECTION; + long h_stan, h_keep; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + h_stan = getl(b, T_ARG1); + if (h_stan <= 0) { setl(b, T_RC, 13); FAIL(b); } + + EXEC SQL SELECT min(msg_id) INTO :h_keep + FROM mg_msg_log WHERE stan = :h_stan AND biz_date = :h_bizdate; + if (sqlca.sqlcode == 100) { setl(b, T_COUNT, 0); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL UPDATE mg_msg_log + SET rc = '94', status = 'X' + WHERE stan = :h_stan AND biz_date = :h_bizdate AND msg_id <> :h_keep; + if (sqlca.sqlcode < 0) FAIL(b); + purged = (long)sqlca.sqlerrd[2]; + + setl(b, T_ID1, h_keep); /* 보존한 원본 msg_id */ + setl(b, T_COUNT, purged); + setl(b, T_RC, (purged > 0) ? 94 : 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_ENCODE.h b/app/src/mg/svc/MG_ENCODE.h new file mode 100644 index 0000000..a5d4d0f --- /dev/null +++ b/app/src/mg/svc/MG_ENCODE.h @@ -0,0 +1,12 @@ +/* + * MG_ENCODE.h - mg (전문게이트웨이) 서비스 MG_ENCODE 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_ENCODE_H +#define MG_ENCODE_H + +#include "mg_svc.h" + +void MG_ENCODE(TPSVCINFO *p); + +#endif /* MG_ENCODE_H */ diff --git a/app/src/mg/svc/MG_ENCODE.pgc b/app/src/mg/svc/MG_ENCODE.pgc new file mode 100644 index 0000000..b32ba25 --- /dev/null +++ b/app/src/mg/svc/MG_ENCODE.pgc @@ -0,0 +1,26 @@ +/* + * MG_ENCODE.pgc - mg 모듈 서비스 MG_ENCODE (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_ENCODE.h" + +/* MG_ENCODE - 확장 고정길이 인코딩(순수계산): MTI(4)+처리코드(6)+금액(12)+ + * STAN(6)+채널(4) 을 이어붙여 32자 프레임으로 조립하고 길이를 회신. */ +void MG_ENCODE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char mti[8], proc[8], chan[16], raw[64]; + long amount, stan; + + gets_(b, T_STR1, mti, sizeof(mti)); if (mti[0] == 0) strcpy(mti, "0200"); + gets_(b, T_STR4, proc, sizeof(proc)); if (proc[0] == 0) strcpy(proc, "000000"); + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + amount = getl(b, T_AMOUNT); + stan = getl(b, T_ARG1); if (stan < 0) stan = 0; + + sprintf(raw, "%.4s%.6s%012ld%06ld%.4s", mti, proc, amount, stan % 1000000, chan); + sets_(b, T_STR3, raw); + setl(b, T_COUNT, (long)strlen(raw)); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_ERRCLASS.h b/app/src/mg/svc/MG_ERRCLASS.h new file mode 100644 index 0000000..4020d84 --- /dev/null +++ b/app/src/mg/svc/MG_ERRCLASS.h @@ -0,0 +1,12 @@ +/* + * MG_ERRCLASS.h - mg (전문게이트웨이) 서비스 MG_ERRCLASS 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_ERRCLASS_H +#define MG_ERRCLASS_H + +#include "mg_svc.h" + +void MG_ERRCLASS(TPSVCINFO *p); + +#endif /* MG_ERRCLASS_H */ diff --git a/app/src/mg/svc/MG_ERRCLASS.pgc b/app/src/mg/svc/MG_ERRCLASS.pgc new file mode 100644 index 0000000..9ef1fce --- /dev/null +++ b/app/src/mg/svc/MG_ERRCLASS.pgc @@ -0,0 +1,30 @@ +/* + * MG_ERRCLASS.pgc - mg 모듈 서비스 MG_ERRCLASS (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_ERRCLASS.h" + +/* MG_ERRCLASS - 오류전문 분류: 응답코드(T_STR2)를 군으로 나눠 재처리 정책을 정한다. + * 68/91=일시오류(재시도 RETRY), 14/54=카드오류(반려 REJECT), 그 외=수동(MANUAL). */ +void MG_ERRCLASS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char rc[4], cls[16]; + long policy; + + gets_(b, T_STR2, rc, sizeof(rc)); + if (rc[0] == 0) { setl(b, T_RC, 30); FAIL(b); } + + if (strcmp(rc, "68") == 0 || strcmp(rc, "91") == 0 || strcmp(rc, "96") == 0) { + strcpy(cls, "RETRY"); policy = 1; /* 일시오류 = 자동재시도 */ + } else if (strcmp(rc, "14") == 0 || strcmp(rc, "54") == 0 || strcmp(rc, "05") == 0) { + strcpy(cls, "REJECT"); policy = 2; /* 카드/승인오류 = 반려 */ + } else { + strcpy(cls, "MANUAL"); policy = 3; /* 기타 = 수동확인 */ + } + + sets_(b, T_STR5, cls); + setl(b, T_ARG3, policy); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_ERRESCAL.h b/app/src/mg/svc/MG_ERRESCAL.h new file mode 100644 index 0000000..0129089 --- /dev/null +++ b/app/src/mg/svc/MG_ERRESCAL.h @@ -0,0 +1,12 @@ +/* + * MG_ERRESCAL.h - mg (전문게이트웨이) 서비스 MG_ERRESCAL 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_ERRESCAL_H +#define MG_ERRESCAL_H + +#include "mg_svc.h" + +void MG_ERRESCAL(TPSVCINFO *p); + +#endif /* MG_ERRESCAL_H */ diff --git a/app/src/mg/svc/MG_ERRESCAL.pgc b/app/src/mg/svc/MG_ERRESCAL.pgc new file mode 100644 index 0000000..52c3843 --- /dev/null +++ b/app/src/mg/svc/MG_ERRESCAL.pgc @@ -0,0 +1,31 @@ +/* + * MG_ERRESCAL.pgc - mg 모듈 서비스 MG_ERRESCAL (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_ERRESCAL.h" + +/* MG_ERRESCAL - 재시도초과 에스컬레이션: 재시도횟수가 임계(기본5)를 넘긴 실패(F) 큐 + * 건을 에스컬레이션(status='E')으로 올려 운영자 개입 대상으로 전이한다. */ +void MG_ERRESCAL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long escalated; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_limit; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + h_limit = getl(b, T_ARG4); if (h_limit <= 0) h_limit = 5; + + EXEC SQL UPDATE mg_queue + SET status = 'E', updated_at = now() + WHERE status = 'F' AND biz_date = :h_bizdate AND retry_cnt > :h_limit; + if (sqlca.sqlcode < 0) { userlog("MG_ERRESCAL FAIL [%d]", sqlca.sqlcode); FAIL(b); } + escalated = (long)sqlca.sqlerrd[2]; + + setl(b, T_COUNT, escalated); + setl(b, T_ARG4, h_limit); + setl(b, T_RC, (escalated > 0) ? 1 : 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_ERRREQUEUE.h b/app/src/mg/svc/MG_ERRREQUEUE.h new file mode 100644 index 0000000..fb03c8b --- /dev/null +++ b/app/src/mg/svc/MG_ERRREQUEUE.h @@ -0,0 +1,12 @@ +/* + * MG_ERRREQUEUE.h - mg (전문게이트웨이) 서비스 MG_ERRREQUEUE 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_ERRREQUEUE_H +#define MG_ERRREQUEUE_H + +#include "mg_svc.h" + +void MG_ERRREQUEUE(TPSVCINFO *p); + +#endif /* MG_ERRREQUEUE_H */ diff --git a/app/src/mg/svc/MG_ERRREQUEUE.pgc b/app/src/mg/svc/MG_ERRREQUEUE.pgc new file mode 100644 index 0000000..cbb2a0a --- /dev/null +++ b/app/src/mg/svc/MG_ERRREQUEUE.pgc @@ -0,0 +1,38 @@ +/* + * MG_ERRREQUEUE.pgc - mg 모듈 서비스 MG_ERRREQUEUE (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_ERRREQUEUE.h" + +/* MG_ERRREQUEUE - 오류전문 큐 재투입: NACK(N) 상태 전문의 원 전문(raw_msg)을 다시 + * 송신큐(Q)에 적재하고, 원 전문을 재처리중(P)으로 전이. 원전문 없으면 rc=25. */ +void MG_ERRREQUEUE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long qid; + EXEC SQL BEGIN DECLARE SECTION; + long h_msgid, h_stan; + char h_chan[16], h_mti[8], h_raw[512], h_status[4]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + h_msgid = getl(b, T_ID1); + if (h_msgid <= 0) { setl(b, T_RC, 13); FAIL(b); } + + EXEC SQL SELECT stan, channel, mti, raw_msg, status + INTO :h_stan, :h_chan, :h_mti, :h_raw, :h_status + FROM mg_msg_log WHERE msg_id = :h_msgid; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 25); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_status, "N") != 0) { setl(b, T_RC, 1); FAIL(b); } /* NACK 아님 */ + + qid = mgdb_next_queue_id(); if (qid < 0) FAIL(b); + if (mgdb_enqueue(qid, h_stan, h_chan, h_mti, h_raw, bizdate) < 0) FAIL(b); + if (mgdb_update_msg_rc(h_msgid, "00", "P") < 0) FAIL(b); + + setl(b, T_ID2, qid); + setl(b, T_ARG1, h_stan); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_FEP.h b/app/src/mg/svc/MG_FEP.h new file mode 100644 index 0000000..05180be --- /dev/null +++ b/app/src/mg/svc/MG_FEP.h @@ -0,0 +1,12 @@ +/* + * MG_FEP.h - mg (전문게이트웨이) 서비스 MG_FEP 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_FEP_H +#define MG_FEP_H + +#include "mg_svc.h" + +void MG_FEP(TPSVCINFO *p); + +#endif /* MG_FEP_H */ diff --git a/app/src/mg/svc/MG_FEP.pgc b/app/src/mg/svc/MG_FEP.pgc new file mode 100644 index 0000000..ebeed1a --- /dev/null +++ b/app/src/mg/svc/MG_FEP.pgc @@ -0,0 +1,31 @@ +/* + * MG_FEP.pgc - mg 모듈 서비스 MG_FEP (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_FEP.h" + +/* MG_FEP - 프론트엔드 프로세서(FEP) 정규화 게이트: 전문 앞뒤 STX/ETX 제어문자와 + * 좌우 공백을 벗겨 순수 페이로드로 정돈하고, 제거 바이트 수를 회신한다. */ +void MG_FEP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char raw[512], out[512]; + long n, i, s, e, trimmed; + + gets_(b, T_STR3, raw, sizeof(raw)); + n = (long)strlen(raw); + + s = 0; e = n - 1; + while (s <= e && (raw[s] == 0x02 || raw[s] == ' ')) s++; /* 앞 STX/공백 */ + while (e >= s && (raw[e] == 0x03 || raw[e] == ' ')) e--; /* 뒤 ETX/공백 */ + + for (i = s; i <= e; i++) out[i - s] = raw[i]; + out[(e >= s) ? e - s + 1 : 0] = 0; + trimmed = n - (long)strlen(out); + + sets_(b, T_STR3, out); + setl(b, T_COUNT, (long)strlen(out)); + setl(b, T_ARG2, trimmed); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_FIELDGET.h b/app/src/mg/svc/MG_FIELDGET.h new file mode 100644 index 0000000..255af59 --- /dev/null +++ b/app/src/mg/svc/MG_FIELDGET.h @@ -0,0 +1,12 @@ +/* + * MG_FIELDGET.h - mg (전문게이트웨이) 서비스 MG_FIELDGET 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_FIELDGET_H +#define MG_FIELDGET_H + +#include "mg_svc.h" + +void MG_FIELDGET(TPSVCINFO *p); + +#endif /* MG_FIELDGET_H */ diff --git a/app/src/mg/svc/MG_FIELDGET.pgc b/app/src/mg/svc/MG_FIELDGET.pgc new file mode 100644 index 0000000..e701846 --- /dev/null +++ b/app/src/mg/svc/MG_FIELDGET.pgc @@ -0,0 +1,31 @@ +/* + * MG_FIELDGET.pgc - mg 모듈 서비스 MG_FIELDGET (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_FIELDGET.h" + +/* MG_FIELDGET - 전문 임의필드 추출(순수계산): 오프셋(T_ARG1)/길이(T_ARG2)로 지정한 + * 구간을 원 전문에서 잘라 회신. 범위가 전문 길이를 벗어나면 rc=31. */ +void MG_FIELDGET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char raw[512], field[256]; + long off, len, n; + + gets_(b, T_STR3, raw, sizeof(raw)); + off = getl(b, T_ARG1); + len = getl(b, T_ARG2); + n = (long)strlen(raw); + + if (off < 0 || len <= 0 || off + len > n || len >= (long)sizeof(field)) { + setl(b, T_RC, 31); + FAIL(b); + } + + memcpy(field, raw + off, len); + field[len] = 0; + sets_(b, T_STR5, field); + setl(b, T_COUNT, len); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_FIELDSET.h b/app/src/mg/svc/MG_FIELDSET.h new file mode 100644 index 0000000..37ac790 --- /dev/null +++ b/app/src/mg/svc/MG_FIELDSET.h @@ -0,0 +1,12 @@ +/* + * MG_FIELDSET.h - mg (전문게이트웨이) 서비스 MG_FIELDSET 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_FIELDSET_H +#define MG_FIELDSET_H + +#include "mg_svc.h" + +void MG_FIELDSET(TPSVCINFO *p); + +#endif /* MG_FIELDSET_H */ diff --git a/app/src/mg/svc/MG_FIELDSET.pgc b/app/src/mg/svc/MG_FIELDSET.pgc new file mode 100644 index 0000000..2a0fd97 --- /dev/null +++ b/app/src/mg/svc/MG_FIELDSET.pgc @@ -0,0 +1,30 @@ +/* + * MG_FIELDSET.pgc - mg 모듈 서비스 MG_FIELDSET (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_FIELDSET.h" + +/* MG_FIELDSET - 전문 임의필드 치환(순수계산): 오프셋(T_ARG1)부터 대체값(T_STR5)을 + * 덮어써 전문을 갱신. 대체값이 남은 길이를 넘어가면 rc=31 로 반려한다. */ +void MG_FIELDSET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char raw[512], val[256]; + long off, vlen, n, i; + + gets_(b, T_STR3, raw, sizeof(raw)); + gets_(b, T_STR5, val, sizeof(val)); + off = getl(b, T_ARG1); + n = (long)strlen(raw); + vlen = (long)strlen(val); + + if (off < 0 || off + vlen > n) { setl(b, T_RC, 31); FAIL(b); } + + for (i = 0; i < vlen; i++) raw[off + i] = val[i]; /* 제자리 덮어쓰기 */ + + sets_(b, T_STR3, raw); + setl(b, T_COUNT, vlen); + setl(b, T_ARG2, off + vlen); /* 다음 필드 시작 오프셋 */ + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_FINADV.h b/app/src/mg/svc/MG_FINADV.h new file mode 100644 index 0000000..38b168c --- /dev/null +++ b/app/src/mg/svc/MG_FINADV.h @@ -0,0 +1,12 @@ +/* + * MG_FINADV.h - mg (전문게이트웨이) 서비스 MG_FINADV 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_FINADV_H +#define MG_FINADV_H + +#include "mg_svc.h" + +void MG_FINADV(TPSVCINFO *p); + +#endif /* MG_FINADV_H */ diff --git a/app/src/mg/svc/MG_FINADV.pgc b/app/src/mg/svc/MG_FINADV.pgc new file mode 100644 index 0000000..5119c8e --- /dev/null +++ b/app/src/mg/svc/MG_FINADV.pgc @@ -0,0 +1,36 @@ +/* + * MG_FINADV.pgc - mg 모듈 서비스 MG_FINADV (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_FINADV.h" + +/* MG_FINADV - 0220 금융통지 수신: 온라인 승인 없이 오프라인 거래를 사후 통지하는 + * 전문을 원장에 0220/IN/통지수신(A)으로 적재. 처리코드 앞2자리를 거래구분으로 회신. */ +void MG_FINADV(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], raw[512], proc[8], txntype[4], bizdate[16]; + long stan, msg_id, amount, n; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_STR3, raw, sizeof(raw)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + n = (long)strlen(raw); + + memset(proc, 0, sizeof(proc)); + if (n >= 10) strncpy(proc, raw + 4, 6); else strcpy(proc, "000000"); + amount = (n >= 22) ? atol(raw + 10) : getl(b, T_AMOUNT); + memset(txntype, 0, sizeof(txntype)); + strncpy(txntype, proc, 2); /* 처리코드 상위 2자리 = 거래구분 */ + + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, stan, "0220", chan, "IN", proc, "", amount, + "", "00", raw, "A", bizdate) < 0) FAIL(b); + + sets_(b, T_STR4, txntype); + setl(b, T_ID1, msg_id); + setl(b, T_ARG1, stan); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_FINADVRSP.h b/app/src/mg/svc/MG_FINADVRSP.h new file mode 100644 index 0000000..f5b4a0c --- /dev/null +++ b/app/src/mg/svc/MG_FINADVRSP.h @@ -0,0 +1,12 @@ +/* + * MG_FINADVRSP.h - mg (전문게이트웨이) 서비스 MG_FINADVRSP 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_FINADVRSP_H +#define MG_FINADVRSP_H + +#include "mg_svc.h" + +void MG_FINADVRSP(TPSVCINFO *p); + +#endif /* MG_FINADVRSP_H */ diff --git a/app/src/mg/svc/MG_FINADVRSP.pgc b/app/src/mg/svc/MG_FINADVRSP.pgc new file mode 100644 index 0000000..11de261 --- /dev/null +++ b/app/src/mg/svc/MG_FINADVRSP.pgc @@ -0,0 +1,36 @@ +/* + * MG_FINADVRSP.pgc - mg 모듈 서비스 MG_FINADVRSP (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_FINADVRSP.h" + +/* MG_FINADVRSP - 0230 금융통지응답 생성: 원 0220 통지를 STAN 으로 조회해 응답전문 + * 0230 을 OUT 으로 채번·적재하고 원 통지를 확인완료(C)로 마감. 원 통지 없으면 rc=25. */ +void MG_FINADVRSP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], bizdate[16], curstat[4]; + long orig_stan, orig_id, stan, msg_id; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + orig_stan = getl(b, T_ARG1); + if (orig_stan <= 0) { setl(b, T_RC, 13); FAIL(b); } + + orig_id = mgdb_find_msg_by_stan(orig_stan); + if (orig_id < 0) { setl(b, T_RC, 25); FAIL(b); } + + curstat[0] = 0; + if (mgdb_get_msg_status(orig_id, curstat) < 0) FAIL(b); + + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, stan, "0230", chan, "OUT", "000000", "", 0, + "", "00", "", "S", bizdate) < 0) FAIL(b); + if (mgdb_update_msg_rc(orig_id, "00", "C") < 0) FAIL(b); + + sets_(b, T_STR5, curstat); /* 마감 직전 상태 */ + setl(b, T_ID1, msg_id); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_FINREQ.h b/app/src/mg/svc/MG_FINREQ.h new file mode 100644 index 0000000..a4abcd9 --- /dev/null +++ b/app/src/mg/svc/MG_FINREQ.h @@ -0,0 +1,12 @@ +/* + * MG_FINREQ.h - mg (전문게이트웨이) 서비스 MG_FINREQ 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_FINREQ_H +#define MG_FINREQ_H + +#include "mg_svc.h" + +void MG_FINREQ(TPSVCINFO *p); + +#endif /* MG_FINREQ_H */ diff --git a/app/src/mg/svc/MG_FINREQ.pgc b/app/src/mg/svc/MG_FINREQ.pgc new file mode 100644 index 0000000..93edc59 --- /dev/null +++ b/app/src/mg/svc/MG_FINREQ.pgc @@ -0,0 +1,41 @@ +/* + * MG_FINREQ.pgc - mg 모듈 서비스 MG_FINREQ (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_FINREQ.h" + +/* MG_FINREQ - 0200 금융요청 수신: 고정길이(MTI/처리코드/금액/PAN/할부개월) 파싱, + * PAN Luhn 검증 후 원장 적재. 할부개월이 있으면 회차금액(month_amount)도 산출. */ +void MG_FINREQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], raw[512], bizdate[16], proc[8], pan[24], rc[4], months_s[4]; + long stan, msg_id, amount, months, month_amt, n; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_STR3, raw, sizeof(raw)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + n = (long)strlen(raw); + + memset(proc, 0, sizeof(proc)); memset(pan, 0, sizeof(pan)); memset(months_s, 0, sizeof(months_s)); + if (n >= 10) strncpy(proc, raw + 4, 6); else strcpy(proc, "000000"); + amount = (n >= 22) ? atol(raw + 10) : getl(b, T_AMOUNT); + if (n >= 38) strncpy(pan, raw + 22, 16); + if (n >= 40) strncpy(months_s, raw + 38, 2); + months = atol(months_s); + if (months < 1) months = 1; + month_amt = amount / months; + + strcpy(rc, (pan[0] && !acq_luhn_ok(pan)) ? "14" : "00"); + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, stan, "0200", chan, "IN", proc, "", amount, + "", rc, raw, "R", bizdate) < 0) FAIL(b); + + sets_(b, T_STR2, rc); + setl(b, T_ID1, msg_id); + setl(b, T_ARG1, stan); + setl(b, T_INSTALL_N, months); + setl(b, T_AMT1, month_amt); + OK(b); +} diff --git a/app/src/mg/svc/MG_FINRSP.h b/app/src/mg/svc/MG_FINRSP.h new file mode 100644 index 0000000..8dc2b89 --- /dev/null +++ b/app/src/mg/svc/MG_FINRSP.h @@ -0,0 +1,12 @@ +/* + * MG_FINRSP.h - mg (전문게이트웨이) 서비스 MG_FINRSP 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_FINRSP_H +#define MG_FINRSP_H + +#include "mg_svc.h" + +void MG_FINRSP(TPSVCINFO *p); + +#endif /* MG_FINRSP_H */ diff --git a/app/src/mg/svc/MG_FINRSP.pgc b/app/src/mg/svc/MG_FINRSP.pgc new file mode 100644 index 0000000..b5cbc6c --- /dev/null +++ b/app/src/mg/svc/MG_FINRSP.pgc @@ -0,0 +1,37 @@ +/* + * MG_FINRSP.pgc - mg 모듈 서비스 MG_FINRSP (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_FINRSP.h" + +/* MG_FINRSP - 0210 금융응답 생성: 승인여부(T_ARG2!=0=승인)를 응답코드로 매핑하여 + * 0210 응답전문을 OUT 으로 채번·적재하고, 원 0200 요청의 응답코드/상태를 갱신. */ +void MG_FINRSP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], bizdate[16], rc[4]; + long orig_stan, orig_id, stan, msg_id, amount, approved; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + amount = getl(b, T_AMOUNT); + approved = getl(b, T_ARG2); + orig_stan = getl(b, T_ARG1); + + strcpy(rc, approved ? "00" : "05"); /* 05 = 승인거절 */ + + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, stan, "0210", chan, "OUT", "000000", "", amount, + "", rc, "", "S", bizdate) < 0) FAIL(b); + + if (orig_stan > 0) { + orig_id = mgdb_find_msg_by_stan(orig_stan); + if (orig_id >= 0 && mgdb_update_msg_rc(orig_id, rc, "S") < 0) FAIL(b); + } + + sets_(b, T_STR2, rc); + setl(b, T_ID1, msg_id); + setl(b, T_RC, approved ? 0 : 5); + OK(b); +} diff --git a/app/src/mg/svc/MG_HOSTADPT.h b/app/src/mg/svc/MG_HOSTADPT.h new file mode 100644 index 0000000..55948ab --- /dev/null +++ b/app/src/mg/svc/MG_HOSTADPT.h @@ -0,0 +1,12 @@ +/* + * MG_HOSTADPT.h - mg (전문게이트웨이) 서비스 MG_HOSTADPT 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_HOSTADPT_H +#define MG_HOSTADPT_H + +#include "mg_svc.h" + +void MG_HOSTADPT(TPSVCINFO *p); + +#endif /* MG_HOSTADPT_H */ diff --git a/app/src/mg/svc/MG_HOSTADPT.pgc b/app/src/mg/svc/MG_HOSTADPT.pgc new file mode 100644 index 0000000..b437060 --- /dev/null +++ b/app/src/mg/svc/MG_HOSTADPT.pgc @@ -0,0 +1,29 @@ +/* + * MG_HOSTADPT.pgc - mg 모듈 서비스 MG_HOSTADPT (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_HOSTADPT.h" + +/* MG_HOSTADPT - 매입사 호스트 직연동 어댑터: 내부 고정길이 전문 뒤에 라우팅 조회로 + * 구한 목적지 노드ID 를 부착한 호스트 프레임 "<전문>@" 를 만든다. */ +void MG_HOSTADPT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], mti[8], raw[512], dest[64], frame[600]; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH02"); + gets_(b, T_STR1, mti, sizeof(mti)); if (mti[0] == 0) strcpy(mti, "0200"); + gets_(b, T_STR3, raw, sizeof(raw)); + + if (mgdb_lookup_route(chan, mti, dest) < 0) { + userlog("MG_HOSTADPT 목적지 미상 chan=%s mti=%s", chan, mti); + strcpy(dest, "DEFAULT"); /* 라우팅 없으면 기본 호스트 */ + } + + snprintf(frame, sizeof(frame), "%s@%s", raw, dest); + sets_(b, T_STR3, frame); + sets_(b, T_STR5, dest); + setl(b, T_COUNT, (long)strlen(frame)); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_INFLIGHT.h b/app/src/mg/svc/MG_INFLIGHT.h new file mode 100644 index 0000000..910b037 --- /dev/null +++ b/app/src/mg/svc/MG_INFLIGHT.h @@ -0,0 +1,12 @@ +/* + * MG_INFLIGHT.h - mg (전문게이트웨이) 서비스 MG_INFLIGHT 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_INFLIGHT_H +#define MG_INFLIGHT_H + +#include "mg_svc.h" + +void MG_INFLIGHT(TPSVCINFO *p); + +#endif /* MG_INFLIGHT_H */ diff --git a/app/src/mg/svc/MG_INFLIGHT.pgc b/app/src/mg/svc/MG_INFLIGHT.pgc new file mode 100644 index 0000000..433ae02 --- /dev/null +++ b/app/src/mg/svc/MG_INFLIGHT.pgc @@ -0,0 +1,31 @@ +/* + * MG_INFLIGHT.pgc - mg 모듈 서비스 MG_INFLIGHT (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_INFLIGHT.h" + +/* MG_INFLIGHT - 미완료(in-flight) 전문 집계: 아직 응답이 확정되지 않은 수신(R)+ + * 송신중(S) 상태 전문 수를 채널별로 세어, 임계(기본500) 초과 시 rc=1 경보. */ +void MG_INFLIGHT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long limit; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16], h_bizdate[16]; + long h_cnt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, h_chan, sizeof(h_chan)); if (h_chan[0] == 0) strcpy(h_chan, "CH01"); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + limit = getl(b, T_ARG4); if (limit <= 0) limit = 500; + + EXEC SQL SELECT count(*) INTO :h_cnt + FROM mg_msg_log + WHERE channel = :h_chan AND biz_date = :h_bizdate AND status IN ('R', 'S'); + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_ARG4, limit); + setl(b, T_RC, (h_cnt > limit) ? 1 : 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_INQRSP.h b/app/src/mg/svc/MG_INQRSP.h new file mode 100644 index 0000000..0e4d6f1 --- /dev/null +++ b/app/src/mg/svc/MG_INQRSP.h @@ -0,0 +1,12 @@ +/* + * MG_INQRSP.h - mg (전문게이트웨이) 서비스 MG_INQRSP 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_INQRSP_H +#define MG_INQRSP_H + +#include "mg_svc.h" + +void MG_INQRSP(TPSVCINFO *p); + +#endif /* MG_INQRSP_H */ diff --git a/app/src/mg/svc/MG_INQRSP.pgc b/app/src/mg/svc/MG_INQRSP.pgc new file mode 100644 index 0000000..895fb1c --- /dev/null +++ b/app/src/mg/svc/MG_INQRSP.pgc @@ -0,0 +1,36 @@ +/* + * MG_INQRSP.pgc - mg 모듈 서비스 MG_INQRSP (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_INQRSP.h" + +/* MG_INQRSP - 0110 조회응답 생성: 조회 잔액(T_AMT1)을 12자리 우측정렬로 응답전문 + * 0110 필드52에 채워 OUT 으로 채번·적재하고, 원 0100 조회요청을 완료(C)로 전이. */ +void MG_INQRSP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], bizdate[16], iso[64]; + long orig_stan, orig_id, stan, msg_id, balance; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + balance = getl(b, T_AMT1); + orig_stan = getl(b, T_ARG1); + + sprintf(iso, "0110%012ld", balance); /* 필드52: 조회잔액 12자리 */ + + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, stan, "0110", chan, "OUT", "310000", "", balance, + "", "00", iso, "S", bizdate) < 0) FAIL(b); + + if (orig_stan > 0) { + orig_id = mgdb_find_msg_by_stan(orig_stan); + if (orig_id >= 0 && mgdb_update_msg_rc(orig_id, "00", "C") < 0) FAIL(b); + } + + sets_(b, T_STR3, iso); + setl(b, T_ID1, msg_id); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_ISOADPT.h b/app/src/mg/svc/MG_ISOADPT.h new file mode 100644 index 0000000..1fade6b --- /dev/null +++ b/app/src/mg/svc/MG_ISOADPT.h @@ -0,0 +1,12 @@ +/* + * MG_ISOADPT.h - mg (전문게이트웨이) 서비스 MG_ISOADPT 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_ISOADPT_H +#define MG_ISOADPT_H + +#include "mg_svc.h" + +void MG_ISOADPT(TPSVCINFO *p); + +#endif /* MG_ISOADPT_H */ diff --git a/app/src/mg/svc/MG_ISOADPT.pgc b/app/src/mg/svc/MG_ISOADPT.pgc new file mode 100644 index 0000000..84c1a6b --- /dev/null +++ b/app/src/mg/svc/MG_ISOADPT.pgc @@ -0,0 +1,43 @@ +/* + * MG_ISOADPT.pgc - mg 모듈 서비스 MG_ISOADPT (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_ISOADPT.h" + +/* ----- service-local pure helper(s) ----- */ +/* ISO8583 MTI 유효성: 4자리 숫자이면서 첫자리(버전)가 0~2 인지 검사. */ +static int mg_iso_mti_ok(const char *mti) +{ + int i; + if (strlen(mti) < 4) return 0; + for (i = 0; i < 4; i++) if (mti[i] < '0' || mti[i] > '9') return 0; + return (mti[0] >= '0' && mti[0] <= '2'); +} + +/* MG_ISOADPT - 표준 ISO8583 직연동 어댑터: 원 전문의 MTI(선두4)를 표준규격으로 + * 검증하고 클래스(승인02/금융02·통지 등)를 판별해 회신. 규격위반 시 rc=30. */ +void MG_ISOADPT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char raw[512], mti[8], cls[16]; + + gets_(b, T_STR3, raw, sizeof(raw)); + memset(mti, 0, sizeof(mti)); + strncpy(mti, raw, 4); + + if (!mg_iso_mti_ok(mti)) { setl(b, T_RC, 30); FAIL(b); } + + switch (mti[1]) { /* 메시지 클래스(둘째자리) */ + case '1': strcpy(cls, "AUTH"); break; /* 1xxx 승인 */ + case '2': strcpy(cls, "FINANCE");break; /* 2xxx 금융 */ + case '4': strcpy(cls, "REVERSE");break; /* 4xxx 취소 */ + case '8': strcpy(cls, "NETMGMT");break; /* 8xxx 망관리 */ + default: strcpy(cls, "OTHER"); break; + } + + sets_(b, T_STR1, mti); + sets_(b, T_STR5, cls); + sets_(b, T_CHANNEL, "CH03"); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_KEYCHECK.h b/app/src/mg/svc/MG_KEYCHECK.h new file mode 100644 index 0000000..a3d3bc9 --- /dev/null +++ b/app/src/mg/svc/MG_KEYCHECK.h @@ -0,0 +1,12 @@ +/* + * MG_KEYCHECK.h - mg (전문게이트웨이) 서비스 MG_KEYCHECK 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_KEYCHECK_H +#define MG_KEYCHECK_H + +#include "mg_svc.h" + +void MG_KEYCHECK(TPSVCINFO *p); + +#endif /* MG_KEYCHECK_H */ diff --git a/app/src/mg/svc/MG_KEYCHECK.pgc b/app/src/mg/svc/MG_KEYCHECK.pgc new file mode 100644 index 0000000..fa95a44 --- /dev/null +++ b/app/src/mg/svc/MG_KEYCHECK.pgc @@ -0,0 +1,49 @@ +/* + * MG_KEYCHECK.pgc - mg 모듈 서비스 MG_KEYCHECK (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_KEYCHECK.h" + +/* ----- service-local pure helper(s) ----- */ +/* 키 문자열의 체크값(KCV): 바이트 합을 세대(keyver)로 섞어 6-hex 로 산출. */ +static void mg_keycheck_kcv(const char *key, long keyver, char *out /* >= 7 */) +{ + unsigned long acc = (unsigned long)(keyver * 2654435761UL); + int i; + for (i = 0; key[i]; i++) acc += (unsigned char)key[i] * (unsigned long)(i + 1); + sprintf(out, "%06lX", acc & 0xFFFFFFUL); +} + +/* MG_KEYCHECK - 키 체크값(KCV) 계산·대조: 채널 세션키로 KCV 를 만들어 입력 기대값 + * (T_KEY2)과 비교. 기대값 미제공이면 계산만 회신, 불일치면 rc=1(키 불일치). */ +void MG_KEYCHECK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char expect[16], kcv[12]; + long keyver, mismatch = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16], h_sk[64]; + long h_kv; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, h_chan, sizeof(h_chan)); if (h_chan[0] == 0) strcpy(h_chan, "CH01"); + gets_(b, T_KEY2, expect, sizeof(expect)); + + h_sk[0] = 0; + EXEC SQL SELECT session_key INTO :h_sk FROM mg_channel WHERE channel = :h_chan; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 25); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (h_sk[0] == 0) { setl(b, T_RC, 1); FAIL(b); } + + h_kv = 0; + EXEC SQL SELECT seq_val INTO :h_kv FROM mg_seq WHERE seq_name = 'KEYVER'; + keyver = (sqlca.sqlcode == 100) ? 0 : h_kv; + + mg_keycheck_kcv(h_sk, keyver, kcv); + if (expect[0] && strcmp(expect, kcv) != 0) mismatch = 1; + + sets_(b, T_KEY1, kcv); + setl(b, T_ARG3, keyver); + setl(b, T_RC, mismatch ? 1 : 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_KEYEXPIRE.h b/app/src/mg/svc/MG_KEYEXPIRE.h new file mode 100644 index 0000000..52faa52 --- /dev/null +++ b/app/src/mg/svc/MG_KEYEXPIRE.h @@ -0,0 +1,12 @@ +/* + * MG_KEYEXPIRE.h - mg (전문게이트웨이) 서비스 MG_KEYEXPIRE 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_KEYEXPIRE_H +#define MG_KEYEXPIRE_H + +#include "mg_svc.h" + +void MG_KEYEXPIRE(TPSVCINFO *p); + +#endif /* MG_KEYEXPIRE_H */ diff --git a/app/src/mg/svc/MG_KEYEXPIRE.pgc b/app/src/mg/svc/MG_KEYEXPIRE.pgc new file mode 100644 index 0000000..295f729 --- /dev/null +++ b/app/src/mg/svc/MG_KEYEXPIRE.pgc @@ -0,0 +1,33 @@ +/* + * MG_KEYEXPIRE.pgc - mg 모듈 서비스 MG_KEYEXPIRE (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_KEYEXPIRE.h" + +/* MG_KEYEXPIRE - 키 만료 판정: 현재 KEYVER 세대와 전문에 실린 사용세대(T_ARG3)의 + * 차이가 허용범위(기본 1세대)를 넘으면 만료(rc=1)로 판정해 재키교환을 유도. */ +void MG_KEYEXPIRE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long used_ver, allow, drift, expired; + EXEC SQL BEGIN DECLARE SECTION; + long h_cur; + EXEC SQL END DECLARE SECTION; + + used_ver = getl(b, T_ARG3); + allow = getl(b, T_ARG4); if (allow <= 0) allow = 1; + + h_cur = 0; + EXEC SQL SELECT seq_val INTO :h_cur FROM mg_seq WHERE seq_name = 'KEYVER'; + if (sqlca.sqlcode < 0) FAIL(b); + + drift = h_cur - used_ver; + expired = (drift > allow) ? 1 : 0; + if (expired) + userlog("MG_KEYEXPIRE 키만료 현재세대=%ld 사용세대=%ld 허용=%ld", h_cur, used_ver, allow); + + setl(b, T_ARG1, h_cur); + setl(b, T_ARG6, drift); + setl(b, T_RC, expired); + OK(b); +} diff --git a/app/src/mg/svc/MG_KEYLOAD.h b/app/src/mg/svc/MG_KEYLOAD.h new file mode 100644 index 0000000..33a2321 --- /dev/null +++ b/app/src/mg/svc/MG_KEYLOAD.h @@ -0,0 +1,12 @@ +/* + * MG_KEYLOAD.h - mg (전문게이트웨이) 서비스 MG_KEYLOAD 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_KEYLOAD_H +#define MG_KEYLOAD_H + +#include "mg_svc.h" + +void MG_KEYLOAD(TPSVCINFO *p); + +#endif /* MG_KEYLOAD_H */ diff --git a/app/src/mg/svc/MG_KEYLOAD.pgc b/app/src/mg/svc/MG_KEYLOAD.pgc new file mode 100644 index 0000000..7988096 --- /dev/null +++ b/app/src/mg/svc/MG_KEYLOAD.pgc @@ -0,0 +1,41 @@ +/* + * MG_KEYLOAD.pgc - mg 모듈 서비스 MG_KEYLOAD (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_KEYLOAD.h" + +/* MG_KEYLOAD - 초기 키 적재(KEK 주입): KEYVER 카운터를 1로 초기 승번하고 대상 채널에 + * 최초 세션키 "K0001-초기STAN" 을 발급, 채널을 SIGNON 준비상태로 만든다. */ +void MG_KEYLOAD(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], newkey[24]; + long stan; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16]; + long h_kv; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + strncpy(h_chan, chan, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0; + + /* KEYVER 를 1 로 (재)초기화 (초기 키적재는 세대 리셋) */ + EXEC SQL UPDATE mg_seq SET seq_val = 1, updated_at = now() WHERE seq_name = 'KEYVER'; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlerrd[2] == 0) { + EXEC SQL INSERT INTO mg_seq (seq_name, seq_val) VALUES ('KEYVER', 1); + if (sqlca.sqlcode < 0) FAIL(b); + } + EXEC SQL SELECT seq_val INTO :h_kv FROM mg_seq WHERE seq_name = 'KEYVER'; + if (sqlca.sqlcode < 0) FAIL(b); + + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + sprintf(newkey, "K%04ld-%04ld", h_kv, stan % 10000); + if (mgdb_set_channel_session(chan, newkey, stan) < 0) FAIL(b); + + userlog("MG_KEYLOAD 초기키 적재 채널=%s 키=%s", chan, newkey); + sets_(b, T_KEY1, newkey); + setl(b, T_ARG3, h_kv); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_LOADBAL.h b/app/src/mg/svc/MG_LOADBAL.h new file mode 100644 index 0000000..8a71bba --- /dev/null +++ b/app/src/mg/svc/MG_LOADBAL.h @@ -0,0 +1,12 @@ +/* + * MG_LOADBAL.h - mg (전문게이트웨이) 서비스 MG_LOADBAL 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_LOADBAL_H +#define MG_LOADBAL_H + +#include "mg_svc.h" + +void MG_LOADBAL(TPSVCINFO *p); + +#endif /* MG_LOADBAL_H */ diff --git a/app/src/mg/svc/MG_LOADBAL.pgc b/app/src/mg/svc/MG_LOADBAL.pgc new file mode 100644 index 0000000..18571cb --- /dev/null +++ b/app/src/mg/svc/MG_LOADBAL.pgc @@ -0,0 +1,44 @@ +/* + * MG_LOADBAL.pgc - mg 모듈 서비스 MG_LOADBAL (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_LOADBAL.h" + +/* MG_LOADBAL - 채널 부하분산 선택: UP 상태 채널들 중 당일 처리건수가 가장 적은 + * 채널을 골라 다음 전문의 송신 채널로 회신한다. UP 채널이 없으면 rc=1. */ +void MG_LOADBAL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char best[16]; + long best_load = -1, considered = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16], h_bizdate[16]; + long h_cnt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + best[0] = 0; + + EXEC SQL DECLARE lbc CURSOR FOR + SELECT channel FROM mg_channel WHERE status = 'UP' ORDER BY channel; + EXEC SQL OPEN lbc; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH lbc INTO :h_chan; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE lbc; FAIL(b); } + considered++; + EXEC SQL SELECT count(*) INTO :h_cnt + FROM mg_msg_log WHERE channel = :h_chan AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE lbc; FAIL(b); } + if (best_load < 0 || h_cnt < best_load) { best_load = h_cnt; strcpy(best, h_chan); } + } + EXEC SQL CLOSE lbc; + + if (best[0] == 0) { setl(b, T_RC, 1); FAIL(b); } + sets_(b, T_CHANNEL, best); + setl(b, T_COUNT, best_load); + setl(b, T_ARG3, considered); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_MACSESSION.h b/app/src/mg/svc/MG_MACSESSION.h new file mode 100644 index 0000000..e86c69d --- /dev/null +++ b/app/src/mg/svc/MG_MACSESSION.h @@ -0,0 +1,12 @@ +/* + * MG_MACSESSION.h - mg (전문게이트웨이) 서비스 MG_MACSESSION 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_MACSESSION_H +#define MG_MACSESSION_H + +#include "mg_svc.h" + +void MG_MACSESSION(TPSVCINFO *p); + +#endif /* MG_MACSESSION_H */ diff --git a/app/src/mg/svc/MG_MACSESSION.pgc b/app/src/mg/svc/MG_MACSESSION.pgc new file mode 100644 index 0000000..a529b8a --- /dev/null +++ b/app/src/mg/svc/MG_MACSESSION.pgc @@ -0,0 +1,46 @@ +/* + * MG_MACSESSION.pgc - mg 모듈 서비스 MG_MACSESSION (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_MACSESSION.h" + +/* ----- service-local pure helper(s) ----- */ +/* 세션키 문자열과 전문을 합성 CBC-MAC 흉내로 섞어 8-hex MAC 산출. */ +static void mg_macsession_calc(const char *sk, const char *msg, char *out /* >= 9 */) +{ + unsigned long acc = 0; + int i; + for (i = 0; sk[i]; i++) acc = acc * 131UL + (unsigned char)sk[i]; /* 키 프리믹스 */ + for (i = 0; msg[i]; i++) { + acc ^= (unsigned char)msg[i]; + acc = (acc << 3) | (acc >> 29); /* 3비트 좌회전(체이닝) */ + } + sprintf(out, "%08lX", acc & 0xFFFFFFFFUL); +} + +/* MG_MACSESSION - 세션키 기반 MAC 생성: 채널의 현재 세션키(mg_channel.session_key)를 + * 읽어 전문과 합성한 세션MAC 을 회신. 세션키 미설정(사인온 전) 채널이면 rc=1. */ +void MG_MACSESSION(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char raw[512], mac[12]; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16], h_sk[64]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, h_chan, sizeof(h_chan)); if (h_chan[0] == 0) strcpy(h_chan, "CH01"); + gets_(b, T_STR3, raw, sizeof(raw)); + if (raw[0] == 0) { setl(b, T_RC, 30); FAIL(b); } + + h_sk[0] = 0; + EXEC SQL SELECT session_key INTO :h_sk FROM mg_channel WHERE channel = :h_chan; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 25); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + if (h_sk[0] == 0) { setl(b, T_RC, 1); FAIL(b); } /* 세션키 없음 */ + + mg_macsession_calc(h_sk, raw, mac); + sets_(b, T_KEY1, mac); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_NMADVICE.h b/app/src/mg/svc/MG_NMADVICE.h new file mode 100644 index 0000000..c59866e --- /dev/null +++ b/app/src/mg/svc/MG_NMADVICE.h @@ -0,0 +1,12 @@ +/* + * MG_NMADVICE.h - mg (전문게이트웨이) 서비스 MG_NMADVICE 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_NMADVICE_H +#define MG_NMADVICE_H + +#include "mg_svc.h" + +void MG_NMADVICE(TPSVCINFO *p); + +#endif /* MG_NMADVICE_H */ diff --git a/app/src/mg/svc/MG_NMADVICE.pgc b/app/src/mg/svc/MG_NMADVICE.pgc new file mode 100644 index 0000000..a2d882b --- /dev/null +++ b/app/src/mg/svc/MG_NMADVICE.pgc @@ -0,0 +1,40 @@ +/* + * MG_NMADVICE.pgc - mg 모듈 서비스 MG_NMADVICE (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_NMADVICE.h" + +/* MG_NMADVICE - 0820 망관리통지 처리: 키교환(NMIC 101)/컷오버(201) 등 무응답 통지를 + * 원장에 0820/IN 으로 적재. NMIC 101 이면 세션키 재발급을 함께 트리거한다. */ +void MG_NMADVICE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], raw[512], nmic[8], bizdate[16], newkey[24]; + long stan, msg_id, n, kv = 0; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_STR3, raw, sizeof(raw)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + n = (long)strlen(raw); + + memset(nmic, 0, sizeof(nmic)); + if (n >= 7) strncpy(nmic, raw + 4, 3); else strcpy(nmic, "201"); + + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, stan, "0820", chan, "IN", nmic, "", 0, + "", "00", raw, "A", bizdate) < 0) FAIL(b); + + if (strcmp(nmic, "101") == 0) { /* 키교환 통지 -> 세션키 재발급 */ + kv = mgdb_seq_bump("KEYVER"); if (kv < 0) FAIL(b); + sprintf(newkey, "K%04ld-%04ld", kv, stan % 10000); + if (mgdb_set_channel_session(chan, newkey, stan) < 0) FAIL(b); + sets_(b, T_KEY1, newkey); + } + + sets_(b, T_STR4, nmic); + setl(b, T_ID1, msg_id); + setl(b, T_ARG3, kv); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_NMRSP.h b/app/src/mg/svc/MG_NMRSP.h new file mode 100644 index 0000000..64824ad --- /dev/null +++ b/app/src/mg/svc/MG_NMRSP.h @@ -0,0 +1,12 @@ +/* + * MG_NMRSP.h - mg (전문게이트웨이) 서비스 MG_NMRSP 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_NMRSP_H +#define MG_NMRSP_H + +#include "mg_svc.h" + +void MG_NMRSP(TPSVCINFO *p); + +#endif /* MG_NMRSP_H */ diff --git a/app/src/mg/svc/MG_NMRSP.pgc b/app/src/mg/svc/MG_NMRSP.pgc new file mode 100644 index 0000000..cb7d9d6 --- /dev/null +++ b/app/src/mg/svc/MG_NMRSP.pgc @@ -0,0 +1,32 @@ +/* + * MG_NMRSP.pgc - mg 모듈 서비스 MG_NMRSP (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_NMRSP.h" + +/* MG_NMRSP - 0810 망관리응답 생성: 수신한 0800 망관리요청의 네트워크정보코드(NMIC, + * 처리코드 상위3자리)를 그대로 되받아 0810 응답전문을 OUT 으로 채번·적재. */ +void MG_NMRSP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], raw[512], nmic[8], bizdate[16]; + long stan, msg_id, n; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_STR3, raw, sizeof(raw)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + n = (long)strlen(raw); + + memset(nmic, 0, sizeof(nmic)); + if (n >= 7) strncpy(nmic, raw + 4, 3); else strcpy(nmic, "001"); /* 001=로그온 */ + + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, stan, "0810", chan, "OUT", nmic, "", 0, + "", "00", raw, "S", bizdate) < 0) FAIL(b); + + sets_(b, T_STR4, nmic); + setl(b, T_ID1, msg_id); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_PINBLOCK.h b/app/src/mg/svc/MG_PINBLOCK.h new file mode 100644 index 0000000..2b8a53f --- /dev/null +++ b/app/src/mg/svc/MG_PINBLOCK.h @@ -0,0 +1,12 @@ +/* + * MG_PINBLOCK.h - mg (전문게이트웨이) 서비스 MG_PINBLOCK 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_PINBLOCK_H +#define MG_PINBLOCK_H + +#include "mg_svc.h" + +void MG_PINBLOCK(TPSVCINFO *p); + +#endif /* MG_PINBLOCK_H */ diff --git a/app/src/mg/svc/MG_PINBLOCK.pgc b/app/src/mg/svc/MG_PINBLOCK.pgc new file mode 100644 index 0000000..50105b2 --- /dev/null +++ b/app/src/mg/svc/MG_PINBLOCK.pgc @@ -0,0 +1,47 @@ +/* + * MG_PINBLOCK.pgc - mg 모듈 서비스 MG_PINBLOCK (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_PINBLOCK.h" + +/* ----- service-local pure helper(s) ----- */ +/* ISO-0 PIN 블록: PIN(0x0N + PIN + F패딩) XOR PAN(0000 + PAN뒤12중앙) -> 16-hex. */ +static void mg_pinblock_iso0(const char *pin, const char *pan, char *out /* >= 17 */) +{ + unsigned char pinb[8], panb[8], i; + char t[17]; + int plen = (int)strlen(pin), panlen = (int)strlen(pan); + + /* PIN 필드 */ + sprintf(t, "0%X%-14.14s", plen, pin); + for (i = 0; i < 14; i++) if (t[2 + i] == ' ') t[2 + i] = 'F'; + for (i = 0; i < 8; i++) { char h[3] = { t[i*2], t[i*2+1], 0 }; pinb[i] = (unsigned char)strtol(h, NULL, 16); } + + /* PAN 필드: 0000 + PAN(체크자리 제외 우측 12) */ + memset(panb, 0, sizeof(panb)); + if (panlen >= 13) { + char pp[13]; memcpy(pp, pan + (panlen - 13), 12); pp[12] = 0; + for (i = 0; i < 6; i++) { char h[3] = { pp[i*2], pp[i*2+1], 0 }; panb[2 + i] = (unsigned char)strtol(h, NULL, 16); } + } + for (i = 0; i < 8; i++) sprintf(out + i*2, "%02X", pinb[i] ^ panb[i]); +} + +/* MG_PINBLOCK - ISO-0 PIN 블록 조립(순수계산): PIN(T_KEY1)과 PAN(T_STR5)으로 + * 포맷0 PIN 블록 16-hex 를 만들어 회신. PIN 길이 4~12 아니면 rc=30. */ +void MG_PINBLOCK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char pin[16], pan[24], block[20]; + long plen; + + gets_(b, T_KEY1, pin, sizeof(pin)); + gets_(b, T_STR5, pan, sizeof(pan)); + plen = (long)strlen(pin); + if (plen < 4 || plen > 12) { setl(b, T_RC, 30); FAIL(b); } + + mg_pinblock_iso0(pin, pan, block); + sets_(b, T_KEY2, block); + setl(b, T_COUNT, plen); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_PREAUTH.h b/app/src/mg/svc/MG_PREAUTH.h new file mode 100644 index 0000000..4fdfb54 --- /dev/null +++ b/app/src/mg/svc/MG_PREAUTH.h @@ -0,0 +1,12 @@ +/* + * MG_PREAUTH.h - mg (전문게이트웨이) 서비스 MG_PREAUTH 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_PREAUTH_H +#define MG_PREAUTH_H + +#include "mg_svc.h" + +void MG_PREAUTH(TPSVCINFO *p); + +#endif /* MG_PREAUTH_H */ diff --git a/app/src/mg/svc/MG_PREAUTH.pgc b/app/src/mg/svc/MG_PREAUTH.pgc new file mode 100644 index 0000000..06de62f --- /dev/null +++ b/app/src/mg/svc/MG_PREAUTH.pgc @@ -0,0 +1,37 @@ +/* + * MG_PREAUTH.pgc - mg 모듈 서비스 MG_PREAUTH (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_PREAUTH.h" + +/* MG_PREAUTH - 0100 예비승인(hold): 요청금액에 홀드율(기본110%)을 적용한 승인예정액을 + * 산출해 원장에 0100/IN/status='H'(보류) 로 적재. 홀드율은 T_ARG4(bps)로 조정 가능. */ +void MG_PREAUTH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], raw[512], bizdate[16], proc[8]; + long stan, msg_id, amount, hold_bps, hold_amt, n; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_STR3, raw, sizeof(raw)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + n = (long)strlen(raw); + + memset(proc, 0, sizeof(proc)); + if (n >= 10) strncpy(proc, raw + 4, 6); else strcpy(proc, "000000"); + amount = (n >= 22) ? atol(raw + 10) : getl(b, T_AMOUNT); + + hold_bps = getl(b, T_ARG4); if (hold_bps <= 0) hold_bps = 11000; /* 110% */ + hold_amt = amount * hold_bps / 10000; + + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, stan, "0100", chan, "IN", proc, "", hold_amt, + "", "00", raw, "H", bizdate) < 0) FAIL(b); + + setl(b, T_AMT1, hold_amt); + setl(b, T_ARG4, hold_bps); + setl(b, T_ID1, msg_id); + setl(b, T_ARG1, stan); + OK(b); +} diff --git a/app/src/mg/svc/MG_RATELIMIT.h b/app/src/mg/svc/MG_RATELIMIT.h new file mode 100644 index 0000000..e02c474 --- /dev/null +++ b/app/src/mg/svc/MG_RATELIMIT.h @@ -0,0 +1,12 @@ +/* + * MG_RATELIMIT.h - mg (전문게이트웨이) 서비스 MG_RATELIMIT 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_RATELIMIT_H +#define MG_RATELIMIT_H + +#include "mg_svc.h" + +void MG_RATELIMIT(TPSVCINFO *p); + +#endif /* MG_RATELIMIT_H */ diff --git a/app/src/mg/svc/MG_RATELIMIT.pgc b/app/src/mg/svc/MG_RATELIMIT.pgc new file mode 100644 index 0000000..af466c2 --- /dev/null +++ b/app/src/mg/svc/MG_RATELIMIT.pgc @@ -0,0 +1,37 @@ +/* + * MG_RATELIMIT.pgc - mg 모듈 서비스 MG_RATELIMIT (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_RATELIMIT.h" + +/* MG_RATELIMIT - 초당 유입 레이트리밋: 채널의 최근 1초간 수신 전문 수를 세어 + * 초당한도(TPS, 기본50) 초과면 차단(rc=91), 80% 이상이면 경보(rc=1)로 판정. */ +void MG_RATELIMIT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long tps, pct; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16]; + long h_cnt, h_tps; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, h_chan, sizeof(h_chan)); if (h_chan[0] == 0) strcpy(h_chan, "CH01"); + tps = getl(b, T_ARG4); if (tps <= 0) tps = 50; + h_tps = tps; + + EXEC SQL SELECT count(*) INTO :h_cnt + FROM mg_msg_log + WHERE channel = :h_chan AND direction = 'IN' + AND created_at >= now() - interval '1 second'; + if (sqlca.sqlcode < 0) FAIL(b); + + pct = (h_cnt * 100L) / h_tps; + setl(b, T_COUNT, h_cnt); + setl(b, T_ARG4, h_tps); + setl(b, T_ARG8, pct); + + if (h_cnt > h_tps) setl(b, T_RC, 91); + else if (pct >= 80) setl(b, T_RC, 1); + else setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_READYZ.h b/app/src/mg/svc/MG_READYZ.h new file mode 100644 index 0000000..ab3c2f5 --- /dev/null +++ b/app/src/mg/svc/MG_READYZ.h @@ -0,0 +1,12 @@ +/* + * MG_READYZ.h - mg (전문게이트웨이) 서비스 MG_READYZ 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_READYZ_H +#define MG_READYZ_H + +#include "mg_svc.h" + +void MG_READYZ(TPSVCINFO *p); + +#endif /* MG_READYZ_H */ diff --git a/app/src/mg/svc/MG_READYZ.pgc b/app/src/mg/svc/MG_READYZ.pgc new file mode 100644 index 0000000..c70ba91 --- /dev/null +++ b/app/src/mg/svc/MG_READYZ.pgc @@ -0,0 +1,36 @@ +/* + * MG_READYZ.pgc - mg 모듈 서비스 MG_READYZ (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_READYZ.h" + +/* MG_READYZ - 게이트웨이 준비상태 종합 헬스: UP 채널 수·큐 적체(Q)·당일 오류전문 + * 수를 한 번에 재서, UP 채널이 1개 이상이면 준비완료(rc=0), 없으면 비준비(rc=1). */ +void MG_READYZ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long ready; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_up, h_backlog, h_err; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT count(*) INTO :h_up FROM mg_channel WHERE status = 'UP'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_backlog + FROM mg_queue WHERE status = 'Q' AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_err + FROM mg_msg_log WHERE biz_date = :h_bizdate AND rc <> '00'; + if (sqlca.sqlcode < 0) FAIL(b); + + ready = (h_up > 0) ? 1 : 0; + setl(b, T_ARG1, h_up); + setl(b, T_ARG2, h_backlog); + setl(b, T_ARG3, h_err); + setl(b, T_ARG8, ready); + setl(b, T_RC, ready ? 0 : 1); + OK(b); +} diff --git a/app/src/mg/svc/MG_RELAYADPT.h b/app/src/mg/svc/MG_RELAYADPT.h new file mode 100644 index 0000000..1d2a2ca --- /dev/null +++ b/app/src/mg/svc/MG_RELAYADPT.h @@ -0,0 +1,12 @@ +/* + * MG_RELAYADPT.h - mg (전문게이트웨이) 서비스 MG_RELAYADPT 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_RELAYADPT_H +#define MG_RELAYADPT_H + +#include "mg_svc.h" + +void MG_RELAYADPT(TPSVCINFO *p); + +#endif /* MG_RELAYADPT_H */ diff --git a/app/src/mg/svc/MG_RELAYADPT.pgc b/app/src/mg/svc/MG_RELAYADPT.pgc new file mode 100644 index 0000000..fb2a0bd --- /dev/null +++ b/app/src/mg/svc/MG_RELAYADPT.pgc @@ -0,0 +1,29 @@ +/* + * MG_RELAYADPT.pgc - mg 모듈 서비스 MG_RELAYADPT (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_RELAYADPT.h" + +/* MG_RELAYADPT - 중계(relay) 채널 어댑터: 중계망 헤더 "RLY<원채널4><전문>" 에서 + * 원 채널ID 를 추출해 T_CHANNEL 로 복원하고 페이로드만 벗겨 통과시킨다. */ +void MG_RELAYADPT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char raw[512], origchan[8], body[512]; + long n; + + gets_(b, T_STR3, raw, sizeof(raw)); + n = (long)strlen(raw); + if (n < 7 || strncmp(raw, "RLY", 3) != 0) { setl(b, T_RC, 30); FAIL(b); } + + memset(origchan, 0, sizeof(origchan)); + strncpy(origchan, raw + 3, 4); /* 원 채널ID 4바이트 */ + strncpy(body, raw + 7, sizeof(body) - 1); body[sizeof(body) - 1] = 0; + + sets_(b, T_CHANNEL, origchan); /* 중계 벗기고 원 채널 복원 */ + sets_(b, T_STR3, body); + sets_(b, T_STR5, "RELAY"); + setl(b, T_COUNT, (long)strlen(body)); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_RESNDONE.h b/app/src/mg/svc/MG_RESNDONE.h new file mode 100644 index 0000000..5d51eb1 --- /dev/null +++ b/app/src/mg/svc/MG_RESNDONE.h @@ -0,0 +1,12 @@ +/* + * MG_RESNDONE.h - mg (전문게이트웨이) 서비스 MG_RESNDONE 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_RESNDONE_H +#define MG_RESNDONE_H + +#include "mg_svc.h" + +void MG_RESNDONE(TPSVCINFO *p); + +#endif /* MG_RESNDONE_H */ diff --git a/app/src/mg/svc/MG_RESNDONE.pgc b/app/src/mg/svc/MG_RESNDONE.pgc new file mode 100644 index 0000000..79e10a7 --- /dev/null +++ b/app/src/mg/svc/MG_RESNDONE.pgc @@ -0,0 +1,32 @@ +/* + * MG_RESNDONE.pgc - mg 모듈 서비스 MG_RESNDONE (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_RESNDONE.h" + +/* MG_RESNDONE - 단건 재전송 확정: 큐(queue_id)의 재시도 성공을 반영해 완료(D)로 + * 전이하고, 재시도횟수가 임계(기본3)를 넘겨 성공했으면 지연성공(rc=1)로 표시. */ +void MG_RESNDONE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long limit, late = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_qid, h_retry; + EXEC SQL END DECLARE SECTION; + + h_qid = getl(b, T_ID2); + limit = getl(b, T_ARG4); if (limit <= 0) limit = 3; + if (h_qid <= 0) { setl(b, T_RC, 13); FAIL(b); } + + EXEC SQL SELECT retry_cnt INTO :h_retry FROM mg_queue WHERE queue_id = :h_qid; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 25); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL UPDATE mg_queue SET status = 'D', updated_at = now() WHERE queue_id = :h_qid; + if (sqlca.sqlcode < 0) FAIL(b); + + if (h_retry > limit) late = 1; + setl(b, T_COUNT, h_retry); + setl(b, T_RC, late ? 1 : 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_REVADVRSP.h b/app/src/mg/svc/MG_REVADVRSP.h new file mode 100644 index 0000000..ddfd432 --- /dev/null +++ b/app/src/mg/svc/MG_REVADVRSP.h @@ -0,0 +1,12 @@ +/* + * MG_REVADVRSP.h - mg (전문게이트웨이) 서비스 MG_REVADVRSP 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_REVADVRSP_H +#define MG_REVADVRSP_H + +#include "mg_svc.h" + +void MG_REVADVRSP(TPSVCINFO *p); + +#endif /* MG_REVADVRSP_H */ diff --git a/app/src/mg/svc/MG_REVADVRSP.pgc b/app/src/mg/svc/MG_REVADVRSP.pgc new file mode 100644 index 0000000..4df2749 --- /dev/null +++ b/app/src/mg/svc/MG_REVADVRSP.pgc @@ -0,0 +1,35 @@ +/* + * MG_REVADVRSP.pgc - mg 모듈 서비스 MG_REVADVRSP (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_REVADVRSP.h" + +/* MG_REVADVRSP - 0430 취소통지응답 생성: 원 0420 취소통지를 STAN 으로 조회해 + * 0430 응답전문을 OUT 으로 채번·적재하고 원 통지를 확인완료(C)로 마감. */ +void MG_REVADVRSP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], bizdate[16]; + long orig_stan, orig_id, stan, msg_id, acked = 0; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + orig_stan = getl(b, T_ARG1); + if (orig_stan <= 0) { setl(b, T_RC, 13); FAIL(b); } + + orig_id = mgdb_find_msg_by_stan(orig_stan); + + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, stan, "0430", chan, "OUT", "000000", "", 0, + "", "00", "", "S", bizdate) < 0) FAIL(b); + if (orig_id >= 0) { + if (mgdb_update_msg_rc(orig_id, "00", "C") < 0) FAIL(b); + acked = 1; + } + + setl(b, T_ID1, msg_id); + setl(b, T_ARG2, acked); /* 원 통지 마감 여부 */ + setl(b, T_RC, acked ? 0 : 25); + OK(b); +} diff --git a/app/src/mg/svc/MG_REVRSP.h b/app/src/mg/svc/MG_REVRSP.h new file mode 100644 index 0000000..d2d2d48 --- /dev/null +++ b/app/src/mg/svc/MG_REVRSP.h @@ -0,0 +1,12 @@ +/* + * MG_REVRSP.h - mg (전문게이트웨이) 서비스 MG_REVRSP 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_REVRSP_H +#define MG_REVRSP_H + +#include "mg_svc.h" + +void MG_REVRSP(TPSVCINFO *p); + +#endif /* MG_REVRSP_H */ diff --git a/app/src/mg/svc/MG_REVRSP.pgc b/app/src/mg/svc/MG_REVRSP.pgc new file mode 100644 index 0000000..bdaa729 --- /dev/null +++ b/app/src/mg/svc/MG_REVRSP.pgc @@ -0,0 +1,34 @@ +/* + * MG_REVRSP.pgc - mg 모듈 서비스 MG_REVRSP (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_REVRSP.h" + +/* MG_REVRSP - 0410 취소응답 생성: 원 0400 취소요청(STAN)을 조회해 취소응답전문 + * 0410 을 OUT 으로 채번·적재하고, 원 취소요청을 취소완료(rc=00/status=C)로 전이. */ +void MG_REVRSP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], bizdate[16], rc[4]; + long orig_stan, orig_id, stan, msg_id, amount; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + amount = getl(b, T_AMOUNT); + orig_stan = getl(b, T_ARG1); + if (orig_stan <= 0) { setl(b, T_RC, 13); FAIL(b); } + + orig_id = mgdb_find_msg_by_stan(orig_stan); + strcpy(rc, (orig_id < 0) ? "25" : "00"); /* 25 = 원거래 없음 */ + + stan = mgdb_next_stan(); if (stan < 0) FAIL(b); + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, stan, "0410", chan, "OUT", "000000", "", amount, + "", rc, "", "S", bizdate) < 0) FAIL(b); + if (orig_id >= 0 && mgdb_update_msg_rc(orig_id, "00", "C") < 0) FAIL(b); + + sets_(b, T_STR2, rc); + setl(b, T_ID1, msg_id); + setl(b, T_RC, (orig_id < 0) ? 25 : 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_RTCOUNT.h b/app/src/mg/svc/MG_RTCOUNT.h new file mode 100644 index 0000000..4d3d100 --- /dev/null +++ b/app/src/mg/svc/MG_RTCOUNT.h @@ -0,0 +1,12 @@ +/* + * MG_RTCOUNT.h - mg (전문게이트웨이) 서비스 MG_RTCOUNT 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_RTCOUNT_H +#define MG_RTCOUNT_H + +#include "mg_svc.h" + +void MG_RTCOUNT(TPSVCINFO *p); + +#endif /* MG_RTCOUNT_H */ diff --git a/app/src/mg/svc/MG_RTCOUNT.pgc b/app/src/mg/svc/MG_RTCOUNT.pgc new file mode 100644 index 0000000..8bd830c --- /dev/null +++ b/app/src/mg/svc/MG_RTCOUNT.pgc @@ -0,0 +1,32 @@ +/* + * MG_RTCOUNT.pgc - mg 모듈 서비스 MG_RTCOUNT (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_RTCOUNT.h" + +/* MG_RTCOUNT - 채널별 라우팅 룰 집계: 지정 채널의 전체/활성/비활성 룰 수를 세어 + * 회신. 활성 룰이 하나도 없으면 rc=1(라우팅 공백)로 경고한다. */ +void MG_RTCOUNT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long inactive; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16]; + long h_total, h_active; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, h_chan, sizeof(h_chan)); if (h_chan[0] == 0) strcpy(h_chan, "CH01"); + + EXEC SQL SELECT count(*) INTO :h_total FROM mg_route WHERE channel = :h_chan; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_active + FROM mg_route WHERE channel = :h_chan AND active = true; + if (sqlca.sqlcode < 0) FAIL(b); + + inactive = h_total - h_active; + setl(b, T_COUNT, h_total); + setl(b, T_ARG2, h_active); + setl(b, T_ARG3, inactive); + setl(b, T_RC, (h_active == 0) ? 1 : 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_RTDISABLE.h b/app/src/mg/svc/MG_RTDISABLE.h new file mode 100644 index 0000000..0dee18e --- /dev/null +++ b/app/src/mg/svc/MG_RTDISABLE.h @@ -0,0 +1,12 @@ +/* + * MG_RTDISABLE.h - mg (전문게이트웨이) 서비스 MG_RTDISABLE 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_RTDISABLE_H +#define MG_RTDISABLE_H + +#include "mg_svc.h" + +void MG_RTDISABLE(TPSVCINFO *p); + +#endif /* MG_RTDISABLE_H */ diff --git a/app/src/mg/svc/MG_RTDISABLE.pgc b/app/src/mg/svc/MG_RTDISABLE.pgc new file mode 100644 index 0000000..29792fb --- /dev/null +++ b/app/src/mg/svc/MG_RTDISABLE.pgc @@ -0,0 +1,34 @@ +/* + * MG_RTDISABLE.pgc - mg 모듈 서비스 MG_RTDISABLE (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_RTDISABLE.h" + +/* MG_RTDISABLE - 라우팅 룰 비활성화: 특정 route_id(있으면) 또는 채널+MTI 전체의 + * 활성 룰을 active=false 로 내린다. 비활성 전이 건수를 회신. */ +void MG_RTDISABLE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long affected; + EXEC SQL BEGIN DECLARE SECTION; + long h_rid; + char h_chan[16], h_mti[8]; + EXEC SQL END DECLARE SECTION; + + h_rid = getl(b, T_ID1); + gets_(b, T_CHANNEL, h_chan, sizeof(h_chan)); if (h_chan[0] == 0) strcpy(h_chan, "CH01"); + gets_(b, T_STR1, h_mti, sizeof(h_mti)); if (h_mti[0] == 0) strcpy(h_mti, "0200"); + + if (h_rid > 0) { + EXEC SQL UPDATE mg_route SET active = false WHERE route_id = :h_rid AND active = true; + } else { + EXEC SQL UPDATE mg_route SET active = false + WHERE channel = :h_chan AND mti = :h_mti AND active = true; + } + if (sqlca.sqlcode < 0) { userlog("MG_RTDISABLE FAIL [%d]", sqlca.sqlcode); FAIL(b); } + affected = (long)sqlca.sqlerrd[2]; + + setl(b, T_COUNT, affected); + setl(b, T_RC, (affected > 0) ? 0 : 1); + OK(b); +} diff --git a/app/src/mg/svc/MG_RTENABLE.h b/app/src/mg/svc/MG_RTENABLE.h new file mode 100644 index 0000000..2ff194c --- /dev/null +++ b/app/src/mg/svc/MG_RTENABLE.h @@ -0,0 +1,12 @@ +/* + * MG_RTENABLE.h - mg (전문게이트웨이) 서비스 MG_RTENABLE 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_RTENABLE_H +#define MG_RTENABLE_H + +#include "mg_svc.h" + +void MG_RTENABLE(TPSVCINFO *p); + +#endif /* MG_RTENABLE_H */ diff --git a/app/src/mg/svc/MG_RTENABLE.pgc b/app/src/mg/svc/MG_RTENABLE.pgc new file mode 100644 index 0000000..b6d053e --- /dev/null +++ b/app/src/mg/svc/MG_RTENABLE.pgc @@ -0,0 +1,34 @@ +/* + * MG_RTENABLE.pgc - mg 모듈 서비스 MG_RTENABLE (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_RTENABLE.h" + +/* MG_RTENABLE - 라우팅 룰 재활성화: 비활성 route_id 를 active=true 로 되살린다. + * 되살린 뒤 같은 채널+MTI 에 활성 룰이 여러 개면 중복(rc=1)을 경고한다. */ +void MG_RTENABLE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_rid, h_cnt; + char h_chan[16], h_mti[8]; + EXEC SQL END DECLARE SECTION; + + h_rid = getl(b, T_ID1); + if (h_rid <= 0) { setl(b, T_RC, 13); FAIL(b); } + + EXEC SQL SELECT channel, mti INTO :h_chan, :h_mti FROM mg_route WHERE route_id = :h_rid; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 25); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL UPDATE mg_route SET active = true WHERE route_id = :h_rid; + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT count(*) INTO :h_cnt + FROM mg_route WHERE channel = :h_chan AND mti = :h_mti AND active = true; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, (h_cnt > 1) ? 1 : 0); /* 활성 룰 중복 경고 */ + OK(b); +} diff --git a/app/src/mg/svc/MG_RTFIND.h b/app/src/mg/svc/MG_RTFIND.h new file mode 100644 index 0000000..efb2b77 --- /dev/null +++ b/app/src/mg/svc/MG_RTFIND.h @@ -0,0 +1,12 @@ +/* + * MG_RTFIND.h - mg (전문게이트웨이) 서비스 MG_RTFIND 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_RTFIND_H +#define MG_RTFIND_H + +#include "mg_svc.h" + +void MG_RTFIND(TPSVCINFO *p); + +#endif /* MG_RTFIND_H */ diff --git a/app/src/mg/svc/MG_RTFIND.pgc b/app/src/mg/svc/MG_RTFIND.pgc new file mode 100644 index 0000000..4b61ea7 --- /dev/null +++ b/app/src/mg/svc/MG_RTFIND.pgc @@ -0,0 +1,36 @@ +/* + * MG_RTFIND.pgc - mg 모듈 서비스 MG_RTFIND (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_RTFIND.h" + +/* MG_RTFIND - 최적 라우팅 선택: 채널+MTI 활성 룰 중 우선순위(priority) 최소값을 + * 가진 목적지를 골라 회신. 후보 수와 선택 우선순위도 함께 반환. 없으면 rc=1. */ +void MG_RTFIND(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16], h_mti[8], h_dest[64]; + long h_prio, h_cnt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, h_chan, sizeof(h_chan)); if (h_chan[0] == 0) strcpy(h_chan, "CH01"); + gets_(b, T_STR1, h_mti, sizeof(h_mti)); if (h_mti[0] == 0) strcpy(h_mti, "0200"); + + EXEC SQL SELECT count(*) INTO :h_cnt + FROM mg_route WHERE channel = :h_chan AND mti = :h_mti AND active = true; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt == 0) { setl(b, T_COUNT, 0); setl(b, T_RC, 1); FAIL(b); } + + EXEC SQL SELECT dest, priority INTO :h_dest, :h_prio + FROM mg_route + WHERE channel = :h_chan AND mti = :h_mti AND active = true + ORDER BY priority ASC, route_id ASC LIMIT 1; + if (sqlca.sqlcode < 0) FAIL(b); + + sets_(b, T_STR3, h_dest); + setl(b, T_ARG4, h_prio); + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_SEQFILL.h b/app/src/mg/svc/MG_SEQFILL.h new file mode 100644 index 0000000..fd2bb66 --- /dev/null +++ b/app/src/mg/svc/MG_SEQFILL.h @@ -0,0 +1,12 @@ +/* + * MG_SEQFILL.h - mg (전문게이트웨이) 서비스 MG_SEQFILL 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_SEQFILL_H +#define MG_SEQFILL_H + +#include "mg_svc.h" + +void MG_SEQFILL(TPSVCINFO *p); + +#endif /* MG_SEQFILL_H */ diff --git a/app/src/mg/svc/MG_SEQFILL.pgc b/app/src/mg/svc/MG_SEQFILL.pgc new file mode 100644 index 0000000..9c6eb77 --- /dev/null +++ b/app/src/mg/svc/MG_SEQFILL.pgc @@ -0,0 +1,46 @@ +/* + * MG_SEQFILL.pgc - mg 모듈 서비스 MG_SEQFILL (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_SEQFILL.h" + +/* MG_SEQFILL - 결번 보정 플레이스홀더 삽입: 채널 last_stan+1 부터 수신 STAN(T_ARG1) + * 직전까지의 결번마다 빈 전문(status='G')을 채워 순번 연속성을 복원한다. */ +void MG_SEQFILL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char chan[16], bizdate[16]; + long recv, s, filled = 0, msg_id; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16]; + long h_last, h_new; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, chan, sizeof(chan)); if (chan[0] == 0) strcpy(chan, "CH01"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + strncpy(h_chan, chan, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0; + recv = getl(b, T_ARG1); + if (recv <= 0) { setl(b, T_RC, 13); FAIL(b); } + + EXEC SQL SELECT last_stan INTO :h_last FROM mg_channel WHERE channel = :h_chan; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 25); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + for (s = h_last + 1; s < recv && filled < 100; s++) { /* 결번 구간을 메움 */ + msg_id = mgdb_next_msg_id(); if (msg_id < 0) FAIL(b); + if (mgdb_insert_msg(msg_id, s, "0000", chan, "IN", "000000", "", 0, + "", "00", "", "G", bizdate) < 0) FAIL(b); + filled++; + } + + if (recv > h_last) { + h_new = recv; + EXEC SQL UPDATE mg_channel SET last_stan = :h_new, updated_at = now() + WHERE channel = :h_chan; + if (sqlca.sqlcode < 0) FAIL(b); + } + + setl(b, T_COUNT, filled); + setl(b, T_RC, (filled > 0) ? 1 : 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_SEQNEXT.h b/app/src/mg/svc/MG_SEQNEXT.h new file mode 100644 index 0000000..e04f24d --- /dev/null +++ b/app/src/mg/svc/MG_SEQNEXT.h @@ -0,0 +1,12 @@ +/* + * MG_SEQNEXT.h - mg (전문게이트웨이) 서비스 MG_SEQNEXT 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_SEQNEXT_H +#define MG_SEQNEXT_H + +#include "mg_svc.h" + +void MG_SEQNEXT(TPSVCINFO *p); + +#endif /* MG_SEQNEXT_H */ diff --git a/app/src/mg/svc/MG_SEQNEXT.pgc b/app/src/mg/svc/MG_SEQNEXT.pgc new file mode 100644 index 0000000..588e2ea --- /dev/null +++ b/app/src/mg/svc/MG_SEQNEXT.pgc @@ -0,0 +1,32 @@ +/* + * MG_SEQNEXT.pgc - mg 모듈 서비스 MG_SEQNEXT (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_SEQNEXT.h" + +/* MG_SEQNEXT - 채널 기대순번 계산: 채널이 기억하는 last_stan 다음 값을 기대순번으로 + * 회신하고, 수신 STAN(T_ARG1)이 기대와 다르면 갭폭을 재서 rc=1 로 알린다. */ +void MG_SEQNEXT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long recv, expect, gap; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16]; + long h_last; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, h_chan, sizeof(h_chan)); if (h_chan[0] == 0) strcpy(h_chan, "CH01"); + recv = getl(b, T_ARG1); + + EXEC SQL SELECT last_stan INTO :h_last FROM mg_channel WHERE channel = :h_chan; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 25); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + expect = h_last + 1; + gap = (recv > 0) ? recv - expect : 0; + + setl(b, T_ARG2, expect); /* 기대순번 */ + setl(b, T_ARG6, gap); /* 갭폭 (0=정상) */ + setl(b, T_RC, (recv > 0 && gap != 0) ? 1 : 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_SEQRESET.h b/app/src/mg/svc/MG_SEQRESET.h new file mode 100644 index 0000000..a6536ef --- /dev/null +++ b/app/src/mg/svc/MG_SEQRESET.h @@ -0,0 +1,12 @@ +/* + * MG_SEQRESET.h - mg (전문게이트웨이) 서비스 MG_SEQRESET 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_SEQRESET_H +#define MG_SEQRESET_H + +#include "mg_svc.h" + +void MG_SEQRESET(TPSVCINFO *p); + +#endif /* MG_SEQRESET_H */ diff --git a/app/src/mg/svc/MG_SEQRESET.pgc b/app/src/mg/svc/MG_SEQRESET.pgc new file mode 100644 index 0000000..153cf0e --- /dev/null +++ b/app/src/mg/svc/MG_SEQRESET.pgc @@ -0,0 +1,35 @@ +/* + * MG_SEQRESET.pgc - mg 모듈 서비스 MG_SEQRESET (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_SEQRESET.h" + +/* MG_SEQRESET - 채널 순번 리셋: 사인온/컷오버 시 채널의 last_stan 을 지정값(T_ARG1, + * 미지정 0)으로 되돌린다. 리셋 전 값을 함께 회신해 감사 근거로 남긴다. */ +void MG_SEQRESET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long newval; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16]; + long h_old, h_new; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, h_chan, sizeof(h_chan)); if (h_chan[0] == 0) strcpy(h_chan, "CH01"); + newval = getl(b, T_ARG1); if (newval < 0) newval = 0; + h_new = newval; + + EXEC SQL SELECT last_stan INTO :h_old FROM mg_channel WHERE channel = :h_chan; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 25); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL UPDATE mg_channel SET last_stan = :h_new, updated_at = now() + WHERE channel = :h_chan; + if (sqlca.sqlcode < 0) FAIL(b); + + userlog("MG_SEQRESET 채널=%s 순번 %ld -> %ld", h_chan, h_old, h_new); + setl(b, T_ARG2, h_old); /* 리셋 전 순번 */ + setl(b, T_ARG3, h_new); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_STALEMARK.h b/app/src/mg/svc/MG_STALEMARK.h new file mode 100644 index 0000000..adab883 --- /dev/null +++ b/app/src/mg/svc/MG_STALEMARK.h @@ -0,0 +1,12 @@ +/* + * MG_STALEMARK.h - mg (전문게이트웨이) 서비스 MG_STALEMARK 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_STALEMARK_H +#define MG_STALEMARK_H + +#include "mg_svc.h" + +void MG_STALEMARK(TPSVCINFO *p); + +#endif /* MG_STALEMARK_H */ diff --git a/app/src/mg/svc/MG_STALEMARK.pgc b/app/src/mg/svc/MG_STALEMARK.pgc new file mode 100644 index 0000000..32d7028 --- /dev/null +++ b/app/src/mg/svc/MG_STALEMARK.pgc @@ -0,0 +1,32 @@ +/* + * MG_STALEMARK.pgc - mg 모듈 서비스 MG_STALEMARK (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_STALEMARK.h" + +/* MG_STALEMARK - 정체 큐 표시: 임계초(기본120초)를 넘도록 대기(Q)로 남은 큐 건을 + * 실패(F)로 전이해 재전송 대상에서 격리. 전이 건수를 회신한다. */ +void MG_STALEMARK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long marked; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_sec; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + h_sec = getl(b, T_ARG4); if (h_sec <= 0) h_sec = 120; + + EXEC SQL UPDATE mg_queue + SET status = 'F', updated_at = now() + WHERE status = 'Q' AND biz_date = :h_bizdate + AND created_at < now() - (:h_sec * interval '1 second'); + if (sqlca.sqlcode < 0) { userlog("MG_STALEMARK 전이 FAIL [%d]", sqlca.sqlcode); FAIL(b); } + marked = (long)sqlca.sqlerrd[2]; + + setl(b, T_COUNT, marked); + setl(b, T_ARG5, h_sec); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_STATAMT.h b/app/src/mg/svc/MG_STATAMT.h new file mode 100644 index 0000000..7443607 --- /dev/null +++ b/app/src/mg/svc/MG_STATAMT.h @@ -0,0 +1,12 @@ +/* + * MG_STATAMT.h - mg (전문게이트웨이) 서비스 MG_STATAMT 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_STATAMT_H +#define MG_STATAMT_H + +#include "mg_svc.h" + +void MG_STATAMT(TPSVCINFO *p); + +#endif /* MG_STATAMT_H */ diff --git a/app/src/mg/svc/MG_STATAMT.pgc b/app/src/mg/svc/MG_STATAMT.pgc new file mode 100644 index 0000000..78744a8 --- /dev/null +++ b/app/src/mg/svc/MG_STATAMT.pgc @@ -0,0 +1,39 @@ +/* + * MG_STATAMT.pgc - mg 모듈 서비스 MG_STATAMT (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_STATAMT.h" + +/* MG_STATAMT - 금액구간별 당일 통계: 경계금액(T_AMT1, 기본 100000)을 기준으로 + * 소액/고액 전문 건수를 나눠 세고, 고액 비율(%)과 평균금액을 회신한다. */ +void MG_STATAMT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long threshold, big_pct, avg, total; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_thr, h_small, h_big, h_sum; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + threshold = getl(b, T_AMT1); if (threshold <= 0) threshold = 100000; + h_thr = threshold; + + EXEC SQL SELECT + count(*) FILTER (WHERE amount < :h_thr), + count(*) FILTER (WHERE amount >= :h_thr), + coalesce(sum(amount),0) + INTO :h_small, :h_big, :h_sum + FROM mg_msg_log WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + total = h_small + h_big; + big_pct = (total > 0) ? (h_big * 100L) / total : 0; + avg = (total > 0) ? h_sum / total : 0; + + setl(b, T_ARG1, h_small); + setl(b, T_ARG2, h_big); + setl(b, T_ARG8, big_pct); + setl(b, T_AMT2, avg); + OK(b); +} diff --git a/app/src/mg/svc/MG_STATCHAN.h b/app/src/mg/svc/MG_STATCHAN.h new file mode 100644 index 0000000..a0fe71d --- /dev/null +++ b/app/src/mg/svc/MG_STATCHAN.h @@ -0,0 +1,12 @@ +/* + * MG_STATCHAN.h - mg (전문게이트웨이) 서비스 MG_STATCHAN 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_STATCHAN_H +#define MG_STATCHAN_H + +#include "mg_svc.h" + +void MG_STATCHAN(TPSVCINFO *p); + +#endif /* MG_STATCHAN_H */ diff --git a/app/src/mg/svc/MG_STATCHAN.pgc b/app/src/mg/svc/MG_STATCHAN.pgc new file mode 100644 index 0000000..fd014c0 --- /dev/null +++ b/app/src/mg/svc/MG_STATCHAN.pgc @@ -0,0 +1,38 @@ +/* + * MG_STATCHAN.pgc - mg 모듈 서비스 MG_STATCHAN (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_STATCHAN.h" + +/* MG_STATCHAN - 채널별 당일 통계: 지정 채널의 수신(IN)/송신(OUT) 건수와 금액합을 + * 집계하고, 전체 건수 대비 그 채널의 점유율(%)을 회신한다. */ +void MG_STATCHAN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long share; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16], h_bizdate[16]; + long h_in, h_out, h_sum, h_all; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, h_chan, sizeof(h_chan)); if (h_chan[0] == 0) strcpy(h_chan, "CH01"); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT + count(*) FILTER (WHERE direction = 'IN'), + count(*) FILTER (WHERE direction = 'OUT'), + coalesce(sum(amount),0) + INTO :h_in, :h_out, :h_sum + FROM mg_msg_log WHERE channel = :h_chan AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT count(*) INTO :h_all FROM mg_msg_log WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + share = (h_all > 0) ? ((h_in + h_out) * 100L) / h_all : 0; + setl(b, T_ARG1, h_in); + setl(b, T_ARG2, h_out); + setl(b, T_GROSS, h_sum); + setl(b, T_ARG8, share); + OK(b); +} diff --git a/app/src/mg/svc/MG_STATERR.h b/app/src/mg/svc/MG_STATERR.h new file mode 100644 index 0000000..975359c --- /dev/null +++ b/app/src/mg/svc/MG_STATERR.h @@ -0,0 +1,12 @@ +/* + * MG_STATERR.h - mg (전문게이트웨이) 서비스 MG_STATERR 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_STATERR_H +#define MG_STATERR_H + +#include "mg_svc.h" + +void MG_STATERR(TPSVCINFO *p); + +#endif /* MG_STATERR_H */ diff --git a/app/src/mg/svc/MG_STATERR.pgc b/app/src/mg/svc/MG_STATERR.pgc new file mode 100644 index 0000000..1ce75b0 --- /dev/null +++ b/app/src/mg/svc/MG_STATERR.pgc @@ -0,0 +1,32 @@ +/* + * MG_STATERR.pgc - mg 모듈 서비스 MG_STATERR (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_STATERR.h" + +/* MG_STATERR - 응답코드 오류율: 당일 전체 전문 중 정상(rc='00') 외 전문 비율을 + * bps(만분율)로 산출해 회신. 오류율이 임계(기본 500bps=5%) 초과면 rc=1. */ +void MG_STATERR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long err_bps, limit; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_all, h_err; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + limit = getl(b, T_ARG4); if (limit <= 0) limit = 500; + + EXEC SQL SELECT count(*), count(*) FILTER (WHERE rc <> '00') + INTO :h_all, :h_err + FROM mg_msg_log WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + err_bps = (h_all > 0) ? (h_err * 10000L) / h_all : 0; + setl(b, T_COUNT, h_all); + setl(b, T_ARG2, h_err); + setl(b, T_ARG8, err_bps); + setl(b, T_RC, (err_bps > limit) ? 1 : 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_STATPEAK.h b/app/src/mg/svc/MG_STATPEAK.h new file mode 100644 index 0000000..15a71d3 --- /dev/null +++ b/app/src/mg/svc/MG_STATPEAK.h @@ -0,0 +1,12 @@ +/* + * MG_STATPEAK.h - mg (전문게이트웨이) 서비스 MG_STATPEAK 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_STATPEAK_H +#define MG_STATPEAK_H + +#include "mg_svc.h" + +void MG_STATPEAK(TPSVCINFO *p); + +#endif /* MG_STATPEAK_H */ diff --git a/app/src/mg/svc/MG_STATPEAK.pgc b/app/src/mg/svc/MG_STATPEAK.pgc new file mode 100644 index 0000000..369a185 --- /dev/null +++ b/app/src/mg/svc/MG_STATPEAK.pgc @@ -0,0 +1,40 @@ +/* + * MG_STATPEAK.pgc - mg 모듈 서비스 MG_STATPEAK (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_STATPEAK.h" + +/* MG_STATPEAK - 피크 시간대 탐지: 당일 전문을 생성시각의 시(0~23)로 그룹핑해 건수가 + * 가장 많은 시간대와 그 건수를 커서로 훑어 찾아낸다 (시간대별 통계의 최댓값판). */ +void MG_STATPEAK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long peak_hour = -1, peak_cnt = 0, buckets = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_hour, h_cnt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL DECLARE pkc CURSOR FOR + SELECT extract(hour from created_at)::int AS hh, count(*) + FROM mg_msg_log WHERE biz_date = :h_bizdate + GROUP BY hh ORDER BY hh; + EXEC SQL OPEN pkc; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH pkc INTO :h_hour, :h_cnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pkc; FAIL(b); } + buckets++; + if (h_cnt > peak_cnt) { peak_cnt = h_cnt; peak_hour = h_hour; } + } + EXEC SQL CLOSE pkc; + + setl(b, T_ARG1, peak_hour); /* 피크 시(-1=무거래) */ + setl(b, T_COUNT, peak_cnt); + setl(b, T_ARG3, buckets); + setl(b, T_RC, (peak_hour < 0) ? 1 : 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_STATSUM.h b/app/src/mg/svc/MG_STATSUM.h new file mode 100644 index 0000000..5d241a9 --- /dev/null +++ b/app/src/mg/svc/MG_STATSUM.h @@ -0,0 +1,12 @@ +/* + * MG_STATSUM.h - mg (전문게이트웨이) 서비스 MG_STATSUM 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_STATSUM_H +#define MG_STATSUM_H + +#include "mg_svc.h" + +void MG_STATSUM(TPSVCINFO *p); + +#endif /* MG_STATSUM_H */ diff --git a/app/src/mg/svc/MG_STATSUM.pgc b/app/src/mg/svc/MG_STATSUM.pgc new file mode 100644 index 0000000..150de73 --- /dev/null +++ b/app/src/mg/svc/MG_STATSUM.pgc @@ -0,0 +1,34 @@ +/* + * MG_STATSUM.pgc - mg 모듈 서비스 MG_STATSUM (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_STATSUM.h" + +/* MG_STATSUM - 게이트웨이 종합 통계: 당일 총 전문건수/총금액/평균금액/고유채널수/ + * 고유MTI수를 한 번에 집계해 대시보드용 요약 한 줄로 회신한다. */ +void MG_STATSUM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long avg; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_sum, h_chans, h_mtis; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL SELECT count(*), coalesce(sum(amount),0), + count(DISTINCT channel), count(DISTINCT mti) + INTO :h_cnt, :h_sum, :h_chans, :h_mtis + FROM mg_msg_log WHERE 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, avg); + setl(b, T_ARG2, h_chans); + setl(b, T_ARG3, h_mtis); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_VAN3ADPT.h b/app/src/mg/svc/MG_VAN3ADPT.h new file mode 100644 index 0000000..03c72ab --- /dev/null +++ b/app/src/mg/svc/MG_VAN3ADPT.h @@ -0,0 +1,12 @@ +/* + * MG_VAN3ADPT.h - mg (전문게이트웨이) 서비스 MG_VAN3ADPT 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_VAN3ADPT_H +#define MG_VAN3ADPT_H + +#include "mg_svc.h" + +void MG_VAN3ADPT(TPSVCINFO *p); + +#endif /* MG_VAN3ADPT_H */ diff --git a/app/src/mg/svc/MG_VAN3ADPT.pgc b/app/src/mg/svc/MG_VAN3ADPT.pgc new file mode 100644 index 0000000..793a889 --- /dev/null +++ b/app/src/mg/svc/MG_VAN3ADPT.pgc @@ -0,0 +1,38 @@ +/* + * MG_VAN3ADPT.pgc - mg 모듈 서비스 MG_VAN3ADPT (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_VAN3ADPT.h" + +/* MG_VAN3ADPT - VAN3 채널 어댑터: 캐럿(^) 구분 전문 "V3^처리코드^MTI^금액" 을 + * (VAN1 과 토큰 순서가 다름) 내부 고정길이 전문으로 정규화. 헤더 V3 아니면 rc=30. */ +void MG_VAN3ADPT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char raw[512], mti[8], proc[8], out[64]; + char *t1, *t2, *t3; + long amount = 0; + + gets_(b, T_STR3, raw, sizeof(raw)); + if (strncmp(raw, "V3^", 3) != 0) { setl(b, T_RC, 30); FAIL(b); } + + memset(mti, 0, sizeof(mti)); memset(proc, 0, sizeof(proc)); + t1 = raw + 3; /* 처리코드 */ + t2 = strchr(t1, '^'); + if (t2 == NULL) { setl(b, T_RC, 30); FAIL(b); } + *t2++ = 0; /* t2 = MTI */ + t3 = strchr(t2, '^'); + if (t3 == NULL) { setl(b, T_RC, 30); FAIL(b); } + *t3++ = 0; /* t3 = 금액 */ + strncpy(proc, t1, 6); + strncpy(mti, t2, 4); + amount = atol(t3); + + sprintf(out, "%.4s%.6s%012ld", mti, proc, amount); + sets_(b, T_STR3, out); + sets_(b, T_STR1, mti); + setl(b, T_AMOUNT, amount); + sets_(b, T_CHANNEL, "CH01"); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mg/svc/MG_VAN4ADPT.h b/app/src/mg/svc/MG_VAN4ADPT.h new file mode 100644 index 0000000..b4e5f35 --- /dev/null +++ b/app/src/mg/svc/MG_VAN4ADPT.h @@ -0,0 +1,12 @@ +/* + * MG_VAN4ADPT.h - mg (전문게이트웨이) 서비스 MG_VAN4ADPT 카피북 (copybook / record header). + * 신규 스케일아웃 서비스; mg_svr 가 tpsvrinit 에서 광고한다. + */ +#ifndef MG_VAN4ADPT_H +#define MG_VAN4ADPT_H + +#include "mg_svc.h" + +void MG_VAN4ADPT(TPSVCINFO *p); + +#endif /* MG_VAN4ADPT_H */ diff --git a/app/src/mg/svc/MG_VAN4ADPT.pgc b/app/src/mg/svc/MG_VAN4ADPT.pgc new file mode 100644 index 0000000..7d3f555 --- /dev/null +++ b/app/src/mg/svc/MG_VAN4ADPT.pgc @@ -0,0 +1,42 @@ +/* + * MG_VAN4ADPT.pgc - mg 모듈 서비스 MG_VAN4ADPT (one service = one file). + * 신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고. + */ +#include "MG_VAN4ADPT.h" + +/* ----- service-local pure helper(s) ----- */ +/* 앞 4바이트 길이헤더(LLLL, 십진)를 파싱 (VAN4 는 길이접두 프레이밍). */ +static long mg_van4_len(const char *s) +{ + char h[5]; memcpy(h, s, 4); h[4] = 0; + return atol(h); +} + +/* MG_VAN4ADPT - VAN4 채널 어댑터: 길이접두(LLLL) 프레이밍 전문에서 선언길이와 + * 실제 바디길이를 대조. 불일치면 rc=31(길이오류), 일치하면 바디를 그대로 통과. */ +void MG_VAN4ADPT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char raw[512], body[512]; + long declen, actlen, n; + + gets_(b, T_STR3, raw, sizeof(raw)); + n = (long)strlen(raw); + if (n < 5) { setl(b, T_RC, 30); FAIL(b); } + + declen = mg_van4_len(raw); + strncpy(body, raw + 4, sizeof(body) - 1); body[sizeof(body) - 1] = 0; + actlen = (long)strlen(body); + + if (declen != actlen) { + userlog("MG_VAN4ADPT 길이불일치 선언=%ld 실제=%ld", declen, actlen); + setl(b, T_RC, 31); + FAIL(b); + } + + sets_(b, T_STR3, body); + setl(b, T_COUNT, actlen); + sets_(b, T_CHANNEL, "CH02"); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/batch/mm_acct_scan_batch.pgc b/app/src/mm/batch/mm_acct_scan_batch.pgc new file mode 100644 index 0000000..b2ee425 --- /dev/null +++ b/app/src/mm/batch/mm_acct_scan_batch.pgc @@ -0,0 +1,59 @@ +/* + * mm_acct_scan_batch.pgc - 정산계좌 형식 점검 배치 (커서 + C 검증 + XA). + * + * 활성 가맹점의 정산계좌를 순회하며 C 코드로 숫자 10~16자리 형식을 검증하고 + * 위반 건에 ACCT_BAD 이력을 남긴다(정산 오류 예방). ONE global XA transaction. + * host 변수는 mm_acct_scan_cpy.h 를 EXEC SQL INCLUDE. + * Usage: mm_acct_scan_batch + */ +#include +#include +#include +#include +#include "mm_dbio.h" + +static int acct_bad(const char *a) +{ + size_t i, len = strlen(a); + if (len < 10 || len > 16) return 1; + for (i = 0; i < len; i++) + if (a[i] < '0' || a[i] > '9') return 1; + return 0; +} + +int main(int argc, char **argv) +{ + (void)argc; (void)argv; + EXEC SQL INCLUDE mm_acct_scan_cpy; + b_bad = 0; b_scanned = 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 acsn CURSOR FOR + SELECT merchant_id, settle_acct FROM mm_merchant_ext + WHERE status = 'ACTIVE' AND settle_acct <> '' + ORDER BY merchant_id; + EXEC SQL OPEN acsn; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH acsn INTO :b_merch, :b_acct; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE acsn; tpabort(0); return 1; } + b_scanned++; + if (!acct_bad(b_acct)) continue; + if (mmdb_insert_merch_hist(b_merch, "ACCT_BAD", b_acct, "FORMAT", "배치 계좌 형식 위반") < 0) { + EXEC SQL CLOSE acsn; tpabort(0); return 1; + } + b_bad++; + } + EXEC SQL CLOSE acsn; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mm_acct_scan_batch COMMIT: 점검=%ld 위반=%ld건\n", b_scanned, b_bad); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mm/batch/mm_acct_scan_cpy.h b/app/src/mm/batch/mm_acct_scan_cpy.h new file mode 100644 index 0000000..3fcd13f --- /dev/null +++ b/app/src/mm/batch/mm_acct_scan_cpy.h @@ -0,0 +1,7 @@ +/* mm_acct_scan_cpy.h - mm_acct_scan_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +char b_merch[64]; +char b_acct[40]; +long b_bad; +long b_scanned; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/mm/batch/mm_apply_rescore_batch.pgc b/app/src/mm/batch/mm_apply_rescore_batch.pgc new file mode 100644 index 0000000..e0173f4 --- /dev/null +++ b/app/src/mm/batch/mm_apply_rescore_batch.pgc @@ -0,0 +1,51 @@ +/* + * mm_apply_rescore_batch.pgc - 심사 신청 서류 재점검 배치 (커서 + XA). + * + * APPLIED 상태 가맹점 중 사업자번호가 10자리가 아닌 건을 자동으로 HOLD 전환하고 + * APPLY_HOLD 이력을 남긴다(서류 보완 요청). ONE global XA transaction. + * host 변수는 mm_apply_rescore_cpy.h 를 EXEC SQL INCLUDE. + * Usage: mm_apply_rescore_batch + */ +#include +#include +#include +#include +#include "mm_dbio.h" + +int main(int argc, char **argv) +{ + (void)argc; (void)argv; + EXEC SQL INCLUDE mm_apply_rescore_cpy; + b_held = 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 ars CURSOR FOR + SELECT merchant_id, biz_reg_no FROM mm_merchant_ext + WHERE status = 'APPLIED' AND length(biz_reg_no) <> 10 + ORDER BY merchant_id; + EXEC SQL OPEN ars; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH ars INTO :b_merch, :b_biz; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ars; tpabort(0); return 1; } + EXEC SQL UPDATE mm_merchant_ext SET status = 'HOLD', updated_at = now() + WHERE merchant_id = :b_merch; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ars; tpabort(0); return 1; } + if (mmdb_insert_merch_hist(b_merch, "APPLY_HOLD", "APPLIED", "HOLD", "배치 서류 미비 보류") < 0) { + EXEC SQL CLOSE ars; tpabort(0); return 1; + } + b_held++; + } + EXEC SQL CLOSE ars; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mm_apply_rescore_batch COMMIT: 보류전환=%ld건\n", b_held); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mm/batch/mm_apply_rescore_cpy.h b/app/src/mm/batch/mm_apply_rescore_cpy.h new file mode 100644 index 0000000..5112981 --- /dev/null +++ b/app/src/mm/batch/mm_apply_rescore_cpy.h @@ -0,0 +1,6 @@ +/* mm_apply_rescore_cpy.h - mm_apply_rescore_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +char b_merch[64]; +char b_biz[16]; +long b_held; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/mm/batch/mm_biz_hibernate_batch.pgc b/app/src/mm/batch/mm_biz_hibernate_batch.pgc new file mode 100644 index 0000000..cabef33 --- /dev/null +++ b/app/src/mm/batch/mm_biz_hibernate_batch.pgc @@ -0,0 +1,57 @@ +/* + * mm_biz_hibernate_batch.pgc - 무거래 가맹점 휴면 전환 배치 (LEFT JOIN 커서 + XA). + * + * 최근 N일간 집계 매출이 전혀 없는 활성 가맹점을 DORMANT로 전환하고 DORMANT 이력을 + * 남긴다(자동 휴면). ONE global XA transaction. + * host 변수는 mm_biz_hibernate_cpy.h 를 EXEC SQL INCLUDE. + * Usage: mm_biz_hibernate_batch [DAYS=180] + */ +#include +#include +#include +#include +#include "mm_dbio.h" + +int main(int argc, char **argv) +{ + EXEC SQL INCLUDE mm_biz_hibernate_cpy; + b_hibernated = 0; + b_days = (argc > 1) ? atol(argv[1]) : 180; + if (b_days < 1) b_days = 180; + + 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 hib CURSOR FOR + SELECT me.merchant_id + FROM mm_merchant_ext me + LEFT JOIN merchant_summary ms + ON ms.merchant_id = me.merchant_id AND ms.biz_date >= current_date - :b_days + WHERE me.status = 'ACTIVE' + GROUP BY me.merchant_id + HAVING coalesce(sum(ms.gross_amount), 0) = 0 + ORDER BY me.merchant_id; + EXEC SQL OPEN hib; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH hib INTO :b_merch; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE hib; tpabort(0); return 1; } + EXEC SQL UPDATE mm_merchant_ext SET status = 'DORMANT', updated_at = now() + WHERE merchant_id = :b_merch; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE hib; tpabort(0); return 1; } + if (mmdb_insert_merch_hist(b_merch, "DORMANT", "ACTIVE", "DORMANT", "무거래 자동 휴면") < 0) { + EXEC SQL CLOSE hib; tpabort(0); return 1; + } + b_hibernated++; + } + EXEC SQL CLOSE hib; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mm_biz_hibernate_batch COMMIT: 기간=%ld일 휴면=%ld건\n", b_days, b_hibernated); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mm/batch/mm_biz_hibernate_cpy.h b/app/src/mm/batch/mm_biz_hibernate_cpy.h new file mode 100644 index 0000000..bb6e126 --- /dev/null +++ b/app/src/mm/batch/mm_biz_hibernate_cpy.h @@ -0,0 +1,6 @@ +/* mm_biz_hibernate_cpy.h - mm_biz_hibernate_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +long b_days; +char b_merch[64]; +long b_hibernated; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/mm/batch/mm_contract_expire_batch.pgc b/app/src/mm/batch/mm_contract_expire_batch.pgc new file mode 100644 index 0000000..0d6a411 --- /dev/null +++ b/app/src/mm/batch/mm_contract_expire_batch.pgc @@ -0,0 +1,55 @@ +/* + * mm_contract_expire_batch.pgc - 만기 경과 계약 만료 처리 배치 (커서 + XA). + * + * 각 가맹점의 최신 CONTRACT 이력 만기일이 오늘 이전이고 아직 CONTRACT_EXP가 + * 기록되지 않은 건에 대해 CONTRACT_EXP 이력을 적재한다(만료 확정). ONE XA txn. + * host 변수는 mm_contract_expire_cpy.h 를 EXEC SQL INCLUDE. + * Usage: mm_contract_expire_batch + */ +#include +#include +#include +#include +#include "mm_dbio.h" + +int main(int argc, char **argv) +{ + (void)argc; (void)argv; + EXEC SQL INCLUDE mm_contract_expire_cpy; + b_expired = 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 cex CURSOR FOR + SELECT h.merchant_id, h.after_val + FROM mm_merch_hist h + WHERE h.action = 'CONTRACT' + AND h.hist_id = (SELECT max(h2.hist_id) FROM mm_merch_hist h2 + WHERE h2.merchant_id = h.merchant_id AND h2.action = 'CONTRACT') + AND h.after_val < to_char(current_date, 'YYYYMMDD') + AND NOT EXISTS (SELECT 1 FROM mm_merch_hist e + WHERE e.merchant_id = h.merchant_id AND e.action = 'CONTRACT_EXP' + AND e.hist_id > h.hist_id) + ORDER BY h.merchant_id; + EXEC SQL OPEN cex; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH cex INTO :b_merch, :b_end; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cex; tpabort(0); return 1; } + if (mmdb_insert_merch_hist(b_merch, "CONTRACT_EXP", b_end, "EXPIRED", "만기 경과 자동 만료") < 0) { + EXEC SQL CLOSE cex; tpabort(0); return 1; + } + b_expired++; + } + EXEC SQL CLOSE cex; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mm_contract_expire_batch COMMIT: 만료=%ld건\n", b_expired); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mm/batch/mm_contract_expire_cpy.h b/app/src/mm/batch/mm_contract_expire_cpy.h new file mode 100644 index 0000000..22f7474 --- /dev/null +++ b/app/src/mm/batch/mm_contract_expire_cpy.h @@ -0,0 +1,6 @@ +/* mm_contract_expire_cpy.h - mm_contract_expire_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +char b_merch[64]; +char b_end[16]; +long b_expired; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/mm/batch/mm_fee_align_batch.pgc b/app/src/mm/batch/mm_fee_align_batch.pgc new file mode 100644 index 0000000..d1fb5bd --- /dev/null +++ b/app/src/mm/batch/mm_fee_align_batch.pgc @@ -0,0 +1,51 @@ +/* + * mm_fee_align_batch.pgc - 요율 마스터 미보유 가맹점 기본 요율 생성 배치 (JOIN 커서 + XA). + * + * CREDIT 요율(mm_fee_rate)이 없는 활성 가맹점에 대해 base merchant.mdr_bps(없으면 25bps)를 + * 기본 요율로 생성한다(요율 마스터 공백 보정). ONE global XA transaction. + * host 변수는 mm_fee_align_cpy.h 를 EXEC SQL INCLUDE. + * Usage: mm_fee_align_batch + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + (void)argc; (void)argv; + EXEC SQL INCLUDE mm_fee_align_cpy; + b_aligned = 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 fal CURSOR FOR + SELECT me.merchant_id, coalesce(m.mdr_bps, 25) + FROM mm_merchant_ext me + LEFT JOIN merchant m ON m.merchant_id = me.merchant_id + WHERE me.status = 'ACTIVE' + AND NOT EXISTS (SELECT 1 FROM mm_fee_rate f + WHERE f.merchant_id = me.merchant_id AND f.card_type = 'CREDIT') + ORDER BY me.merchant_id; + EXEC SQL OPEN fal; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH fal INTO :b_merch, :b_bps; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE fal; tpabort(0); return 1; } + EXEC SQL INSERT INTO mm_fee_rate (merchant_id, card_type, rate_bps) + VALUES (:b_merch, 'CREDIT', :b_bps); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE fal; tpabort(0); return 1; } + b_aligned++; + } + EXEC SQL CLOSE fal; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mm_fee_align_batch COMMIT: 기본요율 생성=%ld건\n", b_aligned); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mm/batch/mm_fee_align_cpy.h b/app/src/mm/batch/mm_fee_align_cpy.h new file mode 100644 index 0000000..bfa7f27 --- /dev/null +++ b/app/src/mm/batch/mm_fee_align_cpy.h @@ -0,0 +1,6 @@ +/* mm_fee_align_cpy.h - mm_fee_align_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +char b_merch[64]; +long b_bps; +long b_aligned; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/mm/batch/mm_limit_seed_batch.pgc b/app/src/mm/batch/mm_limit_seed_batch.pgc new file mode 100644 index 0000000..d6f1e53 --- /dev/null +++ b/app/src/mm/batch/mm_limit_seed_batch.pgc @@ -0,0 +1,51 @@ +/* + * mm_limit_seed_batch.pgc - 한도 마스터 미보유 가맹점 기본 한도 생성 배치 (커서 + XA). + * + * 활성 가맹점 중 mm_limit 행이 없는 건에 대해 기본 한도(일 1억/월 30억/건 1천만)를 + * 생성한다(한도 마스터 정합성 보정). ONE global XA transaction. + * host 변수는 mm_limit_seed_cpy.h 를 EXEC SQL INCLUDE. + * Usage: mm_limit_seed_batch + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + (void)argc; (void)argv; + EXEC SQL INCLUDE mm_limit_seed_cpy; + b_seeded = 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 lsd CURSOR FOR + SELECT me.merchant_id FROM mm_merchant_ext me + WHERE me.status = 'ACTIVE' + AND NOT EXISTS (SELECT 1 FROM mm_limit l WHERE l.merchant_id = me.merchant_id) + ORDER BY me.merchant_id; + EXEC SQL OPEN lsd; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH lsd INTO :b_merch; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE lsd; tpabort(0); return 1; } + EXEC SQL INSERT INTO mm_limit (merchant_id, daily_limit, monthly_limit, per_txn_limit) + VALUES (:b_merch, 100000000, 3000000000, 10000000); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE lsd; tpabort(0); return 1; } + b_seeded++; + } + EXEC SQL CLOSE lsd; + + EXEC SQL SELECT count(*) INTO :b_total FROM mm_limit; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mm_limit_seed_batch COMMIT: 신규한도=%ld 전체한도행=%ld\n", b_seeded, b_total); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mm/batch/mm_limit_seed_cpy.h b/app/src/mm/batch/mm_limit_seed_cpy.h new file mode 100644 index 0000000..5389cc1 --- /dev/null +++ b/app/src/mm/batch/mm_limit_seed_cpy.h @@ -0,0 +1,6 @@ +/* mm_limit_seed_cpy.h - mm_limit_seed_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +char b_merch[64]; +long b_seeded; +long b_total; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/mm/batch/mm_mcc_flag_batch.pgc b/app/src/mm/batch/mm_mcc_flag_batch.pgc new file mode 100644 index 0000000..22d919e --- /dev/null +++ b/app/src/mm/batch/mm_mcc_flag_batch.pgc @@ -0,0 +1,52 @@ +/* + * mm_mcc_flag_batch.pgc - 고위험 업종 가맹점 플래그 배치 (JOIN 커서 + XA). + * + * 리스크 등급이 기준 이상인 MCC를 쓰는 활성 가맹점에 MCC_FLAG 이력을 남겨 + * 모니터링 대상으로 표시한다. ONE global XA transaction. + * host 변수는 mm_mcc_flag_cpy.h 를 EXEC SQL INCLUDE. + * Usage: mm_mcc_flag_batch [MINRISK=4] + */ +#include +#include +#include +#include +#include "mm_dbio.h" + +int main(int argc, char **argv) +{ + char memo[48]; + EXEC SQL INCLUDE mm_mcc_flag_cpy; + b_flagged = 0; + b_minrisk = (argc > 1) ? atol(argv[1]) : 4; + if (b_minrisk < 1 || b_minrisk > 5) b_minrisk = 4; + + 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 mfl CURSOR FOR + SELECT me.merchant_id, me.mcc, m.risk_level + FROM mm_merchant_ext me JOIN mm_mcc m ON m.mcc = me.mcc + WHERE me.status = 'ACTIVE' AND m.risk_level >= :b_minrisk + ORDER BY me.merchant_id; + EXEC SQL OPEN mfl; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH mfl INTO :b_merch, :b_mcc, :b_risk; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE mfl; tpabort(0); return 1; } + snprintf(memo, sizeof(memo), "MCC %s 위험 %ld", b_mcc, b_risk); + if (mmdb_insert_merch_hist(b_merch, "MCC_FLAG", "", b_mcc, memo) < 0) { + EXEC SQL CLOSE mfl; tpabort(0); return 1; + } + b_flagged++; + } + EXEC SQL CLOSE mfl; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mm_mcc_flag_batch COMMIT: 기준위험>=%ld 플래그=%ld건\n", b_minrisk, b_flagged); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mm/batch/mm_mcc_flag_cpy.h b/app/src/mm/batch/mm_mcc_flag_cpy.h new file mode 100644 index 0000000..006ec37 --- /dev/null +++ b/app/src/mm/batch/mm_mcc_flag_cpy.h @@ -0,0 +1,8 @@ +/* mm_mcc_flag_cpy.h - mm_mcc_flag_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +long b_minrisk; +char b_merch[64]; +char b_mcc[8]; +long b_risk; +long b_flagged; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/mm/batch/mm_rate_expire_batch.pgc b/app/src/mm/batch/mm_rate_expire_batch.pgc new file mode 100644 index 0000000..5805b52 --- /dev/null +++ b/app/src/mm/batch/mm_rate_expire_batch.pgc @@ -0,0 +1,54 @@ +/* + * mm_rate_expire_batch.pgc - 장기 미결 요율 요청 자동 반려 배치 (커서 + XA). + * + * 접수 후 N일이 지나도록 승인/반려로 종결되지 않은 RATE_REQ를 자동 RATE_REJ 처리한다. + * (SLA 초과 요청 정리). ONE global XA transaction. + * host 변수는 mm_rate_expire_cpy.h 를 EXEC SQL INCLUDE. + * Usage: mm_rate_expire_batch [DAYS=14] + */ +#include +#include +#include +#include +#include "mm_dbio.h" + +int main(int argc, char **argv) +{ + EXEC SQL INCLUDE mm_rate_expire_cpy; + b_rejected = 0; + b_days = (argc > 1) ? atol(argv[1]) : 14; + if (b_days < 1) b_days = 14; + + 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 rex CURSOR FOR + SELECT DISTINCT r.merchant_id + FROM mm_merch_hist r + WHERE r.action = 'RATE_REQ' + AND r.created_at < now() - (:b_days || ' days')::interval + AND NOT EXISTS (SELECT 1 FROM mm_merch_hist d + WHERE d.merchant_id = r.merchant_id + AND d.action IN ('RATE_APV', 'RATE_REJ') AND d.hist_id > r.hist_id) + ORDER BY r.merchant_id; + EXEC SQL OPEN rex; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH rex INTO :b_merch; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE rex; tpabort(0); return 1; } + if (mmdb_insert_merch_hist(b_merch, "RATE_REJ", "PENDING", "EXPIRED", "SLA 초과 자동 반려") < 0) { + EXEC SQL CLOSE rex; tpabort(0); return 1; + } + b_rejected++; + } + EXEC SQL CLOSE rex; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mm_rate_expire_batch COMMIT: 기준=%ld일 자동반려=%ld건\n", b_days, b_rejected); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mm/batch/mm_rate_expire_cpy.h b/app/src/mm/batch/mm_rate_expire_cpy.h new file mode 100644 index 0000000..a786c0c --- /dev/null +++ b/app/src/mm/batch/mm_rate_expire_cpy.h @@ -0,0 +1,6 @@ +/* mm_rate_expire_cpy.h - mm_rate_expire_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +long b_days; +char b_merch[64]; +long b_rejected; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/mm/batch/mm_rel_count_batch.pgc b/app/src/mm/batch/mm_rel_count_batch.pgc new file mode 100644 index 0000000..1792064 --- /dev/null +++ b/app/src/mm/batch/mm_rel_count_batch.pgc @@ -0,0 +1,49 @@ +/* + * mm_rel_count_batch.pgc - 대표 가맹점별 하위 수 집계 배치 (커서 + XA). + * + * MERCH_REL 활성 관계를 대표(code_name) 기준으로 집계하여 각 대표 가맹점에 + * REL_CNT 이력을 남긴다(하위 규모 스냅샷). ONE global XA transaction. + * host 변수는 mm_rel_count_cpy.h 를 EXEC SQL INCLUDE. + * Usage: mm_rel_count_batch + */ +#include +#include +#include +#include +#include "mm_dbio.h" + +int main(int argc, char **argv) +{ + (void)argc; (void)argv; + EXEC SQL INCLUDE mm_rel_count_cpy; + b_groups = 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 rct CURSOR FOR + SELECT code_name, count(*) FROM mm_code + WHERE code_group = 'MERCH_REL' AND status = 'ACTIVE' + GROUP BY code_name ORDER BY code_name; + EXEC SQL OPEN rct; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH rct INTO :b_parent, :b_children; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE rct; tpabort(0); return 1; } + snprintf(b_memo, sizeof(b_memo), "하위 %ld개", b_children); + if (mmdb_insert_merch_hist(b_parent, "REL_CNT", "", b_memo, "하위 규모 집계") < 0) { + EXEC SQL CLOSE rct; tpabort(0); return 1; + } + b_groups++; + } + EXEC SQL CLOSE rct; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mm_rel_count_batch COMMIT: 대표 %ld개 집계\n", b_groups); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mm/batch/mm_rel_count_cpy.h b/app/src/mm/batch/mm_rel_count_cpy.h new file mode 100644 index 0000000..ea96874 --- /dev/null +++ b/app/src/mm/batch/mm_rel_count_cpy.h @@ -0,0 +1,7 @@ +/* mm_rel_count_cpy.h - mm_rel_count_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +char b_parent[64]; +long b_children; +char b_memo[64]; +long b_groups; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/mm/batch/mm_risk_score_batch.pgc b/app/src/mm/batch/mm_risk_score_batch.pgc new file mode 100644 index 0000000..780ef6f --- /dev/null +++ b/app/src/mm/batch/mm_risk_score_batch.pgc @@ -0,0 +1,60 @@ +/* + * mm_risk_score_batch.pgc - 전 가맹점 리스크 점수 재산정 배치 (JOIN 커서 + C 산정 + XA). + * + * 가맹점 등급/상태/MCC 위험도를 조합해 리스크 점수를 산출하고 밴드(LOW/MID/HIGH)를 + * RISK 이력으로 적재한다(온라인 MM_RISK_SCORE와 동일 공식). ONE global XA transaction. + * host 변수는 mm_risk_score_cpy.h 를 EXEC SQL INCLUDE. + * Usage: mm_risk_score_batch + */ +#include +#include +#include +#include +#include "mm_dbio.h" + +int main(int argc, char **argv) +{ + long score; + const char *band; + char memo[48]; + (void)argc; (void)argv; + EXEC SQL INCLUDE mm_risk_score_cpy; + b_scored = 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 rsc CURSOR FOR + SELECT me.merchant_id, me.grade, me.status, coalesce(m.risk_level, 1) + FROM mm_merchant_ext me + LEFT JOIN mm_mcc m ON m.mcc = me.mcc + WHERE me.status <> 'DELETED' + ORDER BY me.merchant_id; + EXEC SQL OPEN rsc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH rsc INTO :b_merch, :b_grade, :b_stat, :b_risk; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE rsc; tpabort(0); return 1; } + score = b_risk * 20; + if (b_grade[0] == 'C') score += 20; + else if (b_grade[0] == 'B') score += 10; + if (strcmp(b_stat, "ACTIVE") != 0) score += 15; + if (score > 100) score = 100; + band = (score >= 70) ? "HIGH" : (score >= 40) ? "MID" : "LOW"; + snprintf(memo, sizeof(memo), "리스크 %ld", score); + if (mmdb_insert_merch_hist(b_merch, "RISK", "", (char *)band, memo) < 0) { + EXEC SQL CLOSE rsc; tpabort(0); return 1; + } + b_scored++; + } + EXEC SQL CLOSE rsc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mm_risk_score_batch COMMIT: 재산정=%ld건\n", b_scored); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mm/batch/mm_risk_score_cpy.h b/app/src/mm/batch/mm_risk_score_cpy.h new file mode 100644 index 0000000..b2f018f --- /dev/null +++ b/app/src/mm/batch/mm_risk_score_cpy.h @@ -0,0 +1,8 @@ +/* mm_risk_score_cpy.h - mm_risk_score_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +char b_merch[64]; +char b_grade[8]; +char b_stat[16]; +long b_risk; +long b_scored; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/mm/batch/mm_term_retire_batch.pgc b/app/src/mm/batch/mm_term_retire_batch.pgc new file mode 100644 index 0000000..80fcc45 --- /dev/null +++ b/app/src/mm/batch/mm_term_retire_batch.pgc @@ -0,0 +1,34 @@ +/* + * mm_term_retire_batch.pgc - 비활성 단말 일괄 폐기 배치 (set-based UPDATE + XA). + * + * 개통 해지되어 INACTIVE로 남은 단말을 일괄 RETIRED로 확정한다. 활성/이미폐기 단말은 + * 건드리지 않는다. ONE global XA transaction. host 변수는 mm_term_retire_cpy.h. + * Usage: mm_term_retire_batch + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + (void)argc; (void)argv; + EXEC SQL INCLUDE mm_term_retire_cpy; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL UPDATE mm_terminal SET status = 'RETIRED' WHERE status = 'INACTIVE'; + if (sqlca.sqlcode < 0) { fprintf(stderr, "UPDATE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + b_retired = sqlca.sqlerrd[2]; + + EXEC SQL SELECT count(*) INTO :b_remain FROM mm_terminal WHERE status = 'ACTIVE'; + if (sqlca.sqlcode < 0) { tpabort(0); return 1; } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> mm_term_retire_batch COMMIT: 폐기=%ld 잔여활성=%ld\n", b_retired, b_remain); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/mm/batch/mm_term_retire_cpy.h b/app/src/mm/batch/mm_term_retire_cpy.h new file mode 100644 index 0000000..de2cc18 --- /dev/null +++ b/app/src/mm/batch/mm_term_retire_cpy.h @@ -0,0 +1,5 @@ +/* mm_term_retire_cpy.h - mm_term_retire_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +long b_retired; +long b_remain; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/mm/mm_svr.pgc b/app/src/mm/mm_svr.pgc index 27092bb..3be39e4 100644 --- a/app/src/mm/mm_svr.pgc +++ b/app/src/mm/mm_svr.pgc @@ -92,6 +92,66 @@ #include "MM_BIZNO_CHECK.h" #include "MM_BIZNO_UPD.h" +/* ----- 신규 마스터 서비스 58종 (단말 생애주기/심사/계약/제휴/관계/MCC/요율/휴폐업/계좌/리스크/감사/정합성) */ +#include "MM_TERM_INSTALL.h" +#include "MM_TERM_SWAP.h" +#include "MM_TERM_ACTIVATE.h" +#include "MM_TERM_DEACT.h" +#include "MM_TERM_RETIRE.h" +#include "MM_TERM_REASSIGN.h" +#include "MM_TERM_MODEL_UPD.h" +#include "MM_TERM_LIST.h" +#include "MM_TERM_HIST.h" +#include "MM_APPLY_HOLD.h" +#include "MM_APPLY_RESUME.h" +#include "MM_APPLY_DOCCHK.h" +#include "MM_APPLY_SCORE.h" +#include "MM_APPLY_ASSIGN.h" +#include "MM_APPLY_WITHDRAW.h" +#include "MM_APPLY_CNT.h" +#include "MM_CONTRACT_AMEND.h" +#include "MM_CONTRACT_SUSPEND.h" +#include "MM_CONTRACT_REACT.h" +#include "MM_CONTRACT_HIST.h" +#include "MM_CONTRACT_EXPCHK.h" +#include "MM_PARTNER_UPD.h" +#include "MM_PARTNER_LIST.h" +#include "MM_AGENT_REG.h" +#include "MM_AGENT_INQ.h" +#include "MM_AGENT_STATUS.h" +#include "MM_BRANCH_REG.h" +#include "MM_BRANCH_INQ.h" +#include "MM_BRANCH_LIST.h" +#include "MM_REL_PARENT.h" +#include "MM_REL_CHILDREN.h" +#include "MM_REL_MOVE.h" +#include "MM_REL_VERIFY.h" +#include "MM_MCC_DEL.h" +#include "MM_MCC_ASSIGN.h" +#include "MM_MCC_RISKSET.h" +#include "MM_MCC_CNT.h" +#include "MM_RATE_STATUS.h" +#include "MM_RATE_HIST.h" +#include "MM_RATE_CANCEL.h" +#include "MM_RATE_PENDING.h" +#include "MM_MERCH_DORMANT.h" +#include "MM_BIZ_SUSPEND.h" +#include "MM_BIZ_REOPEN.h" +#include "MM_BIZ_CLOSE.h" +#include "MM_ACCT_REG.h" +#include "MM_ACCT_INQ.h" +#include "MM_ACCT_MASK.h" +#include "MM_RISK_SCORE.h" +#include "MM_RISK_SET.h" +#include "MM_RISK_INQ.h" +#include "MM_GRADE_HIST.h" +#include "MM_AUDIT_STAT.h" +#include "MM_AUDIT_DIFF.h" +#include "MM_CHK_ACCT.h" +#include "MM_CHK_MCC.h" +#include "MM_CHK_ORPHAN.h" +#include "MM_CHK_GRADE.h" + /* ----- advertise table: {service name, function} (fn is extern from its file) */ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"MM_MERCH_REG", MM_MERCH_REG}, @@ -169,6 +229,64 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"MM_CODEMAP_DEL", MM_CODEMAP_DEL}, {"MM_BIZNO_CHECK", MM_BIZNO_CHECK}, {"MM_BIZNO_UPD", MM_BIZNO_UPD}, + {"MM_TERM_INSTALL", MM_TERM_INSTALL}, + {"MM_TERM_SWAP", MM_TERM_SWAP}, + {"MM_TERM_ACTIVATE", MM_TERM_ACTIVATE}, + {"MM_TERM_DEACT", MM_TERM_DEACT}, + {"MM_TERM_RETIRE", MM_TERM_RETIRE}, + {"MM_TERM_REASSIGN", MM_TERM_REASSIGN}, + {"MM_TERM_MODEL_UPD", MM_TERM_MODEL_UPD}, + {"MM_TERM_LIST", MM_TERM_LIST}, + {"MM_TERM_HIST", MM_TERM_HIST}, + {"MM_APPLY_HOLD", MM_APPLY_HOLD}, + {"MM_APPLY_RESUME", MM_APPLY_RESUME}, + {"MM_APPLY_DOCCHK", MM_APPLY_DOCCHK}, + {"MM_APPLY_SCORE", MM_APPLY_SCORE}, + {"MM_APPLY_ASSIGN", MM_APPLY_ASSIGN}, + {"MM_APPLY_WITHDRAW", MM_APPLY_WITHDRAW}, + {"MM_APPLY_CNT", MM_APPLY_CNT}, + {"MM_CONTRACT_AMEND", MM_CONTRACT_AMEND}, + {"MM_CONTRACT_SUSPEND", MM_CONTRACT_SUSPEND}, + {"MM_CONTRACT_REACT", MM_CONTRACT_REACT}, + {"MM_CONTRACT_HIST", MM_CONTRACT_HIST}, + {"MM_CONTRACT_EXPCHK", MM_CONTRACT_EXPCHK}, + {"MM_PARTNER_UPD", MM_PARTNER_UPD}, + {"MM_PARTNER_LIST", MM_PARTNER_LIST}, + {"MM_AGENT_REG", MM_AGENT_REG}, + {"MM_AGENT_INQ", MM_AGENT_INQ}, + {"MM_AGENT_STATUS", MM_AGENT_STATUS}, + {"MM_BRANCH_REG", MM_BRANCH_REG}, + {"MM_BRANCH_INQ", MM_BRANCH_INQ}, + {"MM_BRANCH_LIST", MM_BRANCH_LIST}, + {"MM_REL_PARENT", MM_REL_PARENT}, + {"MM_REL_CHILDREN", MM_REL_CHILDREN}, + {"MM_REL_MOVE", MM_REL_MOVE}, + {"MM_REL_VERIFY", MM_REL_VERIFY}, + {"MM_MCC_DEL", MM_MCC_DEL}, + {"MM_MCC_ASSIGN", MM_MCC_ASSIGN}, + {"MM_MCC_RISKSET", MM_MCC_RISKSET}, + {"MM_MCC_CNT", MM_MCC_CNT}, + {"MM_RATE_STATUS", MM_RATE_STATUS}, + {"MM_RATE_HIST", MM_RATE_HIST}, + {"MM_RATE_CANCEL", MM_RATE_CANCEL}, + {"MM_RATE_PENDING", MM_RATE_PENDING}, + {"MM_MERCH_DORMANT", MM_MERCH_DORMANT}, + {"MM_BIZ_SUSPEND", MM_BIZ_SUSPEND}, + {"MM_BIZ_REOPEN", MM_BIZ_REOPEN}, + {"MM_BIZ_CLOSE", MM_BIZ_CLOSE}, + {"MM_ACCT_REG", MM_ACCT_REG}, + {"MM_ACCT_INQ", MM_ACCT_INQ}, + {"MM_ACCT_MASK", MM_ACCT_MASK}, + {"MM_RISK_SCORE", MM_RISK_SCORE}, + {"MM_RISK_SET", MM_RISK_SET}, + {"MM_RISK_INQ", MM_RISK_INQ}, + {"MM_GRADE_HIST", MM_GRADE_HIST}, + {"MM_AUDIT_STAT", MM_AUDIT_STAT}, + {"MM_AUDIT_DIFF", MM_AUDIT_DIFF}, + {"MM_CHK_ACCT", MM_CHK_ACCT}, + {"MM_CHK_MCC", MM_CHK_MCC}, + {"MM_CHK_ORPHAN", MM_CHK_ORPHAN}, + {"MM_CHK_GRADE", MM_CHK_GRADE}, {NULL, NULL} }; diff --git a/app/src/mm/svc/MM_ACCT_INQ.h b/app/src/mm/svc/MM_ACCT_INQ.h new file mode 100644 index 0000000..111439e --- /dev/null +++ b/app/src/mm/svc/MM_ACCT_INQ.h @@ -0,0 +1,12 @@ +/* + * MM_ACCT_INQ.h - mm 서비스 MM_ACCT_INQ 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_STR1 + */ +#ifndef MM_ACCT_INQ_H +#define MM_ACCT_INQ_H + +#include "mm_svc.h" + +void MM_ACCT_INQ(TPSVCINFO *p); + +#endif /* MM_ACCT_INQ_H */ diff --git a/app/src/mm/svc/MM_ACCT_INQ.pgc b/app/src/mm/svc/MM_ACCT_INQ.pgc new file mode 100644 index 0000000..b7e4bd3 --- /dev/null +++ b/app/src/mm/svc/MM_ACCT_INQ.pgc @@ -0,0 +1,29 @@ +/* + * MM_ACCT_INQ.pgc - mm 모듈 서비스 MM_ACCT_INQ (one service = one file). + * 기능: 정산계좌 원문 조회 (등록 여부 판정 포함). + * 입력: T_MERCHANT. 출력: T_STR1=계좌번호, T_ARG1=1이면 등록됨 0이면 미등록. + * RC: 0=조회, 1=가맹점 없음. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_ACCT_INQ.h" + +void MM_ACCT_INQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_acct[40]; + 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 settle_acct INTO :h_acct FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + Bchg(b, T_STR1, 0, h_acct, 0L); + setl(b, T_ARG1, h_acct[0] != 0 ? 1 : 0); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_ACCT_MASK.h b/app/src/mm/svc/MM_ACCT_MASK.h new file mode 100644 index 0000000..d4e6007 --- /dev/null +++ b/app/src/mm/svc/MM_ACCT_MASK.h @@ -0,0 +1,12 @@ +/* + * MM_ACCT_MASK.h - mm 서비스 MM_ACCT_MASK 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_STR1 + */ +#ifndef MM_ACCT_MASK_H +#define MM_ACCT_MASK_H + +#include "mm_svc.h" + +void MM_ACCT_MASK(TPSVCINFO *p); + +#endif /* MM_ACCT_MASK_H */ diff --git a/app/src/mm/svc/MM_ACCT_MASK.pgc b/app/src/mm/svc/MM_ACCT_MASK.pgc new file mode 100644 index 0000000..ba071ca --- /dev/null +++ b/app/src/mm/svc/MM_ACCT_MASK.pgc @@ -0,0 +1,35 @@ +/* + * MM_ACCT_MASK.pgc - mm 모듈 서비스 MM_ACCT_MASK (one service = one file). + * 기능: 정산계좌 마스킹 조회 (앞3 뒤2만 노출, 중간은 * 처리하여 반환 - 개인정보 보호). + * 입력: T_MERCHANT. 출력: T_STR1=마스킹된 계좌. + * RC: 0=조회, 1=가맹점 없음, 2=계좌 미등록. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_ACCT_MASK.h" + +void MM_ACCT_MASK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], masked[40]; + size_t i, len; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_acct[40]; + 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 settle_acct INTO :h_acct FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + len = strlen(h_acct); + if (len == 0) { setl(b, T_RC, 2); OK(b); } + for (i = 0; i < len; i++) + masked[i] = (i < 3 || i >= len - 2) ? h_acct[i] : '*'; + masked[len] = 0; + + Bchg(b, T_STR1, 0, masked, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_ACCT_REG.h b/app/src/mm/svc/MM_ACCT_REG.h new file mode 100644 index 0000000..1803232 --- /dev/null +++ b/app/src/mm/svc/MM_ACCT_REG.h @@ -0,0 +1,12 @@ +/* + * MM_ACCT_REG.h - mm 서비스 MM_ACCT_REG 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_STR1 + */ +#ifndef MM_ACCT_REG_H +#define MM_ACCT_REG_H + +#include "mm_svc.h" + +void MM_ACCT_REG(TPSVCINFO *p); + +#endif /* MM_ACCT_REG_H */ diff --git a/app/src/mm/svc/MM_ACCT_REG.pgc b/app/src/mm/svc/MM_ACCT_REG.pgc new file mode 100644 index 0000000..0766321 --- /dev/null +++ b/app/src/mm/svc/MM_ACCT_REG.pgc @@ -0,0 +1,38 @@ +/* + * MM_ACCT_REG.pgc - mm 모듈 서비스 MM_ACCT_REG (one service = one file). + * 기능: 정산계좌 등록/변경 (숫자 10~16자리 형식 검증 통과분만 마스터에 반영, 검증 이력 기록). + * 입력: T_MERCHANT, T_STR1=계좌번호. RC: 0=등록, 1=가맹점 없음, 3=형식 오류. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_ACCT_REG.h" + +void MM_ACCT_REG(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], acct[40]; + size_t i, len; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_acct[40]; + long h_exist; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_STR1, acct, sizeof(acct)); + len = strlen(acct); + if (len < 10 || len > 16) { setl(b, T_RC, 3); OK(b); } + for (i = 0; i < len; i++) + if (acct[i] < '0' || acct[i] > '9') { setl(b, T_RC, 3); OK(b); } + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + strncpy(h_acct, acct, sizeof(h_acct)-1); h_acct[sizeof(h_acct)-1] = 0; + + EXEC SQL SELECT count(*) INTO :h_exist FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_exist == 0) { setl(b, T_RC, 1); OK(b); } + + EXEC SQL UPDATE mm_merchant_ext SET settle_acct = :h_acct, updated_at = now() + WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + if (mmdb_insert_merch_hist(h_merch, "ACCT_REG", "", h_acct, "정산계좌 등록") < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_AGENT_INQ.h b/app/src/mm/svc/MM_AGENT_INQ.h new file mode 100644 index 0000000..71906da --- /dev/null +++ b/app/src/mm/svc/MM_AGENT_INQ.h @@ -0,0 +1,12 @@ +/* + * MM_AGENT_INQ.h - mm 서비스 MM_AGENT_INQ 카피북 (copybook / record header). + * 사용 UBF 필드: T_KEY1, T_STR1, T_STATUS + */ +#ifndef MM_AGENT_INQ_H +#define MM_AGENT_INQ_H + +#include "mm_svc.h" + +void MM_AGENT_INQ(TPSVCINFO *p); + +#endif /* MM_AGENT_INQ_H */ diff --git a/app/src/mm/svc/MM_AGENT_INQ.pgc b/app/src/mm/svc/MM_AGENT_INQ.pgc new file mode 100644 index 0000000..73f6d6f --- /dev/null +++ b/app/src/mm/svc/MM_AGENT_INQ.pgc @@ -0,0 +1,31 @@ +/* + * MM_AGENT_INQ.pgc - mm 모듈 서비스 MM_AGENT_INQ (one service = one file). + * 기능: 모집인 단건 조회 (AGENT 코드그룹에서 명칭/상태 반환). + * 입력: T_KEY1=모집인 코드. 출력: T_STR1=명칭, T_STATUS=상태. + * RC: 0=조회, 1=모집인 없음. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_AGENT_INQ.h" + +void MM_AGENT_INQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char code[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_code[16], h_name[64], h_stat[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, code, sizeof(code)); + if (code[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_code, code, sizeof(h_code)-1); h_code[sizeof(h_code)-1] = 0; + + EXEC SQL SELECT code_name, status INTO :h_name, :h_stat + FROM mm_code WHERE code_group = 'AGENT' AND code = :h_code; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + Bchg(b, T_STR1, 0, h_name, 0L); + Bchg(b, T_STATUS, 0, h_stat, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_AGENT_REG.h b/app/src/mm/svc/MM_AGENT_REG.h new file mode 100644 index 0000000..6bba0f8 --- /dev/null +++ b/app/src/mm/svc/MM_AGENT_REG.h @@ -0,0 +1,12 @@ +/* + * MM_AGENT_REG.h - mm 서비스 MM_AGENT_REG 카피북 (copybook / record header). + * 사용 UBF 필드: T_KEY1, T_STR1, T_STR2 + */ +#ifndef MM_AGENT_REG_H +#define MM_AGENT_REG_H + +#include "mm_svc.h" + +void MM_AGENT_REG(TPSVCINFO *p); + +#endif /* MM_AGENT_REG_H */ diff --git a/app/src/mm/svc/MM_AGENT_REG.pgc b/app/src/mm/svc/MM_AGENT_REG.pgc new file mode 100644 index 0000000..0ead989 --- /dev/null +++ b/app/src/mm/svc/MM_AGENT_REG.pgc @@ -0,0 +1,41 @@ +/* + * MM_AGENT_REG.pgc - mm 모듈 서비스 MM_AGENT_REG (one service = one file). + * 기능: 모집인 등록 (AGENT 코드그룹 upsert, 소속 제휴사 코드를 T_STR2로 검증). + * 입력: T_KEY1=모집인 코드, T_STR1=모집인명, T_STR2=소속 제휴사 코드(선택). + * RC: 0=등록, 3=필수값 누락, 2=소속 제휴사 미존재. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_AGENT_REG.h" + +void MM_AGENT_REG(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char code[16], name[64], pcode[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_code[16], h_name[64], h_pcode[16]; + long h_pok; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, code, sizeof(code)); + gets_(b, T_STR1, name, sizeof(name)); + gets_(b, T_STR2, pcode, sizeof(pcode)); + if (code[0] == 0 || name[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_code, code, sizeof(h_code)-1); h_code[sizeof(h_code)-1] = 0; + strncpy(h_name, name, sizeof(h_name)-1); h_name[sizeof(h_name)-1] = 0; + strncpy(h_pcode, pcode, sizeof(h_pcode)-1); h_pcode[sizeof(h_pcode)-1] = 0; + + if (h_pcode[0] != 0) { + EXEC SQL SELECT count(*) INTO :h_pok FROM mm_code + WHERE code_group = 'PARTNER' AND code = :h_pcode; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_pok == 0) { setl(b, T_RC, 2); OK(b); } + } + + EXEC SQL INSERT INTO mm_code (code_group, code, code_name, status) + VALUES ('AGENT', :h_code, :h_name, 'ACTIVE') + ON CONFLICT (code_group, code) + DO UPDATE SET code_name = EXCLUDED.code_name, status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_AGENT_STATUS.h b/app/src/mm/svc/MM_AGENT_STATUS.h new file mode 100644 index 0000000..f74c98e --- /dev/null +++ b/app/src/mm/svc/MM_AGENT_STATUS.h @@ -0,0 +1,12 @@ +/* + * MM_AGENT_STATUS.h - mm 서비스 MM_AGENT_STATUS 카피북 (copybook / record header). + * 사용 UBF 필드: T_KEY1, T_STATUS + */ +#ifndef MM_AGENT_STATUS_H +#define MM_AGENT_STATUS_H + +#include "mm_svc.h" + +void MM_AGENT_STATUS(TPSVCINFO *p); + +#endif /* MM_AGENT_STATUS_H */ diff --git a/app/src/mm/svc/MM_AGENT_STATUS.pgc b/app/src/mm/svc/MM_AGENT_STATUS.pgc new file mode 100644 index 0000000..37f6608 --- /dev/null +++ b/app/src/mm/svc/MM_AGENT_STATUS.pgc @@ -0,0 +1,36 @@ +/* + * MM_AGENT_STATUS.pgc - mm 모듈 서비스 MM_AGENT_STATUS (one service = one file). + * 기능: 모집인 상태 변경 (ACTIVE/SUSPENDED/RETIRED 허용값 검증 후 갱신). + * 입력: T_KEY1=모집인 코드, T_STATUS=신규 상태. + * RC: 0=변경, 1=모집인 없음, 3=허용되지 않는 상태값. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_AGENT_STATUS.h" + +void MM_AGENT_STATUS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char code[16], stat[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_code[16], h_stat[16]; + long h_exist; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, code, sizeof(code)); + gets_(b, T_STATUS, stat, sizeof(stat)); + if (strcmp(stat, "ACTIVE") != 0 && strcmp(stat, "SUSPENDED") != 0 + && strcmp(stat, "RETIRED") != 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_code, code, sizeof(h_code)-1); h_code[sizeof(h_code)-1] = 0; + strncpy(h_stat, stat, sizeof(h_stat)-1); h_stat[sizeof(h_stat)-1] = 0; + + EXEC SQL SELECT count(*) INTO :h_exist FROM mm_code + WHERE code_group = 'AGENT' AND code = :h_code; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_exist == 0) { setl(b, T_RC, 1); OK(b); } + + EXEC SQL UPDATE mm_code SET status = :h_stat + WHERE code_group = 'AGENT' AND code = :h_code; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_APPLY_ASSIGN.h b/app/src/mm/svc/MM_APPLY_ASSIGN.h new file mode 100644 index 0000000..68c8e07 --- /dev/null +++ b/app/src/mm/svc/MM_APPLY_ASSIGN.h @@ -0,0 +1,12 @@ +/* + * MM_APPLY_ASSIGN.h - mm 서비스 MM_APPLY_ASSIGN 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_STR1 + */ +#ifndef MM_APPLY_ASSIGN_H +#define MM_APPLY_ASSIGN_H + +#include "mm_svc.h" + +void MM_APPLY_ASSIGN(TPSVCINFO *p); + +#endif /* MM_APPLY_ASSIGN_H */ diff --git a/app/src/mm/svc/MM_APPLY_ASSIGN.pgc b/app/src/mm/svc/MM_APPLY_ASSIGN.pgc new file mode 100644 index 0000000..5601170 --- /dev/null +++ b/app/src/mm/svc/MM_APPLY_ASSIGN.pgc @@ -0,0 +1,31 @@ +/* + * MM_APPLY_ASSIGN.pgc - mm 모듈 서비스 MM_APPLY_ASSIGN (one service = one file). + * 기능: 심사 담당자 배정 (담당자 코드를 REVIEWER 매핑 이력에 기록). + * 입력: T_MERCHANT, T_STR1=담당자 코드. RC: 0=배정, 1=신청 건 없음, 3=담당자 누락, 4=최종 상태(배정 불가). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_APPLY_ASSIGN.h" + +void MM_APPLY_ASSIGN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], rev[32]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_rev[32], h_stat[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_STR1, rev, sizeof(rev)); + if (rev[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + strncpy(h_rev, rev, sizeof(h_rev)-1); h_rev[sizeof(h_rev)-1] = 0; + + EXEC SQL SELECT status INTO :h_stat FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_stat, "APPLIED") != 0 && strcmp(h_stat, "HOLD") != 0) { setl(b, T_RC, 4); OK(b); } + + if (mmdb_insert_merch_hist(h_merch, "APPLY_ASSIGN", "", h_rev, "심사 담당자 배정") < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_APPLY_CNT.h b/app/src/mm/svc/MM_APPLY_CNT.h new file mode 100644 index 0000000..5ef94a3 --- /dev/null +++ b/app/src/mm/svc/MM_APPLY_CNT.h @@ -0,0 +1,12 @@ +/* + * MM_APPLY_CNT.h - mm 서비스 MM_APPLY_CNT 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR1, T_COUNT + */ +#ifndef MM_APPLY_CNT_H +#define MM_APPLY_CNT_H + +#include "mm_svc.h" + +void MM_APPLY_CNT(TPSVCINFO *p); + +#endif /* MM_APPLY_CNT_H */ diff --git a/app/src/mm/svc/MM_APPLY_CNT.pgc b/app/src/mm/svc/MM_APPLY_CNT.pgc new file mode 100644 index 0000000..83c6925 --- /dev/null +++ b/app/src/mm/svc/MM_APPLY_CNT.pgc @@ -0,0 +1,34 @@ +/* + * MM_APPLY_CNT.pgc - mm 모듈 서비스 MM_APPLY_CNT (one service = one file). + * 기능: 심사 상태별 신청 건수 집계 (대시보드용, 특정 상태 또는 전 워크플로 상태). + * 입력: T_STR1=상태 필터(빈값이면 심사 대상 전체 APPLIED+HOLD). 출력: T_COUNT. + * RC: 항상 0. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_APPLY_CNT.h" + +void MM_APPLY_CNT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char filt[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_filt[16]; + long h_cnt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR1, filt, sizeof(filt)); + strncpy(h_filt, filt, sizeof(h_filt)-1); h_filt[sizeof(h_filt)-1] = 0; + + if (h_filt[0] == 0) { + EXEC SQL SELECT count(*) INTO :h_cnt FROM mm_merchant_ext + WHERE status IN ('APPLIED', 'HOLD'); + } else { + EXEC SQL SELECT count(*) INTO :h_cnt FROM mm_merchant_ext + WHERE status = :h_filt; + } + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_APPLY_DOCCHK.h b/app/src/mm/svc/MM_APPLY_DOCCHK.h new file mode 100644 index 0000000..718ad2b --- /dev/null +++ b/app/src/mm/svc/MM_APPLY_DOCCHK.h @@ -0,0 +1,12 @@ +/* + * MM_APPLY_DOCCHK.h - mm 서비스 MM_APPLY_DOCCHK 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_ARG1 + */ +#ifndef MM_APPLY_DOCCHK_H +#define MM_APPLY_DOCCHK_H + +#include "mm_svc.h" + +void MM_APPLY_DOCCHK(TPSVCINFO *p); + +#endif /* MM_APPLY_DOCCHK_H */ diff --git a/app/src/mm/svc/MM_APPLY_DOCCHK.pgc b/app/src/mm/svc/MM_APPLY_DOCCHK.pgc new file mode 100644 index 0000000..2b51e1e --- /dev/null +++ b/app/src/mm/svc/MM_APPLY_DOCCHK.pgc @@ -0,0 +1,38 @@ +/* + * MM_APPLY_DOCCHK.pgc - mm 모듈 서비스 MM_APPLY_DOCCHK (one service = one file). + * 기능: 심사 서류 접수 확인 (필수 마스터 필드 충족도 점검, 심사 서브단계). + * 사업자번호/상호/MCC 충족 여부를 점수화(0~3). 3점이면 서류 완비. + * 입력: T_MERCHANT. 출력: T_ARG1=충족 항목수(0~3), T_STR1=DOC_OK/DOC_LACK. + * RC: 0=조회, 1=신청 건 없음. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_APPLY_DOCCHK.h" + +void MM_APPLY_DOCCHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + int score = 0; + const char *verdict; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_biz[16], h_name[64], h_mcc[8]; + 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 biz_reg_no, name, mcc INTO :h_biz, :h_name, :h_mcc + FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + if (strlen(h_biz) == 10) score++; + if (h_name[0] != 0) score++; + if (h_mcc[0] != 0 && strcmp(h_mcc, "0000") != 0) score++; + verdict = (score == 3) ? "DOC_OK" : "DOC_LACK"; + + Bchg(b, T_STR1, 0, (char *)verdict, 0L); + setl(b, T_ARG1, score); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_APPLY_HOLD.h b/app/src/mm/svc/MM_APPLY_HOLD.h new file mode 100644 index 0000000..a75113a --- /dev/null +++ b/app/src/mm/svc/MM_APPLY_HOLD.h @@ -0,0 +1,12 @@ +/* + * MM_APPLY_HOLD.h - mm 서비스 MM_APPLY_HOLD 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_REASON + */ +#ifndef MM_APPLY_HOLD_H +#define MM_APPLY_HOLD_H + +#include "mm_svc.h" + +void MM_APPLY_HOLD(TPSVCINFO *p); + +#endif /* MM_APPLY_HOLD_H */ diff --git a/app/src/mm/svc/MM_APPLY_HOLD.pgc b/app/src/mm/svc/MM_APPLY_HOLD.pgc new file mode 100644 index 0000000..a0ad522 --- /dev/null +++ b/app/src/mm/svc/MM_APPLY_HOLD.pgc @@ -0,0 +1,34 @@ +/* + * MM_APPLY_HOLD.pgc - mm 모듈 서비스 MM_APPLY_HOLD (one service = one file). + * 기능: 심사 진행 중 신청 건 보류 (APPLIED->HOLD, 사유 이력 기록). + * 입력: T_MERCHANT, T_REASON=보류 사유(기본 "서류 보완 대기"). + * RC: 0=보류, 1=신청 건 없음, 4=심사중(APPLIED) 상태 아님. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_APPLY_HOLD.h" + +void MM_APPLY_HOLD(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], reason[128]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_stat[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_REASON, reason, sizeof(reason)); + if (reason[0] == 0) strcpy(reason, "서류 보완 대기"); + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + + EXEC SQL SELECT status INTO :h_stat FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_stat, "APPLIED") != 0) { setl(b, T_RC, 4); OK(b); } + + EXEC SQL UPDATE mm_merchant_ext SET status = 'HOLD', updated_at = now() + WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + if (mmdb_insert_merch_hist(h_merch, "APPLY_HOLD", "APPLIED", "HOLD", reason) < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_APPLY_RESUME.h b/app/src/mm/svc/MM_APPLY_RESUME.h new file mode 100644 index 0000000..3cf2e1f --- /dev/null +++ b/app/src/mm/svc/MM_APPLY_RESUME.h @@ -0,0 +1,12 @@ +/* + * MM_APPLY_RESUME.h - mm 서비스 MM_APPLY_RESUME 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT + */ +#ifndef MM_APPLY_RESUME_H +#define MM_APPLY_RESUME_H + +#include "mm_svc.h" + +void MM_APPLY_RESUME(TPSVCINFO *p); + +#endif /* MM_APPLY_RESUME_H */ diff --git a/app/src/mm/svc/MM_APPLY_RESUME.pgc b/app/src/mm/svc/MM_APPLY_RESUME.pgc new file mode 100644 index 0000000..0049229 --- /dev/null +++ b/app/src/mm/svc/MM_APPLY_RESUME.pgc @@ -0,0 +1,38 @@ +/* + * MM_APPLY_RESUME.pgc - mm 모듈 서비스 MM_APPLY_RESUME (one service = one file). + * 기능: 보류된 심사 재개 (HOLD->APPLIED, 심사 대기열 복귀). + * 입력: T_MERCHANT. 출력: T_ARG1=해당 가맹점 누적 보류 횟수. + * RC: 0=재개, 1=신청 건 없음, 4=보류(HOLD) 상태 아님. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_APPLY_RESUME.h" + +void MM_APPLY_RESUME(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_stat[16]; + long h_holds; + 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 INTO :h_stat FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_stat, "HOLD") != 0) { setl(b, T_RC, 4); OK(b); } + + EXEC SQL SELECT count(*) INTO :h_holds FROM mm_merch_hist + WHERE merchant_id = :h_merch AND action = 'APPLY_HOLD'; + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL UPDATE mm_merchant_ext SET status = 'APPLIED', updated_at = now() + WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + if (mmdb_insert_merch_hist(h_merch, "APPLY_RESUME", "HOLD", "APPLIED", "심사 재개") < 0) FAIL(b); + setl(b, T_ARG1, h_holds); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_APPLY_SCORE.h b/app/src/mm/svc/MM_APPLY_SCORE.h new file mode 100644 index 0000000..06dfa6d --- /dev/null +++ b/app/src/mm/svc/MM_APPLY_SCORE.h @@ -0,0 +1,12 @@ +/* + * MM_APPLY_SCORE.h - mm 서비스 MM_APPLY_SCORE 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_ARG1, T_STR1 + */ +#ifndef MM_APPLY_SCORE_H +#define MM_APPLY_SCORE_H + +#include "mm_svc.h" + +void MM_APPLY_SCORE(TPSVCINFO *p); + +#endif /* MM_APPLY_SCORE_H */ diff --git a/app/src/mm/svc/MM_APPLY_SCORE.pgc b/app/src/mm/svc/MM_APPLY_SCORE.pgc new file mode 100644 index 0000000..08dcb80 --- /dev/null +++ b/app/src/mm/svc/MM_APPLY_SCORE.pgc @@ -0,0 +1,44 @@ +/* + * MM_APPLY_SCORE.pgc - mm 모듈 서비스 MM_APPLY_SCORE (one service = one file). + * 기능: 심사 점수 산정 (MCC 리스크 + 서류 완비도 가중 합산, 등급 예비판정). + * 점수 = 100 - MCC위험*15 - 서류미비항목*20. 60미만 REVIEW, 이상 PASS. + * 입력: T_MERCHANT. 출력: T_ARG1=점수, T_STR1=PASS/REVIEW. RC: 1=신청 건 없음. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_APPLY_SCORE.h" + +void MM_APPLY_SCORE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], memo[64]; + long score; + const char *verdict; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_biz[16], h_mcc[8]; + long h_risk; + 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 biz_reg_no, mcc INTO :h_biz, :h_mcc + FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + h_risk = 1; + EXEC SQL SELECT risk_level INTO :h_risk FROM mm_mcc WHERE mcc = :h_mcc; + if (sqlca.sqlcode < 0) FAIL(b); + + score = 100 - h_risk * 15; + if (strlen(h_biz) != 10) score -= 20; + if (score < 0) score = 0; + verdict = (score >= 60) ? "PASS" : "REVIEW"; + snprintf(memo, sizeof(memo), "심사점수 %ld", score); + if (mmdb_insert_merch_hist(h_merch, "APPLY_SCORE", "", (char *)verdict, memo) < 0) FAIL(b); + + Bchg(b, T_STR1, 0, (char *)verdict, 0L); + setl(b, T_ARG1, score); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_APPLY_WITHDRAW.h b/app/src/mm/svc/MM_APPLY_WITHDRAW.h new file mode 100644 index 0000000..28f966f --- /dev/null +++ b/app/src/mm/svc/MM_APPLY_WITHDRAW.h @@ -0,0 +1,12 @@ +/* + * MM_APPLY_WITHDRAW.h - mm 서비스 MM_APPLY_WITHDRAW 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_REASON + */ +#ifndef MM_APPLY_WITHDRAW_H +#define MM_APPLY_WITHDRAW_H + +#include "mm_svc.h" + +void MM_APPLY_WITHDRAW(TPSVCINFO *p); + +#endif /* MM_APPLY_WITHDRAW_H */ diff --git a/app/src/mm/svc/MM_APPLY_WITHDRAW.pgc b/app/src/mm/svc/MM_APPLY_WITHDRAW.pgc new file mode 100644 index 0000000..ae03e62 --- /dev/null +++ b/app/src/mm/svc/MM_APPLY_WITHDRAW.pgc @@ -0,0 +1,34 @@ +/* + * MM_APPLY_WITHDRAW.pgc - mm 모듈 서비스 MM_APPLY_WITHDRAW (one service = one file). + * 기능: 신청 철회 (신청/보류 단계 건을 WITHDRAWN 처리, 활성 가맹점은 철회 불가). + * 입력: T_MERCHANT, T_REASON=철회 사유(기본 "신청인 철회"). + * RC: 0=철회, 1=신청 건 없음, 4=철회 불가 상태(ACTIVE 등). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_APPLY_WITHDRAW.h" + +void MM_APPLY_WITHDRAW(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], reason[128]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_stat[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_REASON, reason, sizeof(reason)); + if (reason[0] == 0) strcpy(reason, "신청인 철회"); + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + + EXEC SQL SELECT status INTO :h_stat FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_stat, "APPLIED") != 0 && strcmp(h_stat, "HOLD") != 0) { setl(b, T_RC, 4); OK(b); } + + EXEC SQL UPDATE mm_merchant_ext SET status = 'WITHDRAWN', updated_at = now() + WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + if (mmdb_insert_merch_hist(h_merch, "APPLY_WD", h_stat, "WITHDRAWN", reason) < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_AUDIT_DIFF.h b/app/src/mm/svc/MM_AUDIT_DIFF.h new file mode 100644 index 0000000..f28847f --- /dev/null +++ b/app/src/mm/svc/MM_AUDIT_DIFF.h @@ -0,0 +1,12 @@ +/* + * MM_AUDIT_DIFF.h - mm 서비스 MM_AUDIT_DIFF 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_STR1, T_STR2, T_STR3 + */ +#ifndef MM_AUDIT_DIFF_H +#define MM_AUDIT_DIFF_H + +#include "mm_svc.h" + +void MM_AUDIT_DIFF(TPSVCINFO *p); + +#endif /* MM_AUDIT_DIFF_H */ diff --git a/app/src/mm/svc/MM_AUDIT_DIFF.pgc b/app/src/mm/svc/MM_AUDIT_DIFF.pgc new file mode 100644 index 0000000..9bec6dd --- /dev/null +++ b/app/src/mm/svc/MM_AUDIT_DIFF.pgc @@ -0,0 +1,32 @@ +/* + * MM_AUDIT_DIFF.pgc - mm 모듈 서비스 MM_AUDIT_DIFF (one service = one file). + * 기능: 마스터 최근 변경 전후 비교 (가장 최신 이력 1건의 액션/이전값/이후값 반환). + * 입력: T_MERCHANT. 출력: T_STR1=액션, T_STR2=이전값, T_STR3=이후값. + * RC: 0=조회, 1=이력 없음. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_AUDIT_DIFF.h" + +void MM_AUDIT_DIFF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_act[32], h_bef[128], h_aft[128]; + 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 action, before_val, after_val INTO :h_act, :h_bef, :h_aft + FROM mm_merch_hist WHERE merchant_id = :h_merch + ORDER BY hist_id DESC LIMIT 1; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + Bchg(b, T_STR1, 0, h_act, 0L); + Bchg(b, T_STR2, 0, h_bef, 0L); + Bchg(b, T_STR3, 0, h_aft, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_AUDIT_STAT.h b/app/src/mm/svc/MM_AUDIT_STAT.h new file mode 100644 index 0000000..4e094e1 --- /dev/null +++ b/app/src/mm/svc/MM_AUDIT_STAT.h @@ -0,0 +1,12 @@ +/* + * MM_AUDIT_STAT.h - mm 서비스 MM_AUDIT_STAT 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR1, T_COUNT, T_ARG1 + */ +#ifndef MM_AUDIT_STAT_H +#define MM_AUDIT_STAT_H + +#include "mm_svc.h" + +void MM_AUDIT_STAT(TPSVCINFO *p); + +#endif /* MM_AUDIT_STAT_H */ diff --git a/app/src/mm/svc/MM_AUDIT_STAT.pgc b/app/src/mm/svc/MM_AUDIT_STAT.pgc new file mode 100644 index 0000000..98a0ebc --- /dev/null +++ b/app/src/mm/svc/MM_AUDIT_STAT.pgc @@ -0,0 +1,30 @@ +/* + * MM_AUDIT_STAT.pgc - mm 모듈 서비스 MM_AUDIT_STAT (one service = one file). + * 기능: 전 마스터 감사 통계 (특정 액션의 전체 발생 건수 + 관련 가맹점 수 집계). + * 입력: T_STR1=액션(빈값이면 전체 이력). 출력: T_COUNT=이력 건수, T_ARG1=관련 가맹점 수. + * RC: 항상 0. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_AUDIT_STAT.h" + +void MM_AUDIT_STAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char act[32]; + EXEC SQL BEGIN DECLARE SECTION; + char h_act[32]; + long h_cnt, h_merch; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR1, act, sizeof(act)); + strncpy(h_act, act, sizeof(h_act)-1); h_act[sizeof(h_act)-1] = 0; + + EXEC SQL SELECT count(*), count(DISTINCT merchant_id) INTO :h_cnt, :h_merch + FROM mm_merch_hist WHERE (:h_act = '' OR action = :h_act); + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_ARG1, h_merch); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_BIZ_CLOSE.h b/app/src/mm/svc/MM_BIZ_CLOSE.h new file mode 100644 index 0000000..639914a --- /dev/null +++ b/app/src/mm/svc/MM_BIZ_CLOSE.h @@ -0,0 +1,12 @@ +/* + * MM_BIZ_CLOSE.h - mm 서비스 MM_BIZ_CLOSE 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_REASON + */ +#ifndef MM_BIZ_CLOSE_H +#define MM_BIZ_CLOSE_H + +#include "mm_svc.h" + +void MM_BIZ_CLOSE(TPSVCINFO *p); + +#endif /* MM_BIZ_CLOSE_H */ diff --git a/app/src/mm/svc/MM_BIZ_CLOSE.pgc b/app/src/mm/svc/MM_BIZ_CLOSE.pgc new file mode 100644 index 0000000..c61a79b --- /dev/null +++ b/app/src/mm/svc/MM_BIZ_CLOSE.pgc @@ -0,0 +1,41 @@ +/* + * MM_BIZ_CLOSE.pgc - mm 모듈 서비스 MM_BIZ_CLOSE (one service = one file). + * 기능: 폐업 확정 처리 (상태 CLOSED, 미결 계약 잔여 여부를 함께 점검하여 경고 코드 반환). + * 입력: T_MERCHANT, T_REASON=사유(기본 "폐업 확정"). 출력: T_ARG1=미만기 계약 수. + * RC: 0=폐업, 1=가맹점 없음, 2=이미 폐업/삭제, 6=폐업했으나 미만기 계약 잔존(후속 정리 필요). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_BIZ_CLOSE.h" + +void MM_BIZ_CLOSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], reason[128]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_stat[16]; + long h_open; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_REASON, reason, sizeof(reason)); + if (reason[0] == 0) strcpy(reason, "폐업 확정"); + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + + EXEC SQL SELECT status INTO :h_stat FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_stat, "CLOSED") == 0 || strcmp(h_stat, "DELETED") == 0) { setl(b, T_RC, 2); OK(b); } + + EXEC SQL SELECT count(*) INTO :h_open FROM mm_merch_hist + WHERE merchant_id = :h_merch AND action = 'CONTRACT' + AND after_val > to_char(current_date, 'YYYYMMDD'); + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL UPDATE mm_merchant_ext SET status = 'CLOSED', updated_at = now() + WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + if (mmdb_insert_merch_hist(h_merch, "BIZ_CLOSE", h_stat, "CLOSED", reason) < 0) FAIL(b); + setl(b, T_ARG1, h_open); + setl(b, T_RC, h_open > 0 ? 6 : 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_BIZ_REOPEN.h b/app/src/mm/svc/MM_BIZ_REOPEN.h new file mode 100644 index 0000000..68c6ff2 --- /dev/null +++ b/app/src/mm/svc/MM_BIZ_REOPEN.h @@ -0,0 +1,12 @@ +/* + * MM_BIZ_REOPEN.h - mm 서비스 MM_BIZ_REOPEN 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT + */ +#ifndef MM_BIZ_REOPEN_H +#define MM_BIZ_REOPEN_H + +#include "mm_svc.h" + +void MM_BIZ_REOPEN(TPSVCINFO *p); + +#endif /* MM_BIZ_REOPEN_H */ diff --git a/app/src/mm/svc/MM_BIZ_REOPEN.pgc b/app/src/mm/svc/MM_BIZ_REOPEN.pgc new file mode 100644 index 0000000..f0344c8 --- /dev/null +++ b/app/src/mm/svc/MM_BIZ_REOPEN.pgc @@ -0,0 +1,35 @@ +/* + * MM_BIZ_REOPEN.pgc - mm 모듈 서비스 MM_BIZ_REOPEN (one service = one file). + * 기능: 재개업 처리 (SUSPENDED/DORMANT 가맹점을 ACTIVE로 복귀, 폐업/삭제 건은 불가). + * 입력: T_MERCHANT. 출력: T_STR1=복귀 직전 상태. + * RC: 0=재개, 1=가맹점 없음, 4=재개 대상 상태 아님(이미 활성/폐업 등). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_BIZ_REOPEN.h" + +void MM_BIZ_REOPEN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_stat[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 INTO :h_stat FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_stat, "SUSPENDED") != 0 && strcmp(h_stat, "DORMANT") != 0) { + Bchg(b, T_STR1, 0, h_stat, 0L); setl(b, T_RC, 4); OK(b); + } + + EXEC SQL UPDATE mm_merchant_ext SET status = 'ACTIVE', updated_at = now() + WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + if (mmdb_insert_merch_hist(h_merch, "BIZ_REOPEN", h_stat, "ACTIVE", "재개업 처리") < 0) FAIL(b); + Bchg(b, T_STR1, 0, h_stat, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_BIZ_SUSPEND.h b/app/src/mm/svc/MM_BIZ_SUSPEND.h new file mode 100644 index 0000000..7786be4 --- /dev/null +++ b/app/src/mm/svc/MM_BIZ_SUSPEND.h @@ -0,0 +1,12 @@ +/* + * MM_BIZ_SUSPEND.h - mm 서비스 MM_BIZ_SUSPEND 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_REASON + */ +#ifndef MM_BIZ_SUSPEND_H +#define MM_BIZ_SUSPEND_H + +#include "mm_svc.h" + +void MM_BIZ_SUSPEND(TPSVCINFO *p); + +#endif /* MM_BIZ_SUSPEND_H */ diff --git a/app/src/mm/svc/MM_BIZ_SUSPEND.pgc b/app/src/mm/svc/MM_BIZ_SUSPEND.pgc new file mode 100644 index 0000000..bb13a44 --- /dev/null +++ b/app/src/mm/svc/MM_BIZ_SUSPEND.pgc @@ -0,0 +1,40 @@ +/* + * MM_BIZ_SUSPEND.pgc - mm 모듈 서비스 MM_BIZ_SUSPEND (one service = one file). + * 기능: 휴업 신고 처리 (ACTIVE 가맹점을 SUSPENDED로, 소속 활성 단말도 함께 정지). + * 입력: T_MERCHANT, T_REASON=사유(기본 "휴업 신고"). 출력: T_COUNT=정지된 단말 수. + * RC: 0=휴업, 1=가맹점 없음, 2=활성 아님. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_BIZ_SUSPEND.h" + +void MM_BIZ_SUSPEND(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], reason[128]; + long nterm; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_stat[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_REASON, reason, sizeof(reason)); + if (reason[0] == 0) strcpy(reason, "휴업 신고"); + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + + EXEC SQL SELECT status INTO :h_stat FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_stat, "ACTIVE") != 0) { setl(b, T_RC, 2); OK(b); } + + EXEC SQL UPDATE mm_merchant_ext SET status = 'SUSPENDED', updated_at = now() + WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL UPDATE mm_terminal SET status = 'INACTIVE' + WHERE merchant_id = :h_merch AND status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + nterm = sqlca.sqlerrd[2]; + if (mmdb_insert_merch_hist(h_merch, "BIZ_SUSPEND", "ACTIVE", "SUSPENDED", reason) < 0) FAIL(b); + setl(b, T_COUNT, nterm); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_BRANCH_INQ.h b/app/src/mm/svc/MM_BRANCH_INQ.h new file mode 100644 index 0000000..1ae41b2 --- /dev/null +++ b/app/src/mm/svc/MM_BRANCH_INQ.h @@ -0,0 +1,12 @@ +/* + * MM_BRANCH_INQ.h - mm 서비스 MM_BRANCH_INQ 카피북 (copybook / record header). + * 사용 UBF 필드: T_KEY1, T_STR1 + */ +#ifndef MM_BRANCH_INQ_H +#define MM_BRANCH_INQ_H + +#include "mm_svc.h" + +void MM_BRANCH_INQ(TPSVCINFO *p); + +#endif /* MM_BRANCH_INQ_H */ diff --git a/app/src/mm/svc/MM_BRANCH_INQ.pgc b/app/src/mm/svc/MM_BRANCH_INQ.pgc new file mode 100644 index 0000000..e96e32e --- /dev/null +++ b/app/src/mm/svc/MM_BRANCH_INQ.pgc @@ -0,0 +1,32 @@ +/* + * MM_BRANCH_INQ.pgc - mm 모듈 서비스 MM_BRANCH_INQ (one service = one file). + * 기능: 영업점 조회 ("명칭|상위코드" 저장값을 명칭만 분리하여 반환). + * 입력: T_KEY1=영업점 코드. 출력: T_STR1=영업점명(파이프 앞부분). + * RC: 0=조회, 1=영업점 없음. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_BRANCH_INQ.h" + +void MM_BRANCH_INQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char code[16], *bar; + EXEC SQL BEGIN DECLARE SECTION; + char h_code[16], h_nm[80]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, code, sizeof(code)); + if (code[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_code, code, sizeof(h_code)-1); h_code[sizeof(h_code)-1] = 0; + + EXEC SQL SELECT code_name INTO :h_nm FROM mm_code + WHERE code_group = 'BRANCH' AND code = :h_code; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + bar = strchr(h_nm, '|'); + if (bar != NULL) *bar = 0; + Bchg(b, T_STR1, 0, h_nm, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_BRANCH_LIST.h b/app/src/mm/svc/MM_BRANCH_LIST.h new file mode 100644 index 0000000..6560e1b --- /dev/null +++ b/app/src/mm/svc/MM_BRANCH_LIST.h @@ -0,0 +1,12 @@ +/* + * MM_BRANCH_LIST.h - mm 서비스 MM_BRANCH_LIST 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT + */ +#ifndef MM_BRANCH_LIST_H +#define MM_BRANCH_LIST_H + +#include "mm_svc.h" + +void MM_BRANCH_LIST(TPSVCINFO *p); + +#endif /* MM_BRANCH_LIST_H */ diff --git a/app/src/mm/svc/MM_BRANCH_LIST.pgc b/app/src/mm/svc/MM_BRANCH_LIST.pgc new file mode 100644 index 0000000..20ee7ad --- /dev/null +++ b/app/src/mm/svc/MM_BRANCH_LIST.pgc @@ -0,0 +1,27 @@ +/* + * MM_BRANCH_LIST.pgc - mm 모듈 서비스 MM_BRANCH_LIST (one service = one file). + * 기능: 영업점 계층 요약 (전체 수 + 최상위(상위=-) 영업점 수 집계). + * 출력: T_COUNT=전체 영업점 수, T_ARG1=최상위 영업점 수. + * RC: 항상 0. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_BRANCH_LIST.h" + +void MM_BRANCH_LIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_all, h_top; + EXEC SQL END DECLARE SECTION; + + EXEC SQL SELECT count(*), + count(*) FILTER (WHERE code_name LIKE '%|-') + INTO :h_all, :h_top + FROM mm_code WHERE code_group = 'BRANCH' AND status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_all); + setl(b, T_ARG1, h_top); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_BRANCH_REG.h b/app/src/mm/svc/MM_BRANCH_REG.h new file mode 100644 index 0000000..ad7abcb --- /dev/null +++ b/app/src/mm/svc/MM_BRANCH_REG.h @@ -0,0 +1,12 @@ +/* + * MM_BRANCH_REG.h - mm 서비스 MM_BRANCH_REG 카피북 (copybook / record header). + * 사용 UBF 필드: T_KEY1, T_STR1, T_KEY2 + */ +#ifndef MM_BRANCH_REG_H +#define MM_BRANCH_REG_H + +#include "mm_svc.h" + +void MM_BRANCH_REG(TPSVCINFO *p); + +#endif /* MM_BRANCH_REG_H */ diff --git a/app/src/mm/svc/MM_BRANCH_REG.pgc b/app/src/mm/svc/MM_BRANCH_REG.pgc new file mode 100644 index 0000000..6d5fde0 --- /dev/null +++ b/app/src/mm/svc/MM_BRANCH_REG.pgc @@ -0,0 +1,35 @@ +/* + * MM_BRANCH_REG.pgc - mm 모듈 서비스 MM_BRANCH_REG (one service = one file). + * 기능: 영업점 마스터 등록 (BRANCH 코드그룹 upsert, 상위 영업점 코드를 code_name에 저장). + * 입력: T_KEY1=영업점 코드, T_STR1=영업점명, T_KEY2=상위 영업점(선택). + * RC: 0=등록, 3=필수값 누락, 3=자기 자신을 상위로 지정 불가. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_BRANCH_REG.h" + +void MM_BRANCH_REG(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char code[16], name[64], parent[16], nm[80]; + EXEC SQL BEGIN DECLARE SECTION; + char h_code[16], h_nm[80]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, code, sizeof(code)); + gets_(b, T_STR1, name, sizeof(name)); + gets_(b, T_KEY2, parent, sizeof(parent)); + if (code[0] == 0 || name[0] == 0) { setl(b, T_RC, 3); OK(b); } + if (parent[0] != 0 && strcmp(parent, code) == 0) { setl(b, T_RC, 3); OK(b); } + /* code_name에 "명칭|상위코드" 형태로 계층 정보를 함께 저장 */ + snprintf(nm, sizeof(nm), "%s|%s", name, parent[0] ? parent : "-"); + strncpy(h_code, code, sizeof(h_code)-1); h_code[sizeof(h_code)-1] = 0; + strncpy(h_nm, nm, sizeof(h_nm)-1); h_nm[sizeof(h_nm)-1] = 0; + + EXEC SQL INSERT INTO mm_code (code_group, code, code_name, status) + VALUES ('BRANCH', :h_code, :h_nm, 'ACTIVE') + ON CONFLICT (code_group, code) + DO UPDATE SET code_name = EXCLUDED.code_name, status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_CHK_ACCT.h b/app/src/mm/svc/MM_CHK_ACCT.h new file mode 100644 index 0000000..0982189 --- /dev/null +++ b/app/src/mm/svc/MM_CHK_ACCT.h @@ -0,0 +1,12 @@ +/* + * MM_CHK_ACCT.h - mm 서비스 MM_CHK_ACCT 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT + */ +#ifndef MM_CHK_ACCT_H +#define MM_CHK_ACCT_H + +#include "mm_svc.h" + +void MM_CHK_ACCT(TPSVCINFO *p); + +#endif /* MM_CHK_ACCT_H */ diff --git a/app/src/mm/svc/MM_CHK_ACCT.pgc b/app/src/mm/svc/MM_CHK_ACCT.pgc new file mode 100644 index 0000000..e281fda --- /dev/null +++ b/app/src/mm/svc/MM_CHK_ACCT.pgc @@ -0,0 +1,27 @@ +/* + * MM_CHK_ACCT.pgc - mm 모듈 서비스 MM_CHK_ACCT (one service = one file). + * 기능: 정합성 점검 - 정산계좌 미등록 활성 가맹점 적발 (정산 지급 리스크 사전 탐지). + * 출력: T_COUNT=계좌 미등록 활성 가맹점 수, T_ARG1=활성 가맹점 총수. + * RC: 0=결함 없음, 7=미등록 건 존재. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_CHK_ACCT.h" + +void MM_CHK_ACCT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_bad, h_all; + EXEC SQL END DECLARE SECTION; + + EXEC SQL SELECT + count(*) FILTER (WHERE settle_acct = ''), count(*) + INTO :h_bad, :h_all + FROM mm_merchant_ext WHERE status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_bad); + setl(b, T_ARG1, h_all); + setl(b, T_RC, h_bad > 0 ? 7 : 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_CHK_GRADE.h b/app/src/mm/svc/MM_CHK_GRADE.h new file mode 100644 index 0000000..d664bb1 --- /dev/null +++ b/app/src/mm/svc/MM_CHK_GRADE.h @@ -0,0 +1,12 @@ +/* + * MM_CHK_GRADE.h - mm 서비스 MM_CHK_GRADE 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT + */ +#ifndef MM_CHK_GRADE_H +#define MM_CHK_GRADE_H + +#include "mm_svc.h" + +void MM_CHK_GRADE(TPSVCINFO *p); + +#endif /* MM_CHK_GRADE_H */ diff --git a/app/src/mm/svc/MM_CHK_GRADE.pgc b/app/src/mm/svc/MM_CHK_GRADE.pgc new file mode 100644 index 0000000..709b0f6 --- /dev/null +++ b/app/src/mm/svc/MM_CHK_GRADE.pgc @@ -0,0 +1,37 @@ +/* + * MM_CHK_GRADE.pgc - mm 모듈 서비스 MM_CHK_GRADE (one service = one file). + * 기능: 정합성 점검 - 마스터 등급과 누적 매출 기준 산정 등급의 불일치 적발. + * 기준: S>=10억, A>=3억, B>=5천만, 그 외 C. 활성 가맹점 대상. + * 출력: T_COUNT=불일치 가맹점 수, T_ARG1=점검 대상 총수. RC: 0=정합, 7=불일치 존재. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_CHK_GRADE.h" + +void MM_CHK_GRADE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_bad, h_all; + EXEC SQL END DECLARE SECTION; + + EXEC SQL SELECT count(*), + count(*) FILTER (WHERE me.grade <> calc.g) + INTO :h_all, :h_bad + FROM mm_merchant_ext me + JOIN LATERAL ( + SELECT CASE + WHEN coalesce(s.g, 0) >= 1000000000 THEN 'S' + WHEN coalesce(s.g, 0) >= 300000000 THEN 'A' + WHEN coalesce(s.g, 0) >= 50000000 THEN 'B' + ELSE 'C' END AS g + FROM (SELECT sum(gross_amount) AS g FROM merchant_summary + WHERE merchant_id = me.merchant_id) s + ) calc ON true + WHERE me.status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_bad); + setl(b, T_ARG1, h_all); + setl(b, T_RC, h_bad > 0 ? 7 : 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_CHK_MCC.h b/app/src/mm/svc/MM_CHK_MCC.h new file mode 100644 index 0000000..3e50d78 --- /dev/null +++ b/app/src/mm/svc/MM_CHK_MCC.h @@ -0,0 +1,12 @@ +/* + * MM_CHK_MCC.h - mm 서비스 MM_CHK_MCC 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT + */ +#ifndef MM_CHK_MCC_H +#define MM_CHK_MCC_H + +#include "mm_svc.h" + +void MM_CHK_MCC(TPSVCINFO *p); + +#endif /* MM_CHK_MCC_H */ diff --git a/app/src/mm/svc/MM_CHK_MCC.pgc b/app/src/mm/svc/MM_CHK_MCC.pgc new file mode 100644 index 0000000..7afd4dd --- /dev/null +++ b/app/src/mm/svc/MM_CHK_MCC.pgc @@ -0,0 +1,28 @@ +/* + * MM_CHK_MCC.pgc - mm 모듈 서비스 MM_CHK_MCC (one service = one file). + * 기능: 정합성 점검 - MCC 마스터에 없는 업종코드를 참조하는 가맹점 적발 (외래키 무결성). + * 출력: T_COUNT=고아 MCC 참조 가맹점 수, T_ARG1=적발된 서로 다른 MCC 종류 수. + * RC: 0=결함 없음, 7=고아 참조 존재. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_CHK_MCC.h" + +void MM_CHK_MCC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_kinds; + EXEC SQL END DECLARE SECTION; + + EXEC SQL SELECT count(*), count(DISTINCT me.mcc) INTO :h_cnt, :h_kinds + FROM mm_merchant_ext me + WHERE me.status <> 'DELETED' + AND me.mcc <> '0000' + AND NOT EXISTS (SELECT 1 FROM mm_mcc m WHERE m.mcc = me.mcc); + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_ARG1, h_kinds); + setl(b, T_RC, h_cnt > 0 ? 7 : 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_CHK_ORPHAN.h b/app/src/mm/svc/MM_CHK_ORPHAN.h new file mode 100644 index 0000000..de590c6 --- /dev/null +++ b/app/src/mm/svc/MM_CHK_ORPHAN.h @@ -0,0 +1,12 @@ +/* + * MM_CHK_ORPHAN.h - mm 서비스 MM_CHK_ORPHAN 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT + */ +#ifndef MM_CHK_ORPHAN_H +#define MM_CHK_ORPHAN_H + +#include "mm_svc.h" + +void MM_CHK_ORPHAN(TPSVCINFO *p); + +#endif /* MM_CHK_ORPHAN_H */ diff --git a/app/src/mm/svc/MM_CHK_ORPHAN.pgc b/app/src/mm/svc/MM_CHK_ORPHAN.pgc new file mode 100644 index 0000000..f52c5e5 --- /dev/null +++ b/app/src/mm/svc/MM_CHK_ORPHAN.pgc @@ -0,0 +1,31 @@ +/* + * MM_CHK_ORPHAN.pgc - mm 모듈 서비스 MM_CHK_ORPHAN (one service = one file). + * 기능: 정합성 점검 - 대표가 비활성/부재인 대표-하위 관계 적발 (MERCH_REL 고아 링크). + * 출력: T_COUNT=결함 관계 수, T_ARG1=활성 관계 총수. + * RC: 0=결함 없음, 7=고아 관계 존재. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_CHK_ORPHAN.h" + +void MM_CHK_ORPHAN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_bad, h_all; + EXEC SQL END DECLARE SECTION; + + EXEC SQL SELECT count(*) INTO :h_all FROM mm_code + WHERE code_group = 'MERCH_REL' AND status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT count(*) INTO :h_bad FROM mm_code rel + WHERE rel.code_group = 'MERCH_REL' AND rel.status = 'ACTIVE' + AND NOT EXISTS (SELECT 1 FROM mm_merchant_ext me + WHERE me.merchant_id = rel.code_name AND me.status = 'ACTIVE'); + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_bad); + setl(b, T_ARG1, h_all); + setl(b, T_RC, h_bad > 0 ? 7 : 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_CONTRACT_AMEND.h b/app/src/mm/svc/MM_CONTRACT_AMEND.h new file mode 100644 index 0000000..27aca0e --- /dev/null +++ b/app/src/mm/svc/MM_CONTRACT_AMEND.h @@ -0,0 +1,12 @@ +/* + * MM_CONTRACT_AMEND.h - mm 서비스 MM_CONTRACT_AMEND 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_ARG1, T_STR1 + */ +#ifndef MM_CONTRACT_AMEND_H +#define MM_CONTRACT_AMEND_H + +#include "mm_svc.h" + +void MM_CONTRACT_AMEND(TPSVCINFO *p); + +#endif /* MM_CONTRACT_AMEND_H */ diff --git a/app/src/mm/svc/MM_CONTRACT_AMEND.pgc b/app/src/mm/svc/MM_CONTRACT_AMEND.pgc new file mode 100644 index 0000000..dfae0bf --- /dev/null +++ b/app/src/mm/svc/MM_CONTRACT_AMEND.pgc @@ -0,0 +1,45 @@ +/* + * MM_CONTRACT_AMEND.pgc - mm 모듈 서비스 MM_CONTRACT_AMEND (one service = one file). + * 기능: 유효 계약의 조건 변경 (만기일 연장/단축, 변경 전후를 이력에 함께 기록). + * 입력: T_MERCHANT, T_ARG1=연장 개월(음수면 단축, -60~60). 출력: T_STR1=신 만기일. + * RC: 0=변경, 1=유효 계약 없음, 3=개월 범위 오류, 4=변경 결과가 과거일. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_CONTRACT_AMEND.h" + +void MM_CONTRACT_AMEND(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], beforev[32]; + long mon; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_cur[16], h_new[16]; + long h_mon, h_future; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + mon = getl(b, T_ARG1); + if (mon < -60 || mon > 60 || mon == 0) { setl(b, T_RC, 3); OK(b); } + h_mon = mon; + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + + EXEC SQL SELECT after_val INTO :h_cur FROM mm_merch_hist + WHERE merchant_id = :h_merch AND action = 'CONTRACT' + ORDER BY hist_id DESC LIMIT 1; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT to_char(to_date(:h_cur, 'YYYYMMDD') + :h_mon * interval '1 month', 'YYYYMMDD') + INTO :h_new; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT CASE WHEN to_date(:h_new, 'YYYYMMDD') > current_date THEN 1 ELSE 0 END + INTO :h_future; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_future == 0) { setl(b, T_RC, 4); OK(b); } + + strncpy(beforev, h_cur, sizeof(beforev)-1); beforev[sizeof(beforev)-1] = 0; + if (mmdb_insert_merch_hist(h_merch, "CONTRACT", beforev, h_new, "계약 조건 변경") < 0) FAIL(b); + Bchg(b, T_STR1, 0, h_new, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_CONTRACT_EXPCHK.h b/app/src/mm/svc/MM_CONTRACT_EXPCHK.h new file mode 100644 index 0000000..585a060 --- /dev/null +++ b/app/src/mm/svc/MM_CONTRACT_EXPCHK.h @@ -0,0 +1,12 @@ +/* + * MM_CONTRACT_EXPCHK.h - mm 서비스 MM_CONTRACT_EXPCHK 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_ARG1, T_STR1 + */ +#ifndef MM_CONTRACT_EXPCHK_H +#define MM_CONTRACT_EXPCHK_H + +#include "mm_svc.h" + +void MM_CONTRACT_EXPCHK(TPSVCINFO *p); + +#endif /* MM_CONTRACT_EXPCHK_H */ diff --git a/app/src/mm/svc/MM_CONTRACT_EXPCHK.pgc b/app/src/mm/svc/MM_CONTRACT_EXPCHK.pgc new file mode 100644 index 0000000..683f3ec --- /dev/null +++ b/app/src/mm/svc/MM_CONTRACT_EXPCHK.pgc @@ -0,0 +1,40 @@ +/* + * MM_CONTRACT_EXPCHK.pgc - mm 모듈 서비스 MM_CONTRACT_EXPCHK (one service = one file). + * 기능: 만기 임박 점검 (최신 계약 만기일까지 남은 일수 산출, 갱신 필요 판정). + * 입력: T_MERCHANT, T_ARG1=임박 기준 일수(기본 30). 출력: T_STR1=만기일, T_ARG1=잔여 일수. + * RC: 0=정상, 1=계약 없음, 5=이미 만기 경과, 6=임박(갱신 권고). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_CONTRACT_EXPCHK.h" + +void MM_CONTRACT_EXPCHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + long thresh; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_end[16]; + long h_days; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + thresh = getl(b, T_ARG1); + if (thresh <= 0) thresh = 30; + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + + EXEC SQL SELECT after_val INTO :h_end FROM mm_merch_hist + WHERE merchant_id = :h_merch AND action = 'CONTRACT' + ORDER BY hist_id DESC LIMIT 1; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT (to_date(:h_end, 'YYYYMMDD') - current_date) INTO :h_days; + if (sqlca.sqlcode < 0) FAIL(b); + + Bchg(b, T_STR1, 0, h_end, 0L); + setl(b, T_ARG1, h_days); + if (h_days < 0) { setl(b, T_RC, 5); OK(b); } + if (h_days <= thresh) { setl(b, T_RC, 6); OK(b); } + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_CONTRACT_HIST.h b/app/src/mm/svc/MM_CONTRACT_HIST.h new file mode 100644 index 0000000..5e70b60 --- /dev/null +++ b/app/src/mm/svc/MM_CONTRACT_HIST.h @@ -0,0 +1,12 @@ +/* + * MM_CONTRACT_HIST.h - mm 서비스 MM_CONTRACT_HIST 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_COUNT, T_STR1 + */ +#ifndef MM_CONTRACT_HIST_H +#define MM_CONTRACT_HIST_H + +#include "mm_svc.h" + +void MM_CONTRACT_HIST(TPSVCINFO *p); + +#endif /* MM_CONTRACT_HIST_H */ diff --git a/app/src/mm/svc/MM_CONTRACT_HIST.pgc b/app/src/mm/svc/MM_CONTRACT_HIST.pgc new file mode 100644 index 0000000..ff037fd --- /dev/null +++ b/app/src/mm/svc/MM_CONTRACT_HIST.pgc @@ -0,0 +1,31 @@ +/* + * MM_CONTRACT_HIST.pgc - mm 모듈 서비스 MM_CONTRACT_HIST (one service = one file). + * 기능: 가맹점 계약 변경 이력 요약 (체결/변경 횟수 + 최신 만기일 반환). + * 입력: T_MERCHANT. 출력: T_COUNT=CONTRACT 이력 건수, T_STR1=최신 만기일(없으면 빈값). + * RC: 항상 0. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_CONTRACT_HIST.h" + +void MM_CONTRACT_HIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_last[16]; + long h_cnt; + 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; + h_last[0] = 0; + + EXEC SQL SELECT count(*), coalesce(max(after_val), '') INTO :h_cnt, :h_last + FROM mm_merch_hist WHERE merchant_id = :h_merch AND action = 'CONTRACT'; + if (sqlca.sqlcode < 0) FAIL(b); + + Bchg(b, T_STR1, 0, h_last, 0L); + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_CONTRACT_REACT.h b/app/src/mm/svc/MM_CONTRACT_REACT.h new file mode 100644 index 0000000..2220516 --- /dev/null +++ b/app/src/mm/svc/MM_CONTRACT_REACT.h @@ -0,0 +1,12 @@ +/* + * MM_CONTRACT_REACT.h - mm 서비스 MM_CONTRACT_REACT 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT + */ +#ifndef MM_CONTRACT_REACT_H +#define MM_CONTRACT_REACT_H + +#include "mm_svc.h" + +void MM_CONTRACT_REACT(TPSVCINFO *p); + +#endif /* MM_CONTRACT_REACT_H */ diff --git a/app/src/mm/svc/MM_CONTRACT_REACT.pgc b/app/src/mm/svc/MM_CONTRACT_REACT.pgc new file mode 100644 index 0000000..a2546e6 --- /dev/null +++ b/app/src/mm/svc/MM_CONTRACT_REACT.pgc @@ -0,0 +1,32 @@ +/* + * MM_CONTRACT_REACT.pgc - mm 모듈 서비스 MM_CONTRACT_REACT (one service = one file). + * 기능: 정지된 계약 재개 (미해제 CONTRACT_SUSP가 있을 때만 CONTRACT_REACT 기록). + * 입력: T_MERCHANT. RC: 0=재개, 1=정지 상태 아님(재개할 대상 없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_CONTRACT_REACT.h" + +void MM_CONTRACT_REACT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64]; + long h_susp; + 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 count(*) INTO :h_susp FROM mm_merch_hist s + WHERE s.merchant_id = :h_merch AND s.action = 'CONTRACT_SUSP' + AND NOT EXISTS (SELECT 1 FROM mm_merch_hist r + WHERE r.merchant_id = s.merchant_id + AND r.action = 'CONTRACT_REACT' AND r.hist_id > s.hist_id); + if (sqlca.sqlcode < 0) FAIL(b); + if (h_susp == 0) { setl(b, T_RC, 1); OK(b); } + + if (mmdb_insert_merch_hist(h_merch, "CONTRACT_REACT", "SUSPENDED", "ACTIVE", "계약 재개") < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_CONTRACT_SUSPEND.h b/app/src/mm/svc/MM_CONTRACT_SUSPEND.h new file mode 100644 index 0000000..3aa6317 --- /dev/null +++ b/app/src/mm/svc/MM_CONTRACT_SUSPEND.h @@ -0,0 +1,12 @@ +/* + * MM_CONTRACT_SUSPEND.h - mm 서비스 MM_CONTRACT_SUSPEND 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_REASON + */ +#ifndef MM_CONTRACT_SUSPEND_H +#define MM_CONTRACT_SUSPEND_H + +#include "mm_svc.h" + +void MM_CONTRACT_SUSPEND(TPSVCINFO *p); + +#endif /* MM_CONTRACT_SUSPEND_H */ diff --git a/app/src/mm/svc/MM_CONTRACT_SUSPEND.pgc b/app/src/mm/svc/MM_CONTRACT_SUSPEND.pgc new file mode 100644 index 0000000..9aadc3f --- /dev/null +++ b/app/src/mm/svc/MM_CONTRACT_SUSPEND.pgc @@ -0,0 +1,41 @@ +/* + * MM_CONTRACT_SUSPEND.pgc - mm 모듈 서비스 MM_CONTRACT_SUSPEND (one service = one file). + * 기능: 계약 일시정지 (활성 가맹점을 유지하되 CONTRACT_SUSP 이력 마킹, 중복 정지 차단). + * 입력: T_MERCHANT, T_REASON=정지 사유(기본 "계약 일시정지"). + * RC: 0=정지, 1=유효 계약 없음, 4=이미 정지 상태. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_CONTRACT_SUSPEND.h" + +void MM_CONTRACT_SUSPEND(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], reason[128]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64]; + long h_has, h_susp; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_REASON, reason, sizeof(reason)); + if (reason[0] == 0) strcpy(reason, "계약 일시정지"); + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + + EXEC SQL SELECT count(*) INTO :h_has FROM mm_merch_hist + WHERE merchant_id = :h_merch AND action = 'CONTRACT'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_has == 0) { setl(b, T_RC, 1); OK(b); } + + /* 마지막 정지/재개 이력을 비교하여 현재 정지 상태이면 차단 */ + EXEC SQL SELECT count(*) INTO :h_susp FROM mm_merch_hist s + WHERE s.merchant_id = :h_merch AND s.action = 'CONTRACT_SUSP' + AND NOT EXISTS (SELECT 1 FROM mm_merch_hist r + WHERE r.merchant_id = s.merchant_id + AND r.action = 'CONTRACT_REACT' AND r.hist_id > s.hist_id); + if (sqlca.sqlcode < 0) FAIL(b); + if (h_susp > 0) { setl(b, T_RC, 4); OK(b); } + + if (mmdb_insert_merch_hist(h_merch, "CONTRACT_SUSP", "", "SUSPENDED", reason) < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_GRADE_HIST.h b/app/src/mm/svc/MM_GRADE_HIST.h new file mode 100644 index 0000000..bb89259 --- /dev/null +++ b/app/src/mm/svc/MM_GRADE_HIST.h @@ -0,0 +1,12 @@ +/* + * MM_GRADE_HIST.h - mm 서비스 MM_GRADE_HIST 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_COUNT, T_STR1 + */ +#ifndef MM_GRADE_HIST_H +#define MM_GRADE_HIST_H + +#include "mm_svc.h" + +void MM_GRADE_HIST(TPSVCINFO *p); + +#endif /* MM_GRADE_HIST_H */ diff --git a/app/src/mm/svc/MM_GRADE_HIST.pgc b/app/src/mm/svc/MM_GRADE_HIST.pgc new file mode 100644 index 0000000..daae02b --- /dev/null +++ b/app/src/mm/svc/MM_GRADE_HIST.pgc @@ -0,0 +1,34 @@ +/* + * MM_GRADE_HIST.pgc - mm 모듈 서비스 MM_GRADE_HIST (one service = one file). + * 기능: 등급 변경 이력 요약 (등급 관련 이력 건수 + 마스터 현행 등급 반환). + * 대상 액션: GRADE / GRADE_DEM (강등) / GRADE_ADJ. 입력: T_MERCHANT. + * 출력: T_COUNT=등급 변경 건수, T_STR1=현행 등급. RC: 1=가맹점 없음. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_GRADE_HIST.h" + +void MM_GRADE_HIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_grade[8]; + long h_cnt; + 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 grade INTO :h_grade FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT count(*) INTO :h_cnt FROM mm_merch_hist + WHERE merchant_id = :h_merch AND action IN ('GRADE', 'GRADE_DEM', 'GRADE_ADJ'); + if (sqlca.sqlcode < 0) FAIL(b); + + Bchg(b, T_STR1, 0, h_grade, 0L); + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_MCC_ASSIGN.h b/app/src/mm/svc/MM_MCC_ASSIGN.h new file mode 100644 index 0000000..7316da6 --- /dev/null +++ b/app/src/mm/svc/MM_MCC_ASSIGN.h @@ -0,0 +1,12 @@ +/* + * MM_MCC_ASSIGN.h - mm 서비스 MM_MCC_ASSIGN 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_STR1 + */ +#ifndef MM_MCC_ASSIGN_H +#define MM_MCC_ASSIGN_H + +#include "mm_svc.h" + +void MM_MCC_ASSIGN(TPSVCINFO *p); + +#endif /* MM_MCC_ASSIGN_H */ diff --git a/app/src/mm/svc/MM_MCC_ASSIGN.pgc b/app/src/mm/svc/MM_MCC_ASSIGN.pgc new file mode 100644 index 0000000..a035d2c --- /dev/null +++ b/app/src/mm/svc/MM_MCC_ASSIGN.pgc @@ -0,0 +1,41 @@ +/* + * MM_MCC_ASSIGN.pgc - mm 모듈 서비스 MM_MCC_ASSIGN (one service = one file). + * 기능: 가맹점 업종(MCC) 재배정 (마스터에 등록된 MCC로만 변경, 변경 이력 기록). + * 입력: T_MERCHANT, T_STR1=신규 MCC. 출력: T_STR2=기존 MCC. + * RC: 0=배정, 1=가맹점 없음, 2=MCC 마스터 미등록, 4=동일 MCC. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_MCC_ASSIGN.h" + +void MM_MCC_ASSIGN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], mcc[8]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_mcc[8], h_old[8]; + long h_valid; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_STR1, mcc, sizeof(mcc)); + if (mcc[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + strncpy(h_mcc, mcc, sizeof(h_mcc)-1); h_mcc[sizeof(h_mcc)-1] = 0; + + EXEC SQL SELECT mcc INTO :h_old FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + Bchg(b, T_STR2, 0, h_old, 0L); + if (strcmp(h_old, h_mcc) == 0) { setl(b, T_RC, 4); OK(b); } + + EXEC SQL SELECT count(*) INTO :h_valid FROM mm_mcc WHERE mcc = :h_mcc; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_valid == 0) { setl(b, T_RC, 2); OK(b); } + + EXEC SQL UPDATE mm_merchant_ext SET mcc = :h_mcc, updated_at = now() + WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + if (mmdb_insert_merch_hist(h_merch, "MCC_ASSIGN", h_old, h_mcc, "업종코드 재배정") < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_MCC_CNT.h b/app/src/mm/svc/MM_MCC_CNT.h new file mode 100644 index 0000000..fe281d1 --- /dev/null +++ b/app/src/mm/svc/MM_MCC_CNT.h @@ -0,0 +1,12 @@ +/* + * MM_MCC_CNT.h - mm 서비스 MM_MCC_CNT 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR1, T_COUNT + */ +#ifndef MM_MCC_CNT_H +#define MM_MCC_CNT_H + +#include "mm_svc.h" + +void MM_MCC_CNT(TPSVCINFO *p); + +#endif /* MM_MCC_CNT_H */ diff --git a/app/src/mm/svc/MM_MCC_CNT.pgc b/app/src/mm/svc/MM_MCC_CNT.pgc new file mode 100644 index 0000000..4fa0177 --- /dev/null +++ b/app/src/mm/svc/MM_MCC_CNT.pgc @@ -0,0 +1,34 @@ +/* + * MM_MCC_CNT.pgc - mm 모듈 서비스 MM_MCC_CNT (one service = one file). + * 기능: 특정 MCC를 사용하는 가맹점 수 + 해당 MCC 리스크 등급 동시 반환. + * 입력: T_STR1=MCC. 출력: T_COUNT=사용 가맹점 수, T_ARG1=리스크 등급. + * RC: 0=조회, 1=MCC 마스터 미등록. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_MCC_CNT.h" + +void MM_MCC_CNT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char mcc[8]; + EXEC SQL BEGIN DECLARE SECTION; + char h_mcc[8]; + long h_risk, h_cnt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR1, mcc, sizeof(mcc)); + strncpy(h_mcc, mcc, sizeof(h_mcc)-1); h_mcc[sizeof(h_mcc)-1] = 0; + + EXEC SQL SELECT risk_level INTO :h_risk FROM mm_mcc WHERE mcc = :h_mcc; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT count(*) INTO :h_cnt FROM mm_merchant_ext + WHERE mcc = :h_mcc AND status <> 'DELETED'; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_ARG1, h_risk); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_MCC_DEL.h b/app/src/mm/svc/MM_MCC_DEL.h new file mode 100644 index 0000000..7589dad --- /dev/null +++ b/app/src/mm/svc/MM_MCC_DEL.h @@ -0,0 +1,12 @@ +/* + * MM_MCC_DEL.h - mm 서비스 MM_MCC_DEL 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR1 + */ +#ifndef MM_MCC_DEL_H +#define MM_MCC_DEL_H + +#include "mm_svc.h" + +void MM_MCC_DEL(TPSVCINFO *p); + +#endif /* MM_MCC_DEL_H */ diff --git a/app/src/mm/svc/MM_MCC_DEL.pgc b/app/src/mm/svc/MM_MCC_DEL.pgc new file mode 100644 index 0000000..092110f --- /dev/null +++ b/app/src/mm/svc/MM_MCC_DEL.pgc @@ -0,0 +1,37 @@ +/* + * MM_MCC_DEL.pgc - mm 모듈 서비스 MM_MCC_DEL (one service = one file). + * 기능: MCC 업종코드 삭제 (참조하는 가맹점이 없을 때만 물리 삭제, 안전 삭제). + * 입력: T_STR1=MCC 코드. 출력: T_COUNT=참조 가맹점 수(삭제 실패 시 진단용). + * RC: 0=삭제, 1=MCC 없음, 4=참조 가맹점 존재로 삭제 불가. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_MCC_DEL.h" + +void MM_MCC_DEL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char mcc[8]; + EXEC SQL BEGIN DECLARE SECTION; + char h_mcc[8]; + long h_exist, h_ref; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR1, mcc, sizeof(mcc)); + if (mcc[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_mcc, mcc, sizeof(h_mcc)-1); h_mcc[sizeof(h_mcc)-1] = 0; + + EXEC SQL SELECT count(*) INTO :h_exist FROM mm_mcc WHERE mcc = :h_mcc; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_exist == 0) { setl(b, T_RC, 1); OK(b); } + + EXEC SQL SELECT count(*) INTO :h_ref FROM mm_merchant_ext + WHERE mcc = :h_mcc AND status <> 'DELETED'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_ref); + if (h_ref > 0) { setl(b, T_RC, 4); OK(b); } + + EXEC SQL DELETE FROM mm_mcc WHERE mcc = :h_mcc; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_MCC_RISKSET.h b/app/src/mm/svc/MM_MCC_RISKSET.h new file mode 100644 index 0000000..38b7b19 --- /dev/null +++ b/app/src/mm/svc/MM_MCC_RISKSET.h @@ -0,0 +1,12 @@ +/* + * MM_MCC_RISKSET.h - mm 서비스 MM_MCC_RISKSET 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR1, T_ARG1 + */ +#ifndef MM_MCC_RISKSET_H +#define MM_MCC_RISKSET_H + +#include "mm_svc.h" + +void MM_MCC_RISKSET(TPSVCINFO *p); + +#endif /* MM_MCC_RISKSET_H */ diff --git a/app/src/mm/svc/MM_MCC_RISKSET.pgc b/app/src/mm/svc/MM_MCC_RISKSET.pgc new file mode 100644 index 0000000..a4bb924 --- /dev/null +++ b/app/src/mm/svc/MM_MCC_RISKSET.pgc @@ -0,0 +1,35 @@ +/* + * MM_MCC_RISKSET.pgc - mm 모듈 서비스 MM_MCC_RISKSET (one service = one file). + * 기능: MCC 리스크 등급 설정 (1~5 범위 검증 후 mm_mcc.risk_level 갱신). + * 입력: T_STR1=MCC, T_ARG1=리스크 등급(1~5). 출력: T_ARG2=이전 등급. + * RC: 0=변경, 1=MCC 없음, 3=등급 범위 오류. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_MCC_RISKSET.h" + +void MM_MCC_RISKSET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char mcc[8]; + long lvl; + EXEC SQL BEGIN DECLARE SECTION; + char h_mcc[8]; + long h_lvl, h_old; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR1, mcc, sizeof(mcc)); + lvl = getl(b, T_ARG1); + if (lvl < 1 || lvl > 5) { setl(b, T_RC, 3); OK(b); } + h_lvl = lvl; + strncpy(h_mcc, mcc, sizeof(h_mcc)-1); h_mcc[sizeof(h_mcc)-1] = 0; + + EXEC SQL SELECT risk_level INTO :h_old FROM mm_mcc WHERE mcc = :h_mcc; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL UPDATE mm_mcc SET risk_level = :h_lvl WHERE mcc = :h_mcc; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG2, h_old); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_MERCH_DORMANT.h b/app/src/mm/svc/MM_MERCH_DORMANT.h new file mode 100644 index 0000000..cc726b0 --- /dev/null +++ b/app/src/mm/svc/MM_MERCH_DORMANT.h @@ -0,0 +1,12 @@ +/* + * MM_MERCH_DORMANT.h - mm 서비스 MM_MERCH_DORMANT 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_REASON + */ +#ifndef MM_MERCH_DORMANT_H +#define MM_MERCH_DORMANT_H + +#include "mm_svc.h" + +void MM_MERCH_DORMANT(TPSVCINFO *p); + +#endif /* MM_MERCH_DORMANT_H */ diff --git a/app/src/mm/svc/MM_MERCH_DORMANT.pgc b/app/src/mm/svc/MM_MERCH_DORMANT.pgc new file mode 100644 index 0000000..a78346a --- /dev/null +++ b/app/src/mm/svc/MM_MERCH_DORMANT.pgc @@ -0,0 +1,42 @@ +/* + * MM_MERCH_DORMANT.pgc - mm 모듈 서비스 MM_MERCH_DORMANT (one service = one file). + * 기능: 장기 무거래 가맹점 휴면 전환 (ACTIVE->DORMANT, 최근 집계 매출 0 확인). + * 입력: T_MERCHANT, T_REASON=사유(기본 "장기 무거래 휴면"). 출력: T_GROSS=최근 누적 매출. + * RC: 0=휴면, 1=가맹점 없음, 2=활성 아님, 4=최근 매출 존재로 휴면 불가. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_MERCH_DORMANT.h" + +void MM_MERCH_DORMANT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], reason[128]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_stat[16]; + long h_gross; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_REASON, reason, sizeof(reason)); + if (reason[0] == 0) strcpy(reason, "장기 무거래 휴면"); + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + + EXEC SQL SELECT status INTO :h_stat FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_stat, "ACTIVE") != 0) { setl(b, T_RC, 2); OK(b); } + + EXEC SQL SELECT coalesce(sum(gross_amount), 0) INTO :h_gross + FROM merchant_summary + WHERE merchant_id = :h_merch AND biz_date >= current_date - 90; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_GROSS, h_gross); + if (h_gross > 0) { setl(b, T_RC, 4); OK(b); } + + EXEC SQL UPDATE mm_merchant_ext SET status = 'DORMANT', updated_at = now() + WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + if (mmdb_insert_merch_hist(h_merch, "DORMANT", "ACTIVE", "DORMANT", reason) < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_PARTNER_LIST.h b/app/src/mm/svc/MM_PARTNER_LIST.h new file mode 100644 index 0000000..ccdb93d --- /dev/null +++ b/app/src/mm/svc/MM_PARTNER_LIST.h @@ -0,0 +1,12 @@ +/* + * MM_PARTNER_LIST.h - mm 서비스 MM_PARTNER_LIST 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT + */ +#ifndef MM_PARTNER_LIST_H +#define MM_PARTNER_LIST_H + +#include "mm_svc.h" + +void MM_PARTNER_LIST(TPSVCINFO *p); + +#endif /* MM_PARTNER_LIST_H */ diff --git a/app/src/mm/svc/MM_PARTNER_LIST.pgc b/app/src/mm/svc/MM_PARTNER_LIST.pgc new file mode 100644 index 0000000..0a64ef1 --- /dev/null +++ b/app/src/mm/svc/MM_PARTNER_LIST.pgc @@ -0,0 +1,27 @@ +/* + * MM_PARTNER_LIST.pgc - mm 모듈 서비스 MM_PARTNER_LIST (one service = one file). + * 기능: 활성 제휴사 총수 조회 (PARTNER 코드그룹 중 ACTIVE 상태 집계). + * 출력: T_COUNT=활성 제휴사 수, T_ARG1=전체(상태 무관) 수. + * RC: 항상 0. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_PARTNER_LIST.h" + +void MM_PARTNER_LIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_active, h_all; + EXEC SQL END DECLARE SECTION; + + EXEC SQL SELECT + count(*) FILTER (WHERE status = 'ACTIVE'), count(*) + INTO :h_active, :h_all + FROM mm_code WHERE code_group = 'PARTNER'; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_active); + setl(b, T_ARG1, h_all); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_PARTNER_UPD.h b/app/src/mm/svc/MM_PARTNER_UPD.h new file mode 100644 index 0000000..5d26119 --- /dev/null +++ b/app/src/mm/svc/MM_PARTNER_UPD.h @@ -0,0 +1,12 @@ +/* + * MM_PARTNER_UPD.h - mm 서비스 MM_PARTNER_UPD 카피북 (copybook / record header). + * 사용 UBF 필드: T_KEY1, T_STR1, T_ARG1 + */ +#ifndef MM_PARTNER_UPD_H +#define MM_PARTNER_UPD_H + +#include "mm_svc.h" + +void MM_PARTNER_UPD(TPSVCINFO *p); + +#endif /* MM_PARTNER_UPD_H */ diff --git a/app/src/mm/svc/MM_PARTNER_UPD.pgc b/app/src/mm/svc/MM_PARTNER_UPD.pgc new file mode 100644 index 0000000..d31ee2b --- /dev/null +++ b/app/src/mm/svc/MM_PARTNER_UPD.pgc @@ -0,0 +1,36 @@ +/* + * MM_PARTNER_UPD.pgc - mm 모듈 서비스 MM_PARTNER_UPD (one service = one file). + * 기능: 제휴사 마스터 명칭/표시순서 변경 (PARTNER 코드그룹, 기존 건만 갱신). + * 입력: T_KEY1=제휴사 코드, T_STR1=신규 명칭, T_ARG1=신규 순서. + * RC: 0=변경, 1=제휴사 없음, 3=명칭 누락. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_PARTNER_UPD.h" + +void MM_PARTNER_UPD(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char code[16], name[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_code[16], h_name[64]; + long h_sort, h_exist; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, code, sizeof(code)); + gets_(b, T_STR1, name, sizeof(name)); + h_sort = getl(b, T_ARG1); + if (name[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_code, code, sizeof(h_code)-1); h_code[sizeof(h_code)-1] = 0; + strncpy(h_name, name, sizeof(h_name)-1); h_name[sizeof(h_name)-1] = 0; + + EXEC SQL SELECT count(*) INTO :h_exist FROM mm_code + WHERE code_group = 'PARTNER' AND code = :h_code; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_exist == 0) { setl(b, T_RC, 1); OK(b); } + + EXEC SQL UPDATE mm_code SET code_name = :h_name, sort_order = :h_sort + WHERE code_group = 'PARTNER' AND code = :h_code; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_RATE_CANCEL.h b/app/src/mm/svc/MM_RATE_CANCEL.h new file mode 100644 index 0000000..4c8e054 --- /dev/null +++ b/app/src/mm/svc/MM_RATE_CANCEL.h @@ -0,0 +1,12 @@ +/* + * MM_RATE_CANCEL.h - mm 서비스 MM_RATE_CANCEL 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT + */ +#ifndef MM_RATE_CANCEL_H +#define MM_RATE_CANCEL_H + +#include "mm_svc.h" + +void MM_RATE_CANCEL(TPSVCINFO *p); + +#endif /* MM_RATE_CANCEL_H */ diff --git a/app/src/mm/svc/MM_RATE_CANCEL.pgc b/app/src/mm/svc/MM_RATE_CANCEL.pgc new file mode 100644 index 0000000..6956573 --- /dev/null +++ b/app/src/mm/svc/MM_RATE_CANCEL.pgc @@ -0,0 +1,33 @@ +/* + * MM_RATE_CANCEL.pgc - mm 모듈 서비스 MM_RATE_CANCEL (one service = one file). + * 기능: 대기중 요율 변경 요청 철회 (미처리 RATE_REQ가 있을 때만 RATE_REJ로 자체 종결). + * 입력: T_MERCHANT. RC: 0=철회, 1=대기중 요청 없음. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_RATE_CANCEL.h" + +void MM_RATE_CANCEL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64]; + long h_pending; + 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 count(*) INTO :h_pending FROM mm_merch_hist r + WHERE r.merchant_id = :h_merch AND r.action = 'RATE_REQ' + AND NOT EXISTS (SELECT 1 FROM mm_merch_hist d + WHERE d.merchant_id = r.merchant_id + AND d.action IN ('RATE_APV', 'RATE_REJ') + AND d.hist_id > r.hist_id); + if (sqlca.sqlcode < 0) FAIL(b); + if (h_pending == 0) { setl(b, T_RC, 1); OK(b); } + + if (mmdb_insert_merch_hist(h_merch, "RATE_REJ", "PENDING", "CANCELLED", "요청인 철회") < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_RATE_HIST.h b/app/src/mm/svc/MM_RATE_HIST.h new file mode 100644 index 0000000..5d5a83b --- /dev/null +++ b/app/src/mm/svc/MM_RATE_HIST.h @@ -0,0 +1,12 @@ +/* + * MM_RATE_HIST.h - mm 서비스 MM_RATE_HIST 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_COUNT + */ +#ifndef MM_RATE_HIST_H +#define MM_RATE_HIST_H + +#include "mm_svc.h" + +void MM_RATE_HIST(TPSVCINFO *p); + +#endif /* MM_RATE_HIST_H */ diff --git a/app/src/mm/svc/MM_RATE_HIST.pgc b/app/src/mm/svc/MM_RATE_HIST.pgc new file mode 100644 index 0000000..cdafab6 --- /dev/null +++ b/app/src/mm/svc/MM_RATE_HIST.pgc @@ -0,0 +1,35 @@ +/* + * MM_RATE_HIST.pgc - mm 모듈 서비스 MM_RATE_HIST (one service = one file). + * 기능: 요율 승인 워크플로 이력 집계 (요청/승인/반려 건수를 각각 반환). + * 입력: T_MERCHANT. 출력: T_COUNT=요청, T_ARG1=승인, T_ARG2=반려. + * RC: 항상 0. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_RATE_HIST.h" + +void MM_RATE_HIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64]; + long h_req, h_apv, h_rej; + 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 + count(*) FILTER (WHERE action = 'RATE_REQ'), + count(*) FILTER (WHERE action = 'RATE_APV'), + count(*) FILTER (WHERE action = 'RATE_REJ') + INTO :h_req, :h_apv, :h_rej + FROM mm_merch_hist WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_req); + setl(b, T_ARG1, h_apv); + setl(b, T_ARG2, h_rej); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_RATE_PENDING.h b/app/src/mm/svc/MM_RATE_PENDING.h new file mode 100644 index 0000000..9b975e7 --- /dev/null +++ b/app/src/mm/svc/MM_RATE_PENDING.h @@ -0,0 +1,12 @@ +/* + * MM_RATE_PENDING.h - mm 서비스 MM_RATE_PENDING 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT + */ +#ifndef MM_RATE_PENDING_H +#define MM_RATE_PENDING_H + +#include "mm_svc.h" + +void MM_RATE_PENDING(TPSVCINFO *p); + +#endif /* MM_RATE_PENDING_H */ diff --git a/app/src/mm/svc/MM_RATE_PENDING.pgc b/app/src/mm/svc/MM_RATE_PENDING.pgc new file mode 100644 index 0000000..957fef0 --- /dev/null +++ b/app/src/mm/svc/MM_RATE_PENDING.pgc @@ -0,0 +1,30 @@ +/* + * MM_RATE_PENDING.pgc - mm 모듈 서비스 MM_RATE_PENDING (one service = one file). + * 기능: 전 가맹점 미처리 요율 요청 수 집계 (승인/반려로 종결되지 않은 RATE_REQ 카운트). + * 출력: T_COUNT=대기중 요청 총수, T_ARG1=대기 가맹점 수(중복 제거). + * RC: 항상 0. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_RATE_PENDING.h" + +void MM_RATE_PENDING(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_merch; + EXEC SQL END DECLARE SECTION; + + EXEC SQL SELECT count(*), count(DISTINCT merchant_id) INTO :h_cnt, :h_merch + FROM mm_merch_hist r + WHERE r.action = 'RATE_REQ' + AND NOT EXISTS (SELECT 1 FROM mm_merch_hist d + WHERE d.merchant_id = r.merchant_id + AND d.action IN ('RATE_APV', 'RATE_REJ') + AND d.hist_id > r.hist_id); + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_ARG1, h_merch); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_RATE_STATUS.h b/app/src/mm/svc/MM_RATE_STATUS.h new file mode 100644 index 0000000..606cb2b --- /dev/null +++ b/app/src/mm/svc/MM_RATE_STATUS.h @@ -0,0 +1,12 @@ +/* + * MM_RATE_STATUS.h - mm 서비스 MM_RATE_STATUS 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_STR1, T_ARG1 + */ +#ifndef MM_RATE_STATUS_H +#define MM_RATE_STATUS_H + +#include "mm_svc.h" + +void MM_RATE_STATUS(TPSVCINFO *p); + +#endif /* MM_RATE_STATUS_H */ diff --git a/app/src/mm/svc/MM_RATE_STATUS.pgc b/app/src/mm/svc/MM_RATE_STATUS.pgc new file mode 100644 index 0000000..d30d75d --- /dev/null +++ b/app/src/mm/svc/MM_RATE_STATUS.pgc @@ -0,0 +1,41 @@ +/* + * MM_RATE_STATUS.pgc - mm 모듈 서비스 MM_RATE_STATUS (one service = one file). + * 기능: 최신 요율 변경 요청의 진행 상태 판정 (요청 이후 승인/반려 이력 존재 여부). + * 입력: T_MERCHANT. 출력: T_STR1=PENDING/APPROVED/REJECTED/NONE, T_ARG1=요청 건수. + * RC: 항상 0. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_RATE_STATUS.h" + +void MM_RATE_STATUS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + const char *st; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_lastact[16]; + long h_req; + 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 count(*) INTO :h_req FROM mm_merch_hist + WHERE merchant_id = :h_merch AND action = 'RATE_REQ'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_req); + if (h_req == 0) { Bchg(b, T_STR1, 0, (char *)"NONE", 0L); setl(b, T_RC, 0); OK(b); } + + h_lastact[0] = 0; + EXEC SQL SELECT action INTO :h_lastact FROM mm_merch_hist + WHERE merchant_id = :h_merch AND action IN ('RATE_REQ', 'RATE_APV', 'RATE_REJ') + ORDER BY hist_id DESC LIMIT 1; + if (sqlca.sqlcode < 0) FAIL(b); + + if (strcmp(h_lastact, "RATE_APV") == 0) st = "APPROVED"; + else if (strcmp(h_lastact, "RATE_REJ") == 0) st = "REJECTED"; + else st = "PENDING"; + Bchg(b, T_STR1, 0, (char *)st, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_REL_CHILDREN.h b/app/src/mm/svc/MM_REL_CHILDREN.h new file mode 100644 index 0000000..0a6ea27 --- /dev/null +++ b/app/src/mm/svc/MM_REL_CHILDREN.h @@ -0,0 +1,12 @@ +/* + * MM_REL_CHILDREN.h - mm 서비스 MM_REL_CHILDREN 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_COUNT + */ +#ifndef MM_REL_CHILDREN_H +#define MM_REL_CHILDREN_H + +#include "mm_svc.h" + +void MM_REL_CHILDREN(TPSVCINFO *p); + +#endif /* MM_REL_CHILDREN_H */ diff --git a/app/src/mm/svc/MM_REL_CHILDREN.pgc b/app/src/mm/svc/MM_REL_CHILDREN.pgc new file mode 100644 index 0000000..30311be --- /dev/null +++ b/app/src/mm/svc/MM_REL_CHILDREN.pgc @@ -0,0 +1,33 @@ +/* + * MM_REL_CHILDREN.pgc - mm 모듈 서비스 MM_REL_CHILDREN (one service = one file). + * 기능: 대표 가맹점에 연결된 하위 가맹점 수 집계 (MERCH_REL code_name=대표 기준). + * 입력: T_MERCHANT=대표 가맹점. 출력: T_COUNT=활성 하위 수. + * RC: 0=정상, 1=대표 가맹점 자체가 마스터에 없음. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_REL_CHILDREN.h" + +void MM_REL_CHILDREN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char parent[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_parent[64]; + long h_ok, h_cnt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, parent, sizeof(parent)); + strncpy(h_parent, parent, sizeof(h_parent)-1); h_parent[sizeof(h_parent)-1] = 0; + + EXEC SQL SELECT count(*) INTO :h_ok FROM mm_merchant_ext WHERE merchant_id = :h_parent; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_ok == 0) { setl(b, T_RC, 1); OK(b); } + + EXEC SQL SELECT count(*) INTO :h_cnt FROM mm_code + WHERE code_group = 'MERCH_REL' AND code_name = :h_parent AND status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_REL_MOVE.h b/app/src/mm/svc/MM_REL_MOVE.h new file mode 100644 index 0000000..46331ad --- /dev/null +++ b/app/src/mm/svc/MM_REL_MOVE.h @@ -0,0 +1,12 @@ +/* + * MM_REL_MOVE.h - mm 서비스 MM_REL_MOVE 카피북 (copybook / record header). + * 사용 UBF 필드: T_KEY1, T_MERCHANT + */ +#ifndef MM_REL_MOVE_H +#define MM_REL_MOVE_H + +#include "mm_svc.h" + +void MM_REL_MOVE(TPSVCINFO *p); + +#endif /* MM_REL_MOVE_H */ diff --git a/app/src/mm/svc/MM_REL_MOVE.pgc b/app/src/mm/svc/MM_REL_MOVE.pgc new file mode 100644 index 0000000..cdef453 --- /dev/null +++ b/app/src/mm/svc/MM_REL_MOVE.pgc @@ -0,0 +1,42 @@ +/* + * MM_REL_MOVE.pgc - mm 모듈 서비스 MM_REL_MOVE (one service = one file). + * 기능: 하위 가맹점을 다른 대표로 이관 (기존 연결의 대표를 신규 활성 대표로 교체). + * 입력: T_KEY1=하위 가맹점, T_MERCHANT=신규 대표. 출력: T_STR1=기존 대표. + * RC: 0=이관, 1=기존 연결 없음, 2=신규 대표 비활성, 3=신규 대표=하위(자기연결). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_REL_MOVE.h" + +void MM_REL_MOVE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char child[64], newp[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_child[64], h_new[64], h_old[64]; + long h_alive; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, child, sizeof(child)); + gets_(b, T_MERCHANT, newp, sizeof(newp)); + if (strcmp(child, newp) == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_child, child, sizeof(h_child)-1); h_child[sizeof(h_child)-1] = 0; + strncpy(h_new, newp, sizeof(h_new)-1); h_new[sizeof(h_new)-1] = 0; + + EXEC SQL SELECT code_name INTO :h_old FROM mm_code + WHERE code_group = 'MERCH_REL' AND code = :h_child AND status = 'ACTIVE'; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT count(*) INTO :h_alive FROM mm_merchant_ext + WHERE merchant_id = :h_new AND status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_alive == 0) { setl(b, T_RC, 2); OK(b); } + + EXEC SQL UPDATE mm_code SET code_name = :h_new + WHERE code_group = 'MERCH_REL' AND code = :h_child; + if (sqlca.sqlcode < 0) FAIL(b); + if (mmdb_insert_merch_hist(h_child, "REL_MOVE", h_old, h_new, "대표 가맹점 이관") < 0) FAIL(b); + Bchg(b, T_STR1, 0, h_old, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_REL_PARENT.h b/app/src/mm/svc/MM_REL_PARENT.h new file mode 100644 index 0000000..8482ae6 --- /dev/null +++ b/app/src/mm/svc/MM_REL_PARENT.h @@ -0,0 +1,12 @@ +/* + * MM_REL_PARENT.h - mm 서비스 MM_REL_PARENT 카피북 (copybook / record header). + * 사용 UBF 필드: T_KEY1, T_STR1 + */ +#ifndef MM_REL_PARENT_H +#define MM_REL_PARENT_H + +#include "mm_svc.h" + +void MM_REL_PARENT(TPSVCINFO *p); + +#endif /* MM_REL_PARENT_H */ diff --git a/app/src/mm/svc/MM_REL_PARENT.pgc b/app/src/mm/svc/MM_REL_PARENT.pgc new file mode 100644 index 0000000..2e9dae9 --- /dev/null +++ b/app/src/mm/svc/MM_REL_PARENT.pgc @@ -0,0 +1,30 @@ +/* + * MM_REL_PARENT.pgc - mm 모듈 서비스 MM_REL_PARENT (one service = one file). + * 기능: 하위 가맹점의 대표 가맹점 조회 (MERCH_REL 코드그룹에서 code=하위 -> code_name=대표). + * 입력: T_KEY1=하위 가맹점. 출력: T_STR1=대표 가맹점. + * RC: 0=연결됨, 1=연결 없음(독립 가맹점). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_REL_PARENT.h" + +void MM_REL_PARENT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char child[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_child[64], h_parent[64]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, child, sizeof(child)); + if (child[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_child, child, sizeof(h_child)-1); h_child[sizeof(h_child)-1] = 0; + + EXEC SQL SELECT code_name INTO :h_parent FROM mm_code + WHERE code_group = 'MERCH_REL' AND code = :h_child AND status = 'ACTIVE'; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + Bchg(b, T_STR1, 0, h_parent, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_REL_VERIFY.h b/app/src/mm/svc/MM_REL_VERIFY.h new file mode 100644 index 0000000..400531c --- /dev/null +++ b/app/src/mm/svc/MM_REL_VERIFY.h @@ -0,0 +1,12 @@ +/* + * MM_REL_VERIFY.h - mm 서비스 MM_REL_VERIFY 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_COUNT, T_ARG1 + */ +#ifndef MM_REL_VERIFY_H +#define MM_REL_VERIFY_H + +#include "mm_svc.h" + +void MM_REL_VERIFY(TPSVCINFO *p); + +#endif /* MM_REL_VERIFY_H */ diff --git a/app/src/mm/svc/MM_REL_VERIFY.pgc b/app/src/mm/svc/MM_REL_VERIFY.pgc new file mode 100644 index 0000000..3eca381 --- /dev/null +++ b/app/src/mm/svc/MM_REL_VERIFY.pgc @@ -0,0 +1,39 @@ +/* + * MM_REL_VERIFY.pgc - mm 모듈 서비스 MM_REL_VERIFY (one service = one file). + * 기능: 대표-하위 관계 정합성 검증 (대표가 활성 마스터에 존재하는지, 순환/자기참조 여부). + * 입력: T_MERCHANT=검증 대상 하위. 출력: T_COUNT=관계 수, T_ARG1=결함 수(대표 부재/자기참조). + * RC: 0=정합, 1=관계 없음, 7=결함 발견. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_REL_VERIFY.h" + +void MM_REL_VERIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char child[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_child[64], h_parent[64]; + long h_defect, h_self, h_palive; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, child, sizeof(child)); + strncpy(h_child, child, sizeof(h_child)-1); h_child[sizeof(h_child)-1] = 0; + + EXEC SQL SELECT code_name INTO :h_parent FROM mm_code + WHERE code_group = 'MERCH_REL' AND code = :h_child AND status = 'ACTIVE'; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + h_defect = 0; + h_self = (strcmp(h_child, h_parent) == 0) ? 1 : 0; + h_defect += h_self; + EXEC SQL SELECT count(*) INTO :h_palive FROM mm_merchant_ext + WHERE merchant_id = :h_parent AND status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_palive == 0) h_defect++; + + setl(b, T_COUNT, 1); + setl(b, T_ARG1, h_defect); + setl(b, T_RC, h_defect > 0 ? 7 : 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_RISK_INQ.h b/app/src/mm/svc/MM_RISK_INQ.h new file mode 100644 index 0000000..1105385 --- /dev/null +++ b/app/src/mm/svc/MM_RISK_INQ.h @@ -0,0 +1,12 @@ +/* + * MM_RISK_INQ.h - mm 서비스 MM_RISK_INQ 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_ARG1, T_STR1 + */ +#ifndef MM_RISK_INQ_H +#define MM_RISK_INQ_H + +#include "mm_svc.h" + +void MM_RISK_INQ(TPSVCINFO *p); + +#endif /* MM_RISK_INQ_H */ diff --git a/app/src/mm/svc/MM_RISK_INQ.pgc b/app/src/mm/svc/MM_RISK_INQ.pgc new file mode 100644 index 0000000..283b546 --- /dev/null +++ b/app/src/mm/svc/MM_RISK_INQ.pgc @@ -0,0 +1,41 @@ +/* + * MM_RISK_INQ.pgc - mm 모듈 서비스 MM_RISK_INQ (one service = one file). + * 기능: 현행 리스크 등급 조회 (최신 RISK_SET 수동값 우선, 없으면 최신 RISK 산정 밴드). + * 입력: T_MERCHANT. 출력: T_ARG1=수동 등급(없으면 0), T_STR1=최신 산정 밴드(없으면 빈값). + * RC: 0=조회, 1=가맹점 없음. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_RISK_INQ.h" + +void MM_RISK_INQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_setval[8], h_band[16]; + long h_ok, h_setcnt; + 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 count(*) INTO :h_ok FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_ok == 0) { setl(b, T_RC, 1); OK(b); } + + h_setcnt = 0; h_setval[0] = 0; h_band[0] = 0; + EXEC SQL SELECT count(*), coalesce(max(CASE WHEN action = 'RISK_SET' THEN after_val END), '0') + INTO :h_setcnt, :h_setval FROM mm_merch_hist + WHERE merchant_id = :h_merch AND action = 'RISK_SET'; + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT after_val INTO :h_band FROM mm_merch_hist + WHERE merchant_id = :h_merch AND action = 'RISK' + ORDER BY hist_id DESC LIMIT 1; + if (sqlca.sqlcode < 0 && sqlca.sqlcode != 100) FAIL(b); + + Bchg(b, T_STR1, 0, h_band, 0L); + setl(b, T_ARG1, h_setcnt > 0 ? atol(h_setval) : 0); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_RISK_SCORE.h b/app/src/mm/svc/MM_RISK_SCORE.h new file mode 100644 index 0000000..6c4d789 --- /dev/null +++ b/app/src/mm/svc/MM_RISK_SCORE.h @@ -0,0 +1,12 @@ +/* + * MM_RISK_SCORE.h - mm 서비스 MM_RISK_SCORE 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_ARG1, T_STR1 + */ +#ifndef MM_RISK_SCORE_H +#define MM_RISK_SCORE_H + +#include "mm_svc.h" + +void MM_RISK_SCORE(TPSVCINFO *p); + +#endif /* MM_RISK_SCORE_H */ diff --git a/app/src/mm/svc/MM_RISK_SCORE.pgc b/app/src/mm/svc/MM_RISK_SCORE.pgc new file mode 100644 index 0000000..f4da298 --- /dev/null +++ b/app/src/mm/svc/MM_RISK_SCORE.pgc @@ -0,0 +1,46 @@ +/* + * MM_RISK_SCORE.pgc - mm 모듈 서비스 MM_RISK_SCORE (one service = one file). + * 기능: 가맹점 리스크 점수 산정 (MCC 위험도*20 + 저등급 가산 + 비활성 상태 가산). + * 점수 구간: 0~39 LOW, 40~69 MID, 70+ HIGH. 산정 결과는 RISK 이력으로 남긴다. + * 입력: T_MERCHANT. 출력: T_ARG1=점수, T_STR1=LOW/MID/HIGH. RC: 1=가맹점 없음. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_RISK_SCORE.h" + +void MM_RISK_SCORE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], memo[48]; + long score; + const char *band; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_grade[8], h_stat[16], h_mcc[8]; + long h_risk; + 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 grade, status, mcc INTO :h_grade, :h_stat, :h_mcc + FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + h_risk = 1; + EXEC SQL SELECT risk_level INTO :h_risk FROM mm_mcc WHERE mcc = :h_mcc; + if (sqlca.sqlcode < 0) FAIL(b); + + score = h_risk * 20; + if (h_grade[0] == 'C') score += 20; + else if (h_grade[0] == 'B') score += 10; + if (strcmp(h_stat, "ACTIVE") != 0) score += 15; + if (score > 100) score = 100; + band = (score >= 70) ? "HIGH" : (score >= 40) ? "MID" : "LOW"; + snprintf(memo, sizeof(memo), "리스크 %ld", score); + if (mmdb_insert_merch_hist(h_merch, "RISK", "", (char *)band, memo) < 0) FAIL(b); + + Bchg(b, T_STR1, 0, (char *)band, 0L); + setl(b, T_ARG1, score); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_RISK_SET.h b/app/src/mm/svc/MM_RISK_SET.h new file mode 100644 index 0000000..de63e9d --- /dev/null +++ b/app/src/mm/svc/MM_RISK_SET.h @@ -0,0 +1,12 @@ +/* + * MM_RISK_SET.h - mm 서비스 MM_RISK_SET 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_ARG1 + */ +#ifndef MM_RISK_SET_H +#define MM_RISK_SET_H + +#include "mm_svc.h" + +void MM_RISK_SET(TPSVCINFO *p); + +#endif /* MM_RISK_SET_H */ diff --git a/app/src/mm/svc/MM_RISK_SET.pgc b/app/src/mm/svc/MM_RISK_SET.pgc new file mode 100644 index 0000000..66d9950 --- /dev/null +++ b/app/src/mm/svc/MM_RISK_SET.pgc @@ -0,0 +1,32 @@ +/* + * MM_RISK_SET.pgc - mm 모듈 서비스 MM_RISK_SET (one service = one file). + * 기능: 가맹점 리스크 등급 수동 설정 (1~5, 심사역 재량 오버라이드를 RISK_SET 이력에 기록). + * 입력: T_MERCHANT, T_ARG1=리스크 등급(1~5). RC: 0=설정, 1=가맹점 없음, 3=등급 범위 오류. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_RISK_SET.h" + +void MM_RISK_SET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], val[8]; + long lvl; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64]; + long h_exist; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + lvl = getl(b, T_ARG1); + if (lvl < 1 || lvl > 5) { setl(b, T_RC, 3); OK(b); } + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + + EXEC SQL SELECT count(*) INTO :h_exist FROM mm_merchant_ext WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_exist == 0) { setl(b, T_RC, 1); OK(b); } + + snprintf(val, sizeof(val), "%ld", lvl); + if (mmdb_insert_merch_hist(h_merch, "RISK_SET", "", val, "리스크 등급 수동 설정") < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_TERM_ACTIVATE.h b/app/src/mm/svc/MM_TERM_ACTIVATE.h new file mode 100644 index 0000000..e10ad8e --- /dev/null +++ b/app/src/mm/svc/MM_TERM_ACTIVATE.h @@ -0,0 +1,12 @@ +/* + * MM_TERM_ACTIVATE.h - mm 서비스 MM_TERM_ACTIVATE 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR5 + */ +#ifndef MM_TERM_ACTIVATE_H +#define MM_TERM_ACTIVATE_H + +#include "mm_svc.h" + +void MM_TERM_ACTIVATE(TPSVCINFO *p); + +#endif /* MM_TERM_ACTIVATE_H */ diff --git a/app/src/mm/svc/MM_TERM_ACTIVATE.pgc b/app/src/mm/svc/MM_TERM_ACTIVATE.pgc new file mode 100644 index 0000000..db6945f --- /dev/null +++ b/app/src/mm/svc/MM_TERM_ACTIVATE.pgc @@ -0,0 +1,32 @@ +/* + * MM_TERM_ACTIVATE.pgc - mm 모듈 서비스 MM_TERM_ACTIVATE (one service = one file). + * 기능: 단말 개통 (정지/비활성 단말을 ACTIVE로 전환). + * 입력: T_STR5=단말ID. 출력: T_STATUS=이전 상태. + * RC: 0=개통, 1=단말 없음, 4=이미 활성. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_TERM_ACTIVATE.h" + +void MM_TERM_ACTIVATE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char term[32]; + EXEC SQL BEGIN DECLARE SECTION; + char h_term[32], h_prev[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR5, term, sizeof(term)); + if (term[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_term, term, sizeof(h_term)-1); h_term[sizeof(h_term)-1] = 0; + + EXEC SQL SELECT status INTO :h_prev FROM mm_terminal WHERE terminal_id = :h_term; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_prev, "ACTIVE") == 0) { Bchg(b, T_STATUS, 0, h_prev, 0L); setl(b, T_RC, 4); OK(b); } + + EXEC SQL UPDATE mm_terminal SET status = 'ACTIVE' WHERE terminal_id = :h_term; + if (sqlca.sqlcode < 0) FAIL(b); + Bchg(b, T_STATUS, 0, h_prev, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_TERM_DEACT.h b/app/src/mm/svc/MM_TERM_DEACT.h new file mode 100644 index 0000000..c659fc3 --- /dev/null +++ b/app/src/mm/svc/MM_TERM_DEACT.h @@ -0,0 +1,12 @@ +/* + * MM_TERM_DEACT.h - mm 서비스 MM_TERM_DEACT 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR5, T_REASON + */ +#ifndef MM_TERM_DEACT_H +#define MM_TERM_DEACT_H + +#include "mm_svc.h" + +void MM_TERM_DEACT(TPSVCINFO *p); + +#endif /* MM_TERM_DEACT_H */ diff --git a/app/src/mm/svc/MM_TERM_DEACT.pgc b/app/src/mm/svc/MM_TERM_DEACT.pgc new file mode 100644 index 0000000..7103a32 --- /dev/null +++ b/app/src/mm/svc/MM_TERM_DEACT.pgc @@ -0,0 +1,38 @@ +/* + * MM_TERM_DEACT.pgc - mm 모듈 서비스 MM_TERM_DEACT (one service = one file). + * 기능: 단말 개통 해지 (ACTIVE 단말을 INACTIVE로, 사유 기록). + * 입력: T_STR5=단말ID, T_REASON=해지 사유(선택). 출력: T_COUNT=해당 가맹점 잔여 활성단말 수. + * RC: 0=해지, 1=단말 없음, 4=활성 아님. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_TERM_DEACT.h" + +void MM_TERM_DEACT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char term[32]; + EXEC SQL BEGIN DECLARE SECTION; + char h_term[32], h_stat[16], h_merch[64]; + long h_left; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR5, term, sizeof(term)); + if (term[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_term, term, sizeof(h_term)-1); h_term[sizeof(h_term)-1] = 0; + + EXEC SQL SELECT status, merchant_id INTO :h_stat, :h_merch + FROM mm_terminal WHERE terminal_id = :h_term; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_stat, "ACTIVE") != 0) { setl(b, T_RC, 4); OK(b); } + + EXEC SQL UPDATE mm_terminal SET status = 'INACTIVE' WHERE terminal_id = :h_term; + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT count(*) INTO :h_left FROM mm_terminal + WHERE merchant_id = :h_merch AND status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_left); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_TERM_HIST.h b/app/src/mm/svc/MM_TERM_HIST.h new file mode 100644 index 0000000..4daa1f4 --- /dev/null +++ b/app/src/mm/svc/MM_TERM_HIST.h @@ -0,0 +1,12 @@ +/* + * MM_TERM_HIST.h - mm 서비스 MM_TERM_HIST 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR5, T_COUNT + */ +#ifndef MM_TERM_HIST_H +#define MM_TERM_HIST_H + +#include "mm_svc.h" + +void MM_TERM_HIST(TPSVCINFO *p); + +#endif /* MM_TERM_HIST_H */ diff --git a/app/src/mm/svc/MM_TERM_HIST.pgc b/app/src/mm/svc/MM_TERM_HIST.pgc new file mode 100644 index 0000000..d03c1a2 --- /dev/null +++ b/app/src/mm/svc/MM_TERM_HIST.pgc @@ -0,0 +1,32 @@ +/* + * MM_TERM_HIST.pgc - mm 모듈 서비스 MM_TERM_HIST (one service = one file). + * 기능: 단말 상태 확인 조회 (현재 상태/소속/모델 반환, 존재 여부 판정). + * 입력: T_STR5=단말ID. 출력: T_STATUS, T_MERCHANT, T_STR6=모델. + * RC: 0=조회, 1=단말 없음. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_TERM_HIST.h" + +void MM_TERM_HIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char term[32]; + EXEC SQL BEGIN DECLARE SECTION; + char h_term[32], h_stat[16], h_merch[64], h_model[32]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR5, term, sizeof(term)); + if (term[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_term, term, sizeof(h_term)-1); h_term[sizeof(h_term)-1] = 0; + + EXEC SQL SELECT status, merchant_id, model INTO :h_stat, :h_merch, :h_model + FROM mm_terminal WHERE terminal_id = :h_term; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + Bchg(b, T_STATUS, 0, h_stat, 0L); + Bchg(b, T_MERCHANT, 0, h_merch, 0L); + Bchg(b, T_STR6, 0, h_model, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_TERM_INSTALL.h b/app/src/mm/svc/MM_TERM_INSTALL.h new file mode 100644 index 0000000..4915eb6 --- /dev/null +++ b/app/src/mm/svc/MM_TERM_INSTALL.h @@ -0,0 +1,12 @@ +/* + * MM_TERM_INSTALL.h - mm 서비스 MM_TERM_INSTALL 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR5, T_MERCHANT, T_STR6 + */ +#ifndef MM_TERM_INSTALL_H +#define MM_TERM_INSTALL_H + +#include "mm_svc.h" + +void MM_TERM_INSTALL(TPSVCINFO *p); + +#endif /* MM_TERM_INSTALL_H */ diff --git a/app/src/mm/svc/MM_TERM_INSTALL.pgc b/app/src/mm/svc/MM_TERM_INSTALL.pgc new file mode 100644 index 0000000..5b9f3d2 --- /dev/null +++ b/app/src/mm/svc/MM_TERM_INSTALL.pgc @@ -0,0 +1,40 @@ +/* + * MM_TERM_INSTALL.pgc - mm 모듈 서비스 MM_TERM_INSTALL (one service = one file). + * 기능: 등록된 단말의 설치 확정 (상태 REGISTERED->ACTIVE, 가맹점 활성 여부 확인). + * 입력: T_STR5=단말ID, T_MERCHANT=설치 가맹점, T_STR6=설치 모델(선택). + * RC: 0=설치완료, 1=단말 미등록, 2=가맹점 비활성, 4=이미 설치됨. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_TERM_INSTALL.h" + +void MM_TERM_INSTALL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char term[32], merch[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_term[32], h_merch[64], h_stat[16]; + long h_alive; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR5, term, sizeof(term)); + gets_(b, T_MERCHANT, merch, sizeof(merch)); + if (term[0] == 0 || merch[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_term, term, sizeof(h_term)-1); h_term[sizeof(h_term)-1] = 0; + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + + EXEC SQL SELECT status INTO :h_stat FROM mm_terminal WHERE terminal_id = :h_term; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_stat, "ACTIVE") == 0) { setl(b, T_RC, 4); OK(b); } + + EXEC SQL SELECT count(*) INTO :h_alive FROM mm_merchant_ext + WHERE merchant_id = :h_merch AND status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_alive == 0) { setl(b, T_RC, 2); OK(b); } + + EXEC SQL UPDATE mm_terminal SET status = 'ACTIVE', merchant_id = :h_merch + WHERE terminal_id = :h_term; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_TERM_LIST.h b/app/src/mm/svc/MM_TERM_LIST.h new file mode 100644 index 0000000..e8497c5 --- /dev/null +++ b/app/src/mm/svc/MM_TERM_LIST.h @@ -0,0 +1,12 @@ +/* + * MM_TERM_LIST.h - mm 서비스 MM_TERM_LIST 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT, T_COUNT, T_ARG1, T_ARG2 + */ +#ifndef MM_TERM_LIST_H +#define MM_TERM_LIST_H + +#include "mm_svc.h" + +void MM_TERM_LIST(TPSVCINFO *p); + +#endif /* MM_TERM_LIST_H */ diff --git a/app/src/mm/svc/MM_TERM_LIST.pgc b/app/src/mm/svc/MM_TERM_LIST.pgc new file mode 100644 index 0000000..d168a20 --- /dev/null +++ b/app/src/mm/svc/MM_TERM_LIST.pgc @@ -0,0 +1,34 @@ +/* + * MM_TERM_LIST.pgc - mm 모듈 서비스 MM_TERM_LIST (one service = one file). + * 기능: 가맹점별 단말 현황 요약 (전체/활성/폐기 건수 집계). + * 입력: T_MERCHANT. 출력: T_COUNT=전체, T_ARG1=활성, T_ARG2=폐기. + * RC: 항상 0 (0건이어도 정상). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_TERM_LIST.h" + +void MM_TERM_LIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64]; + long h_all, h_act, h_ret; + 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 count(*), + count(*) FILTER (WHERE status = 'ACTIVE'), + count(*) FILTER (WHERE status = 'RETIRED') + INTO :h_all, :h_act, :h_ret + FROM mm_terminal WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_COUNT, h_all); + setl(b, T_ARG1, h_act); + setl(b, T_ARG2, h_ret); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_TERM_MODEL_UPD.h b/app/src/mm/svc/MM_TERM_MODEL_UPD.h new file mode 100644 index 0000000..e44b951 --- /dev/null +++ b/app/src/mm/svc/MM_TERM_MODEL_UPD.h @@ -0,0 +1,12 @@ +/* + * MM_TERM_MODEL_UPD.h - mm 서비스 MM_TERM_MODEL_UPD 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR5, T_STR6 + */ +#ifndef MM_TERM_MODEL_UPD_H +#define MM_TERM_MODEL_UPD_H + +#include "mm_svc.h" + +void MM_TERM_MODEL_UPD(TPSVCINFO *p); + +#endif /* MM_TERM_MODEL_UPD_H */ diff --git a/app/src/mm/svc/MM_TERM_MODEL_UPD.pgc b/app/src/mm/svc/MM_TERM_MODEL_UPD.pgc new file mode 100644 index 0000000..cc1275e --- /dev/null +++ b/app/src/mm/svc/MM_TERM_MODEL_UPD.pgc @@ -0,0 +1,35 @@ +/* + * MM_TERM_MODEL_UPD.pgc - mm 모듈 서비스 MM_TERM_MODEL_UPD (one service = one file). + * 기능: 단말 모델(기종) 정보 변경. 출력: T_STR6에 이전 모델을 실어 반환. + * 입력: T_STR5=단말ID, T_STR6=신규 모델코드. + * RC: 0=변경, 1=단말 없음, 3=모델값 누락, 4=동일 모델(변경 없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_TERM_MODEL_UPD.h" + +void MM_TERM_MODEL_UPD(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char term[32], model[32]; + EXEC SQL BEGIN DECLARE SECTION; + char h_term[32], h_model[32], h_old[32]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR5, term, sizeof(term)); + gets_(b, T_STR6, model, sizeof(model)); + if (term[0] == 0) { setl(b, T_RC, 3); OK(b); } + if (model[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_term, term, sizeof(h_term)-1); h_term[sizeof(h_term)-1] = 0; + strncpy(h_model, model, sizeof(h_model)-1); h_model[sizeof(h_model)-1] = 0; + + EXEC SQL SELECT model INTO :h_old FROM mm_terminal WHERE terminal_id = :h_term; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + Bchg(b, T_STR6, 0, h_old, 0L); + if (strcmp(h_old, h_model) == 0) { setl(b, T_RC, 4); OK(b); } + + EXEC SQL UPDATE mm_terminal SET model = :h_model WHERE terminal_id = :h_term; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_TERM_REASSIGN.h b/app/src/mm/svc/MM_TERM_REASSIGN.h new file mode 100644 index 0000000..5c87821 --- /dev/null +++ b/app/src/mm/svc/MM_TERM_REASSIGN.h @@ -0,0 +1,12 @@ +/* + * MM_TERM_REASSIGN.h - mm 서비스 MM_TERM_REASSIGN 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR5, T_MERCHANT + */ +#ifndef MM_TERM_REASSIGN_H +#define MM_TERM_REASSIGN_H + +#include "mm_svc.h" + +void MM_TERM_REASSIGN(TPSVCINFO *p); + +#endif /* MM_TERM_REASSIGN_H */ diff --git a/app/src/mm/svc/MM_TERM_REASSIGN.pgc b/app/src/mm/svc/MM_TERM_REASSIGN.pgc new file mode 100644 index 0000000..3470d30 --- /dev/null +++ b/app/src/mm/svc/MM_TERM_REASSIGN.pgc @@ -0,0 +1,40 @@ +/* + * MM_TERM_REASSIGN.pgc - mm 모듈 서비스 MM_TERM_REASSIGN (one service = one file). + * 기능: 단말 소속 가맹점 재배정 (기존 소속에서 신규 활성 가맹점으로 이관). + * 입력: T_STR5=단말ID, T_MERCHANT=신규 가맹점. 출력: T_STR1=기존 가맹점. + * RC: 0=재배정, 1=단말 없음, 2=신규 가맹점 비활성, 4=이미 해당 가맹점 소속. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_TERM_REASSIGN.h" + +void MM_TERM_REASSIGN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char term[32], merch[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_term[32], h_new[64], h_old[64]; + long h_alive; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR5, term, sizeof(term)); + gets_(b, T_MERCHANT, merch, sizeof(merch)); + if (term[0] == 0 || merch[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_term, term, sizeof(h_term)-1); h_term[sizeof(h_term)-1] = 0; + strncpy(h_new, merch, sizeof(h_new)-1); h_new[sizeof(h_new)-1] = 0; + + EXEC SQL SELECT merchant_id INTO :h_old FROM mm_terminal WHERE terminal_id = :h_term; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_old, h_new) == 0) { Bchg(b, T_STR1, 0, h_old, 0L); setl(b, T_RC, 4); OK(b); } + + EXEC SQL SELECT count(*) INTO :h_alive FROM mm_merchant_ext + WHERE merchant_id = :h_new AND status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_alive == 0) { setl(b, T_RC, 2); OK(b); } + + EXEC SQL UPDATE mm_terminal SET merchant_id = :h_new WHERE terminal_id = :h_term; + if (sqlca.sqlcode < 0) FAIL(b); + Bchg(b, T_STR1, 0, h_old, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_TERM_RETIRE.h b/app/src/mm/svc/MM_TERM_RETIRE.h new file mode 100644 index 0000000..f3d0ce4 --- /dev/null +++ b/app/src/mm/svc/MM_TERM_RETIRE.h @@ -0,0 +1,12 @@ +/* + * MM_TERM_RETIRE.h - mm 서비스 MM_TERM_RETIRE 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR5, T_REASON + */ +#ifndef MM_TERM_RETIRE_H +#define MM_TERM_RETIRE_H + +#include "mm_svc.h" + +void MM_TERM_RETIRE(TPSVCINFO *p); + +#endif /* MM_TERM_RETIRE_H */ diff --git a/app/src/mm/svc/MM_TERM_RETIRE.pgc b/app/src/mm/svc/MM_TERM_RETIRE.pgc new file mode 100644 index 0000000..e667cca --- /dev/null +++ b/app/src/mm/svc/MM_TERM_RETIRE.pgc @@ -0,0 +1,32 @@ +/* + * MM_TERM_RETIRE.pgc - mm 모듈 서비스 MM_TERM_RETIRE (one service = one file). + * 기능: 단말 폐기 처리 (상태 RETIRED로 확정, 활성 단말은 폐기 전 해지 필요). + * 입력: T_STR5=단말ID, T_REASON=폐기 사유(선택). + * RC: 0=폐기, 1=단말 없음, 4=활성 단말은 폐기 불가(먼저 해지), 5=이미 폐기됨. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_TERM_RETIRE.h" + +void MM_TERM_RETIRE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char term[32]; + EXEC SQL BEGIN DECLARE SECTION; + char h_term[32], h_stat[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR5, term, sizeof(term)); + if (term[0] == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_term, term, sizeof(h_term)-1); h_term[sizeof(h_term)-1] = 0; + + EXEC SQL SELECT status INTO :h_stat FROM mm_terminal WHERE terminal_id = :h_term; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_stat, "ACTIVE") == 0) { setl(b, T_RC, 4); OK(b); } + if (strcmp(h_stat, "RETIRED") == 0) { setl(b, T_RC, 5); OK(b); } + + EXEC SQL UPDATE mm_terminal SET status = 'RETIRED' WHERE terminal_id = :h_term; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/mm/svc/MM_TERM_SWAP.h b/app/src/mm/svc/MM_TERM_SWAP.h new file mode 100644 index 0000000..a54bd50 --- /dev/null +++ b/app/src/mm/svc/MM_TERM_SWAP.h @@ -0,0 +1,12 @@ +/* + * MM_TERM_SWAP.h - mm 서비스 MM_TERM_SWAP 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR5, T_STR6, T_MERCHANT + */ +#ifndef MM_TERM_SWAP_H +#define MM_TERM_SWAP_H + +#include "mm_svc.h" + +void MM_TERM_SWAP(TPSVCINFO *p); + +#endif /* MM_TERM_SWAP_H */ diff --git a/app/src/mm/svc/MM_TERM_SWAP.pgc b/app/src/mm/svc/MM_TERM_SWAP.pgc new file mode 100644 index 0000000..aa3e855 --- /dev/null +++ b/app/src/mm/svc/MM_TERM_SWAP.pgc @@ -0,0 +1,42 @@ +/* + * MM_TERM_SWAP.pgc - mm 모듈 서비스 MM_TERM_SWAP (one service = one file). + * 기능: 단말 교체 (구단말 폐기 + 신단말이 구단말의 가맹점/모델을 승계 등록). + * 입력: T_STR5=구단말ID, T_STR6=신단말ID. 신단말은 구단말의 소속을 이어받는다. + * RC: 0=교체완료, 1=구단말 없음, 3=신구 동일, 4=신단말 이미 존재. + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다. + */ +#include "MM_TERM_SWAP.h" + +void MM_TERM_SWAP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char oldt[32], newt[32]; + EXEC SQL BEGIN DECLARE SECTION; + char h_old[32], h_new[32], h_merch[64], h_model[32]; + long h_exist; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_STR5, oldt, sizeof(oldt)); + gets_(b, T_STR6, newt, sizeof(newt)); + if (oldt[0] == 0 || newt[0] == 0) { setl(b, T_RC, 3); OK(b); } + if (strcmp(oldt, newt) == 0) { setl(b, T_RC, 3); OK(b); } + strncpy(h_old, oldt, sizeof(h_old)-1); h_old[sizeof(h_old)-1] = 0; + strncpy(h_new, newt, sizeof(h_new)-1); h_new[sizeof(h_new)-1] = 0; + + EXEC SQL SELECT merchant_id, model INTO :h_merch, :h_model + FROM mm_terminal WHERE terminal_id = :h_old; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (sqlca.sqlcode < 0) FAIL(b); + + EXEC SQL SELECT count(*) INTO :h_exist FROM mm_terminal WHERE terminal_id = :h_new; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_exist > 0) { setl(b, T_RC, 4); OK(b); } + + EXEC SQL INSERT INTO mm_terminal (terminal_id, merchant_id, model, status) + VALUES (:h_new, :h_merch, :h_model, 'ACTIVE'); + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL UPDATE mm_terminal SET status = 'RETIRED' WHERE terminal_id = :h_old; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/batch/py_apprpend_batch.pgc b/app/src/py/batch/py_apprpend_batch.pgc new file mode 100644 index 0000000..1e4189b --- /dev/null +++ b/app/src/py/batch/py_apprpend_batch.pgc @@ -0,0 +1,45 @@ +/* + * py_apprpend_batch.pgc - py 승인대기 리포트 배치 (커서 GROUP BY, XA). + * + * 아직 승인 전인 요청(R) 지급건을 가맹점별로 묶어 대기 건수/요청금액 합을 + * 건수 내림차순 커서로 훑어 stdout 리포트로 출력한다 (승인 워크로드 파악). + * Usage: py_apprpend_batch + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + long groups = 0, waiting = 0; + + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL DECLARE pap CURSOR FOR + SELECT merchant_id, count(*), coalesce(sum(amount),0) + FROM py_payout WHERE status = 'R' + GROUP BY merchant_id ORDER BY 2 DESC, 1; + EXEC SQL OPEN pap; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH pap INTO :h_merch, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pap; tpabort(0); return 1; } + groups++; waiting += h_cnt; + printf(" %s: 대기 %ld건 요청 %ld원\n", h_merch, h_cnt, h_amt); + } + EXEC SQL CLOSE pap; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> py_apprpend_batch COMMIT: merchants=%ld waiting=%ld\n", groups, waiting); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/py/batch/py_bankverify_batch.pgc b/app/src/py/batch/py_bankverify_batch.pgc new file mode 100644 index 0000000..7ebf478 --- /dev/null +++ b/app/src/py/batch/py_bankverify_batch.pgc @@ -0,0 +1,47 @@ +/* + * py_bankverify_batch.pgc - py 지급파일 전건 대사 배치 (커서 검증, XA). + * + * 생성(G) 상태 파일을 커서로 훑어 파일 헤더의 건수/금액과 실제 편성 지급건의 + * 건수/net 합을 비교해 불일치 파일을 리포트한다. 갱신 없이 검증만 수행한다. + * host 변수는 전용 카피북 py_bankverify_cpy.h 를 EXEC SQL INCLUDE. + * Usage: py_bankverify_batch + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + long files = 0, mism = 0; + + EXEC SQL INCLUDE py_bankverify_cpy; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(120, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL DECLARE pbv CURSOR FOR + SELECT file_id, record_count, total_amount FROM py_file WHERE status = 'G' ORDER BY file_id; + EXEC SQL OPEN pbv; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH pbv INTO :c_fid, :c_hcnt, :c_hsum; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pbv; tpabort(0); return 1; } + EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :c_acnt, :c_asum + FROM py_payout WHERE file_id = :c_fid; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pbv; tpabort(0); return 1; } + files++; + if (c_hcnt != c_acnt || c_hsum != c_asum) { + mism++; + printf(" 파일 %ld 불일치: hdr(%ld/%ld) act(%ld/%ld)\n", c_fid, c_hcnt, c_hsum, c_acnt, c_asum); + } + } + EXEC SQL CLOSE pbv; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> py_bankverify_batch COMMIT: files=%ld mismatch=%ld\n", files, mism); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/py/batch/py_bankverify_cpy.h b/app/src/py/batch/py_bankverify_cpy.h new file mode 100644 index 0000000..c551018 --- /dev/null +++ b/app/src/py/batch/py_bankverify_cpy.h @@ -0,0 +1,4 @@ +/* py_bankverify_cpy.h - py_bankverify_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +long c_fid, c_hcnt, c_hsum, c_acnt, c_asum; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/py/batch/py_carryover_batch.pgc b/app/src/py/batch/py_carryover_batch.pgc new file mode 100644 index 0000000..48eef4e --- /dev/null +++ b/app/src/py/batch/py_carryover_batch.pgc @@ -0,0 +1,44 @@ +/* + * py_carryover_batch.pgc - py 미지급 이월 배치 (SELECT 카운트 + UPDATE, XA). + * + * 지정 기준일 이전의 미지급 요청(R) 건 수를 먼저 집계한 뒤, 그 건들의 + * biz_date 를 차기 영업일로 일괄 이월한다. ONE XA transaction. + * Usage: py_carryover_batch [CUTOFF-YYYY-MM-DD] [NEXT-YYYY-MM-DD] + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + const char *cut = (argc > 1) ? argv[1] : "2026-07-19"; + const char *next = (argc > 2) ? argv[2] : "2026-07-20"; + long moved; + + EXEC SQL BEGIN DECLARE SECTION; + char h_cut[16], h_next[16]; + long h_cnt; + EXEC SQL END DECLARE SECTION; + + strncpy(h_cut, cut, sizeof(h_cut)-1); h_cut[sizeof(h_cut)-1] = 0; + strncpy(h_next, next, sizeof(h_next)-1); h_next[sizeof(h_next)-1] = 0; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL SELECT count(*) INTO :h_cnt + FROM py_payout WHERE status = 'R' AND biz_date < :h_cut; + if (sqlca.sqlcode < 0) { fprintf(stderr, "CNT FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + EXEC SQL UPDATE py_payout SET biz_date = :h_next, updated_at = now() + WHERE status = 'R' AND biz_date < :h_cut; + if (sqlca.sqlcode < 0) { fprintf(stderr, "UPD FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + moved = sqlca.sqlerrd[2]; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> py_carryover_batch COMMIT: cutoff=%s next=%s candidates=%ld moved=%ld\n", cut, next, h_cnt, moved); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/py/batch/py_dormscan_batch.pgc b/app/src/py/batch/py_dormscan_batch.pgc new file mode 100644 index 0000000..819e6e8 --- /dev/null +++ b/app/src/py/batch/py_dormscan_batch.pgc @@ -0,0 +1,39 @@ +/* + * py_dormscan_batch.pgc - py 휴면 계좌 스캔 배치 (NOT EXISTS UPDATE, XA). + * + * 지정 기준일 이후 지급 이력이 전혀 없는 활성(ACTIVE) 계좌를 NOT EXISTS + * 서브쿼리로 골라 휴면(DORMANT)으로 일괄 전환한다. ONE XA transaction. + * Usage: py_dormscan_batch [SINCE-YYYY-MM-DD] + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + const char *since = (argc > 1) ? argv[1] : "2026-04-01"; + long dormant; + + EXEC SQL BEGIN DECLARE SECTION; + char h_since[16]; + EXEC SQL END DECLARE SECTION; + + strncpy(h_since, since, sizeof(h_since)-1); h_since[sizeof(h_since)-1] = 0; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(90, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL UPDATE py_account SET status = 'DORMANT', updated_at = now() + WHERE status = 'ACTIVE' + AND NOT EXISTS (SELECT 1 FROM py_payout p + WHERE p.account_no = py_account.account_no AND p.biz_date >= :h_since); + if (sqlca.sqlcode < 0) { fprintf(stderr, "UPD FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + dormant = sqlca.sqlerrd[2]; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> py_dormscan_batch COMMIT: since=%s dormant=%ld\n", since, dormant); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/py/batch/py_feecap_batch.pgc b/app/src/py/batch/py_feecap_batch.pgc new file mode 100644 index 0000000..b6ce9ad --- /dev/null +++ b/app/src/py/batch/py_feecap_batch.pgc @@ -0,0 +1,41 @@ +/* + * py_feecap_batch.pgc - py 수수료 상한 일괄 적용 배치 (INSERT..SELECT + UPDATE, XA). + * + * 미지급(R/A) 건 중 수수료가 상한(인자)을 넘는 건을 대상으로, 초과분을 + * py_adjust 에 양수 조정으로 적재한 뒤 fee 를 상한까지 낮추고 net 을 보정한다. + * host 변수는 전용 카피북 py_feecap_cpy.h 를 EXEC SQL INCLUDE. + * Usage: py_feecap_batch [YYYY-MM-DD] [CAP] + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + + EXEC SQL INCLUDE py_feecap_cpy; + + strncpy(c_bizdate, bizdate, sizeof(c_bizdate)-1); c_bizdate[sizeof(c_bizdate)-1] = 0; + c_cap = (argc > 2) ? atol(argv[2]) : 5000; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(90, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL INSERT INTO py_adjust (adj_id, payout_id, delta, reason, biz_date) + SELECT nextval('py_adjust_seq'), payout_id, fee - :c_cap, '수수료상한배치', :c_bizdate + FROM py_payout WHERE status IN ('R','A') AND fee > :c_cap; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ADJ FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + EXEC SQL UPDATE py_payout SET net = net + (fee - :c_cap), fee = :c_cap, updated_at = now() + WHERE status IN ('R','A') AND fee > :c_cap; + if (sqlca.sqlcode < 0) { fprintf(stderr, "UPD FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + c_capped = sqlca.sqlerrd[2]; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> py_feecap_batch COMMIT: bizdate=%s cap=%ld capped=%ld\n", bizdate, c_cap, c_capped); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/py/batch/py_feecap_cpy.h b/app/src/py/batch/py_feecap_cpy.h new file mode 100644 index 0000000..93c81af --- /dev/null +++ b/app/src/py/batch/py_feecap_cpy.h @@ -0,0 +1,5 @@ +/* py_feecap_cpy.h - py_feecap_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +char c_bizdate[16]; +long c_cap, c_capped; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/py/batch/py_garnish_batch.pgc b/app/src/py/batch/py_garnish_batch.pgc new file mode 100644 index 0000000..c1dab60 --- /dev/null +++ b/app/src/py/batch/py_garnish_batch.pgc @@ -0,0 +1,45 @@ +/* + * py_garnish_batch.pgc - py 압류/가압류 계좌 지급보류 배치 (커서 + UPDATE, XA). + * + * 압류(SEIZED)/가압류(GARNISH) 상태 계좌를 커서로 훑어 각 계좌의 미지급(R/A) + * 건을 '압류보류'로 보류(H) 전이한다. 계좌별 보류 건수를 리포트한다. + * host 변수는 전용 카피북 py_garnish_cpy.h 를 EXEC SQL INCLUDE. + * Usage: py_garnish_batch + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + long accts = 0, held = 0; + + EXEC SQL INCLUDE py_garnish_cpy; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(120, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL DECLARE pgr CURSOR FOR + SELECT account_no FROM py_account WHERE status IN ('SEIZED','GARNISH') ORDER BY account_no; + EXEC SQL OPEN pgr; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH pgr INTO :c_acct; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pgr; tpabort(0); return 1; } + EXEC SQL UPDATE py_payout SET status = 'H', reject_reason = '압류보류', updated_at = now() + WHERE account_no = :c_acct AND status IN ('R','A'); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pgr; tpabort(0); return 1; } + c_held = sqlca.sqlerrd[2]; + accts++; held += c_held; + if (c_held > 0) printf(" 계좌 %s: %ld건 압류보류\n", c_acct, c_held); + } + EXEC SQL CLOSE pgr; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> py_garnish_batch COMMIT: accounts=%ld held=%ld\n", accts, held); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/py/batch/py_garnish_cpy.h b/app/src/py/batch/py_garnish_cpy.h new file mode 100644 index 0000000..8ceac18 --- /dev/null +++ b/app/src/py/batch/py_garnish_cpy.h @@ -0,0 +1,5 @@ +/* py_garnish_cpy.h - py_garnish_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +char c_acct[32]; +long c_held; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/py/batch/py_notifypush_batch.pgc b/app/src/py/batch/py_notifypush_batch.pgc new file mode 100644 index 0000000..fe83fe1 --- /dev/null +++ b/app/src/py/batch/py_notifypush_batch.pgc @@ -0,0 +1,47 @@ +/* + * py_notifypush_batch.pgc - py 지급완료 푸시 통지 시뮬 배치 (조인 커서, XA). + * + * 당일 지급완료(P) 건을 py_account 조인 커서로 훑어 예금주/은행/금액으로 + * 푸시 통지 발송을 stdout 으로 시뮬레이션한다 (실발송 없음). 은행별 집계 없이 + * 건별 발송. host 변수는 전용 카피북 py_notifypush_cpy.h 를 EXEC SQL INCLUDE. + * Usage: py_notifypush_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long pushed = 0, total = 0; + + EXEC SQL INCLUDE py_notifypush_cpy; + + strncpy(c_bizdate, bizdate, sizeof(c_bizdate)-1); c_bizdate[sizeof(c_bizdate)-1] = 0; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL DECLARE pnp CURSOR FOR + SELECT p.payout_id, p.net, a.holder, a.bank_code + FROM py_payout p JOIN py_account a ON p.account_no = a.account_no + WHERE p.biz_date = :c_bizdate AND p.status = 'P' + ORDER BY a.bank_code, p.payout_id; + EXEC SQL OPEN pnp; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH pnp INTO :c_pid, :c_net, :c_holder, :c_bank; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pnp; tpabort(0); return 1; } + printf(" [PUSH시뮬] %s(%s) %ld원 지급알림 (payout=%ld)\n", c_holder, c_bank, c_net, c_pid); + pushed++; total += c_net; + } + EXEC SQL CLOSE pnp; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> py_notifypush_batch COMMIT: bizdate=%s pushed=%ld total=%ld\n", bizdate, pushed, total); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/py/batch/py_notifypush_cpy.h b/app/src/py/batch/py_notifypush_cpy.h new file mode 100644 index 0000000..79b5501 --- /dev/null +++ b/app/src/py/batch/py_notifypush_cpy.h @@ -0,0 +1,7 @@ +/* py_notifypush_cpy.h - py_notifypush_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +char c_bizdate[16]; +char c_holder[64]; +char c_bank[8]; +long c_pid, c_net; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/py/batch/py_pendaging_batch.pgc b/app/src/py/batch/py_pendaging_batch.pgc new file mode 100644 index 0000000..0a94d14 --- /dev/null +++ b/app/src/py/batch/py_pendaging_batch.pgc @@ -0,0 +1,41 @@ +/* + * py_pendaging_batch.pgc - py 미지급 경과일 집계 배치 (CASE 단일 집계, XA). + * + * 미지급(R/A/H) 건을 기준일 대비 경과일수로 당일/3일내/장기(초과) 3구간으로 + * CASE 분류해 구간별 건수를 한 번의 집계 쿼리로 산출해 리포트한다. + * Usage: py_pendaging_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_fresh, h_mid, h_old; + EXEC SQL END DECLARE SECTION; + + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL SELECT + coalesce(sum(CASE WHEN :h_bizdate::date - biz_date <= 0 THEN 1 ELSE 0 END),0), + coalesce(sum(CASE WHEN :h_bizdate::date - biz_date BETWEEN 1 AND 3 THEN 1 ELSE 0 END),0), + coalesce(sum(CASE WHEN :h_bizdate::date - biz_date > 3 THEN 1 ELSE 0 END),0) + INTO :h_fresh, :h_mid, :h_old + FROM py_payout WHERE status IN ('R','A','H'); + if (sqlca.sqlcode < 0) { fprintf(stderr, "AGG FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> py_pendaging_batch COMMIT: base=%s 당일=%ld 3일내=%ld 장기=%ld\n", + bizdate, h_fresh, h_mid, h_old); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/py/batch/py_requeue_batch.pgc b/app/src/py/batch/py_requeue_batch.pgc new file mode 100644 index 0000000..87b8c66 --- /dev/null +++ b/app/src/py/batch/py_requeue_batch.pgc @@ -0,0 +1,38 @@ +/* + * py_requeue_batch.pgc - py 실패 재처리 일괄 적재 배치 (단일 UPDATE, XA). + * + * 당일 반려(J) 건 중 재시도 여력(retry_count<3)이 있는 건을 요청(R)으로 + * 되돌리며 retry_count 를 +1 해 재처리 큐에 일괄 적재한다. ONE XA txn. + * Usage: py_requeue_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long requeued; + + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL UPDATE py_payout + SET status = 'R', retry_count = retry_count + 1, reject_reason = '', updated_at = now() + WHERE biz_date = :h_bizdate AND status = 'J' AND retry_count < 3; + if (sqlca.sqlcode < 0) { fprintf(stderr, "UPD FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + requeued = sqlca.sqlerrd[2]; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> py_requeue_batch COMMIT: bizdate=%s requeued=%ld\n", bizdate, requeued); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/py/batch/py_statmonth_batch.pgc b/app/src/py/batch/py_statmonth_batch.pgc new file mode 100644 index 0000000..0ffc9ee --- /dev/null +++ b/app/src/py/batch/py_statmonth_batch.pgc @@ -0,0 +1,50 @@ +/* + * py_statmonth_batch.pgc - py 월별 지급 집계 리포트 배치 (커서 GROUP BY, XA). + * + * 지정 기간의 지급건을 'YYYY-MM' 월 단위로 묶어 월별 건수/net 합을 커서로 + * 훑어 stdout 리포트로 출력한다 (갱신 없음, 읽기 전용 리포트). + * Usage: py_statmonth_batch [FROM-YYYY-MM-DD] [TO-YYYY-MM-DD] + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + const char *from = (argc > 1) ? argv[1] : "2026-01-01"; + const char *to = (argc > 2) ? argv[2] : "2026-12-31"; + long months = 0, grand = 0; + + EXEC SQL BEGIN DECLARE SECTION; + char h_from[16], h_to[16], h_mon[16]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + + strncpy(h_from, from, sizeof(h_from)-1); h_from[sizeof(h_from)-1] = 0; + strncpy(h_to, to, sizeof(h_to)-1); h_to[sizeof(h_to)-1] = 0; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(90, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL DECLARE psm CURSOR FOR + SELECT to_char(biz_date, 'YYYY-MM'), count(*), coalesce(sum(net),0) + FROM py_payout WHERE biz_date BETWEEN :h_from AND :h_to + GROUP BY 1 ORDER BY 1; + EXEC SQL OPEN psm; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH psm INTO :h_mon, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE psm; tpabort(0); return 1; } + months++; grand += h_amt; + printf(" %s: %ld건 %ld원\n", h_mon, h_cnt, h_amt); + } + EXEC SQL CLOSE psm; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> py_statmonth_batch COMMIT: months=%ld grand=%ld\n", months, grand); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/py/batch/py_xferconf_batch.pgc b/app/src/py/batch/py_xferconf_batch.pgc new file mode 100644 index 0000000..1b08249 --- /dev/null +++ b/app/src/py/batch/py_xferconf_batch.pgc @@ -0,0 +1,50 @@ +/* + * py_xferconf_batch.pgc - py 이체 기표 일괄 확정 배치 (커서 + INSERT, XA). + * + * 당일 지급완료(P) 이면서 XFER 기표가 아직 없는 건을 NOT EXISTS 커서로 뽑아 + * py_ledger 에 net 만큼 XFER 기표를 일괄 생성한다. ONE XA txn. + * host 변수는 전용 카피북 py_xferconf_cpy.h 를 EXEC SQL INCLUDE. + * Usage: py_xferconf_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long posted = 0; + + EXEC SQL INCLUDE py_xferconf_cpy; + + strncpy(c_bizdate, bizdate, sizeof(c_bizdate)-1); c_bizdate[sizeof(c_bizdate)-1] = 0; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(120, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL DECLARE pxc CURSOR FOR + SELECT payout_id, net FROM py_payout p + WHERE p.biz_date = :c_bizdate AND p.status = 'P' + AND NOT EXISTS (SELECT 1 FROM py_ledger l + WHERE l.payout_id = p.payout_id AND l.entry_type = 'XFER') + ORDER BY payout_id; + EXEC SQL OPEN pxc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH pxc INTO :c_pid, :c_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pxc; tpabort(0); return 1; } + EXEC SQL INSERT INTO py_ledger (ledger_id, payout_id, entry_type, amount, biz_date) + VALUES (nextval('py_ledger_seq'), :c_pid, 'XFER', :c_net, :c_bizdate); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pxc; tpabort(0); return 1; } + posted++; + } + EXEC SQL CLOSE pxc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> py_xferconf_batch COMMIT: bizdate=%s posted=%ld\n", bizdate, posted); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/py/batch/py_xferconf_cpy.h b/app/src/py/batch/py_xferconf_cpy.h new file mode 100644 index 0000000..0637973 --- /dev/null +++ b/app/src/py/batch/py_xferconf_cpy.h @@ -0,0 +1,5 @@ +/* py_xferconf_cpy.h - py_xferconf_batch 전용 카피북 (host 변수 선언). */ +EXEC SQL BEGIN DECLARE SECTION; +char c_bizdate[16]; +long c_pid, c_net; +EXEC SQL END DECLARE SECTION; diff --git a/app/src/py/py_svr.pgc b/app/src/py/py_svr.pgc index 7f4897a..4b6db7e 100644 --- a/app/src/py/py_svr.pgc +++ b/app/src/py/py_svr.pgc @@ -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 75 online services now lives in its own file + * business logic. Each of py's 133 online services now lives in its own file * (app/src/py/svc/.pgc) with its own copybook header * (app/src/py/svc/.h); those objects are archived into libpysvc.a and * linked into this server. Here we only: @@ -94,6 +94,64 @@ #include "PAY_LEDGERSUM.h" #include "PAY_ACCTLIST.h" #include "PAY_NETRECALC.h" +#include "PAY_XFERFILE.h" +#include "PAY_FILECONF.h" +#include "PAY_FILECANCEL.h" +#include "PAY_BANKVERIFY.h" +#include "PAY_XFERTYPE.h" +#include "PAY_NOTIFYPUSH.h" +#include "PAY_NOTIFYKAKAO.h" +#include "PAY_NOTIFYALL.h" +#include "PAY_NOTIFYFAIL.h" +#include "PAY_NOTIFYRESEND.h" +#include "PAY_ACCTVERIFY.h" +#include "PAY_ACCTFREEZE.h" +#include "PAY_ACCTREOPEN.h" +#include "PAY_REPAYACCT.h" +#include "PAY_HOLDBYRSN.h" +#include "PAY_HOLDCLEAR.h" +#include "PAY_HOLDEXT.h" +#include "PAY_HOLDNOTE.h" +#include "PAY_SEIZELIST.h" +#include "PAY_SEIZEAMT.h" +#include "PAY_OFFSETAPPLY.h" +#include "PAY_GARNISH.h" +#include "PAY_ADVGRANT.h" +#include "PAY_URGENTQ.h" +#include "PAY_RESVLIST.h" +#include "PAY_RESVMODIFY.h" +#include "PAY_SPLITEVEN.h" +#include "PAY_CARRYOVER.h" +#include "PAY_COMBINE.h" +#include "PAY_ROLLFWD.h" +#include "PAY_FEEFIXED.h" +#include "PAY_FEECAP.h" +#include "PAY_FEEREFUND.h" +#include "PAY_FEESUM.h" +#include "PAY_FAILRSN.h" +#include "PAY_REQUEUE.h" +#include "PAY_FAILCLEAR.h" +#include "PAY_RETRYONE.h" +#include "PAY_XFERDIFF.h" +#include "PAY_XFERUNMATCH.h" +#include "PAY_VOIDONE.h" +#include "PAY_RECLAIM.h" +#include "PAY_CHARGEBACK.h" +#include "PAY_LIMITSET.h" +#include "PAY_LIMITLIST.h" +#include "PAY_LIMITWARN.h" +#include "PAY_APPRHIST.h" +#include "PAY_APPRCANCEL.h" +#include "PAY_CLOSEDSTOP.h" +#include "PAY_DORMLIST.h" +#include "PAY_REACTIVATE.h" +#include "PAY_STATSTATUS.h" +#include "PAY_STATAMT.h" +#include "PAY_STATFEE.h" +#include "PAY_STATMONTH.h" +#include "PAY_PENDLIST.h" +#include "PAY_APPRPEND.h" +#include "PAY_XFERCONF.h" /* ----- advertise table: {service name, function} (fn is extern from its file) */ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { @@ -135,6 +193,64 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"PAY_STATRANGE", PAY_STATRANGE}, {"PAY_SCHEDCANCEL", PAY_SCHEDCANCEL}, {"PAY_LEDGERSUM", PAY_LEDGERSUM}, {"PAY_ACCTLIST", PAY_ACCTLIST}, {"PAY_NETRECALC", PAY_NETRECALC}, + {"PAY_XFERFILE", PAY_XFERFILE}, + {"PAY_FILECONF", PAY_FILECONF}, + {"PAY_FILECANCEL", PAY_FILECANCEL}, + {"PAY_BANKVERIFY", PAY_BANKVERIFY}, + {"PAY_XFERTYPE", PAY_XFERTYPE}, + {"PAY_NOTIFYPUSH", PAY_NOTIFYPUSH}, + {"PAY_NOTIFYKAKAO", PAY_NOTIFYKAKAO}, + {"PAY_NOTIFYALL", PAY_NOTIFYALL}, + {"PAY_NOTIFYFAIL", PAY_NOTIFYFAIL}, + {"PAY_NOTIFYRESEND", PAY_NOTIFYRESEND}, + {"PAY_ACCTVERIFY", PAY_ACCTVERIFY}, + {"PAY_ACCTFREEZE", PAY_ACCTFREEZE}, + {"PAY_ACCTREOPEN", PAY_ACCTREOPEN}, + {"PAY_REPAYACCT", PAY_REPAYACCT}, + {"PAY_HOLDBYRSN", PAY_HOLDBYRSN}, + {"PAY_HOLDCLEAR", PAY_HOLDCLEAR}, + {"PAY_HOLDEXT", PAY_HOLDEXT}, + {"PAY_HOLDNOTE", PAY_HOLDNOTE}, + {"PAY_SEIZELIST", PAY_SEIZELIST}, + {"PAY_SEIZEAMT", PAY_SEIZEAMT}, + {"PAY_OFFSETAPPLY", PAY_OFFSETAPPLY}, + {"PAY_GARNISH", PAY_GARNISH}, + {"PAY_ADVGRANT", PAY_ADVGRANT}, + {"PAY_URGENTQ", PAY_URGENTQ}, + {"PAY_RESVLIST", PAY_RESVLIST}, + {"PAY_RESVMODIFY", PAY_RESVMODIFY}, + {"PAY_SPLITEVEN", PAY_SPLITEVEN}, + {"PAY_CARRYOVER", PAY_CARRYOVER}, + {"PAY_COMBINE", PAY_COMBINE}, + {"PAY_ROLLFWD", PAY_ROLLFWD}, + {"PAY_FEEFIXED", PAY_FEEFIXED}, + {"PAY_FEECAP", PAY_FEECAP}, + {"PAY_FEEREFUND", PAY_FEEREFUND}, + {"PAY_FEESUM", PAY_FEESUM}, + {"PAY_FAILRSN", PAY_FAILRSN}, + {"PAY_REQUEUE", PAY_REQUEUE}, + {"PAY_FAILCLEAR", PAY_FAILCLEAR}, + {"PAY_RETRYONE", PAY_RETRYONE}, + {"PAY_XFERDIFF", PAY_XFERDIFF}, + {"PAY_XFERUNMATCH", PAY_XFERUNMATCH}, + {"PAY_VOIDONE", PAY_VOIDONE}, + {"PAY_RECLAIM", PAY_RECLAIM}, + {"PAY_CHARGEBACK", PAY_CHARGEBACK}, + {"PAY_LIMITSET", PAY_LIMITSET}, + {"PAY_LIMITLIST", PAY_LIMITLIST}, + {"PAY_LIMITWARN", PAY_LIMITWARN}, + {"PAY_APPRHIST", PAY_APPRHIST}, + {"PAY_APPRCANCEL", PAY_APPRCANCEL}, + {"PAY_CLOSEDSTOP", PAY_CLOSEDSTOP}, + {"PAY_DORMLIST", PAY_DORMLIST}, + {"PAY_REACTIVATE", PAY_REACTIVATE}, + {"PAY_STATSTATUS", PAY_STATSTATUS}, + {"PAY_STATAMT", PAY_STATAMT}, + {"PAY_STATFEE", PAY_STATFEE}, + {"PAY_STATMONTH", PAY_STATMONTH}, + {"PAY_PENDLIST", PAY_PENDLIST}, + {"PAY_APPRPEND", PAY_APPRPEND}, + {"PAY_XFERCONF", PAY_XFERCONF}, {NULL, NULL} }; diff --git a/app/src/py/svc/PAY_ACCTFREEZE.h b/app/src/py/svc/PAY_ACCTFREEZE.h new file mode 100644 index 0000000..61e4855 --- /dev/null +++ b/app/src/py/svc/PAY_ACCTFREEZE.h @@ -0,0 +1,20 @@ +/* + * PAY_ACCTFREEZE.h - py 서비스 PAY_ACCTFREEZE 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_ACCTFREEZE.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_ACCTFREEZE_H +#define PAY_ACCTFREEZE_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_acctfreeze_rec_t; + +void PAY_ACCTFREEZE(TPSVCINFO *p); + +#endif /* PAY_ACCTFREEZE_H */ diff --git a/app/src/py/svc/PAY_ACCTFREEZE.pgc b/app/src/py/svc/PAY_ACCTFREEZE.pgc new file mode 100644 index 0000000..0e7b646 --- /dev/null +++ b/app/src/py/svc/PAY_ACCTFREEZE.pgc @@ -0,0 +1,34 @@ +/* + * PAY_ACCTFREEZE.pgc - py 모듈 서비스 PAY_ACCTFREEZE (one service = one file). + * 지급계좌 동결: 활성(ACTIVE) 계좌만 FROZEN 으로 전환한다. 먼저 해당 계좌의 + * 미지급(R/A) 건수를 조회해 함께 회신하고, 동결 대상이 아니면 거절한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_ACCTFREEZE.h" + +void PAY_ACCTFREEZE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_acct[32]; + long h_pend; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_acct, sizeof(h_acct)); + EXEC SQL SELECT count(*) INTO :h_pend + FROM py_payout WHERE account_no = :h_acct AND status IN ('R','A'); + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL UPDATE py_account SET status = 'FROZEN', updated_at = now() + WHERE account_no = :h_acct AND status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlerrd[2] == 0) { + setl(b, T_RC, 1); + Bchg(b, T_MSG, 0, "동결 대상 아님(활성계좌 아님)", 0L); + FAIL(b); + } + setl(b, T_RC, 0); + setl(b, T_COUNT, h_pend); + Bchg(b, T_STATUS, 0, "FROZEN", 0L); + userlog("PAY_ACCTFREEZE acct=%s pending=%ld", h_acct, h_pend); + OK(b); +} diff --git a/app/src/py/svc/PAY_ACCTREOPEN.h b/app/src/py/svc/PAY_ACCTREOPEN.h new file mode 100644 index 0000000..bd78fec --- /dev/null +++ b/app/src/py/svc/PAY_ACCTREOPEN.h @@ -0,0 +1,20 @@ +/* + * PAY_ACCTREOPEN.h - py 서비스 PAY_ACCTREOPEN 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_ACCTREOPEN.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_ACCTREOPEN_H +#define PAY_ACCTREOPEN_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_acctreopen_rec_t; + +void PAY_ACCTREOPEN(TPSVCINFO *p); + +#endif /* PAY_ACCTREOPEN_H */ diff --git a/app/src/py/svc/PAY_ACCTREOPEN.pgc b/app/src/py/svc/PAY_ACCTREOPEN.pgc new file mode 100644 index 0000000..66bc0df --- /dev/null +++ b/app/src/py/svc/PAY_ACCTREOPEN.pgc @@ -0,0 +1,36 @@ +/* + * PAY_ACCTREOPEN.pgc - py 모듈 서비스 PAY_ACCTREOPEN (one service = one file). + * 동결 해제: 동결(FROZEN) 계좌를 ACTIVE 로 복구하고, 동결 사유로 보류(H)됐던 + * 그 계좌의 지급건을 승인(A)으로 되돌린다. 동결 계좌가 아니면 거절한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_ACCTREOPEN.h" + +void PAY_ACCTREOPEN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_acct[32]; + long h_rel; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_acct, sizeof(h_acct)); + EXEC SQL UPDATE py_account SET status = 'ACTIVE', updated_at = now() + WHERE account_no = :h_acct AND status = 'FROZEN'; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlerrd[2] == 0) { + setl(b, T_RC, 1); + Bchg(b, T_MSG, 0, "동결 계좌 아님", 0L); + FAIL(b); + } + EXEC SQL UPDATE py_payout SET status = 'A', reject_reason = '', updated_at = now() + WHERE account_no = :h_acct AND status = 'H' AND reject_reason LIKE '동결%'; + if (sqlca.sqlcode < 0) FAIL(b); + h_rel = sqlca.sqlerrd[2]; + + setl(b, T_RC, 0); + setl(b, T_COUNT, h_rel); + Bchg(b, T_STATUS, 0, "ACTIVE", 0L); + userlog("PAY_ACCTREOPEN acct=%s released=%ld", h_acct, h_rel); + OK(b); +} diff --git a/app/src/py/svc/PAY_ACCTVERIFY.h b/app/src/py/svc/PAY_ACCTVERIFY.h new file mode 100644 index 0000000..92b1a8d --- /dev/null +++ b/app/src/py/svc/PAY_ACCTVERIFY.h @@ -0,0 +1,20 @@ +/* + * PAY_ACCTVERIFY.h - py 서비스 PAY_ACCTVERIFY 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_ACCTVERIFY.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_ACCTVERIFY_H +#define PAY_ACCTVERIFY_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_acctverify_rec_t; + +void PAY_ACCTVERIFY(TPSVCINFO *p); + +#endif /* PAY_ACCTVERIFY_H */ diff --git a/app/src/py/svc/PAY_ACCTVERIFY.pgc b/app/src/py/svc/PAY_ACCTVERIFY.pgc new file mode 100644 index 0000000..b2ce53e --- /dev/null +++ b/app/src/py/svc/PAY_ACCTVERIFY.pgc @@ -0,0 +1,34 @@ +/* + * PAY_ACCTVERIFY.pgc - py 모듈 서비스 PAY_ACCTVERIFY (one service = one file). + * 지급계좌 검증: 계좌의 상태/예금주/잔액을 조회하고, 요청에 예금주가 주어지면 + * 일치 여부까지 확인한다. 미등록/불일치는 사유코드로 구분해 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_ACCTVERIFY.h" + +void PAY_ACCTVERIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char inh[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_acct[32], h_st[16], h_holder[64]; + long h_bal; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_acct, sizeof(h_acct)); + gets_(b, T_STR1, inh, sizeof(inh)); + EXEC SQL SELECT status, holder, balance INTO :h_st, :h_holder, :h_bal + FROM py_account WHERE account_no = :h_acct; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "미등록 계좌", 0L); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (inh[0] != 0 && strcmp(inh, h_holder) != 0) { + setl(b, T_RC, 1); + Bchg(b, T_MSG, 0, "예금주 불일치", 0L); + FAIL(b); + } + setl(b, T_RC, 0); + setl(b, T_AMT1, h_bal); + Bchg(b, T_STATUS, 0, h_st, 0L); + Bchg(b, T_STR2, 0, h_holder, 0L); + OK(b); +} diff --git a/app/src/py/svc/PAY_ADVGRANT.h b/app/src/py/svc/PAY_ADVGRANT.h new file mode 100644 index 0000000..9cedf39 --- /dev/null +++ b/app/src/py/svc/PAY_ADVGRANT.h @@ -0,0 +1,20 @@ +/* + * PAY_ADVGRANT.h - py 서비스 PAY_ADVGRANT 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_ADVGRANT.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_ADVGRANT_H +#define PAY_ADVGRANT_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_advgrant_rec_t; + +void PAY_ADVGRANT(TPSVCINFO *p); + +#endif /* PAY_ADVGRANT_H */ diff --git a/app/src/py/svc/PAY_ADVGRANT.pgc b/app/src/py/svc/PAY_ADVGRANT.pgc new file mode 100644 index 0000000..fc383a1 --- /dev/null +++ b/app/src/py/svc/PAY_ADVGRANT.pgc @@ -0,0 +1,32 @@ +/* + * PAY_ADVGRANT.pgc - py 모듈 서비스 PAY_ADVGRANT (one service = one file). + * 선지급 승인: 선지급(ADVANCE) 채널의 요청(R) 건만 승인(A)으로 전이한다. + * 채널/상태를 함께 가드하며, 대상이 아니면 사유코드로 구분해 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_ADVGRANT.h" + +void PAY_ADVGRANT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_amt; + char h_st[4], h_chan[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + EXEC SQL SELECT status, channel, amount INTO :h_st, :h_chan, :h_amt + 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, 2); Bchg(b, T_MSG, 0, "선지급 건 아님", 0L); FAIL(b); } + if (h_st[0] != 'R') { setl(b, T_RC, 1); 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); + + setl(b, T_RC, 0); + setl(b, T_AMOUNT, h_amt); + Bchg(b, T_STATUS, 0, "A", 0L); + userlog("PAY_ADVGRANT payout=%ld amt=%ld", h_pid, h_amt); + OK(b); +} diff --git a/app/src/py/svc/PAY_APPRCANCEL.h b/app/src/py/svc/PAY_APPRCANCEL.h new file mode 100644 index 0000000..20156db --- /dev/null +++ b/app/src/py/svc/PAY_APPRCANCEL.h @@ -0,0 +1,20 @@ +/* + * PAY_APPRCANCEL.h - py 서비스 PAY_APPRCANCEL 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_APPRCANCEL.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_APPRCANCEL_H +#define PAY_APPRCANCEL_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_apprcancel_rec_t; + +void PAY_APPRCANCEL(TPSVCINFO *p); + +#endif /* PAY_APPRCANCEL_H */ diff --git a/app/src/py/svc/PAY_APPRCANCEL.pgc b/app/src/py/svc/PAY_APPRCANCEL.pgc new file mode 100644 index 0000000..c29b772 --- /dev/null +++ b/app/src/py/svc/PAY_APPRCANCEL.pgc @@ -0,0 +1,28 @@ +/* + * PAY_APPRCANCEL.pgc - py 모듈 서비스 PAY_APPRCANCEL (one service = one file). + * 지급 승인 취소: 아직 파일에 편성되지 않은 승인(A) 건을 요청(R)으로 되돌린다. + * 파일편성(F)/지급완료(P) 이후에는 승인 취소가 불가하다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_APPRCANCEL.h" + +void PAY_APPRCANCEL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long reverted; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + EXEC SQL UPDATE py_payout SET status = 'R', updated_at = now() + WHERE payout_id = :h_pid AND status = 'A'; + if (sqlca.sqlcode < 0) FAIL(b); + reverted = sqlca.sqlerrd[2]; + if (reverted == 0) { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "승인 상태 건만 취소 가능", 0L); FAIL(b); } + + setl(b, T_RC, 0); + Bchg(b, T_STATUS, 0, "R", 0L); + userlog("PAY_APPRCANCEL payout=%ld -> R", h_pid); + OK(b); +} diff --git a/app/src/py/svc/PAY_APPRHIST.h b/app/src/py/svc/PAY_APPRHIST.h new file mode 100644 index 0000000..3d1dbdf --- /dev/null +++ b/app/src/py/svc/PAY_APPRHIST.h @@ -0,0 +1,20 @@ +/* + * PAY_APPRHIST.h - py 서비스 PAY_APPRHIST 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_APPRHIST.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_APPRHIST_H +#define PAY_APPRHIST_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_apprhist_rec_t; + +void PAY_APPRHIST(TPSVCINFO *p); + +#endif /* PAY_APPRHIST_H */ diff --git a/app/src/py/svc/PAY_APPRHIST.pgc b/app/src/py/svc/PAY_APPRHIST.pgc new file mode 100644 index 0000000..a28cb5a --- /dev/null +++ b/app/src/py/svc/PAY_APPRHIST.pgc @@ -0,0 +1,41 @@ +/* + * PAY_APPRHIST.pgc - py 모듈 서비스 PAY_APPRHIST (one service = one file). + * 지급 승인/원장 이력: 지정 지급건의 py_ledger 기표를 시간순 커서로 훑어 + * 기표유형/금액/일자를 occurrence 로 회신한다 (승인·이체·환입 흐름 추적). + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_APPRHIST.h" + +void PAY_APPRHIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_amt; + char h_type[16], h_date[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + EXEC SQL DECLARE cur_apprhist CURSOR FOR + SELECT entry_type, amount, CAST(biz_date AS text) + FROM py_ledger WHERE payout_id = :h_pid ORDER BY ledger_id; + EXEC SQL OPEN cur_apprhist; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_apprhist INTO :h_type, :h_amt, :h_date; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_apprhist; FAIL(b); } + rows++; + if (i < 12) { + Bchg(b, T_STR3, i, h_type, 0L); + Bchg(b, T_AMT1, i, (char *)&h_amt, 0L); + Bchg(b, T_STR4, i, h_date, 0L); + i++; + } + } + EXEC SQL CLOSE cur_apprhist; + setl(b, T_COUNT, rows); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_APPRPEND.h b/app/src/py/svc/PAY_APPRPEND.h new file mode 100644 index 0000000..4504837 --- /dev/null +++ b/app/src/py/svc/PAY_APPRPEND.h @@ -0,0 +1,20 @@ +/* + * PAY_APPRPEND.h - py 서비스 PAY_APPRPEND 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_APPRPEND.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_APPRPEND_H +#define PAY_APPRPEND_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_apprpend_rec_t; + +void PAY_APPRPEND(TPSVCINFO *p); + +#endif /* PAY_APPRPEND_H */ diff --git a/app/src/py/svc/PAY_APPRPEND.pgc b/app/src/py/svc/PAY_APPRPEND.pgc new file mode 100644 index 0000000..307bb5b --- /dev/null +++ b/app/src/py/svc/PAY_APPRPEND.pgc @@ -0,0 +1,43 @@ +/* + * PAY_APPRPEND.pgc - py 모듈 서비스 PAY_APPRPEND (one service = one file). + * 승인 대기 집계: 아직 승인 전인 요청(R) 지급건을 가맹점별로 묶어 대기 건수/ + * 요청금액 합을 건수 내림차순 occurrence 로 회신한다 (승인 워크로드 파악). + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_APPRPEND.h" + +void PAY_APPRPEND(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long grp = 0, waiting = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + + EXEC SQL DECLARE cur_apprpend CURSOR FOR + SELECT merchant_id, count(*), coalesce(sum(amount),0) + FROM py_payout WHERE status = 'R' + GROUP BY merchant_id ORDER BY 2 DESC, 1; + EXEC SQL OPEN cur_apprpend; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_apprpend INTO :h_merch, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_apprpend; FAIL(b); } + grp++; + waiting += h_cnt; + if (i < 10) { + Bchg(b, T_STR2, i, h_merch, 0L); + Bchg(b, T_ARG3, i, (char *)&h_cnt, 0L); + Bchg(b, T_AMT1, i, (char *)&h_amt, 0L); + i++; + } + } + EXEC SQL CLOSE cur_apprpend; + setl(b, T_COUNT, grp); + setl(b, T_ARG2, waiting); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_BANKVERIFY.h b/app/src/py/svc/PAY_BANKVERIFY.h new file mode 100644 index 0000000..ba97a47 --- /dev/null +++ b/app/src/py/svc/PAY_BANKVERIFY.h @@ -0,0 +1,20 @@ +/* + * PAY_BANKVERIFY.h - py 서비스 PAY_BANKVERIFY 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_BANKVERIFY.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_BANKVERIFY_H +#define PAY_BANKVERIFY_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_bankverify_rec_t; + +void PAY_BANKVERIFY(TPSVCINFO *p); + +#endif /* PAY_BANKVERIFY_H */ diff --git a/app/src/py/svc/PAY_BANKVERIFY.pgc b/app/src/py/svc/PAY_BANKVERIFY.pgc new file mode 100644 index 0000000..8463d85 --- /dev/null +++ b/app/src/py/svc/PAY_BANKVERIFY.pgc @@ -0,0 +1,37 @@ +/* + * PAY_BANKVERIFY.pgc - py 모듈 서비스 PAY_BANKVERIFY (one service = one file). + * 지급파일 검증: 파일 헤더(record_count/total_amount)와 실제 편성 지급건의 + * 건수/net 합을 대사해 일치 여부와 금액 차액(T_DELTA)을 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_BANKVERIFY.h" + +void PAY_BANKVERIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_fid, h_hcnt, h_hsum, h_acnt, h_asum; + EXEC SQL END DECLARE SECTION; + + h_fid = getl(b, T_ID2); + EXEC SQL SELECT record_count, total_amount INTO :h_hcnt, :h_hsum + 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_AMT1, h_hsum); + setl(b, T_AMT2, h_asum); + setl(b, T_COUNT, h_acnt); + setl(b, T_DELTA, h_hsum - h_asum); + if (h_hcnt == h_acnt && h_hsum == 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_BANKVERIFY 불일치 file=%ld hcnt=%ld acnt=%ld", h_fid, h_hcnt, h_acnt); + } + OK(b); +} diff --git a/app/src/py/svc/PAY_CARRYOVER.h b/app/src/py/svc/PAY_CARRYOVER.h new file mode 100644 index 0000000..48da6d0 --- /dev/null +++ b/app/src/py/svc/PAY_CARRYOVER.h @@ -0,0 +1,20 @@ +/* + * PAY_CARRYOVER.h - py 서비스 PAY_CARRYOVER 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_CARRYOVER.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_CARRYOVER_H +#define PAY_CARRYOVER_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_carryover_rec_t; + +void PAY_CARRYOVER(TPSVCINFO *p); + +#endif /* PAY_CARRYOVER_H */ diff --git a/app/src/py/svc/PAY_CARRYOVER.pgc b/app/src/py/svc/PAY_CARRYOVER.pgc new file mode 100644 index 0000000..7b5575d --- /dev/null +++ b/app/src/py/svc/PAY_CARRYOVER.pgc @@ -0,0 +1,40 @@ +/* + * PAY_CARRYOVER.pgc - py 모듈 서비스 PAY_CARRYOVER (one service = one file). + * 지급 이월(단건): 미지급(R/A) 건의 지급기준일(biz_date)을 지정 차기일자로 + * 이월하고, 이월 사유를 py_adjust 에 0원 조정으로 증적으로 남긴다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_CARRYOVER.h" + +void PAY_CARRYOVER(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char nextdate[16], reason[96]; + long adj; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid; + char h_st[4], h_next[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + gets_(b, T_KEY2, nextdate, sizeof(nextdate)); + gets_(b, T_REASON, reason, sizeof(reason)); + if (nextdate[0] == 0) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "이월일자 미지정", 0L); FAIL(b); } + if (reason[0] == 0) strcpy(reason, "지급이월"); + strncpy(h_next, nextdate, sizeof(h_next)-1); h_next[sizeof(h_next)-1] = 0; + + EXEC SQL SELECT status INTO :h_st 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] != 'A') { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "미지급 건만 이월 가능", 0L); FAIL(b); } + + EXEC SQL UPDATE py_payout SET biz_date = :h_next, updated_at = now() WHERE payout_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + adj = pydb_insert_adjust(h_pid, 0, reason, nextdate); + if (adj < 0) FAIL(b); + + setl(b, T_RC, 0); + setl(b, T_ADJ, adj); + Bchg(b, T_STR1, 0, nextdate, 0L); + userlog("PAY_CARRYOVER id=%ld to=%s", h_pid, nextdate); + OK(b); +} diff --git a/app/src/py/svc/PAY_CHARGEBACK.h b/app/src/py/svc/PAY_CHARGEBACK.h new file mode 100644 index 0000000..cc7303d --- /dev/null +++ b/app/src/py/svc/PAY_CHARGEBACK.h @@ -0,0 +1,20 @@ +/* + * PAY_CHARGEBACK.h - py 서비스 PAY_CHARGEBACK 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_CHARGEBACK.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_CHARGEBACK_H +#define PAY_CHARGEBACK_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_chargeback_rec_t; + +void PAY_CHARGEBACK(TPSVCINFO *p); + +#endif /* PAY_CHARGEBACK_H */ diff --git a/app/src/py/svc/PAY_CHARGEBACK.pgc b/app/src/py/svc/PAY_CHARGEBACK.pgc new file mode 100644 index 0000000..63d0735 --- /dev/null +++ b/app/src/py/svc/PAY_CHARGEBACK.pgc @@ -0,0 +1,36 @@ +/* + * PAY_CHARGEBACK.pgc - py 모듈 서비스 PAY_CHARGEBACK (one service = one file). + * 지급 되물림: 분쟁 등으로 지급완료(P) 건을 되물릴 때, 원건은 보존한 채 + * 음수 금액의 상계 지급건(CHGBK 채널)을 신규 생성해 상쇄 처리한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_CHARGEBACK.h" + +void PAY_CHARGEBACK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long newid, neg; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_net; + char h_merch[64], h_acct[32], h_st[4]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + EXEC SQL SELECT merchant_id, account_no, net, status + INTO :h_merch, :h_acct, :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, "지급완료 건만 되물림 가능", 0L); FAIL(b); } + + neg = -h_net; + newid = pydb_next_payout_id(); + if (newid < 0) FAIL(b); + if (pydb_insert_payout(newid, h_merch, h_acct, neg, 0, neg, "R", "CHGBK", bizdate) < 0) FAIL(b); + + setl(b, T_RC, 0); + setl(b, T_ID2, newid); + setl(b, T_NET, neg); + userlog("PAY_CHARGEBACK src=%ld offset=%ld net=%ld", h_pid, newid, neg); + OK(b); +} diff --git a/app/src/py/svc/PAY_CLOSEDSTOP.h b/app/src/py/svc/PAY_CLOSEDSTOP.h new file mode 100644 index 0000000..740c3ca --- /dev/null +++ b/app/src/py/svc/PAY_CLOSEDSTOP.h @@ -0,0 +1,20 @@ +/* + * PAY_CLOSEDSTOP.h - py 서비스 PAY_CLOSEDSTOP 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_CLOSEDSTOP.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_CLOSEDSTOP_H +#define PAY_CLOSEDSTOP_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_closedstop_rec_t; + +void PAY_CLOSEDSTOP(TPSVCINFO *p); + +#endif /* PAY_CLOSEDSTOP_H */ diff --git a/app/src/py/svc/PAY_CLOSEDSTOP.pgc b/app/src/py/svc/PAY_CLOSEDSTOP.pgc new file mode 100644 index 0000000..b7faeed --- /dev/null +++ b/app/src/py/svc/PAY_CLOSEDSTOP.pgc @@ -0,0 +1,32 @@ +/* + * PAY_CLOSEDSTOP.pgc - py 모듈 서비스 PAY_CLOSEDSTOP (one service = one file). + * 폐업 가맹점 지급정지: 가맹점 상태가 CLOSED 인 가맹점의 미지급(R/A) 건을 + * 모두 보류(H)+'폐업정지' 사유로 전이한다. 지급망 유출을 사전 차단한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_CLOSEDSTOP.h" + +void PAY_CLOSEDSTOP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long stopped; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64]; + char h_mst[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, h_merch, sizeof(h_merch)); + EXEC SQL SELECT status INTO :h_mst FROM merchant WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + if (strcmp(h_mst, "CLOSED") != 0) { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "폐업 가맹점 아님", 0L); FAIL(b); } + + EXEC SQL UPDATE py_payout SET status = 'H', reject_reason = '폐업정지', updated_at = now() + WHERE merchant_id = :h_merch AND status IN ('R','A'); + if (sqlca.sqlcode < 0) FAIL(b); + stopped = sqlca.sqlerrd[2]; + + setl(b, T_RC, 0); + setl(b, T_COUNT, stopped); + userlog("PAY_CLOSEDSTOP merch=%s stopped=%ld", h_merch, stopped); + OK(b); +} diff --git a/app/src/py/svc/PAY_COMBINE.h b/app/src/py/svc/PAY_COMBINE.h new file mode 100644 index 0000000..a4335b9 --- /dev/null +++ b/app/src/py/svc/PAY_COMBINE.h @@ -0,0 +1,20 @@ +/* + * PAY_COMBINE.h - py 서비스 PAY_COMBINE 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_COMBINE.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_COMBINE_H +#define PAY_COMBINE_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_combine_rec_t; + +void PAY_COMBINE(TPSVCINFO *p); + +#endif /* PAY_COMBINE_H */ diff --git a/app/src/py/svc/PAY_COMBINE.pgc b/app/src/py/svc/PAY_COMBINE.pgc new file mode 100644 index 0000000..c1950ff --- /dev/null +++ b/app/src/py/svc/PAY_COMBINE.pgc @@ -0,0 +1,42 @@ +/* + * PAY_COMBINE.pgc - py 모듈 서비스 PAY_COMBINE (one service = one file). + * 지급 합산: 같은 계좌의 당일 요청(R) 건들을 하나로 합산 지급한다. 합계 + * 금액으로 신규 합산건(BULK)을 만들고 원건들은 취소(C) 처리한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_COMBINE.h" + +void PAY_COMBINE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long newid; + EXEC SQL BEGIN DECLARE SECTION; + char h_acct[32], h_bizdate[16], h_merch[64]; + long h_cnt, h_sum; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_acct, sizeof(h_acct)); + 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(amount),0), coalesce(max(merchant_id),'') + INTO :h_cnt, :h_sum, :h_merch + FROM py_payout WHERE account_no = :h_acct AND biz_date = :h_bizdate AND status = 'R'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt < 2) { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "합산 대상(2건 이상) 없음", 0L); FAIL(b); } + + EXEC SQL UPDATE py_payout SET status = 'C', updated_at = now() + WHERE account_no = :h_acct AND biz_date = :h_bizdate AND status = 'R'; + if (sqlca.sqlcode < 0) FAIL(b); + newid = pydb_next_payout_id(); + if (newid < 0) FAIL(b); + if (pydb_insert_payout(newid, h_merch, h_acct, h_sum, 0, h_sum, "R", "BULK", bizdate) < 0) FAIL(b); + + setl(b, T_RC, 0); + setl(b, T_ID2, newid); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_sum); + userlog("PAY_COMBINE acct=%s merged=%ld sum=%ld new=%ld", h_acct, h_cnt, h_sum, newid); + OK(b); +} diff --git a/app/src/py/svc/PAY_DORMLIST.h b/app/src/py/svc/PAY_DORMLIST.h new file mode 100644 index 0000000..b15d52c --- /dev/null +++ b/app/src/py/svc/PAY_DORMLIST.h @@ -0,0 +1,20 @@ +/* + * PAY_DORMLIST.h - py 서비스 PAY_DORMLIST 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_DORMLIST.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_DORMLIST_H +#define PAY_DORMLIST_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_dormlist_rec_t; + +void PAY_DORMLIST(TPSVCINFO *p); + +#endif /* PAY_DORMLIST_H */ diff --git a/app/src/py/svc/PAY_DORMLIST.pgc b/app/src/py/svc/PAY_DORMLIST.pgc new file mode 100644 index 0000000..3fe29ae --- /dev/null +++ b/app/src/py/svc/PAY_DORMLIST.pgc @@ -0,0 +1,44 @@ +/* + * PAY_DORMLIST.pgc - py 모듈 서비스 PAY_DORMLIST (one service = one file). + * 휴면 계좌 목록: 지정 기준일 이후 지급 이력이 전혀 없는 활성(ACTIVE) 계좌를 + * NOT EXISTS 커서로 뽑아 계좌/예금주/잔액을 occurrence 로 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_DORMLIST.h" + +void PAY_DORMLIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long n = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_since[16], h_acct[32], h_holder[64]; + long h_bal; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_since, sizeof(h_since)); + EXEC SQL DECLARE cur_dormlist CURSOR FOR + SELECT a.account_no, a.holder, a.balance FROM py_account a + WHERE a.status = 'ACTIVE' + AND NOT EXISTS (SELECT 1 FROM py_payout p + WHERE p.account_no = a.account_no AND p.biz_date >= :h_since) + ORDER BY a.account_no; + EXEC SQL OPEN cur_dormlist; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_dormlist INTO :h_acct, :h_holder, :h_bal; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_dormlist; FAIL(b); } + n++; + if (i < 10) { + Bchg(b, T_STR5, i, h_acct, 0L); + Bchg(b, T_STR6, i, h_holder, 0L); + Bchg(b, T_AMT1, i, (char *)&h_bal, 0L); + i++; + } + } + EXEC SQL CLOSE cur_dormlist; + setl(b, T_COUNT, n); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_FAILCLEAR.h b/app/src/py/svc/PAY_FAILCLEAR.h new file mode 100644 index 0000000..45a7493 --- /dev/null +++ b/app/src/py/svc/PAY_FAILCLEAR.h @@ -0,0 +1,20 @@ +/* + * PAY_FAILCLEAR.h - py 서비스 PAY_FAILCLEAR 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_FAILCLEAR.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_FAILCLEAR_H +#define PAY_FAILCLEAR_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_failclear_rec_t; + +void PAY_FAILCLEAR(TPSVCINFO *p); + +#endif /* PAY_FAILCLEAR_H */ diff --git a/app/src/py/svc/PAY_FAILCLEAR.pgc b/app/src/py/svc/PAY_FAILCLEAR.pgc new file mode 100644 index 0000000..869c7cf --- /dev/null +++ b/app/src/py/svc/PAY_FAILCLEAR.pgc @@ -0,0 +1,34 @@ +/* + * PAY_FAILCLEAR.pgc - py 모듈 서비스 PAY_FAILCLEAR (one service = one file). + * 실패 종결: 재처리 불가한 반려(J) 단건을 취소(C)로 종결하고 종결 사유를 + * reject_reason 에 남긴다. 반려 상태가 아니면 거절한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_FAILCLEAR.h" + +void PAY_FAILCLEAR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long payout_id = getl(b, T_ID1); + char st[4], reason[128]; + + gets_(b, T_REASON, reason, sizeof(reason)); + if (reason[0] == 0) strcpy(reason, "재처리불가종결"); + if (pydb_get_status(payout_id, st) < 0) FAIL(b); + if (st[0] != 'J') { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "반려 건만 종결 가능", 0L); FAIL(b); } + + { + EXEC SQL BEGIN DECLARE SECTION; + long h_pid = payout_id; + char h_rsn[128]; + EXEC SQL END DECLARE SECTION; + strncpy(h_rsn, reason, sizeof(h_rsn)-1); h_rsn[sizeof(h_rsn)-1] = 0; + EXEC SQL UPDATE py_payout SET status = 'C', reject_reason = :h_rsn, updated_at = now() + WHERE payout_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + } + setl(b, T_RC, 0); + Bchg(b, T_STATUS, 0, "C", 0L); + Bchg(b, T_REASON, 0, reason, 0L); + OK(b); +} diff --git a/app/src/py/svc/PAY_FAILRSN.h b/app/src/py/svc/PAY_FAILRSN.h new file mode 100644 index 0000000..553b177 --- /dev/null +++ b/app/src/py/svc/PAY_FAILRSN.h @@ -0,0 +1,20 @@ +/* + * PAY_FAILRSN.h - py 서비스 PAY_FAILRSN 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_FAILRSN.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_FAILRSN_H +#define PAY_FAILRSN_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_failrsn_rec_t; + +void PAY_FAILRSN(TPSVCINFO *p); + +#endif /* PAY_FAILRSN_H */ diff --git a/app/src/py/svc/PAY_FAILRSN.pgc b/app/src/py/svc/PAY_FAILRSN.pgc new file mode 100644 index 0000000..e3269a3 --- /dev/null +++ b/app/src/py/svc/PAY_FAILRSN.pgc @@ -0,0 +1,42 @@ +/* + * PAY_FAILRSN.pgc - py 모듈 서비스 PAY_FAILRSN (one service = one file). + * 실패 재시도횟수별 분포: 당일 반려(J) 건을 retry_count 별 커서로 건수/net + * 집계해 재시도 횟수 오름차순 occurrence 로 회신한다 (재처리 난이도 파악). + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_FAILRSN.h" + +void PAY_FAILRSN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long buckets = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_rc, h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL DECLARE cur_failrsn CURSOR FOR + SELECT retry_count, count(*), coalesce(sum(net),0) + FROM py_payout WHERE status = 'J' AND biz_date = :h_bizdate + GROUP BY retry_count ORDER BY retry_count; + EXEC SQL OPEN cur_failrsn; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_failrsn INTO :h_rc, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_failrsn; FAIL(b); } + buckets++; + if (i < 8) { + Bchg(b, T_ARG6, i, (char *)&h_rc, 0L); + Bchg(b, T_ARG7, i, (char *)&h_cnt, 0L); + Bchg(b, T_AMT2, i, (char *)&h_amt, 0L); + i++; + } + } + EXEC SQL CLOSE cur_failrsn; + setl(b, T_COUNT, buckets); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_FEECAP.h b/app/src/py/svc/PAY_FEECAP.h new file mode 100644 index 0000000..a0212cf --- /dev/null +++ b/app/src/py/svc/PAY_FEECAP.h @@ -0,0 +1,20 @@ +/* + * PAY_FEECAP.h - py 서비스 PAY_FEECAP 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_FEECAP.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_FEECAP_H +#define PAY_FEECAP_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_feecap_rec_t; + +void PAY_FEECAP(TPSVCINFO *p); + +#endif /* PAY_FEECAP_H */ diff --git a/app/src/py/svc/PAY_FEECAP.pgc b/app/src/py/svc/PAY_FEECAP.pgc new file mode 100644 index 0000000..0ee30d2 --- /dev/null +++ b/app/src/py/svc/PAY_FEECAP.pgc @@ -0,0 +1,45 @@ +/* + * PAY_FEECAP.pgc - py 모듈 서비스 PAY_FEECAP (one service = one file). + * 수수료 상한 적용: 미지급 건 수수료가 상한(T_AMOUNT)을 넘으면 상한까지 + * 낮추고 초과분만큼 net 을 늘린다. 초과분은 py_adjust 에 양수로 남긴다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_FEECAP.h" + +void PAY_FEECAP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char reason[96], bizdate[16]; + long cap, cut, adj; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_fee, h_net, h_newfee, h_newnet; + char h_st[4]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + cap = getl(b, T_AMOUNT); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_REASON, reason, sizeof(reason)); + if (reason[0] == 0) strcpy(reason, "수수료상한"); + + EXEC SQL SELECT fee, net, status INTO :h_fee, :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] != 'R' && h_st[0] != 'A') { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "미지급 건만 적용 가능", 0L); FAIL(b); } + if (h_fee <= cap) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "수수료가 상한 이내", 0L); FAIL(b); } + + cut = h_fee - cap; + h_newfee = cap; + h_newnet = h_net + cut; + adj = pydb_insert_adjust(h_pid, cut, reason, bizdate); + if (adj < 0) FAIL(b); + EXEC SQL UPDATE py_payout SET fee = :h_newfee, net = :h_newnet, updated_at = now() + WHERE payout_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_RC, 0); + setl(b, T_ADJ, adj); + setl(b, T_FEE, h_newfee); + setl(b, T_NET, h_newnet); + userlog("PAY_FEECAP id=%ld cap=%ld cut=%ld", h_pid, cap, cut); + OK(b); +} diff --git a/app/src/py/svc/PAY_FEEFIXED.h b/app/src/py/svc/PAY_FEEFIXED.h new file mode 100644 index 0000000..dfacf16 --- /dev/null +++ b/app/src/py/svc/PAY_FEEFIXED.h @@ -0,0 +1,20 @@ +/* + * PAY_FEEFIXED.h - py 서비스 PAY_FEEFIXED 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_FEEFIXED.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_FEEFIXED_H +#define PAY_FEEFIXED_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_feefixed_rec_t; + +void PAY_FEEFIXED(TPSVCINFO *p); + +#endif /* PAY_FEEFIXED_H */ diff --git a/app/src/py/svc/PAY_FEEFIXED.pgc b/app/src/py/svc/PAY_FEEFIXED.pgc new file mode 100644 index 0000000..6050820 --- /dev/null +++ b/app/src/py/svc/PAY_FEEFIXED.pgc @@ -0,0 +1,36 @@ +/* + * PAY_FEEFIXED.pgc - py 모듈 서비스 PAY_FEEFIXED (one service = one file). + * 정액 수수료 적용: 미지급(R/A) 건에 요청 정액 수수료(T_AMOUNT)를 부과하고 + * net 을 amount-fee 로 재계산한다. 수수료가 원금을 넘으면 거절한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_FEEFIXED.h" + +void PAY_FEEFIXED(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long fixed; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_amt, h_fee, h_net; + char h_st[4]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + fixed = getl(b, T_AMOUNT); + EXEC SQL SELECT amount, status INTO :h_amt, :h_st 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] != 'A') { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "미지급 건만 적용 가능", 0L); FAIL(b); } + if (fixed > h_amt) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "수수료가 원금 초과", 0L); FAIL(b); } + + h_fee = fixed; + h_net = h_amt - fixed; + 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); + userlog("PAY_FEEFIXED id=%ld fee=%ld net=%ld", h_pid, h_fee, h_net); + OK(b); +} diff --git a/app/src/py/svc/PAY_FEEREFUND.h b/app/src/py/svc/PAY_FEEREFUND.h new file mode 100644 index 0000000..e28cd8d --- /dev/null +++ b/app/src/py/svc/PAY_FEEREFUND.h @@ -0,0 +1,20 @@ +/* + * PAY_FEEREFUND.h - py 서비스 PAY_FEEREFUND 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_FEEREFUND.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_FEEREFUND_H +#define PAY_FEEREFUND_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_feerefund_rec_t; + +void PAY_FEEREFUND(TPSVCINFO *p); + +#endif /* PAY_FEEREFUND_H */ diff --git a/app/src/py/svc/PAY_FEEREFUND.pgc b/app/src/py/svc/PAY_FEEREFUND.pgc new file mode 100644 index 0000000..e6e18fe --- /dev/null +++ b/app/src/py/svc/PAY_FEEREFUND.pgc @@ -0,0 +1,34 @@ +/* + * PAY_FEEREFUND.pgc - py 모듈 서비스 PAY_FEEREFUND (one service = one file). + * 수수료 환급: 지급완료(P) 건의 수수료를 지급계좌에 되돌려 입금하고, + * py_ledger 에 양수 FEEREFUND 기표를 남긴다. 수수료가 없으면 거절한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_FEEREFUND.h" + +void PAY_FEEREFUND(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_fee; + char h_acct[32], h_st[4], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + EXEC SQL SELECT account_no, fee, status, CAST(biz_date AS text) + INTO :h_acct, :h_fee, :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 (h_fee <= 0) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "환급할 수수료 없음", 0L); FAIL(b); } + + if (pydb_account_add(h_acct, h_fee) < 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, 'FEEREFUND', :h_fee, :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_RC, 0); + setl(b, T_FEE, h_fee); + Bchg(b, T_STR1, 0, h_acct, 0L); + userlog("PAY_FEEREFUND id=%ld fee=%ld acct=%s", h_pid, h_fee, h_acct); + OK(b); +} diff --git a/app/src/py/svc/PAY_FEESUM.h b/app/src/py/svc/PAY_FEESUM.h new file mode 100644 index 0000000..32b7623 --- /dev/null +++ b/app/src/py/svc/PAY_FEESUM.h @@ -0,0 +1,20 @@ +/* + * PAY_FEESUM.h - py 서비스 PAY_FEESUM 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_FEESUM.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_FEESUM_H +#define PAY_FEESUM_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_feesum_rec_t; + +void PAY_FEESUM(TPSVCINFO *p); + +#endif /* PAY_FEESUM_H */ diff --git a/app/src/py/svc/PAY_FEESUM.pgc b/app/src/py/svc/PAY_FEESUM.pgc new file mode 100644 index 0000000..adcd8c2 --- /dev/null +++ b/app/src/py/svc/PAY_FEESUM.pgc @@ -0,0 +1,32 @@ +/* + * PAY_FEESUM.pgc - py 모듈 서비스 PAY_FEESUM (one service = one file). + * 수수료 집계: 당일 지급건의 수수료 합/건수와, py_adjust 에 쌓인 조정 합을 + * 각각 조회해 순수수료(수수료-조정)를 산출해 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_FEESUM.h" + +void PAY_FEESUM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_fee, h_adj; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL SELECT count(*), coalesce(sum(fee),0) INTO :h_cnt, :h_fee + FROM py_payout WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(delta),0) INTO :h_adj + FROM py_adjust WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_RC, 0); + setl(b, T_COUNT, h_cnt); + setl(b, T_FEE, h_fee); + setl(b, T_ADJ, h_adj); + setl(b, T_NET, h_fee - h_adj); + userlog("PAY_FEESUM date=%s fee=%ld adj=%ld", h_bizdate, h_fee, h_adj); + OK(b); +} diff --git a/app/src/py/svc/PAY_FILECANCEL.h b/app/src/py/svc/PAY_FILECANCEL.h new file mode 100644 index 0000000..1fb7540 --- /dev/null +++ b/app/src/py/svc/PAY_FILECANCEL.h @@ -0,0 +1,20 @@ +/* + * PAY_FILECANCEL.h - py 서비스 PAY_FILECANCEL 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_FILECANCEL.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_FILECANCEL_H +#define PAY_FILECANCEL_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_filecancel_rec_t; + +void PAY_FILECANCEL(TPSVCINFO *p); + +#endif /* PAY_FILECANCEL_H */ diff --git a/app/src/py/svc/PAY_FILECANCEL.pgc b/app/src/py/svc/PAY_FILECANCEL.pgc new file mode 100644 index 0000000..ac7315e --- /dev/null +++ b/app/src/py/svc/PAY_FILECANCEL.pgc @@ -0,0 +1,34 @@ +/* + * PAY_FILECANCEL.pgc - py 모듈 서비스 PAY_FILECANCEL (one service = one file). + * 지급파일 취소: 확정(C) 전 파일만 취소 가능. 편성(F) 건을 승인(A)+file_id=0 + * 으로 되돌린 뒤 파일 헤더를 삭제한다. 확정/부재 파일은 사유코드로 거절. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_FILECANCEL.h" + +void PAY_FILECANCEL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_fid, h_back; + 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 == 100) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "파일 없음", 0L); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (h_st[0] == 'C') { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "확정 파일은 취소 불가", 0L); FAIL(b); } + + EXEC SQL UPDATE py_payout SET status = 'A', file_id = 0, updated_at = now() + WHERE file_id = :h_fid AND status = 'F'; + if (sqlca.sqlcode < 0) FAIL(b); + h_back = sqlca.sqlerrd[2]; + EXEC SQL DELETE FROM py_file WHERE file_id = :h_fid; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_RC, 0); + setl(b, T_COUNT, h_back); + userlog("PAY_FILECANCEL file=%ld reverted=%ld", h_fid, h_back); + OK(b); +} diff --git a/app/src/py/svc/PAY_FILECONF.h b/app/src/py/svc/PAY_FILECONF.h new file mode 100644 index 0000000..f384329 --- /dev/null +++ b/app/src/py/svc/PAY_FILECONF.h @@ -0,0 +1,20 @@ +/* + * PAY_FILECONF.h - py 서비스 PAY_FILECONF 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_FILECONF.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_FILECONF_H +#define PAY_FILECONF_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_fileconf_rec_t; + +void PAY_FILECONF(TPSVCINFO *p); + +#endif /* PAY_FILECONF_H */ diff --git a/app/src/py/svc/PAY_FILECONF.pgc b/app/src/py/svc/PAY_FILECONF.pgc new file mode 100644 index 0000000..d5e50e3 --- /dev/null +++ b/app/src/py/svc/PAY_FILECONF.pgc @@ -0,0 +1,34 @@ +/* + * PAY_FILECONF.pgc - py 모듈 서비스 PAY_FILECONF (one service = one file). + * 지급파일 확정: 생성(G) 파일만 확정(C)+confirmed_at 로 전이하고, 그 파일에 + * 편성(F)된 지급건을 지급완료(P)로 확정한다. 이미 확정 파일은 거절. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_FILECONF.h" + +void PAY_FILECONF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_fid, h_paid; + 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 UPDATE py_file SET status = 'C', confirmed_at = now() WHERE file_id = :h_fid; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL UPDATE py_payout SET status = 'P', updated_at = now() + WHERE file_id = :h_fid AND status = 'F'; + if (sqlca.sqlcode < 0) FAIL(b); + h_paid = sqlca.sqlerrd[2]; + + setl(b, T_RC, 0); + setl(b, T_COUNT, h_paid); + Bchg(b, T_STATUS, 0, "C", 0L); + userlog("PAY_FILECONF file=%ld paid=%ld", h_fid, h_paid); + OK(b); +} diff --git a/app/src/py/svc/PAY_GARNISH.h b/app/src/py/svc/PAY_GARNISH.h new file mode 100644 index 0000000..d3b3981 --- /dev/null +++ b/app/src/py/svc/PAY_GARNISH.h @@ -0,0 +1,20 @@ +/* + * PAY_GARNISH.h - py 서비스 PAY_GARNISH 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_GARNISH.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_GARNISH_H +#define PAY_GARNISH_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_garnish_rec_t; + +void PAY_GARNISH(TPSVCINFO *p); + +#endif /* PAY_GARNISH_H */ diff --git a/app/src/py/svc/PAY_GARNISH.pgc b/app/src/py/svc/PAY_GARNISH.pgc new file mode 100644 index 0000000..323c5a9 --- /dev/null +++ b/app/src/py/svc/PAY_GARNISH.pgc @@ -0,0 +1,38 @@ +/* + * PAY_GARNISH.pgc - py 모듈 서비스 PAY_GARNISH (one service = one file). + * 가압류 등록: 활성 계좌를 GARNISH 로 표시하고, 그 계좌 미지급(R/A) 잔액을 + * 집계해 가압류 채권으로 py_ledger 에 음수 GARNISH 기표를 남긴다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_GARNISH.h" + +void PAY_GARNISH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_acct[32], h_bizdate[16]; + long h_cnt, h_sum, h_neg; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_acct, sizeof(h_acct)); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + + EXEC SQL UPDATE py_account SET status = 'GARNISH', updated_at = now() + WHERE account_no = :h_acct AND status = 'ACTIVE'; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlerrd[2] == 0) { 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 account_no = :h_acct AND status IN ('R','A'); + if (sqlca.sqlcode < 0) FAIL(b); + h_neg = -h_sum; + EXEC SQL INSERT INTO py_ledger (ledger_id, payout_id, entry_type, amount, biz_date) + VALUES (nextval('py_ledger_seq'), 0, 'GARNISH', :h_neg, :h_bizdate); + 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_GARNISH acct=%s frozen=%ld amt=%ld", h_acct, h_cnt, h_sum); + OK(b); +} diff --git a/app/src/py/svc/PAY_HOLDBYRSN.h b/app/src/py/svc/PAY_HOLDBYRSN.h new file mode 100644 index 0000000..6798c88 --- /dev/null +++ b/app/src/py/svc/PAY_HOLDBYRSN.h @@ -0,0 +1,20 @@ +/* + * PAY_HOLDBYRSN.h - py 서비스 PAY_HOLDBYRSN 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_HOLDBYRSN.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_HOLDBYRSN_H +#define PAY_HOLDBYRSN_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_holdbyrsn_rec_t; + +void PAY_HOLDBYRSN(TPSVCINFO *p); + +#endif /* PAY_HOLDBYRSN_H */ diff --git a/app/src/py/svc/PAY_HOLDBYRSN.pgc b/app/src/py/svc/PAY_HOLDBYRSN.pgc new file mode 100644 index 0000000..2cfe232 --- /dev/null +++ b/app/src/py/svc/PAY_HOLDBYRSN.pgc @@ -0,0 +1,42 @@ +/* + * PAY_HOLDBYRSN.pgc - py 모듈 서비스 PAY_HOLDBYRSN (one service = one file). + * 보류 사유별 집계: 당일 보류(H) 건을 reject_reason 별 커서로 건수/net 합 + * 집계해 건수 내림차순 occurrence 로 회신한다 (보류 원인 분포 파악). + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_HOLDBYRSN.h" + +void PAY_HOLDBYRSN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_rsn[128]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL DECLARE cur_holdbyrsn CURSOR FOR + SELECT coalesce(nullif(reject_reason,''),'미지정'), count(*), coalesce(sum(net),0) + FROM py_payout WHERE status = 'H' AND biz_date = :h_bizdate + GROUP BY 1 ORDER BY 2 DESC, 1; + EXEC SQL OPEN cur_holdbyrsn; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_holdbyrsn INTO :h_rsn, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_holdbyrsn; FAIL(b); } + rows++; + if (i < 10) { + Bchg(b, T_STR4, i, h_rsn, 0L); + Bchg(b, T_ARG5, i, (char *)&h_cnt, 0L); + Bchg(b, T_AMT4, i, (char *)&h_amt, 0L); + i++; + } + } + EXEC SQL CLOSE cur_holdbyrsn; + setl(b, T_COUNT, rows); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_HOLDCLEAR.h b/app/src/py/svc/PAY_HOLDCLEAR.h new file mode 100644 index 0000000..268fa3b --- /dev/null +++ b/app/src/py/svc/PAY_HOLDCLEAR.h @@ -0,0 +1,20 @@ +/* + * PAY_HOLDCLEAR.h - py 서비스 PAY_HOLDCLEAR 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_HOLDCLEAR.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_HOLDCLEAR_H +#define PAY_HOLDCLEAR_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_holdclear_rec_t; + +void PAY_HOLDCLEAR(TPSVCINFO *p); + +#endif /* PAY_HOLDCLEAR_H */ diff --git a/app/src/py/svc/PAY_HOLDCLEAR.pgc b/app/src/py/svc/PAY_HOLDCLEAR.pgc new file mode 100644 index 0000000..0fe575b --- /dev/null +++ b/app/src/py/svc/PAY_HOLDCLEAR.pgc @@ -0,0 +1,33 @@ +/* + * PAY_HOLDCLEAR.pgc - py 모듈 서비스 PAY_HOLDCLEAR (one service = one file). + * 보류 일괄 해제: 지정 계좌의 보류(H) 지급건을 모두 승인(A)으로 되돌리고 + * 반려사유를 비운다. 해제 대상이 없으면 사유코드 1 로 안내한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_HOLDCLEAR.h" + +void PAY_HOLDCLEAR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long cleared; + EXEC SQL BEGIN DECLARE SECTION; + char h_acct[32]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_acct, sizeof(h_acct)); + EXEC SQL UPDATE py_payout SET status = 'A', reject_reason = '', updated_at = now() + WHERE account_no = :h_acct AND status = 'H'; + if (sqlca.sqlcode < 0) FAIL(b); + cleared = sqlca.sqlerrd[2]; + + setl(b, T_COUNT, cleared); + if (cleared == 0) { + setl(b, T_RC, 1); + Bchg(b, T_MSG, 0, "해제할 보류 건 없음", 0L); + } else { + setl(b, T_RC, 0); + Bchg(b, T_MSG, 0, "보류 일괄 해제 완료", 0L); + userlog("PAY_HOLDCLEAR acct=%s cleared=%ld", h_acct, cleared); + } + OK(b); +} diff --git a/app/src/py/svc/PAY_HOLDEXT.h b/app/src/py/svc/PAY_HOLDEXT.h new file mode 100644 index 0000000..5f63663 --- /dev/null +++ b/app/src/py/svc/PAY_HOLDEXT.h @@ -0,0 +1,20 @@ +/* + * PAY_HOLDEXT.h - py 서비스 PAY_HOLDEXT 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_HOLDEXT.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_HOLDEXT_H +#define PAY_HOLDEXT_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_holdext_rec_t; + +void PAY_HOLDEXT(TPSVCINFO *p); + +#endif /* PAY_HOLDEXT_H */ diff --git a/app/src/py/svc/PAY_HOLDEXT.pgc b/app/src/py/svc/PAY_HOLDEXT.pgc new file mode 100644 index 0000000..7a11157 --- /dev/null +++ b/app/src/py/svc/PAY_HOLDEXT.pgc @@ -0,0 +1,32 @@ +/* + * PAY_HOLDEXT.pgc - py 모듈 서비스 PAY_HOLDEXT (one service = one file). + * 보류 연장: 보류(H) 건의 반려사유 뒤에 '(연장)' 표식을 덧붙여 갱신한다. + * 보류 상태가 아니면 거절한다. 재보류 없이 사유 텍스트만 확장한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_HOLDEXT.h" + +void PAY_HOLDEXT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid; + char h_st[4], h_rsn[128], newrsn[160]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + EXEC SQL SELECT status, reject_reason INTO :h_st, :h_rsn + FROM py_payout WHERE payout_id = :h_pid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + if (h_st[0] != 'H') { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "보류 건만 연장 가능", 0L); FAIL(b); } + + snprintf(newrsn, sizeof(newrsn), "%s(연장)", h_rsn[0] ? h_rsn : "보류"); + EXEC SQL UPDATE py_payout SET reject_reason = :newrsn, updated_at = now() + WHERE payout_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_RC, 0); + Bchg(b, T_REASON, 0, newrsn, 0L); + userlog("PAY_HOLDEXT payout=%ld reason=%s", h_pid, newrsn); + OK(b); +} diff --git a/app/src/py/svc/PAY_HOLDNOTE.h b/app/src/py/svc/PAY_HOLDNOTE.h new file mode 100644 index 0000000..25080cc --- /dev/null +++ b/app/src/py/svc/PAY_HOLDNOTE.h @@ -0,0 +1,20 @@ +/* + * PAY_HOLDNOTE.h - py 서비스 PAY_HOLDNOTE 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_HOLDNOTE.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_HOLDNOTE_H +#define PAY_HOLDNOTE_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_holdnote_rec_t; + +void PAY_HOLDNOTE(TPSVCINFO *p); + +#endif /* PAY_HOLDNOTE_H */ diff --git a/app/src/py/svc/PAY_HOLDNOTE.pgc b/app/src/py/svc/PAY_HOLDNOTE.pgc new file mode 100644 index 0000000..b611f7f --- /dev/null +++ b/app/src/py/svc/PAY_HOLDNOTE.pgc @@ -0,0 +1,33 @@ +/* + * PAY_HOLDNOTE.pgc - py 모듈 서비스 PAY_HOLDNOTE (one service = one file). + * 보류 사유 갱신: 보류(H) 단건의 반려사유를 요청 사유로 교체한다. dbio 로 + * 현재 상태를 확인해 보류 건만 허용하고, 사유 미입력 시 거절한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_HOLDNOTE.h" + +void PAY_HOLDNOTE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long payout_id = getl(b, T_ID1); + char st[4], reason[128]; + + gets_(b, T_REASON, reason, sizeof(reason)); + if (reason[0] == 0) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "사유 미입력", 0L); FAIL(b); } + if (pydb_get_status(payout_id, st) < 0) FAIL(b); + if (st[0] != 'H') { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "보류 건만 갱신 가능", 0L); FAIL(b); } + + { + EXEC SQL BEGIN DECLARE SECTION; + long h_pid = payout_id; + char h_rsn[128]; + EXEC SQL END DECLARE SECTION; + strncpy(h_rsn, reason, sizeof(h_rsn)-1); h_rsn[sizeof(h_rsn)-1] = 0; + EXEC SQL UPDATE py_payout SET reject_reason = :h_rsn, updated_at = now() + WHERE payout_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + } + setl(b, T_RC, 0); + Bchg(b, T_REASON, 0, reason, 0L); + OK(b); +} diff --git a/app/src/py/svc/PAY_LIMITLIST.h b/app/src/py/svc/PAY_LIMITLIST.h new file mode 100644 index 0000000..4b8186a --- /dev/null +++ b/app/src/py/svc/PAY_LIMITLIST.h @@ -0,0 +1,20 @@ +/* + * PAY_LIMITLIST.h - py 서비스 PAY_LIMITLIST 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_LIMITLIST.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_LIMITLIST_H +#define PAY_LIMITLIST_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_limitlist_rec_t; + +void PAY_LIMITLIST(TPSVCINFO *p); + +#endif /* PAY_LIMITLIST_H */ diff --git a/app/src/py/svc/PAY_LIMITLIST.pgc b/app/src/py/svc/PAY_LIMITLIST.pgc new file mode 100644 index 0000000..c35129a --- /dev/null +++ b/app/src/py/svc/PAY_LIMITLIST.pgc @@ -0,0 +1,44 @@ +/* + * PAY_LIMITLIST.pgc - py 모듈 서비스 PAY_LIMITLIST (one service = one file). + * 지급 한도 사용현황 목록: 가맹점별 일한도와 당일 확정성(A/F/P) 사용액을 + * 조인 커서로 훑어 가맹점/한도/사용액을 occurrence 로 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_LIMITLIST.h" + +void PAY_LIMITLIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long n = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_merch[64]; + long h_lim, h_used; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL DECLARE cur_limitlist CURSOR FOR + SELECT m.merchant_id, m.daily_limit, + coalesce((SELECT sum(net) FROM py_payout p + WHERE p.merchant_id = m.merchant_id AND p.biz_date = :h_bizdate + AND p.status IN ('A','F','P')), 0) + FROM merchant m ORDER BY m.merchant_id; + EXEC SQL OPEN cur_limitlist; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_limitlist INTO :h_merch, :h_lim, :h_used; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_limitlist; FAIL(b); } + n++; + if (i < 10) { + Bchg(b, T_STR2, i, h_merch, 0L); + Bchg(b, T_AMT1, i, (char *)&h_lim, 0L); + Bchg(b, T_AMT2, i, (char *)&h_used, 0L); + i++; + } + } + EXEC SQL CLOSE cur_limitlist; + setl(b, T_COUNT, n); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_LIMITSET.h b/app/src/py/svc/PAY_LIMITSET.h new file mode 100644 index 0000000..40528d7 --- /dev/null +++ b/app/src/py/svc/PAY_LIMITSET.h @@ -0,0 +1,20 @@ +/* + * PAY_LIMITSET.h - py 서비스 PAY_LIMITSET 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_LIMITSET.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_LIMITSET_H +#define PAY_LIMITSET_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_limitset_rec_t; + +void PAY_LIMITSET(TPSVCINFO *p); + +#endif /* PAY_LIMITSET_H */ diff --git a/app/src/py/svc/PAY_LIMITSET.pgc b/app/src/py/svc/PAY_LIMITSET.pgc new file mode 100644 index 0000000..b8ef1f4 --- /dev/null +++ b/app/src/py/svc/PAY_LIMITSET.pgc @@ -0,0 +1,31 @@ +/* + * PAY_LIMITSET.pgc - py 모듈 서비스 PAY_LIMITSET (one service = one file). + * 지급 일한도 설정: 가맹점의 일 지급한도(merchant.daily_limit)를 새 값으로 + * 갱신한다. 존재하지 않는 가맹점이거나 음수 한도면 거절한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_LIMITSET.h" + +void PAY_LIMITSET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long newlim; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64]; + long h_lim; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, h_merch, sizeof(h_merch)); + newlim = getl(b, T_AMOUNT); + if (newlim < 0) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "한도는 0 이상이어야 함", 0L); FAIL(b); } + h_lim = newlim; + + EXEC SQL UPDATE merchant SET daily_limit = :h_lim WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlerrd[2] == 0) { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "가맹점 없음", 0L); FAIL(b); } + + setl(b, T_RC, 0); + setl(b, T_AMT1, h_lim); + userlog("PAY_LIMITSET merch=%s limit=%ld", h_merch, h_lim); + OK(b); +} diff --git a/app/src/py/svc/PAY_LIMITWARN.h b/app/src/py/svc/PAY_LIMITWARN.h new file mode 100644 index 0000000..9fb6a9e --- /dev/null +++ b/app/src/py/svc/PAY_LIMITWARN.h @@ -0,0 +1,20 @@ +/* + * PAY_LIMITWARN.h - py 서비스 PAY_LIMITWARN 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_LIMITWARN.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_LIMITWARN_H +#define PAY_LIMITWARN_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_limitwarn_rec_t; + +void PAY_LIMITWARN(TPSVCINFO *p); + +#endif /* PAY_LIMITWARN_H */ diff --git a/app/src/py/svc/PAY_LIMITWARN.pgc b/app/src/py/svc/PAY_LIMITWARN.pgc new file mode 100644 index 0000000..b7f61b2 --- /dev/null +++ b/app/src/py/svc/PAY_LIMITWARN.pgc @@ -0,0 +1,49 @@ +/* + * PAY_LIMITWARN.pgc - py 모듈 서비스 PAY_LIMITWARN (one service = one file). + * 지급 한도 근접 경보: 당일 사용액이 일한도의 지정 비율(T_ARG1 %, 기본 80) + * 이상인 가맹점을 커서로 뽑아 경보 목록을 occurrence 로 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_LIMITWARN.h" + +void PAY_LIMITWARN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long warns = 0, ratio; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_merch[64]; + long h_ratio, h_lim, h_used; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + ratio = getl(b, T_ARG1); + h_ratio = (ratio > 0) ? ratio : 80; + + EXEC SQL DECLARE cur_limitwarn CURSOR FOR + SELECT m.merchant_id, m.daily_limit, u.used + FROM merchant m JOIN ( + SELECT merchant_id, sum(net) AS used FROM py_payout + WHERE biz_date = :h_bizdate AND status IN ('A','F','P') + GROUP BY merchant_id) u ON u.merchant_id = m.merchant_id + WHERE u.used * 100 >= m.daily_limit * :h_ratio + ORDER BY u.used DESC; + EXEC SQL OPEN cur_limitwarn; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_limitwarn INTO :h_merch, :h_lim, :h_used; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_limitwarn; FAIL(b); } + warns++; + if (i < 10) { + Bchg(b, T_STR2, i, h_merch, 0L); + Bchg(b, T_AMT1, i, (char *)&h_used, 0L); + Bchg(b, T_AMT2, i, (char *)&h_lim, 0L); + i++; + } + } + EXEC SQL CLOSE cur_limitwarn; + setl(b, T_COUNT, warns); + setl(b, T_RC, warns > 0 ? 1 : 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_NOTIFYALL.h b/app/src/py/svc/PAY_NOTIFYALL.h new file mode 100644 index 0000000..9f09270 --- /dev/null +++ b/app/src/py/svc/PAY_NOTIFYALL.h @@ -0,0 +1,20 @@ +/* + * PAY_NOTIFYALL.h - py 서비스 PAY_NOTIFYALL 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_NOTIFYALL.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_NOTIFYALL_H +#define PAY_NOTIFYALL_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_notifyall_rec_t; + +void PAY_NOTIFYALL(TPSVCINFO *p); + +#endif /* PAY_NOTIFYALL_H */ diff --git a/app/src/py/svc/PAY_NOTIFYALL.pgc b/app/src/py/svc/PAY_NOTIFYALL.pgc new file mode 100644 index 0000000..2d81fb4 --- /dev/null +++ b/app/src/py/svc/PAY_NOTIFYALL.pgc @@ -0,0 +1,34 @@ +/* + * PAY_NOTIFYALL.pgc - py 모듈 서비스 PAY_NOTIFYALL (one service = one file). + * 전 채널 일괄 통지(시뮬): 지급완료(P) 단건을 SMS/EMAIL/PUSH 세 채널로 각각 + * 발송 시뮬레이션하고 발송 채널 수를 회신한다 (C 배열 순회). + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_NOTIFYALL.h" + +void PAY_NOTIFYALL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + const char *chans[3] = { "SMS", "EMAIL", "PUSH" }; + int k, sent = 0; + 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, "지급완료 건만 통지", 0L); FAIL(b); } + + for (k = 0; k < 3; k++) { + userlog("PAY_NOTIFYALL ch=%s payout=%ld to=%s", chans[k], h_pid, h_merch); + sent++; + } + setl(b, T_RC, 0); + setl(b, T_COUNT, sent); + setl(b, T_NET, h_net); + Bchg(b, T_MSG, 0, "전 채널 통지 완료", 0L); + OK(b); +} diff --git a/app/src/py/svc/PAY_NOTIFYFAIL.h b/app/src/py/svc/PAY_NOTIFYFAIL.h new file mode 100644 index 0000000..245e3f4 --- /dev/null +++ b/app/src/py/svc/PAY_NOTIFYFAIL.h @@ -0,0 +1,20 @@ +/* + * PAY_NOTIFYFAIL.h - py 서비스 PAY_NOTIFYFAIL 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_NOTIFYFAIL.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_NOTIFYFAIL_H +#define PAY_NOTIFYFAIL_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_notifyfail_rec_t; + +void PAY_NOTIFYFAIL(TPSVCINFO *p); + +#endif /* PAY_NOTIFYFAIL_H */ diff --git a/app/src/py/svc/PAY_NOTIFYFAIL.pgc b/app/src/py/svc/PAY_NOTIFYFAIL.pgc new file mode 100644 index 0000000..7aeb0b6 --- /dev/null +++ b/app/src/py/svc/PAY_NOTIFYFAIL.pgc @@ -0,0 +1,31 @@ +/* + * PAY_NOTIFYFAIL.pgc - py 모듈 서비스 PAY_NOTIFYFAIL (one service = one file). + * 지급실패 통지(시뮬): 반려(J) 건의 가맹점/반려사유를 읽어 실패 안내 본문을 + * 조립해 발송 시뮬레이션한다. 반려 건이 아니면 거절한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_NOTIFYFAIL.h" + +void PAY_NOTIFYFAIL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char msg[224]; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid; + char h_merch[64], h_st[4], h_rsn[128]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + EXEC SQL SELECT merchant_id, status, reject_reason INTO :h_merch, :h_st, :h_rsn + 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); } + if (h_rsn[0] == 0) strcpy(h_rsn, "사유미상"); + + snprintf(msg, sizeof(msg), "[Forge] %s 지급실패: %s", h_merch, h_rsn); + userlog("PAY_NOTIFYFAIL payout=%ld reason=%s", h_pid, h_rsn); + setl(b, T_RC, 0); + Bchg(b, T_REASON, 0, h_rsn, 0L); + Bchg(b, T_MSG, 0, msg, 0L); + OK(b); +} diff --git a/app/src/py/svc/PAY_NOTIFYKAKAO.h b/app/src/py/svc/PAY_NOTIFYKAKAO.h new file mode 100644 index 0000000..24f3442 --- /dev/null +++ b/app/src/py/svc/PAY_NOTIFYKAKAO.h @@ -0,0 +1,20 @@ +/* + * PAY_NOTIFYKAKAO.h - py 서비스 PAY_NOTIFYKAKAO 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_NOTIFYKAKAO.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_NOTIFYKAKAO_H +#define PAY_NOTIFYKAKAO_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_notifykakao_rec_t; + +void PAY_NOTIFYKAKAO(TPSVCINFO *p); + +#endif /* PAY_NOTIFYKAKAO_H */ diff --git a/app/src/py/svc/PAY_NOTIFYKAKAO.pgc b/app/src/py/svc/PAY_NOTIFYKAKAO.pgc new file mode 100644 index 0000000..1c52a14 --- /dev/null +++ b/app/src/py/svc/PAY_NOTIFYKAKAO.pgc @@ -0,0 +1,29 @@ +/* + * PAY_NOTIFYKAKAO.pgc - py 모듈 서비스 PAY_NOTIFYKAKAO (one service = one file). + * 알림톡 일괄 통지(시뮬): 당일 지급완료(P) 건수/금액을 한 번에 집계해 알림톡 + * 대량발송을 시뮬레이션한다. 대상이 없으면 안내 후 정상 종료한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_NOTIFYKAKAO.h" + +void PAY_NOTIFYKAKAO(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(net),0) INTO :h_cnt, :h_sum + FROM py_payout WHERE 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); } + + userlog("PAY_NOTIFYKAKAO 일괄발송시뮬 bizdate=%s targets=%ld amt=%ld", h_bizdate, h_cnt, h_sum); + setl(b, T_RC, 0); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_sum); + Bchg(b, T_MSG, 0, "알림톡 발송 완료", 0L); + OK(b); +} diff --git a/app/src/py/svc/PAY_NOTIFYPUSH.h b/app/src/py/svc/PAY_NOTIFYPUSH.h new file mode 100644 index 0000000..77e43ce --- /dev/null +++ b/app/src/py/svc/PAY_NOTIFYPUSH.h @@ -0,0 +1,20 @@ +/* + * PAY_NOTIFYPUSH.h - py 서비스 PAY_NOTIFYPUSH 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_NOTIFYPUSH.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_NOTIFYPUSH_H +#define PAY_NOTIFYPUSH_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_notifypush_rec_t; + +void PAY_NOTIFYPUSH(TPSVCINFO *p); + +#endif /* PAY_NOTIFYPUSH_H */ diff --git a/app/src/py/svc/PAY_NOTIFYPUSH.pgc b/app/src/py/svc/PAY_NOTIFYPUSH.pgc new file mode 100644 index 0000000..0f6e099 --- /dev/null +++ b/app/src/py/svc/PAY_NOTIFYPUSH.pgc @@ -0,0 +1,33 @@ +/* + * PAY_NOTIFYPUSH.pgc - py 모듈 서비스 PAY_NOTIFYPUSH (one service = one file). + * 지급완료 푸시 통지(시뮬): 지급완료(P)건을 py_account 조인으로 예금주/은행을 + * 읽어 푸시 본문을 조립하고 발송은 userlog 로 시뮬레이션한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_NOTIFYPUSH.h" + +void PAY_NOTIFYPUSH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char msg[192]; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_net; + char h_holder[64], h_st[4], h_bank[8]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + EXEC SQL SELECT p.net, p.status, a.holder, a.bank_code + INTO :h_net, :h_st, :h_holder, :h_bank + FROM py_payout p JOIN py_account a ON p.account_no = a.account_no + WHERE p.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); } + + snprintf(msg, sizeof(msg), "[Forge] %s(%s) %ld원 지급 알림", h_holder, h_bank, h_net); + userlog("PAY_NOTIFYPUSH 발송시뮬 payout=%ld holder=%s", h_pid, h_holder); + setl(b, T_RC, 0); + setl(b, T_NET, h_net); + Bchg(b, T_STR2, 0, h_bank, 0L); + Bchg(b, T_MSG, 0, msg, 0L); + OK(b); +} diff --git a/app/src/py/svc/PAY_NOTIFYRESEND.h b/app/src/py/svc/PAY_NOTIFYRESEND.h new file mode 100644 index 0000000..bbfb750 --- /dev/null +++ b/app/src/py/svc/PAY_NOTIFYRESEND.h @@ -0,0 +1,20 @@ +/* + * PAY_NOTIFYRESEND.h - py 서비스 PAY_NOTIFYRESEND 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_NOTIFYRESEND.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_NOTIFYRESEND_H +#define PAY_NOTIFYRESEND_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_notifyresend_rec_t; + +void PAY_NOTIFYRESEND(TPSVCINFO *p); + +#endif /* PAY_NOTIFYRESEND_H */ diff --git a/app/src/py/svc/PAY_NOTIFYRESEND.pgc b/app/src/py/svc/PAY_NOTIFYRESEND.pgc new file mode 100644 index 0000000..4411b58 --- /dev/null +++ b/app/src/py/svc/PAY_NOTIFYRESEND.pgc @@ -0,0 +1,32 @@ +/* + * PAY_NOTIFYRESEND.pgc - py 모듈 서비스 PAY_NOTIFYRESEND (one service = one file). + * 통지 재발송: 지급완료(P) 건의 retry_count 를 재발송 횟수로 삼아 3회까지만 + * 허용하며, 재발송 시 카운터를 +1 하고 현재 시도 회차를 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_NOTIFYRESEND.h" + +void PAY_NOTIFYRESEND(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_rc; + char h_st[4]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + EXEC SQL SELECT status, retry_count INTO :h_st, :h_rc + 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, 2); Bchg(b, T_MSG, 0, "지급완료 건만 재발송", 0L); FAIL(b); } + if (h_rc >= 3) { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "재발송 한도 초과", 0L); FAIL(b); } + + EXEC SQL UPDATE py_payout SET retry_count = retry_count + 1, updated_at = now() + WHERE payout_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + userlog("PAY_NOTIFYRESEND payout=%ld attempt=%ld", h_pid, h_rc + 1); + setl(b, T_RC, 0); + setl(b, T_COUNT, h_rc + 1); + Bchg(b, T_MSG, 0, "통지 재발송 완료", 0L); + OK(b); +} diff --git a/app/src/py/svc/PAY_OFFSETAPPLY.h b/app/src/py/svc/PAY_OFFSETAPPLY.h new file mode 100644 index 0000000..b705d5a --- /dev/null +++ b/app/src/py/svc/PAY_OFFSETAPPLY.h @@ -0,0 +1,20 @@ +/* + * PAY_OFFSETAPPLY.h - py 서비스 PAY_OFFSETAPPLY 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_OFFSETAPPLY.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_OFFSETAPPLY_H +#define PAY_OFFSETAPPLY_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_offsetapply_rec_t; + +void PAY_OFFSETAPPLY(TPSVCINFO *p); + +#endif /* PAY_OFFSETAPPLY_H */ diff --git a/app/src/py/svc/PAY_OFFSETAPPLY.pgc b/app/src/py/svc/PAY_OFFSETAPPLY.pgc new file mode 100644 index 0000000..7f31c8b --- /dev/null +++ b/app/src/py/svc/PAY_OFFSETAPPLY.pgc @@ -0,0 +1,41 @@ +/* + * PAY_OFFSETAPPLY.pgc - py 모듈 서비스 PAY_OFFSETAPPLY (one service = one file). + * 상계 적용: 미지급(R/A) 건의 net 에서 상계액을 차감(net 미만으로 캡)하고, + * 차감분을 py_adjust 에 음수 조정으로 남긴다. 지급 전 채권 상계 반영. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_OFFSETAPPLY.h" + +void PAY_OFFSETAPPLY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char reason[96], bizdate[16]; + long off, adj; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_net, h_cut; + char h_st[4]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + off = getl(b, T_AMOUNT); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_REASON, reason, sizeof(reason)); + if (reason[0] == 0) strcpy(reason, "상계"); + + EXEC SQL SELECT net, status INTO :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] != 'R' && h_st[0] != 'A') { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "미지급 건만 상계 가능", 0L); FAIL(b); } + h_cut = (off > h_net) ? h_net : off; + if (h_cut <= 0) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "상계액 무효", 0L); FAIL(b); } + + adj = pydb_insert_adjust(h_pid, -h_cut, reason, bizdate); + if (adj < 0) FAIL(b); + EXEC SQL UPDATE py_payout SET net = net - :h_cut, updated_at = now() WHERE payout_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_RC, 0); + setl(b, T_ADJ, adj); + setl(b, T_NET, h_net - h_cut); + userlog("PAY_OFFSETAPPLY id=%ld off=%ld", h_pid, h_cut); + OK(b); +} diff --git a/app/src/py/svc/PAY_PENDLIST.h b/app/src/py/svc/PAY_PENDLIST.h new file mode 100644 index 0000000..a6f63fb --- /dev/null +++ b/app/src/py/svc/PAY_PENDLIST.h @@ -0,0 +1,20 @@ +/* + * PAY_PENDLIST.h - py 서비스 PAY_PENDLIST 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_PENDLIST.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_PENDLIST_H +#define PAY_PENDLIST_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_pendlist_rec_t; + +void PAY_PENDLIST(TPSVCINFO *p); + +#endif /* PAY_PENDLIST_H */ diff --git a/app/src/py/svc/PAY_PENDLIST.pgc b/app/src/py/svc/PAY_PENDLIST.pgc new file mode 100644 index 0000000..971b262 --- /dev/null +++ b/app/src/py/svc/PAY_PENDLIST.pgc @@ -0,0 +1,43 @@ +/* + * PAY_PENDLIST.pgc - py 모듈 서비스 PAY_PENDLIST (one service = one file). + * 미지급 목록: 당일 미완결(요청R/승인A/보류H) 지급건을 커서로 훑어 지급ID/ + * 가맹점/net/상태를 occurrence 로 회신한다 (집계 없는 상세 리스트). + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_PENDLIST.h" + +void PAY_PENDLIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long n = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_merch[64], h_st[4]; + long h_pid, h_net; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL DECLARE cur_pendlist CURSOR FOR + SELECT payout_id, merchant_id, net, status FROM py_payout + WHERE biz_date = :h_bizdate AND status IN ('R','A','H') + ORDER BY payout_id; + EXEC SQL OPEN cur_pendlist; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_pendlist INTO :h_pid, :h_merch, :h_net, :h_st; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_pendlist; FAIL(b); } + n++; + if (i < 12) { + Bchg(b, T_ARG1, i, (char *)&h_pid, 0L); + Bchg(b, T_STR2, i, h_merch, 0L); + Bchg(b, T_AMT1, i, (char *)&h_net, 0L); + Bchg(b, T_STR3, i, h_st, 0L); + i++; + } + } + EXEC SQL CLOSE cur_pendlist; + setl(b, T_COUNT, n); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_REACTIVATE.h b/app/src/py/svc/PAY_REACTIVATE.h new file mode 100644 index 0000000..4c53f15 --- /dev/null +++ b/app/src/py/svc/PAY_REACTIVATE.h @@ -0,0 +1,20 @@ +/* + * PAY_REACTIVATE.h - py 서비스 PAY_REACTIVATE 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_REACTIVATE.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_REACTIVATE_H +#define PAY_REACTIVATE_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_reactivate_rec_t; + +void PAY_REACTIVATE(TPSVCINFO *p); + +#endif /* PAY_REACTIVATE_H */ diff --git a/app/src/py/svc/PAY_REACTIVATE.pgc b/app/src/py/svc/PAY_REACTIVATE.pgc new file mode 100644 index 0000000..4412ab0 --- /dev/null +++ b/app/src/py/svc/PAY_REACTIVATE.pgc @@ -0,0 +1,26 @@ +/* + * PAY_REACTIVATE.pgc - py 모듈 서비스 PAY_REACTIVATE (one service = one file). + * 휴면 계좌 재활성: 휴면(DORMANT) 처리된 지급계좌를 다시 활성(ACTIVE)으로 + * 되돌린다. 휴면 상태가 아니면 재활성 대상이 아니므로 거절한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_REACTIVATE.h" + +void PAY_REACTIVATE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_acct[32]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_acct, sizeof(h_acct)); + EXEC SQL UPDATE py_account SET status = 'ACTIVE', updated_at = now() + WHERE account_no = :h_acct AND status = 'DORMANT'; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlerrd[2] == 0) { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "휴면 계좌 아님", 0L); FAIL(b); } + + setl(b, T_RC, 0); + Bchg(b, T_STATUS, 0, "ACTIVE", 0L); + userlog("PAY_REACTIVATE acct=%s -> ACTIVE", h_acct); + OK(b); +} diff --git a/app/src/py/svc/PAY_RECLAIM.h b/app/src/py/svc/PAY_RECLAIM.h new file mode 100644 index 0000000..5ead7eb --- /dev/null +++ b/app/src/py/svc/PAY_RECLAIM.h @@ -0,0 +1,20 @@ +/* + * PAY_RECLAIM.h - py 서비스 PAY_RECLAIM 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_RECLAIM.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_RECLAIM_H +#define PAY_RECLAIM_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_reclaim_rec_t; + +void PAY_RECLAIM(TPSVCINFO *p); + +#endif /* PAY_RECLAIM_H */ diff --git a/app/src/py/svc/PAY_RECLAIM.pgc b/app/src/py/svc/PAY_RECLAIM.pgc new file mode 100644 index 0000000..cfc681f --- /dev/null +++ b/app/src/py/svc/PAY_RECLAIM.pgc @@ -0,0 +1,38 @@ +/* + * PAY_RECLAIM.pgc - py 모듈 서비스 PAY_RECLAIM (one service = one file). + * 과지급 환수: 지급완료(P) 건에서 요청 환수액(T_AMOUNT, net 한도)만큼을 + * 지급계좌에서 차감(음수 account_add)하고 py_ledger 에 음수 RECLAIM 기표. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_RECLAIM.h" + +void PAY_RECLAIM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long want, recl; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_net, h_neg; + char h_acct[32], h_st[4], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + want = getl(b, T_AMOUNT); + 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); } + + recl = (want > 0 && want < h_net) ? want : h_net; + if (recl <= 0) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "환수액 무효", 0L); FAIL(b); } + if (pydb_account_add(h_acct, -recl) < 0) FAIL(b); + h_neg = -recl; + EXEC SQL INSERT INTO py_ledger (ledger_id, payout_id, entry_type, amount, biz_date) + VALUES (nextval('py_ledger_seq'), :h_pid, 'RECLAIM', :h_neg, :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_RC, 0); + setl(b, T_AMT1, recl); + Bchg(b, T_STR1, 0, h_acct, 0L); + userlog("PAY_RECLAIM id=%ld reclaimed=%ld acct=%s", h_pid, recl, h_acct); + OK(b); +} diff --git a/app/src/py/svc/PAY_REPAYACCT.h b/app/src/py/svc/PAY_REPAYACCT.h new file mode 100644 index 0000000..901d5c2 --- /dev/null +++ b/app/src/py/svc/PAY_REPAYACCT.h @@ -0,0 +1,20 @@ +/* + * PAY_REPAYACCT.h - py 서비스 PAY_REPAYACCT 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_REPAYACCT.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_REPAYACCT_H +#define PAY_REPAYACCT_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_repayacct_rec_t; + +void PAY_REPAYACCT(TPSVCINFO *p); + +#endif /* PAY_REPAYACCT_H */ diff --git a/app/src/py/svc/PAY_REPAYACCT.pgc b/app/src/py/svc/PAY_REPAYACCT.pgc new file mode 100644 index 0000000..0484040 --- /dev/null +++ b/app/src/py/svc/PAY_REPAYACCT.pgc @@ -0,0 +1,39 @@ +/* + * PAY_REPAYACCT.pgc - py 모듈 서비스 PAY_REPAYACCT (one service = one file). + * 계좌변경 재지급: 반려(J)된 지급건의 금액/수수료를 승계해 새 계좌(T_KEY2)로 + * 신규 요청(R) 지급건을 복제 생성한다 (원건은 그대로 두어 이력 보존). + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_REPAYACCT.h" + +void PAY_REPAYACCT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char newacct[32], bizdate[16]; + long newid; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_amt, h_fee, h_net; + char h_merch[64], h_st[4]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + gets_(b, T_KEY2, newacct, sizeof(newacct)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (newacct[0] == 0) { setl(b, T_RC, 3); Bchg(b, T_MSG, 0, "새 계좌 미지정", 0L); FAIL(b); } + + EXEC SQL SELECT merchant_id, amount, fee, net, status + INTO :h_merch, :h_amt, :h_fee, :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] != 'J') { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "반려 건만 재지급 대상", 0L); FAIL(b); } + + newid = pydb_next_payout_id(); + if (newid < 0) FAIL(b); + if (pydb_insert_payout(newid, h_merch, newacct, h_amt, h_fee, h_net, "R", "STD", bizdate) < 0) FAIL(b); + + setl(b, T_RC, 0); + setl(b, T_ID2, newid); + setl(b, T_NET, h_net); + userlog("PAY_REPAYACCT old=%ld new=%ld acct=%s", h_pid, newid, newacct); + OK(b); +} diff --git a/app/src/py/svc/PAY_REQUEUE.h b/app/src/py/svc/PAY_REQUEUE.h new file mode 100644 index 0000000..cf5ee2f --- /dev/null +++ b/app/src/py/svc/PAY_REQUEUE.h @@ -0,0 +1,20 @@ +/* + * PAY_REQUEUE.h - py 서비스 PAY_REQUEUE 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_REQUEUE.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_REQUEUE_H +#define PAY_REQUEUE_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_requeue_rec_t; + +void PAY_REQUEUE(TPSVCINFO *p); + +#endif /* PAY_REQUEUE_H */ diff --git a/app/src/py/svc/PAY_REQUEUE.pgc b/app/src/py/svc/PAY_REQUEUE.pgc new file mode 100644 index 0000000..45e724a --- /dev/null +++ b/app/src/py/svc/PAY_REQUEUE.pgc @@ -0,0 +1,28 @@ +/* + * PAY_REQUEUE.pgc - py 모듈 서비스 PAY_REQUEUE (one service = one file). + * 실패 일괄 재처리 적재: 당일 반려(J) 건 중 재시도 여력(retry_count<3)이 + * 있는 건들을 요청(R)으로 되돌리고 retry_count 를 +1 해 재처리 큐에 올린다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_REQUEUE.h" + +void PAY_REQUEUE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long requeued; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL UPDATE py_payout + SET status = 'R', retry_count = retry_count + 1, reject_reason = '', updated_at = now() + WHERE biz_date = :h_bizdate AND status = 'J' AND retry_count < 3; + if (sqlca.sqlcode < 0) FAIL(b); + requeued = sqlca.sqlerrd[2]; + + setl(b, T_RC, requeued > 0 ? 0 : 1); + setl(b, T_COUNT, requeued); + userlog("PAY_REQUEUE date=%s requeued=%ld", h_bizdate, requeued); + OK(b); +} diff --git a/app/src/py/svc/PAY_RESVLIST.h b/app/src/py/svc/PAY_RESVLIST.h new file mode 100644 index 0000000..50da581 --- /dev/null +++ b/app/src/py/svc/PAY_RESVLIST.h @@ -0,0 +1,20 @@ +/* + * PAY_RESVLIST.h - py 서비스 PAY_RESVLIST 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_RESVLIST.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_RESVLIST_H +#define PAY_RESVLIST_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_resvlist_rec_t; + +void PAY_RESVLIST(TPSVCINFO *p); + +#endif /* PAY_RESVLIST_H */ diff --git a/app/src/py/svc/PAY_RESVLIST.pgc b/app/src/py/svc/PAY_RESVLIST.pgc new file mode 100644 index 0000000..8490744 --- /dev/null +++ b/app/src/py/svc/PAY_RESVLIST.pgc @@ -0,0 +1,43 @@ +/* + * PAY_RESVLIST.pgc - py 모듈 서비스 PAY_RESVLIST (one service = one file). + * 예약 지급 목록: 지정 실행일(T_BIZDATE)에 걸린 예약(S) 을 py_schedule 과 + * py_payout 조인 커서로 훑어 예약ID/지급ID/금액을 occurrence 로 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_RESVLIST.h" + +void PAY_RESVLIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long n = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_rundate[16]; + long h_sid, h_pid, h_amt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_rundate, sizeof(h_rundate)); + EXEC SQL DECLARE cur_resvlist CURSOR FOR + SELECT s.schedule_id, s.payout_id, coalesce(p.net,0) + FROM py_schedule s LEFT JOIN py_payout p ON s.payout_id = p.payout_id + WHERE s.run_date = :h_rundate AND s.status = 'S' + ORDER BY s.schedule_id; + EXEC SQL OPEN cur_resvlist; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_resvlist INTO :h_sid, :h_pid, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_resvlist; FAIL(b); } + n++; + if (i < 12) { + Bchg(b, T_ARG1, i, (char *)&h_sid, 0L); + Bchg(b, T_ARG2, i, (char *)&h_pid, 0L); + Bchg(b, T_AMT1, i, (char *)&h_amt, 0L); + i++; + } + } + EXEC SQL CLOSE cur_resvlist; + setl(b, T_COUNT, n); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_RESVMODIFY.h b/app/src/py/svc/PAY_RESVMODIFY.h new file mode 100644 index 0000000..d55c3b1 --- /dev/null +++ b/app/src/py/svc/PAY_RESVMODIFY.h @@ -0,0 +1,20 @@ +/* + * PAY_RESVMODIFY.h - py 서비스 PAY_RESVMODIFY 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_RESVMODIFY.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_RESVMODIFY_H +#define PAY_RESVMODIFY_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_resvmodify_rec_t; + +void PAY_RESVMODIFY(TPSVCINFO *p); + +#endif /* PAY_RESVMODIFY_H */ diff --git a/app/src/py/svc/PAY_RESVMODIFY.pgc b/app/src/py/svc/PAY_RESVMODIFY.pgc new file mode 100644 index 0000000..bff1366 --- /dev/null +++ b/app/src/py/svc/PAY_RESVMODIFY.pgc @@ -0,0 +1,33 @@ +/* + * PAY_RESVMODIFY.pgc - py 모듈 서비스 PAY_RESVMODIFY (one service = one file). + * 예약 변경: 아직 실행되지 않은 예약(S) 의 실행일(run_date)을 새 일자로 + * 변경한다. 이미 실행(D)된 예약이거나 없으면 사유코드로 구분해 거절한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_RESVMODIFY.h" + +void PAY_RESVMODIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_sid; + char h_new[16], h_st[4]; + EXEC SQL END DECLARE SECTION; + + h_sid = getl(b, T_ID2); + gets_(b, T_BIZDATE, h_new, sizeof(h_new)); + if (h_new[0] == 0) { setl(b, T_RC, 3); Bchg(b, T_MSG, 0, "변경일자 미지정", 0L); FAIL(b); } + + EXEC SQL SELECT status INTO :h_st FROM py_schedule WHERE schedule_id = :h_sid; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "예약 없음", 0L); FAIL(b); } + if (sqlca.sqlcode < 0) FAIL(b); + if (h_st[0] != 'S') { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "실행 전 예약만 변경 가능", 0L); FAIL(b); } + + EXEC SQL UPDATE py_schedule SET run_date = :h_new WHERE schedule_id = :h_sid; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_RC, 0); + Bchg(b, T_STR1, 0, h_new, 0L); + userlog("PAY_RESVMODIFY sid=%ld newdate=%s", h_sid, h_new); + OK(b); +} diff --git a/app/src/py/svc/PAY_RETRYONE.h b/app/src/py/svc/PAY_RETRYONE.h new file mode 100644 index 0000000..c28295d --- /dev/null +++ b/app/src/py/svc/PAY_RETRYONE.h @@ -0,0 +1,20 @@ +/* + * PAY_RETRYONE.h - py 서비스 PAY_RETRYONE 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_RETRYONE.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_RETRYONE_H +#define PAY_RETRYONE_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_retryone_rec_t; + +void PAY_RETRYONE(TPSVCINFO *p); + +#endif /* PAY_RETRYONE_H */ diff --git a/app/src/py/svc/PAY_RETRYONE.pgc b/app/src/py/svc/PAY_RETRYONE.pgc new file mode 100644 index 0000000..94a5ce9 --- /dev/null +++ b/app/src/py/svc/PAY_RETRYONE.pgc @@ -0,0 +1,32 @@ +/* + * PAY_RETRYONE.pgc - py 모듈 서비스 PAY_RETRYONE (one service = one file). + * 단건 재시도: 반려(J) 지급건 하나를 요청(R)으로 되돌리며 retry_count 를 + * +1 한다. 재시도 횟수가 5회 이상이면 더 이상 재시도하지 않고 거절한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_RETRYONE.h" + +void PAY_RETRYONE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_rc; + char h_st[4]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + EXEC SQL SELECT status, retry_count INTO :h_st, :h_rc 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); } + if (h_rc >= 5) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "재시도 한도 초과", 0L); FAIL(b); } + + EXEC SQL UPDATE py_payout SET status = 'R', retry_count = retry_count + 1, updated_at = now() + WHERE payout_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_RC, 0); + setl(b, T_COUNT, h_rc + 1); + Bchg(b, T_STATUS, 0, "R", 0L); + userlog("PAY_RETRYONE payout=%ld attempt=%ld", h_pid, h_rc + 1); + OK(b); +} diff --git a/app/src/py/svc/PAY_ROLLFWD.h b/app/src/py/svc/PAY_ROLLFWD.h new file mode 100644 index 0000000..81bcfec --- /dev/null +++ b/app/src/py/svc/PAY_ROLLFWD.h @@ -0,0 +1,20 @@ +/* + * PAY_ROLLFWD.h - py 서비스 PAY_ROLLFWD 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_ROLLFWD.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_ROLLFWD_H +#define PAY_ROLLFWD_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_rollfwd_rec_t; + +void PAY_ROLLFWD(TPSVCINFO *p); + +#endif /* PAY_ROLLFWD_H */ diff --git a/app/src/py/svc/PAY_ROLLFWD.pgc b/app/src/py/svc/PAY_ROLLFWD.pgc new file mode 100644 index 0000000..7297a8e --- /dev/null +++ b/app/src/py/svc/PAY_ROLLFWD.pgc @@ -0,0 +1,31 @@ +/* + * PAY_ROLLFWD.pgc - py 모듈 서비스 PAY_ROLLFWD (one service = one file). + * 미지급 일괄 이월: 지정 기준일 이전의 미지급(R) 건 전부를 차기 영업일 + * (T_KEY2)로 biz_date 를 밀어 이월한다. 이월 건수를 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_ROLLFWD.h" + +void PAY_ROLLFWD(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long moved; + EXEC SQL BEGIN DECLARE SECTION; + char h_cut[16], h_next[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_cut, sizeof(h_cut)); + gets_(b, T_KEY2, h_next, sizeof(h_next)); + if (h_next[0] == 0) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "차기일자 미지정", 0L); FAIL(b); } + + EXEC SQL UPDATE py_payout SET biz_date = :h_next, updated_at = now() + WHERE status = 'R' AND biz_date < :h_cut; + if (sqlca.sqlcode < 0) FAIL(b); + moved = sqlca.sqlerrd[2]; + + setl(b, T_RC, 0); + setl(b, T_COUNT, moved); + Bchg(b, T_STR1, 0, h_next, 0L); + userlog("PAY_ROLLFWD cut=%s next=%s moved=%ld", h_cut, h_next, moved); + OK(b); +} diff --git a/app/src/py/svc/PAY_SEIZEAMT.h b/app/src/py/svc/PAY_SEIZEAMT.h new file mode 100644 index 0000000..268de7c --- /dev/null +++ b/app/src/py/svc/PAY_SEIZEAMT.h @@ -0,0 +1,20 @@ +/* + * PAY_SEIZEAMT.h - py 서비스 PAY_SEIZEAMT 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_SEIZEAMT.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_SEIZEAMT_H +#define PAY_SEIZEAMT_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_seizeamt_rec_t; + +void PAY_SEIZEAMT(TPSVCINFO *p); + +#endif /* PAY_SEIZEAMT_H */ diff --git a/app/src/py/svc/PAY_SEIZEAMT.pgc b/app/src/py/svc/PAY_SEIZEAMT.pgc new file mode 100644 index 0000000..12bf803 --- /dev/null +++ b/app/src/py/svc/PAY_SEIZEAMT.pgc @@ -0,0 +1,38 @@ +/* + * PAY_SEIZEAMT.pgc - py 모듈 서비스 PAY_SEIZEAMT (one service = one file). + * 압류 금액 상계: 압류 계좌의 보류(H) 지급건 net 합을 압류가능액으로 산정하고, + * 요청 압류액(T_AMOUNT)과 비교해 상계 실행액을 py_ledger 에 SEIZE 로 기표한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_SEIZEAMT.h" + +void PAY_SEIZEAMT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long claim, applied; + EXEC SQL BEGIN DECLARE SECTION; + char h_acct[32], h_bizdate[16]; + long h_avail, h_led; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_KEY1, h_acct, sizeof(h_acct)); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + claim = getl(b, T_AMOUNT); + + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_avail + FROM py_payout WHERE account_no = :h_acct AND status = 'H'; + if (sqlca.sqlcode < 0) FAIL(b); + applied = (claim > 0 && claim < h_avail) ? claim : h_avail; + if (applied <= 0) { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "압류 가능액 없음", 0L); FAIL(b); } + + h_led = -applied; + EXEC SQL INSERT INTO py_ledger (ledger_id, payout_id, entry_type, amount, biz_date) + VALUES (nextval('py_ledger_seq'), 0, 'SEIZE', :h_led, :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_RC, 0); + setl(b, T_AMT1, h_avail); + setl(b, T_AMT2, applied); + userlog("PAY_SEIZEAMT acct=%s avail=%ld applied=%ld", h_acct, h_avail, applied); + OK(b); +} diff --git a/app/src/py/svc/PAY_SEIZELIST.h b/app/src/py/svc/PAY_SEIZELIST.h new file mode 100644 index 0000000..d33df07 --- /dev/null +++ b/app/src/py/svc/PAY_SEIZELIST.h @@ -0,0 +1,20 @@ +/* + * PAY_SEIZELIST.h - py 서비스 PAY_SEIZELIST 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_SEIZELIST.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_SEIZELIST_H +#define PAY_SEIZELIST_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_seizelist_rec_t; + +void PAY_SEIZELIST(TPSVCINFO *p); + +#endif /* PAY_SEIZELIST_H */ diff --git a/app/src/py/svc/PAY_SEIZELIST.pgc b/app/src/py/svc/PAY_SEIZELIST.pgc new file mode 100644 index 0000000..40fc11a --- /dev/null +++ b/app/src/py/svc/PAY_SEIZELIST.pgc @@ -0,0 +1,42 @@ +/* + * PAY_SEIZELIST.pgc - py 모듈 서비스 PAY_SEIZELIST (one service = one file). + * 압류 계좌 목록: 압류(SEIZED) 상태 계좌를 커서로 훑어 계좌번호/예금주와 그 + * 계좌의 보류(H) 지급 잔액을 occurrence 로 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_SEIZELIST.h" + +void PAY_SEIZELIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long total = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_acct[32], h_holder[64]; + long h_held; + EXEC SQL END DECLARE SECTION; + + EXEC SQL DECLARE cur_seizelist CURSOR FOR + SELECT a.account_no, a.holder, + coalesce((SELECT sum(net) FROM py_payout p + WHERE p.account_no = a.account_no AND p.status = 'H'), 0) + FROM py_account a WHERE a.status = 'SEIZED' ORDER BY a.account_no; + EXEC SQL OPEN cur_seizelist; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_seizelist INTO :h_acct, :h_holder, :h_held; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_seizelist; FAIL(b); } + total++; + if (i < 10) { + Bchg(b, T_STR5, i, h_acct, 0L); + Bchg(b, T_STR6, i, h_holder, 0L); + Bchg(b, T_AMT1, i, (char *)&h_held, 0L); + i++; + } + } + EXEC SQL CLOSE cur_seizelist; + setl(b, T_COUNT, total); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_SPLITEVEN.h b/app/src/py/svc/PAY_SPLITEVEN.h new file mode 100644 index 0000000..7ab0ed1 --- /dev/null +++ b/app/src/py/svc/PAY_SPLITEVEN.h @@ -0,0 +1,20 @@ +/* + * PAY_SPLITEVEN.h - py 서비스 PAY_SPLITEVEN 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_SPLITEVEN.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_SPLITEVEN_H +#define PAY_SPLITEVEN_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_spliteven_rec_t; + +void PAY_SPLITEVEN(TPSVCINFO *p); + +#endif /* PAY_SPLITEVEN_H */ diff --git a/app/src/py/svc/PAY_SPLITEVEN.pgc b/app/src/py/svc/PAY_SPLITEVEN.pgc new file mode 100644 index 0000000..dd32f55 --- /dev/null +++ b/app/src/py/svc/PAY_SPLITEVEN.pgc @@ -0,0 +1,42 @@ +/* + * PAY_SPLITEVEN.pgc - py 모듈 서비스 PAY_SPLITEVEN (one service = one file). + * 균등 분할: 요청(R) 지급건을 N 건으로 균등 분할하되 나머지는 첫 건에 몰아 + * 넣고, 원건은 취소(C)한 뒤 자식 지급건(SPLIT)들을 새로 생성한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_SPLITEVEN.h" + +void PAY_SPLITEVEN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long n, base, rem, k, part, cid; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_amt; + char h_merch[64], h_acct[32], h_st[4]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + n = getl(b, T_COUNT); if (n < 2) n = 2; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + + EXEC SQL SELECT merchant_id, account_no, amount, status + INTO :h_merch, :h_acct, :h_amt, :h_st 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); } + + base = h_amt / n; + rem = h_amt - base * n; + if (pydb_update_status(h_pid, "C") < 0) FAIL(b); + for (k = 0; k < n; k++) { + part = base + (k == 0 ? rem : 0); + cid = pydb_next_payout_id(); + if (cid < 0) FAIL(b); + if (pydb_insert_payout(cid, h_merch, h_acct, part, 0, part, "R", "SPLIT", bizdate) < 0) FAIL(b); + } + setl(b, T_RC, 0); + setl(b, T_COUNT, n); + setl(b, T_AMOUNT, h_amt); + userlog("PAY_SPLITEVEN src=%ld parts=%ld base=%ld rem=%ld", h_pid, n, base, rem); + OK(b); +} diff --git a/app/src/py/svc/PAY_STATAMT.h b/app/src/py/svc/PAY_STATAMT.h new file mode 100644 index 0000000..4a83bf7 --- /dev/null +++ b/app/src/py/svc/PAY_STATAMT.h @@ -0,0 +1,20 @@ +/* + * PAY_STATAMT.h - py 서비스 PAY_STATAMT 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_STATAMT.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_STATAMT_H +#define PAY_STATAMT_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_statamt_rec_t; + +void PAY_STATAMT(TPSVCINFO *p); + +#endif /* PAY_STATAMT_H */ diff --git a/app/src/py/svc/PAY_STATAMT.pgc b/app/src/py/svc/PAY_STATAMT.pgc new file mode 100644 index 0000000..49f5b13 --- /dev/null +++ b/app/src/py/svc/PAY_STATAMT.pgc @@ -0,0 +1,44 @@ +/* + * PAY_STATAMT.pgc - py 모듈 서비스 PAY_STATAMT (one service = one file). + * 금액대별 지급 통계: 당일 지급건을 net 기준 소액/중액/고액 구간으로 CASE + * 분류해 구간별 건수/합계를 occurrence 로 회신한다 (금액 분포 파악). + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_STATAMT.h" + +void PAY_STATAMT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long bands = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_band[16]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL DECLARE cur_statamt CURSOR FOR + SELECT CASE WHEN net < 100000 THEN '소액' + WHEN net < 1000000 THEN '중액' + ELSE '고액' END AS band, + count(*), coalesce(sum(net),0) + FROM py_payout WHERE biz_date = :h_bizdate GROUP BY 1 ORDER BY 1; + EXEC SQL OPEN cur_statamt; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_statamt INTO :h_band, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_statamt; FAIL(b); } + bands++; + if (i < 6) { + Bchg(b, T_STR7, i, h_band, 0L); + Bchg(b, T_ARG8, i, (char *)&h_cnt, 0L); + Bchg(b, T_AMT1, i, (char *)&h_amt, 0L); + i++; + } + } + EXEC SQL CLOSE cur_statamt; + setl(b, T_COUNT, bands); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_STATFEE.h b/app/src/py/svc/PAY_STATFEE.h new file mode 100644 index 0000000..ee36303 --- /dev/null +++ b/app/src/py/svc/PAY_STATFEE.h @@ -0,0 +1,20 @@ +/* + * PAY_STATFEE.h - py 서비스 PAY_STATFEE 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_STATFEE.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_STATFEE_H +#define PAY_STATFEE_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_statfee_rec_t; + +void PAY_STATFEE(TPSVCINFO *p); + +#endif /* PAY_STATFEE_H */ diff --git a/app/src/py/svc/PAY_STATFEE.pgc b/app/src/py/svc/PAY_STATFEE.pgc new file mode 100644 index 0000000..2162e6d --- /dev/null +++ b/app/src/py/svc/PAY_STATFEE.pgc @@ -0,0 +1,42 @@ +/* + * PAY_STATFEE.pgc - py 모듈 서비스 PAY_STATFEE (one service = one file). + * 채널별 수수료 통계: 당일 지급건을 channel 별로 수수료합/net합 집계하되 + * 수수료가 발생한 채널만 HAVING 으로 남겨 occurrence 로 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_STATFEE.h" + +void PAY_STATFEE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_chan[16]; + long h_fee, h_net; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL DECLARE cur_statfee CURSOR FOR + SELECT channel, coalesce(sum(fee),0), coalesce(sum(net),0) + FROM py_payout WHERE biz_date = :h_bizdate + GROUP BY channel HAVING coalesce(sum(fee),0) > 0 ORDER BY 2 DESC; + EXEC SQL OPEN cur_statfee; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_statfee INTO :h_chan, :h_fee, :h_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_statfee; FAIL(b); } + rows++; + if (i < 8) { + Bchg(b, T_STR3, i, h_chan, 0L); + Bchg(b, T_AMT2, i, (char *)&h_fee, 0L); + Bchg(b, T_AMT3, i, (char *)&h_net, 0L); + i++; + } + } + EXEC SQL CLOSE cur_statfee; + setl(b, T_COUNT, rows); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_STATMONTH.h b/app/src/py/svc/PAY_STATMONTH.h new file mode 100644 index 0000000..a2db515 --- /dev/null +++ b/app/src/py/svc/PAY_STATMONTH.h @@ -0,0 +1,20 @@ +/* + * PAY_STATMONTH.h - py 서비스 PAY_STATMONTH 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_STATMONTH.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_STATMONTH_H +#define PAY_STATMONTH_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_statmonth_rec_t; + +void PAY_STATMONTH(TPSVCINFO *p); + +#endif /* PAY_STATMONTH_H */ diff --git a/app/src/py/svc/PAY_STATMONTH.pgc b/app/src/py/svc/PAY_STATMONTH.pgc new file mode 100644 index 0000000..e21832f --- /dev/null +++ b/app/src/py/svc/PAY_STATMONTH.pgc @@ -0,0 +1,45 @@ +/* + * PAY_STATMONTH.pgc - py 모듈 서비스 PAY_STATMONTH (one service = one file). + * 월별 지급 통계: 지정 기간(T_BIZDATE~T_KEY2)의 지급건을 'YYYY-MM' 단위로 + * 묶어 월별 건수/net합을 occurrence 로 회신한다 (월 추이 파악). + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_STATMONTH.h" + +void PAY_STATMONTH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long months = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_from[16], h_to[16], h_mon[16]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_from, sizeof(h_from)); + gets_(b, T_KEY2, h_to, sizeof(h_to)); + if (h_to[0] == 0) strcpy(h_to, h_from); + + EXEC SQL DECLARE cur_statmonth CURSOR FOR + SELECT to_char(biz_date, 'YYYY-MM'), count(*), coalesce(sum(net),0) + FROM py_payout WHERE biz_date BETWEEN :h_from AND :h_to + GROUP BY 1 ORDER BY 1; + EXEC SQL OPEN cur_statmonth; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_statmonth INTO :h_mon, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_statmonth; FAIL(b); } + months++; + if (i < 12) { + Bchg(b, T_STR4, i, h_mon, 0L); + Bchg(b, T_ARG3, i, (char *)&h_cnt, 0L); + Bchg(b, T_AMT2, i, (char *)&h_amt, 0L); + i++; + } + } + EXEC SQL CLOSE cur_statmonth; + setl(b, T_COUNT, months); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_STATSTATUS.h b/app/src/py/svc/PAY_STATSTATUS.h new file mode 100644 index 0000000..4fa0e87 --- /dev/null +++ b/app/src/py/svc/PAY_STATSTATUS.h @@ -0,0 +1,20 @@ +/* + * PAY_STATSTATUS.h - py 서비스 PAY_STATSTATUS 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_STATSTATUS.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_STATSTATUS_H +#define PAY_STATSTATUS_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_statstatus_rec_t; + +void PAY_STATSTATUS(TPSVCINFO *p); + +#endif /* PAY_STATSTATUS_H */ diff --git a/app/src/py/svc/PAY_STATSTATUS.pgc b/app/src/py/svc/PAY_STATSTATUS.pgc new file mode 100644 index 0000000..c2fcbec --- /dev/null +++ b/app/src/py/svc/PAY_STATSTATUS.pgc @@ -0,0 +1,42 @@ +/* + * PAY_STATSTATUS.pgc - py 모듈 서비스 PAY_STATSTATUS (one service = one file). + * 상태별 지급 통계: 당일 지급건을 status 별 커서로 건수/net합/수수료합까지 + * 4열 집계해 occurrence 로 회신한다 (요청/승인/지급/취소 흐름 분포 파악). + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_STATSTATUS.h" + +void PAY_STATSTATUS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_st[4]; + long h_cnt, h_net, h_fee; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL DECLARE cur_statstatus CURSOR FOR + SELECT status, count(*), coalesce(sum(net),0), coalesce(sum(fee),0) + FROM py_payout WHERE biz_date = :h_bizdate GROUP BY status ORDER BY status; + EXEC SQL OPEN cur_statstatus; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_statstatus INTO :h_st, :h_cnt, :h_net, :h_fee; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_statstatus; FAIL(b); } + rows++; + if (i < 9) { + Bchg(b, T_STR3, i, h_st, 0L); + Bchg(b, T_ARG4, i, (char *)&h_cnt, 0L); + Bchg(b, T_AMT3, i, (char *)&h_net, 0L); + Bchg(b, T_AMT4, i, (char *)&h_fee, 0L); + i++; + } + } + EXEC SQL CLOSE cur_statstatus; + setl(b, T_COUNT, rows); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_URGENTQ.h b/app/src/py/svc/PAY_URGENTQ.h new file mode 100644 index 0000000..46acefe --- /dev/null +++ b/app/src/py/svc/PAY_URGENTQ.h @@ -0,0 +1,20 @@ +/* + * PAY_URGENTQ.h - py 서비스 PAY_URGENTQ 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_URGENTQ.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_URGENTQ_H +#define PAY_URGENTQ_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_urgentq_rec_t; + +void PAY_URGENTQ(TPSVCINFO *p); + +#endif /* PAY_URGENTQ_H */ diff --git a/app/src/py/svc/PAY_URGENTQ.pgc b/app/src/py/svc/PAY_URGENTQ.pgc new file mode 100644 index 0000000..241c180 --- /dev/null +++ b/app/src/py/svc/PAY_URGENTQ.pgc @@ -0,0 +1,44 @@ +/* + * PAY_URGENTQ.pgc - py 모듈 서비스 PAY_URGENTQ (one service = one file). + * 긴급 지급 큐: 당일 요청(R) 건 중 금액이 임계치(T_AMOUNT) 이상인 건을 금액 + * 내림차순 커서로 뽑아 긴급 처리 후보 목록을 occurrence 로 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_URGENTQ.h" + +void PAY_URGENTQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long hits = 0, thr; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_thr, h_pid, h_amt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + thr = getl(b, T_AMOUNT); + h_thr = (thr > 0) ? thr : 1000000; + + EXEC SQL DECLARE cur_urgentq CURSOR FOR + SELECT payout_id, amount FROM py_payout + WHERE biz_date = :h_bizdate AND status = 'R' AND amount >= :h_thr + ORDER BY amount DESC; + EXEC SQL OPEN cur_urgentq; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_urgentq INTO :h_pid, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_urgentq; FAIL(b); } + hits++; + if (i < 12) { + Bchg(b, T_ARG1, i, (char *)&h_pid, 0L); + Bchg(b, T_AMT1, i, (char *)&h_amt, 0L); + i++; + } + } + EXEC SQL CLOSE cur_urgentq; + setl(b, T_COUNT, hits); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_VOIDONE.h b/app/src/py/svc/PAY_VOIDONE.h new file mode 100644 index 0000000..779415e --- /dev/null +++ b/app/src/py/svc/PAY_VOIDONE.h @@ -0,0 +1,20 @@ +/* + * PAY_VOIDONE.h - py 서비스 PAY_VOIDONE 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_VOIDONE.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_VOIDONE_H +#define PAY_VOIDONE_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_voidone_rec_t; + +void PAY_VOIDONE(TPSVCINFO *p); + +#endif /* PAY_VOIDONE_H */ diff --git a/app/src/py/svc/PAY_VOIDONE.pgc b/app/src/py/svc/PAY_VOIDONE.pgc new file mode 100644 index 0000000..1ed3830 --- /dev/null +++ b/app/src/py/svc/PAY_VOIDONE.pgc @@ -0,0 +1,33 @@ +/* + * PAY_VOIDONE.pgc - py 모듈 서비스 PAY_VOIDONE (one service = one file). + * 지급 무효(단건): 아직 지급 전인 요청/승인(R/A) 건만 무효 취소(C)로 전이하고 + * 무효 사유를 남긴다. 파일편성/지급완료 이후 건은 무효 대상이 아니다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_VOIDONE.h" + +void PAY_VOIDONE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long voided; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid; + char h_rsn[128]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + gets_(b, T_REASON, h_rsn, sizeof(h_rsn)); + if (h_rsn[0] == 0) strcpy(h_rsn, "무효처리"); + + EXEC SQL UPDATE py_payout SET status = 'C', reject_reason = :h_rsn, updated_at = now() + WHERE payout_id = :h_pid AND status IN ('R','A'); + if (sqlca.sqlcode < 0) FAIL(b); + voided = sqlca.sqlerrd[2]; + if (voided == 0) { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "무효 대상 아님(지급 전 건만)", 0L); FAIL(b); } + + setl(b, T_RC, 0); + Bchg(b, T_STATUS, 0, "C", 0L); + Bchg(b, T_REASON, 0, h_rsn, 0L); + userlog("PAY_VOIDONE payout=%ld reason=%s", h_pid, h_rsn); + OK(b); +} diff --git a/app/src/py/svc/PAY_XFERCONF.h b/app/src/py/svc/PAY_XFERCONF.h new file mode 100644 index 0000000..414eada --- /dev/null +++ b/app/src/py/svc/PAY_XFERCONF.h @@ -0,0 +1,20 @@ +/* + * PAY_XFERCONF.h - py 서비스 PAY_XFERCONF 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_XFERCONF.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_XFERCONF_H +#define PAY_XFERCONF_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_xferconf_rec_t; + +void PAY_XFERCONF(TPSVCINFO *p); + +#endif /* PAY_XFERCONF_H */ diff --git a/app/src/py/svc/PAY_XFERCONF.pgc b/app/src/py/svc/PAY_XFERCONF.pgc new file mode 100644 index 0000000..189a1bf --- /dev/null +++ b/app/src/py/svc/PAY_XFERCONF.pgc @@ -0,0 +1,37 @@ +/* + * PAY_XFERCONF.pgc - py 모듈 서비스 PAY_XFERCONF (one service = one file). + * 이체 확정: 지급완료(P) 건에 대해 이체 실행 결과를 py_ledger 에 XFER 기표로 + * 확정한다. 이미 XFER 기표가 있으면 중복 확정을 막고 사유코드로 거절한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_XFERCONF.h" + +void PAY_XFERCONF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_net, h_exist; + char h_st[4], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_ID1); + 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] != 'P') { setl(b, T_RC, 1); Bchg(b, T_MSG, 0, "지급완료 건만 이체확정 가능", 0L); FAIL(b); } + + EXEC SQL SELECT count(*) INTO :h_exist + FROM py_ledger WHERE payout_id = :h_pid AND entry_type = 'XFER'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_exist > 0) { setl(b, T_RC, 2); Bchg(b, T_MSG, 0, "이미 이체확정된 건", 0L); FAIL(b); } + + EXEC SQL INSERT INTO py_ledger (ledger_id, payout_id, entry_type, amount, biz_date) + VALUES (nextval('py_ledger_seq'), :h_pid, 'XFER', :h_net, :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + + setl(b, T_RC, 0); + setl(b, T_NET, h_net); + Bchg(b, T_MSG, 0, "이체 확정 완료", 0L); + userlog("PAY_XFERCONF payout=%ld net=%ld", h_pid, h_net); + OK(b); +} diff --git a/app/src/py/svc/PAY_XFERDIFF.h b/app/src/py/svc/PAY_XFERDIFF.h new file mode 100644 index 0000000..e8c9486 --- /dev/null +++ b/app/src/py/svc/PAY_XFERDIFF.h @@ -0,0 +1,20 @@ +/* + * PAY_XFERDIFF.h - py 서비스 PAY_XFERDIFF 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_XFERDIFF.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_XFERDIFF_H +#define PAY_XFERDIFF_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_xferdiff_rec_t; + +void PAY_XFERDIFF(TPSVCINFO *p); + +#endif /* PAY_XFERDIFF_H */ diff --git a/app/src/py/svc/PAY_XFERDIFF.pgc b/app/src/py/svc/PAY_XFERDIFF.pgc new file mode 100644 index 0000000..72220dc --- /dev/null +++ b/app/src/py/svc/PAY_XFERDIFF.pgc @@ -0,0 +1,46 @@ +/* + * PAY_XFERDIFF.pgc - py 모듈 서비스 PAY_XFERDIFF (one service = one file). + * 이체 차액 목록: 당일 지급건의 net 과 py_ledger XFER 기표 합이 어긋나는 + * 건을 커서로 뽑아 지급ID/net/이체합/차액을 occurrence 로 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_XFERDIFF.h" + +void PAY_XFERDIFF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long diffs = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pid, h_net, h_xfer; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL DECLARE cur_xferdiff CURSOR FOR + SELECT p.payout_id, p.net, + coalesce((SELECT sum(amount) FROM py_ledger l + WHERE l.payout_id = p.payout_id AND l.entry_type = 'XFER'), 0) + FROM py_payout p WHERE p.biz_date = :h_bizdate AND p.status = 'P' + ORDER BY p.payout_id; + EXEC SQL OPEN cur_xferdiff; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_xferdiff INTO :h_pid, :h_net, :h_xfer; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_xferdiff; FAIL(b); } + if (h_net == h_xfer) continue; + diffs++; + if (i < 12) { + long d = h_net - h_xfer; + Bchg(b, T_ARG1, i, (char *)&h_pid, 0L); + Bchg(b, T_AMT1, i, (char *)&h_net, 0L); + Bchg(b, T_AMT2, i, (char *)&d, 0L); + i++; + } + } + EXEC SQL CLOSE cur_xferdiff; + setl(b, T_COUNT, diffs); + setl(b, T_RC, diffs > 0 ? 1 : 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_XFERFILE.h b/app/src/py/svc/PAY_XFERFILE.h new file mode 100644 index 0000000..53189df --- /dev/null +++ b/app/src/py/svc/PAY_XFERFILE.h @@ -0,0 +1,20 @@ +/* + * PAY_XFERFILE.h - py 서비스 PAY_XFERFILE 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_XFERFILE.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_XFERFILE_H +#define PAY_XFERFILE_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_xferfile_rec_t; + +void PAY_XFERFILE(TPSVCINFO *p); + +#endif /* PAY_XFERFILE_H */ diff --git a/app/src/py/svc/PAY_XFERFILE.pgc b/app/src/py/svc/PAY_XFERFILE.pgc new file mode 100644 index 0000000..6f68e4a --- /dev/null +++ b/app/src/py/svc/PAY_XFERFILE.pgc @@ -0,0 +1,45 @@ +/* + * PAY_XFERFILE.pgc - py 모듈 서비스 PAY_XFERFILE (one service = one file). + * 이체유형별 지급파일 생성: 지정 채널(STD/SPLIT/BULK/ADVANCE)의 승인(A) 건만 + * 모아 집계 -> py_file 생성 -> 대상 건을 파일편성(F)+file_id 로 일괄 전이한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_XFERFILE.h" + +void PAY_XFERFILE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char fname[64]; + EXEC SQL BEGIN DECLARE SECTION; + char h_chan[16], h_bizdate[16], h_fname[64]; + long h_fid, h_cnt, h_sum; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_CHANNEL, h_chan, sizeof(h_chan)); + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_chan[0] == 0) strcpy(h_chan, "STD"); + + EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_cnt, :h_sum + FROM py_payout WHERE biz_date = :h_bizdate AND status = 'A' AND channel = :h_chan; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt == 0) { setl(b, T_RC, 1); setl(b, T_COUNT, 0); OK(b); } + + EXEC SQL SELECT nextval('py_file_seq') INTO :h_fid; + if (sqlca.sqlcode < 0) FAIL(b); + snprintf(fname, sizeof(fname), "XFER_%s_%s.dat", h_chan, h_bizdate); + strncpy(h_fname, fname, sizeof(h_fname)-1); h_fname[sizeof(h_fname)-1] = 0; + 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() + WHERE biz_date = :h_bizdate AND status = 'A' AND channel = :h_chan; + 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_XFERFILE chan=%s file=%ld recs=%ld amt=%ld", h_chan, h_fid, h_cnt, h_sum); + OK(b); +} diff --git a/app/src/py/svc/PAY_XFERTYPE.h b/app/src/py/svc/PAY_XFERTYPE.h new file mode 100644 index 0000000..ad6357b --- /dev/null +++ b/app/src/py/svc/PAY_XFERTYPE.h @@ -0,0 +1,20 @@ +/* + * PAY_XFERTYPE.h - py 서비스 PAY_XFERTYPE 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_XFERTYPE.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_XFERTYPE_H +#define PAY_XFERTYPE_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_xfertype_rec_t; + +void PAY_XFERTYPE(TPSVCINFO *p); + +#endif /* PAY_XFERTYPE_H */ diff --git a/app/src/py/svc/PAY_XFERTYPE.pgc b/app/src/py/svc/PAY_XFERTYPE.pgc new file mode 100644 index 0000000..f093d89 --- /dev/null +++ b/app/src/py/svc/PAY_XFERTYPE.pgc @@ -0,0 +1,42 @@ +/* + * PAY_XFERTYPE.pgc - py 모듈 서비스 PAY_XFERTYPE (one service = one file). + * 이체유형별 지급 집계: 당일 지급건을 channel 별 커서로 건수/net 합 집계해 + * occurrence 로 회신한다 (표준/분할/대량/선지급 물량 분포 파악). + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_XFERTYPE.h" + +void PAY_XFERTYPE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long kinds = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_chan[16]; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL DECLARE cur_xfertype CURSOR FOR + SELECT channel, count(*), coalesce(sum(net),0) + FROM py_payout WHERE biz_date = :h_bizdate + GROUP BY channel ORDER BY 1; + EXEC SQL OPEN cur_xfertype; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_xfertype INTO :h_chan, :h_cnt, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_xfertype; FAIL(b); } + kinds++; + if (i < 8) { + Bchg(b, T_STR3, i, h_chan, 0L); + Bchg(b, T_ARG4, i, (char *)&h_cnt, 0L); + Bchg(b, T_AMT3, i, (char *)&h_amt, 0L); + i++; + } + } + EXEC SQL CLOSE cur_xfertype; + setl(b, T_COUNT, kinds); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/py/svc/PAY_XFERUNMATCH.h b/app/src/py/svc/PAY_XFERUNMATCH.h new file mode 100644 index 0000000..3eaf2a6 --- /dev/null +++ b/app/src/py/svc/PAY_XFERUNMATCH.h @@ -0,0 +1,20 @@ +/* + * PAY_XFERUNMATCH.h - py 서비스 PAY_XFERUNMATCH 카피북 (copybook / record header). + * Shared base(py_svc.h) + 이 서비스의 레코드 구조체 + 함수 프로토타입. + * Request/Response 필드는 PAY_XFERUNMATCH.pgc 본문의 get/set 호출을 참조. + */ +#ifndef PAY_XFERUNMATCH_H +#define PAY_XFERUNMATCH_H + +#include "py_svc.h" + +typedef struct { + long payout_id; + long amount; + long net; + char status[8]; +} pay_xferunmatch_rec_t; + +void PAY_XFERUNMATCH(TPSVCINFO *p); + +#endif /* PAY_XFERUNMATCH_H */ diff --git a/app/src/py/svc/PAY_XFERUNMATCH.pgc b/app/src/py/svc/PAY_XFERUNMATCH.pgc new file mode 100644 index 0000000..8cc2373 --- /dev/null +++ b/app/src/py/svc/PAY_XFERUNMATCH.pgc @@ -0,0 +1,44 @@ +/* + * PAY_XFERUNMATCH.pgc - py 모듈 서비스 PAY_XFERUNMATCH (one service = one file). + * 미대사 목록: 지급완료(P) 이면서 이체 기표(py_ledger XFER)가 전혀 없는 + * 건을 NOT EXISTS 커서로 뽑아 지급ID/계좌/net 을 occurrence 로 회신한다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by py_svr. + */ +#include "PAY_XFERUNMATCH.h" + +void PAY_XFERUNMATCH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + int i = 0; + long miss = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_acct[32]; + long h_pid, h_net; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + EXEC SQL DECLARE cur_xferunmatch CURSOR FOR + SELECT payout_id, account_no, net FROM py_payout p + WHERE p.biz_date = :h_bizdate AND p.status = 'P' + AND NOT EXISTS (SELECT 1 FROM py_ledger l + WHERE l.payout_id = p.payout_id AND l.entry_type = 'XFER') + ORDER BY payout_id; + EXEC SQL OPEN cur_xferunmatch; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_xferunmatch INTO :h_pid, :h_acct, :h_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_xferunmatch; FAIL(b); } + miss++; + if (i < 12) { + Bchg(b, T_ARG1, i, (char *)&h_pid, 0L); + Bchg(b, T_STR2, i, h_acct, 0L); + Bchg(b, T_AMT1, i, (char *)&h_net, 0L); + i++; + } + } + EXEC SQL CLOSE cur_xferunmatch; + setl(b, T_COUNT, miss); + setl(b, T_RC, miss > 0 ? 1 : 0); + OK(b); +} diff --git a/app/src/rc/batch/rc_backdate_batch.pgc b/app/src/rc/batch/rc_backdate_batch.pgc new file mode 100644 index 0000000..ba411a2 --- /dev/null +++ b/app/src/rc/batch/rc_backdate_batch.pgc @@ -0,0 +1,52 @@ +/* + * rc_backdate_batch.pgc - 소급 대사 재처리 배치 (커서 + XA): 원장 기표일이 매입일보다 늦은 지연 건을 소급 대사 로그로 재적재. + * ATMI 클라이언트: tpinit/tpopen 으로 ECPG XA RM 를 열고 tpbegin 으로 ONE global tx + * 를 시작, EXEC SQL 수행 후 tpcommit 이 XA 2PC 를 구동. 사용법: rc_backdate_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "rc_backdate_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid, h_amt; + 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 bcur CURSOR FOR + SELECT DISTINCT p.purchase_id, p.net FROM purchase p + JOIN ledger l ON l.purchase_id = p.purchase_id + WHERE p.biz_date = :h_bizdate AND l.biz_date > p.biz_date + ORDER BY p.purchase_id; + EXEC SQL OPEN bcur; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH bcur INTO :h_pid, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + EXEC SQL SELECT nextval('rc_match_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_match_log (match_id, purchase_id, match_type, amount, status, biz_date) + VALUES (:h_id, :h_pid, 'BACKDATE', :h_amt, 'MATCHED', :h_bizdate); + if (sqlca.sqlcode < 0) { fprintf(stderr, "INS FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + rows++; + } + EXEC SQL CLOSE bcur; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> rc_backdate_batch COMMIT: bizdate=%s backdated=%ld\n", bizdate, rows); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/rc/batch/rc_chanrate_batch.pgc b/app/src/rc/batch/rc_chanrate_batch.pgc new file mode 100644 index 0000000..913386f --- /dev/null +++ b/app/src/rc/batch/rc_chanrate_batch.pgc @@ -0,0 +1,53 @@ +/* + * rc_chanrate_batch.pgc - 채널별 대사율 집계 배치 (커서 + XA): 채널마다 당일 대사완료율을 계산해 rc_alert 에 INFO 로 적재. + * ATMI 클라이언트: tpinit/tpopen 으로 ECPG XA RM 를 열고 tpbegin 으로 ONE global tx + * 를 시작, EXEC SQL 수행 후 tpcommit 이 XA 2PC 를 구동. 사용법: rc_chanrate_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "rc_chanrate_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_total, h_done, h_rate; + char h_chan[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 DECLARE bcur CURSOR FOR + SELECT channel, count(*), + count(*) FILTER (WHERE status IN ('M','S')) + FROM purchase WHERE biz_date = :h_bizdate GROUP BY channel ORDER BY channel; + EXEC SQL OPEN bcur; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH bcur INTO :h_chan, :h_total, :h_done; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + h_rate = (h_total > 0) ? (h_done * 100 / h_total) : 0; + EXEC SQL SELECT nextval('rc_alert_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_alert (alert_id, lvl, message, biz_date) + VALUES (:h_id, 'INFO', 'CHAN_RATE', :h_bizdate); + if (sqlca.sqlcode < 0) { fprintf(stderr, "INS FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + rows++; + } + EXEC SQL CLOSE bcur; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> rc_chanrate_batch COMMIT: bizdate=%s channels=%ld\n", bizdate, rows); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/rc/batch/rc_fxdiff_batch.pgc b/app/src/rc/batch/rc_fxdiff_batch.pgc new file mode 100644 index 0000000..34b153b --- /dev/null +++ b/app/src/rc/batch/rc_fxdiff_batch.pgc @@ -0,0 +1,52 @@ +/* + * rc_fxdiff_batch.pgc - 외화 환산 차이 배치 (커서 + XA): ac_fx 원화환산과 원통화*환율 이론치의 차이가 있는 건을 rc_diff 에 기록. + * ATMI 클라이언트: tpinit/tpopen 으로 ECPG XA RM 를 열고 tpbegin 으로 ONE global tx + * 를 시작, EXEC SQL 수행 후 tpcommit 이 XA 2PC 를 구동. 사용법: rc_fxdiff_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "rc_fxdiff_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid, h_krw, h_calc, h_diff; + 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 bcur CURSOR FOR + SELECT purchase_id, krw_amount, fx_amount * rate_bps / 10000 + FROM ac_fx WHERE biz_date = :h_bizdate ORDER BY fx_id; + EXEC SQL OPEN bcur; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH bcur INTO :h_pid, :h_krw, :h_calc; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + h_diff = h_krw - h_calc; + if (h_diff == 0) continue; + EXEC SQL SELECT nextval('rc_diff_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_diff (diff_id, purchase_id, expected_amt, actual_amt, diff_amt, diff_kind, biz_date) + VALUES (:h_id, :h_pid, :h_calc, :h_krw, :h_diff, 'FX', :h_bizdate); + if (sqlca.sqlcode < 0) { fprintf(stderr, "INS FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + rows++; + } + EXEC SQL CLOSE bcur; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> rc_fxdiff_batch COMMIT: bizdate=%s fxdiff=%ld\n", bizdate, rows); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/rc/batch/rc_holdsweep_batch.pgc b/app/src/rc/batch/rc_holdsweep_batch.pgc new file mode 100644 index 0000000..f460f98 --- /dev/null +++ b/app/src/rc/batch/rc_holdsweep_batch.pgc @@ -0,0 +1,53 @@ +/* + * rc_holdsweep_batch.pgc - 보류 만기 스윕 배치 (커서 + XA): 당일 보류(H) 매입의 미해결 예외를 조회해 만기 경보를 rc_alert 에 적재. + * ATMI 클라이언트: tpinit/tpopen 으로 ECPG XA RM 를 열고 tpbegin 으로 ONE global tx + * 를 시작, EXEC SQL 수행 후 tpcommit 이 XA 2PC 를 구동. 사용법: rc_holdsweep_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "rc_holdsweep_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid; + 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 bcur CURSOR FOR + SELECT p.purchase_id FROM purchase p + WHERE p.biz_date = :h_bizdate AND p.status = 'H' + AND EXISTS (SELECT 1 FROM rc_exception e + WHERE e.purchase_id = p.purchase_id AND e.resolved = false) + ORDER BY p.purchase_id; + EXEC SQL OPEN bcur; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH bcur INTO :h_pid; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + EXEC SQL SELECT nextval('rc_alert_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_alert (alert_id, purchase_id, lvl, message, biz_date) + VALUES (:h_id, :h_pid, 'WARN', 'HOLD_EXPIRE', :h_bizdate); + if (sqlca.sqlcode < 0) { fprintf(stderr, "INS FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + rows++; + } + EXEC SQL CLOSE bcur; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> rc_holdsweep_batch COMMIT: bizdate=%s swept=%ld\n", bizdate, rows); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/rc/batch/rc_issrate_batch.pgc b/app/src/rc/batch/rc_issrate_batch.pgc new file mode 100644 index 0000000..b7f1b82 --- /dev/null +++ b/app/src/rc/batch/rc_issrate_batch.pgc @@ -0,0 +1,52 @@ +/* + * rc_issrate_batch.pgc - 발급사별 대사율 집계 배치 (커서 + XA): 발급사마다 대사완료율을 계산해 rc_match_log 에 요약 로그로 남긴다. + * ATMI 클라이언트: tpinit/tpopen 으로 ECPG XA RM 를 열고 tpbegin 으로 ONE global tx + * 를 시작, EXEC SQL 수행 후 tpcommit 이 XA 2PC 를 구동. 사용법: rc_issrate_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "rc_issrate_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_total, h_done; + char h_iss[32]; + 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 bcur CURSOR FOR + SELECT issuer, count(*), + count(*) FILTER (WHERE status IN ('M','S')) + FROM purchase WHERE biz_date = :h_bizdate GROUP BY issuer ORDER BY issuer; + EXEC SQL OPEN bcur; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH bcur INTO :h_iss, :h_total, :h_done; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + EXEC SQL SELECT nextval('rc_match_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_match_log (match_id, purchase_id, match_type, amount, status, biz_date) + VALUES (:h_id, 0, 'ISS_RATE', :h_done, 'MATCHED', :h_bizdate); + if (sqlca.sqlcode < 0) { fprintf(stderr, "INS FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + rows++; + } + EXEC SQL CLOSE bcur; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> rc_issrate_batch COMMIT: bizdate=%s issuers=%ld\n", bizdate, rows); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/rc/batch/rc_manwf_batch.pgc b/app/src/rc/batch/rc_manwf_batch.pgc new file mode 100644 index 0000000..412bcfe --- /dev/null +++ b/app/src/rc/batch/rc_manwf_batch.pgc @@ -0,0 +1,50 @@ +/* + * rc_manwf_batch.pgc - 수작업 대사 워크플로 정리 배치 (커서 + XA): 확정(CONFIRMED)된 수작업 로그의 매입 상태를 대사완료(M)로 갱신. + * ATMI 클라이언트: tpinit/tpopen 으로 ECPG XA RM 를 열고 tpbegin 으로 ONE global tx + * 를 시작, EXEC SQL 수행 후 tpcommit 이 XA 2PC 를 구동. 사용법: rc_manwf_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "rc_manwf_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pid; + 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 bcur CURSOR FOR + SELECT purchase_id FROM rc_match_log + WHERE biz_date = :h_bizdate AND match_type = 'MANUAL' AND status = 'CONFIRMED' + ORDER BY match_id; + EXEC SQL OPEN bcur; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH bcur INTO :h_pid; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + EXEC SQL UPDATE purchase SET status = 'M', updated_at = now() + WHERE purchase_id = :h_pid AND status = 'H'; + if (sqlca.sqlcode < 0) { fprintf(stderr, "UPD FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + rows++; + } + EXEC SQL CLOSE bcur; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> rc_manwf_batch COMMIT: bizdate=%s promoted=%ld\n", bizdate, rows); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/rc/batch/rc_monthrate_batch.pgc b/app/src/rc/batch/rc_monthrate_batch.pgc new file mode 100644 index 0000000..155d259 --- /dev/null +++ b/app/src/rc/batch/rc_monthrate_batch.pgc @@ -0,0 +1,54 @@ +/* + * rc_monthrate_batch.pgc - 월별 대사율 집계 배치 (커서 + XA): 기준 월 소속 매입을 가맹점별로 묶어 대사완료율을 rc_match_log 에 남긴다. + * ATMI 클라이언트: tpinit/tpopen 으로 ECPG XA RM 를 열고 tpbegin 으로 ONE global tx + * 를 시작, EXEC SQL 수행 후 tpcommit 이 XA 2PC 를 구동. 사용법: rc_monthrate_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "rc_monthrate_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_total, h_done; + char h_mid[64]; + 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 bcur CURSOR FOR + SELECT merchant_id, count(*), + count(*) FILTER (WHERE status IN ('M','S')) + FROM purchase + WHERE date_trunc('month', biz_date) = date_trunc('month', :h_bizdate::date) + GROUP BY merchant_id ORDER BY merchant_id; + EXEC SQL OPEN bcur; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH bcur INTO :h_mid, :h_total, :h_done; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + EXEC SQL SELECT nextval('rc_match_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_match_log (match_id, purchase_id, match_type, amount, status, biz_date) + VALUES (:h_id, 0, 'MON_RATE', :h_done, 'MATCHED', :h_bizdate); + if (sqlca.sqlcode < 0) { fprintf(stderr, "INS FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + rows++; + } + EXEC SQL CLOSE bcur; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> rc_monthrate_batch COMMIT: month-of=%s merchants=%ld\n", bizdate, rows); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/rc/batch/rc_overshort_batch.pgc b/app/src/rc/batch/rc_overshort_batch.pgc new file mode 100644 index 0000000..8755129 --- /dev/null +++ b/app/src/rc/batch/rc_overshort_batch.pgc @@ -0,0 +1,54 @@ +/* + * rc_overshort_batch.pgc - 과부족 차이 스캔 배치 (커서 + XA): 매입 순액과 SETTLE 실적 차이가 0 이 아닌 건을 과부족 유형으로 rc_diff 적재. + * ATMI 클라이언트: tpinit/tpopen 으로 ECPG XA RM 를 열고 tpbegin 으로 ONE global tx + * 를 시작, EXEC SQL 수행 후 tpcommit 이 XA 2PC 를 구동. 사용법: rc_overshort_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "rc_overshort_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid, h_exp, h_act, h_diff; + 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 bcur CURSOR FOR + SELECT p.purchase_id, p.net, + coalesce((SELECT sum(l.amount) FROM ledger l + WHERE l.purchase_id = p.purchase_id AND l.entry_type = 'SETTLE'),0) + FROM purchase p WHERE p.biz_date = :h_bizdate ORDER BY p.purchase_id; + EXEC SQL OPEN bcur; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH bcur INTO :h_pid, :h_exp, :h_act; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + h_diff = h_exp - h_act; + if (h_diff == 0) continue; + EXEC SQL SELECT nextval('rc_diff_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_diff (diff_id, purchase_id, expected_amt, actual_amt, diff_amt, diff_kind, biz_date) + VALUES (:h_id, :h_pid, :h_exp, :h_act, :h_diff, (CASE WHEN :h_diff > 0 THEN 'SHORT' ELSE 'OVER' END), :h_bizdate); + if (sqlca.sqlcode < 0) { fprintf(stderr, "INS FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + rows++; + } + EXEC SQL CLOSE bcur; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> rc_overshort_batch COMMIT: bizdate=%s overshort=%ld\n", bizdate, rows); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/rc/batch/rc_refund_batch.pgc b/app/src/rc/batch/rc_refund_batch.pgc new file mode 100644 index 0000000..c495033 --- /dev/null +++ b/app/src/rc/batch/rc_refund_batch.pgc @@ -0,0 +1,51 @@ +/* + * rc_refund_batch.pgc - 환불분 대사 배치 (커서 + XA): 당일 REFUND 취소 건을 순회하며 환불 예외를 rc_exception 에 등록. + * ATMI 클라이언트: tpinit/tpopen 으로 ECPG XA RM 를 열고 tpbegin 으로 ONE global tx + * 를 시작, EXEC SQL 수행 후 tpcommit 이 XA 2PC 를 구동. 사용법: rc_refund_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "rc_refund_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid; + 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 bcur CURSOR FOR + SELECT purchase_id FROM ac_cancel + WHERE biz_date = :h_bizdate AND cancel_type = 'REFUND' + ORDER BY cancel_id; + EXEC SQL OPEN bcur; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH bcur INTO :h_pid; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + EXEC SQL SELECT nextval('rc_exc_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_exception (exc_id, purchase_id, reason, biz_date) + VALUES (:h_id, :h_pid, 'REFUND_RECON', :h_bizdate); + if (sqlca.sqlcode < 0) { fprintf(stderr, "INS FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + rows++; + } + EXEC SQL CLOSE bcur; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> rc_refund_batch COMMIT: bizdate=%s refunds=%ld\n", bizdate, rows); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/rc/batch/rc_rounding_batch.pgc b/app/src/rc/batch/rc_rounding_batch.pgc new file mode 100644 index 0000000..8ed76c9 --- /dev/null +++ b/app/src/rc/batch/rc_rounding_batch.pgc @@ -0,0 +1,51 @@ +/* + * rc_rounding_batch.pgc - 반올림 차이 보정 배치 (커서 + XA): 절대차 1 이하 단수 차이 rc_diff 를 ac_fee_adj 로 상계 보정. + * ATMI 클라이언트: tpinit/tpopen 으로 ECPG XA RM 를 열고 tpbegin 으로 ONE global tx + * 를 시작, EXEC SQL 수행 후 tpcommit 이 XA 2PC 를 구동. 사용법: rc_rounding_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "rc_rounding_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid, h_diff; + 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 bcur CURSOR FOR + SELECT purchase_id, diff_amt FROM rc_diff + WHERE biz_date = :h_bizdate AND abs(diff_amt) <= 1 AND diff_amt <> 0 + ORDER BY diff_id; + EXEC SQL OPEN bcur; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH bcur INTO :h_pid, :h_diff; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + EXEC SQL SELECT nextval('ac_feeadj_seq') INTO :h_id; + EXEC SQL INSERT INTO ac_fee_adj (adj_id, purchase_id, delta, reason, biz_date) + VALUES (:h_id, :h_pid, :h_diff, 'ROUND_FIX', :h_bizdate); + if (sqlca.sqlcode < 0) { fprintf(stderr, "INS FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + rows++; + } + EXEC SQL CLOSE bcur; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> rc_rounding_batch COMMIT: bizdate=%s fixed=%ld\n", bizdate, rows); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/rc/batch/rc_weekrate_batch.pgc b/app/src/rc/batch/rc_weekrate_batch.pgc new file mode 100644 index 0000000..cc04630 --- /dev/null +++ b/app/src/rc/batch/rc_weekrate_batch.pgc @@ -0,0 +1,56 @@ +/* + * rc_weekrate_batch.pgc - 주별 대사율 집계 배치 (커서 + XA): 최근 7영업일을 일자별로 순회하며 대사완료율을 rc_summary 에 upsert. + * ATMI 클라이언트: tpinit/tpopen 으로 ECPG XA RM 를 열고 tpbegin 으로 ONE global tx + * 를 시작, EXEC SQL 수행 후 tpcommit 이 XA 2PC 를 구동. 사용법: rc_weekrate_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "rc_weekrate_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + long rows = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_total, h_done, h_un; + char h_day[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 DECLARE bcur CURSOR FOR + SELECT to_char(d, 'YYYY-MM-DD'), + (SELECT count(*) FROM purchase p WHERE p.biz_date = d), + (SELECT count(*) FROM purchase p WHERE p.biz_date = d AND p.status IN ('M','S')) + FROM generate_series(:h_bizdate::date - 6, :h_bizdate::date, interval '1 day') d + ORDER BY d; + EXEC SQL OPEN bcur; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + + for (;;) { + EXEC SQL FETCH bcur INTO :h_day, :h_total, :h_done; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + h_un = h_total - h_done; + EXEC SQL INSERT INTO rc_summary (biz_date, matched_cnt, unmatched_cnt, gross_amount, updated_at) + VALUES (:h_day, :h_done, :h_un, 0, now()) + ON CONFLICT (biz_date) DO UPDATE SET matched_cnt = EXCLUDED.matched_cnt, + unmatched_cnt = EXCLUDED.unmatched_cnt, updated_at = now(); + if (sqlca.sqlcode < 0) { fprintf(stderr, "UPS FAIL [%d]\n", sqlca.sqlcode); EXEC SQL CLOSE bcur; tpabort(0); return 1; } + rows++; + } + EXEC SQL CLOSE bcur; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> rc_weekrate_batch COMMIT: asof=%s days=%ld\n", bizdate, rows); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/rc/dbio/rc_backdate_batch.h b/app/src/rc/dbio/rc_backdate_batch.h new file mode 100644 index 0000000..858871b --- /dev/null +++ b/app/src/rc/dbio/rc_backdate_batch.h @@ -0,0 +1,14 @@ +/* + * rc_backdate_batch.h - 소급 대사 재처리 배치 (커서 + XA): 원장 기표일이 매입일보다 늦은 지연 건을 소급 대사 로그로 재적재. (배치 카피북 / copybook). + * 배치 실행에 필요한 표준 헤더와 rc dbio 프로토타입을 묶어 제공한다. + */ +#ifndef RC_BACKDATE_BATCH_H +#define RC_BACKDATE_BATCH_H + +#include +#include +#include +#include +#include "rc_dbio.h" + +#endif /* RC_BACKDATE_BATCH_H */ diff --git a/app/src/rc/dbio/rc_chanrate_batch.h b/app/src/rc/dbio/rc_chanrate_batch.h new file mode 100644 index 0000000..7c7a788 --- /dev/null +++ b/app/src/rc/dbio/rc_chanrate_batch.h @@ -0,0 +1,14 @@ +/* + * rc_chanrate_batch.h - 채널별 대사율 집계 배치 (커서 + XA): 채널마다 당일 대사완료율을 계산해 rc_alert 에 INFO 로 적재. (배치 카피북 / copybook). + * 배치 실행에 필요한 표준 헤더와 rc dbio 프로토타입을 묶어 제공한다. + */ +#ifndef RC_CHANRATE_BATCH_H +#define RC_CHANRATE_BATCH_H + +#include +#include +#include +#include +#include "rc_dbio.h" + +#endif /* RC_CHANRATE_BATCH_H */ diff --git a/app/src/rc/dbio/rc_fxdiff_batch.h b/app/src/rc/dbio/rc_fxdiff_batch.h new file mode 100644 index 0000000..f4c7d80 --- /dev/null +++ b/app/src/rc/dbio/rc_fxdiff_batch.h @@ -0,0 +1,14 @@ +/* + * rc_fxdiff_batch.h - 외화 환산 차이 배치 (커서 + XA): ac_fx 원화환산과 원통화*환율 이론치의 차이가 있는 건을 rc_diff 에 기록. (배치 카피북 / copybook). + * 배치 실행에 필요한 표준 헤더와 rc dbio 프로토타입을 묶어 제공한다. + */ +#ifndef RC_FXDIFF_BATCH_H +#define RC_FXDIFF_BATCH_H + +#include +#include +#include +#include +#include "rc_dbio.h" + +#endif /* RC_FXDIFF_BATCH_H */ diff --git a/app/src/rc/dbio/rc_holdsweep_batch.h b/app/src/rc/dbio/rc_holdsweep_batch.h new file mode 100644 index 0000000..cde1a44 --- /dev/null +++ b/app/src/rc/dbio/rc_holdsweep_batch.h @@ -0,0 +1,14 @@ +/* + * rc_holdsweep_batch.h - 보류 만기 스윕 배치 (커서 + XA): 당일 보류(H) 매입의 미해결 예외를 조회해 만기 경보를 rc_alert 에 적재. (배치 카피북 / copybook). + * 배치 실행에 필요한 표준 헤더와 rc dbio 프로토타입을 묶어 제공한다. + */ +#ifndef RC_HOLDSWEEP_BATCH_H +#define RC_HOLDSWEEP_BATCH_H + +#include +#include +#include +#include +#include "rc_dbio.h" + +#endif /* RC_HOLDSWEEP_BATCH_H */ diff --git a/app/src/rc/dbio/rc_issrate_batch.h b/app/src/rc/dbio/rc_issrate_batch.h new file mode 100644 index 0000000..36d630f --- /dev/null +++ b/app/src/rc/dbio/rc_issrate_batch.h @@ -0,0 +1,14 @@ +/* + * rc_issrate_batch.h - 발급사별 대사율 집계 배치 (커서 + XA): 발급사마다 대사완료율을 계산해 rc_match_log 에 요약 로그로 남긴다. (배치 카피북 / copybook). + * 배치 실행에 필요한 표준 헤더와 rc dbio 프로토타입을 묶어 제공한다. + */ +#ifndef RC_ISSRATE_BATCH_H +#define RC_ISSRATE_BATCH_H + +#include +#include +#include +#include +#include "rc_dbio.h" + +#endif /* RC_ISSRATE_BATCH_H */ diff --git a/app/src/rc/dbio/rc_manwf_batch.h b/app/src/rc/dbio/rc_manwf_batch.h new file mode 100644 index 0000000..fbcce92 --- /dev/null +++ b/app/src/rc/dbio/rc_manwf_batch.h @@ -0,0 +1,14 @@ +/* + * rc_manwf_batch.h - 수작업 대사 워크플로 정리 배치 (커서 + XA): 확정(CONFIRMED)된 수작업 로그의 매입 상태를 대사완료(M)로 갱신. (배치 카피북 / copybook). + * 배치 실행에 필요한 표준 헤더와 rc dbio 프로토타입을 묶어 제공한다. + */ +#ifndef RC_MANWF_BATCH_H +#define RC_MANWF_BATCH_H + +#include +#include +#include +#include +#include "rc_dbio.h" + +#endif /* RC_MANWF_BATCH_H */ diff --git a/app/src/rc/dbio/rc_monthrate_batch.h b/app/src/rc/dbio/rc_monthrate_batch.h new file mode 100644 index 0000000..21f6dde --- /dev/null +++ b/app/src/rc/dbio/rc_monthrate_batch.h @@ -0,0 +1,14 @@ +/* + * rc_monthrate_batch.h - 월별 대사율 집계 배치 (커서 + XA): 기준 월 소속 매입을 가맹점별로 묶어 대사완료율을 rc_match_log 에 남긴다. (배치 카피북 / copybook). + * 배치 실행에 필요한 표준 헤더와 rc dbio 프로토타입을 묶어 제공한다. + */ +#ifndef RC_MONTHRATE_BATCH_H +#define RC_MONTHRATE_BATCH_H + +#include +#include +#include +#include +#include "rc_dbio.h" + +#endif /* RC_MONTHRATE_BATCH_H */ diff --git a/app/src/rc/dbio/rc_overshort_batch.h b/app/src/rc/dbio/rc_overshort_batch.h new file mode 100644 index 0000000..dd2b62b --- /dev/null +++ b/app/src/rc/dbio/rc_overshort_batch.h @@ -0,0 +1,14 @@ +/* + * rc_overshort_batch.h - 과부족 차이 스캔 배치 (커서 + XA): 매입 순액과 SETTLE 실적 차이가 0 이 아닌 건을 과부족 유형으로 rc_diff 적재. (배치 카피북 / copybook). + * 배치 실행에 필요한 표준 헤더와 rc dbio 프로토타입을 묶어 제공한다. + */ +#ifndef RC_OVERSHORT_BATCH_H +#define RC_OVERSHORT_BATCH_H + +#include +#include +#include +#include +#include "rc_dbio.h" + +#endif /* RC_OVERSHORT_BATCH_H */ diff --git a/app/src/rc/dbio/rc_refund_batch.h b/app/src/rc/dbio/rc_refund_batch.h new file mode 100644 index 0000000..356401f --- /dev/null +++ b/app/src/rc/dbio/rc_refund_batch.h @@ -0,0 +1,14 @@ +/* + * rc_refund_batch.h - 환불분 대사 배치 (커서 + XA): 당일 REFUND 취소 건을 순회하며 환불 예외를 rc_exception 에 등록. (배치 카피북 / copybook). + * 배치 실행에 필요한 표준 헤더와 rc dbio 프로토타입을 묶어 제공한다. + */ +#ifndef RC_REFUND_BATCH_H +#define RC_REFUND_BATCH_H + +#include +#include +#include +#include +#include "rc_dbio.h" + +#endif /* RC_REFUND_BATCH_H */ diff --git a/app/src/rc/dbio/rc_rounding_batch.h b/app/src/rc/dbio/rc_rounding_batch.h new file mode 100644 index 0000000..3861f20 --- /dev/null +++ b/app/src/rc/dbio/rc_rounding_batch.h @@ -0,0 +1,14 @@ +/* + * rc_rounding_batch.h - 반올림 차이 보정 배치 (커서 + XA): 절대차 1 이하 단수 차이 rc_diff 를 ac_fee_adj 로 상계 보정. (배치 카피북 / copybook). + * 배치 실행에 필요한 표준 헤더와 rc dbio 프로토타입을 묶어 제공한다. + */ +#ifndef RC_ROUNDING_BATCH_H +#define RC_ROUNDING_BATCH_H + +#include +#include +#include +#include +#include "rc_dbio.h" + +#endif /* RC_ROUNDING_BATCH_H */ diff --git a/app/src/rc/dbio/rc_weekrate_batch.h b/app/src/rc/dbio/rc_weekrate_batch.h new file mode 100644 index 0000000..eb4d74d --- /dev/null +++ b/app/src/rc/dbio/rc_weekrate_batch.h @@ -0,0 +1,14 @@ +/* + * rc_weekrate_batch.h - 주별 대사율 집계 배치 (커서 + XA): 최근 7영업일을 일자별로 순회하며 대사완료율을 rc_summary 에 upsert. (배치 카피북 / copybook). + * 배치 실행에 필요한 표준 헤더와 rc dbio 프로토타입을 묶어 제공한다. + */ +#ifndef RC_WEEKRATE_BATCH_H +#define RC_WEEKRATE_BATCH_H + +#include +#include +#include +#include +#include "rc_dbio.h" + +#endif /* RC_WEEKRATE_BATCH_H */ diff --git a/app/src/rc/rc_svr.pgc b/app/src/rc/rc_svr.pgc index 831aba7..e159fc8 100644 --- a/app/src/rc/rc_svr.pgc +++ b/app/src/rc/rc_svr.pgc @@ -2,14 +2,14 @@ * rc_svr.pgc - rc (대사) MODULE SERVER (thin dispatcher). * * The legacy "one module server, many services" pattern: this binary owns NO - * business logic. Each of rc's 75 online services now lives in its own file + * business logic. Each of rc's 133 online services now lives in its own file * (app/src/rc/svc/.pgc) with its own copybook header * (app/src/rc/svc/.h); those objects are archived into librcsvc.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. * - * rc_svr: ONE binary that tpadvertise()s ALL 75 online rc(대사) services. + * rc_svr: ONE binary that tpadvertise()s ALL 133 online rc(대사) services. * RECONCILE keeps the proven chain: distinct XA branch, own recon ledger row, * then tpcall()s SETTLE inside the SAME global tx. */ @@ -93,6 +93,64 @@ #include "RC_CLOSEDAY.h" #include "RC_REOPEN.h" #include "RC_SNAPSHOT.h" +#include "RC_APPRMATCH.h" +#include "RC_ACQMATCH.h" +#include "RC_DEPPAY.h" +#include "RC_PAYLEDGER.h" +#include "RC_LEDGERBAL.h" +#include "RC_APPRGAP.h" +#include "RC_DEPGAP.h" +#include "RC_PAYGAP.h" +#include "RC_CHANRECON.h" +#include "RC_ISSRECON.h" +#include "RC_BRANDRECON.h" +#include "RC_MGRPRECON.h" +#include "RC_POSRECON.h" +#include "RC_EDCRECON.h" +#include "RC_CHANDIFF.h" +#include "RC_ISSDIFF.h" +#include "RC_BRANDDIFF.h" +#include "RC_MGRPDIFF.h" +#include "RC_OVERAMT.h" +#include "RC_UNDERAMT.h" +#include "RC_ROUNDDIFF.h" +#include "RC_TIMEDIFF.h" +#include "RC_SIGNDIFF.h" +#include "RC_KINDSPLIT.h" +#include "RC_DIFFRANK.h" +#include "RC_MANREQ.h" +#include "RC_MANOK.h" +#include "RC_MANNG.h" +#include "RC_MANCANCEL.h" +#include "RC_MANLIST.h" +#include "RC_HOLDSET.h" +#include "RC_HOLDCLR.h" +#include "RC_CARRYIN.h" +#include "RC_CARRYFWD.h" +#include "RC_BACKDATE.h" +#include "RC_HOLDLIST.h" +#include "RC_FIXROUND.h" +#include "RC_FIXFEE.h" +#include "RC_FIXFX.h" +#include "RC_FIXDUP.h" +#include "RC_FIXSIGN.h" +#include "RC_REDIFF.h" +#include "RC_RELATE.h" +#include "RC_RECORR.h" +#include "RC_REFULL.h" +#include "RC_PARTDEP.h" +#include "RC_CANCMATCH.h" +#include "RC_REFUNDREC.h" +#include "RC_FEEBRK.h" +#include "RC_FXDIFF.h" +#include "RC_RATEDAY.h" +#include "RC_RATEWEEK.h" +#include "RC_RATEMON.h" +#include "RC_RATECHAN.h" +#include "RC_RATEMERCH.h" +#include "RC_EXCNOTIFY.h" +#include "RC_AUDITADD.h" +#include "RC_TRAILQRY.h" /* ----- advertise table: {service name, function} ---------------------- */ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { @@ -134,6 +192,35 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"RC_LIMITCHK", RC_LIMITCHK}, {"RC_ESCALATE", RC_ESCALATE}, {"RC_CLOSEDAY", RC_CLOSEDAY}, {"RC_REOPEN", RC_REOPEN}, {"RC_SNAPSHOT", RC_SNAPSHOT}, + {"RC_APPRMATCH", RC_APPRMATCH}, {"RC_ACQMATCH", RC_ACQMATCH}, + {"RC_DEPPAY", RC_DEPPAY}, {"RC_PAYLEDGER", RC_PAYLEDGER}, + {"RC_LEDGERBAL", RC_LEDGERBAL}, {"RC_APPRGAP", RC_APPRGAP}, + {"RC_DEPGAP", RC_DEPGAP}, {"RC_PAYGAP", RC_PAYGAP}, + {"RC_CHANRECON", RC_CHANRECON}, {"RC_ISSRECON", RC_ISSRECON}, + {"RC_BRANDRECON", RC_BRANDRECON}, {"RC_MGRPRECON", RC_MGRPRECON}, + {"RC_POSRECON", RC_POSRECON}, {"RC_EDCRECON", RC_EDCRECON}, + {"RC_CHANDIFF", RC_CHANDIFF}, {"RC_ISSDIFF", RC_ISSDIFF}, + {"RC_BRANDDIFF", RC_BRANDDIFF}, {"RC_MGRPDIFF", RC_MGRPDIFF}, + {"RC_OVERAMT", RC_OVERAMT}, {"RC_UNDERAMT", RC_UNDERAMT}, + {"RC_ROUNDDIFF", RC_ROUNDDIFF}, {"RC_TIMEDIFF", RC_TIMEDIFF}, + {"RC_SIGNDIFF", RC_SIGNDIFF}, {"RC_KINDSPLIT", RC_KINDSPLIT}, + {"RC_DIFFRANK", RC_DIFFRANK}, {"RC_MANREQ", RC_MANREQ}, + {"RC_MANOK", RC_MANOK}, {"RC_MANNG", RC_MANNG}, + {"RC_MANCANCEL", RC_MANCANCEL}, {"RC_MANLIST", RC_MANLIST}, + {"RC_HOLDSET", RC_HOLDSET}, {"RC_HOLDCLR", RC_HOLDCLR}, + {"RC_CARRYIN", RC_CARRYIN}, {"RC_CARRYFWD", RC_CARRYFWD}, + {"RC_BACKDATE", RC_BACKDATE}, {"RC_HOLDLIST", RC_HOLDLIST}, + {"RC_FIXROUND", RC_FIXROUND}, {"RC_FIXFEE", RC_FIXFEE}, + {"RC_FIXFX", RC_FIXFX}, {"RC_FIXDUP", RC_FIXDUP}, + {"RC_FIXSIGN", RC_FIXSIGN}, {"RC_REDIFF", RC_REDIFF}, + {"RC_RELATE", RC_RELATE}, {"RC_RECORR", RC_RECORR}, + {"RC_REFULL", RC_REFULL}, {"RC_PARTDEP", RC_PARTDEP}, + {"RC_CANCMATCH", RC_CANCMATCH}, {"RC_REFUNDREC", RC_REFUNDREC}, + {"RC_FEEBRK", RC_FEEBRK}, {"RC_FXDIFF", RC_FXDIFF}, + {"RC_RATEDAY", RC_RATEDAY}, {"RC_RATEWEEK", RC_RATEWEEK}, + {"RC_RATEMON", RC_RATEMON}, {"RC_RATECHAN", RC_RATECHAN}, + {"RC_RATEMERCH", RC_RATEMERCH}, {"RC_EXCNOTIFY", RC_EXCNOTIFY}, + {"RC_AUDITADD", RC_AUDITADD}, {"RC_TRAILQRY", RC_TRAILQRY}, {NULL, NULL} }; diff --git a/app/src/rc/svc/RC_ACQMATCH.h b/app/src/rc/svc/RC_ACQMATCH.h new file mode 100644 index 0000000..6f3dd51 --- /dev/null +++ b/app/src/rc/svc/RC_ACQMATCH.h @@ -0,0 +1,18 @@ +/* + * RC_ACQMATCH.h - rc 서비스 RC_ACQMATCH 카피북 (copybook / record header). + * 매입-입금 축 대사: 당일 매입 순액 합계와 원장 RECON 입금 합계를 비교해 차액을 산출. + */ +#ifndef RC_ACQMATCH_H +#define RC_ACQMATCH_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_acqmatch_rec_t; + +void RC_ACQMATCH(TPSVCINFO *p); + +#endif /* RC_ACQMATCH_H */ diff --git a/app/src/rc/svc/RC_ACQMATCH.pgc b/app/src/rc/svc/RC_ACQMATCH.pgc new file mode 100644 index 0000000..bd169d4 --- /dev/null +++ b/app/src/rc/svc/RC_ACQMATCH.pgc @@ -0,0 +1,33 @@ +/* + * RC_ACQMATCH.pgc - rc 모듈 서비스 RC_ACQMATCH (one service = one file). + * 매입-입금 축 대사: 당일 매입 순액 합계와 원장 RECON 입금 합계를 비교해 차액을 산출. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_ACQMATCH.h" + +/* 77. RC_ACQMATCH - 매입-입금 축 대사: 당일 매입 순액 합계와 원장 RECON 입금 합계를 비교해 차액을 산출. */ +void RC_ACQMATCH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_net, h_recon; + 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_net FROM purchase + WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_recon FROM ledger + WHERE biz_date = :h_bizdate AND entry_type = 'RECON'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_net); + setl(b, T_ARG2, h_recon); + setl(b, T_DELTA, h_net - h_recon); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_APPRGAP.h b/app/src/rc/svc/RC_APPRGAP.h new file mode 100644 index 0000000..21d42d1 --- /dev/null +++ b/app/src/rc/svc/RC_APPRGAP.h @@ -0,0 +1,18 @@ +/* + * RC_APPRGAP.h - rc 서비스 RC_APPRGAP 카피북 (copybook / record header). + * 승인 누락 감지: 당일 매입 중 승인 원장이 없는 건을 예외로 등록하고 누락 건수를 반환. + */ +#ifndef RC_APPRGAP_H +#define RC_APPRGAP_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_apprgap_rec_t; + +void RC_APPRGAP(TPSVCINFO *p); + +#endif /* RC_APPRGAP_H */ diff --git a/app/src/rc/svc/RC_APPRGAP.pgc b/app/src/rc/svc/RC_APPRGAP.pgc new file mode 100644 index 0000000..40e071b --- /dev/null +++ b/app/src/rc/svc/RC_APPRGAP.pgc @@ -0,0 +1,42 @@ +/* + * RC_APPRGAP.pgc - rc 모듈 서비스 RC_APPRGAP (one service = one file). + * 승인 누락 감지: 당일 매입 중 승인 원장이 없는 건을 예외로 등록하고 누락 건수를 반환. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_APPRGAP.h" + +/* 81. RC_APPRGAP - 승인 누락 감지: 당일 매입 중 승인 원장이 없는 건을 예외로 등록하고 누락 건수를 반환. */ +void RC_APPRGAP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid, h_cnt = 0; + 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 cur_appr CURSOR FOR + SELECT p.purchase_id FROM purchase p + WHERE p.biz_date = :h_bizdate + AND NOT EXISTS (SELECT 1 FROM approval a WHERE a.purchase_id = p.purchase_id); + EXEC SQL OPEN cur_appr; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_appr INTO :h_pid; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_appr; FAIL(b); } + EXEC SQL SELECT nextval('rc_exc_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_exception (exc_id, purchase_id, reason, biz_date) + VALUES (:h_id, :h_pid, 'APPR_GAP', :h_bizdate); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_appr; FAIL(b); } + h_cnt++; + } + EXEC SQL CLOSE cur_appr; + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_APPRMATCH.h b/app/src/rc/svc/RC_APPRMATCH.h new file mode 100644 index 0000000..bd6d3fc --- /dev/null +++ b/app/src/rc/svc/RC_APPRMATCH.h @@ -0,0 +1,18 @@ +/* + * RC_APPRMATCH.h - rc 서비스 RC_APPRMATCH 카피북 (copybook / record header). + * 승인-매입 축 대사: 당일 매입 중 승인건과 매칭된 건수와 승인 누락 건수를 분리 집계. + */ +#ifndef RC_APPRMATCH_H +#define RC_APPRMATCH_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_apprmatch_rec_t; + +void RC_APPRMATCH(TPSVCINFO *p); + +#endif /* RC_APPRMATCH_H */ diff --git a/app/src/rc/svc/RC_APPRMATCH.pgc b/app/src/rc/svc/RC_APPRMATCH.pgc new file mode 100644 index 0000000..557e4fe --- /dev/null +++ b/app/src/rc/svc/RC_APPRMATCH.pgc @@ -0,0 +1,35 @@ +/* + * RC_APPRMATCH.pgc - rc 모듈 서비스 RC_APPRMATCH (one service = one file). + * 승인-매입 축 대사: 당일 매입 중 승인건과 매칭된 건수와 승인 누락 건수를 분리 집계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_APPRMATCH.h" + +/* 76. RC_APPRMATCH - 승인-매입 축 대사: 당일 매입 중 승인건과 매칭된 건수와 승인 누락 건수를 분리 집계. */ +void RC_APPRMATCH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_matched, h_missing; + 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(*) INTO :h_matched FROM purchase p + JOIN approval a ON a.purchase_id = p.purchase_id + WHERE p.biz_date = :h_bizdate AND a.approved = true; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_missing FROM purchase p + WHERE p.biz_date = :h_bizdate + AND NOT EXISTS (SELECT 1 FROM approval a WHERE a.purchase_id = p.purchase_id); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_matched); + setl(b, T_ARG2, h_missing); + setl(b, T_COUNT, h_matched + h_missing); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_AUDITADD.h b/app/src/rc/svc/RC_AUDITADD.h new file mode 100644 index 0000000..9d52f2d --- /dev/null +++ b/app/src/rc/svc/RC_AUDITADD.h @@ -0,0 +1,18 @@ +/* + * RC_AUDITADD.h - rc 서비스 RC_AUDITADD 카피북 (copybook / record header). + * 대사 감사추적 기록: 지정 매입의 대사 처리 이력을 AUDIT 유형 로그로 감사 추적에 적재. + */ +#ifndef RC_AUDITADD_H +#define RC_AUDITADD_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_auditadd_rec_t; + +void RC_AUDITADD(TPSVCINFO *p); + +#endif /* RC_AUDITADD_H */ diff --git a/app/src/rc/svc/RC_AUDITADD.pgc b/app/src/rc/svc/RC_AUDITADD.pgc new file mode 100644 index 0000000..970989e --- /dev/null +++ b/app/src/rc/svc/RC_AUDITADD.pgc @@ -0,0 +1,30 @@ +/* + * RC_AUDITADD.pgc - rc 모듈 서비스 RC_AUDITADD (one service = one file). + * 대사 감사추적 기록: 지정 매입의 대사 처리 이력을 AUDIT 유형 로그로 감사 추적에 적재. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_AUDITADD.h" + +/* 132. RC_AUDITADD - 대사 감사추적 기록: 지정 매입의 대사 처리 이력을 AUDIT 유형 로그로 감사 추적에 적재. */ +void RC_AUDITADD(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid; + 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; + + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL SELECT nextval('rc_match_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_match_log (match_id, purchase_id, match_type, amount, status, biz_date) + VALUES (:h_id, :h_pid, 'AUDIT', 0, 'CONFIRMED', :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ID1, h_id); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_BACKDATE.h b/app/src/rc/svc/RC_BACKDATE.h new file mode 100644 index 0000000..9e6ea1c --- /dev/null +++ b/app/src/rc/svc/RC_BACKDATE.h @@ -0,0 +1,18 @@ +/* + * RC_BACKDATE.h - rc 서비스 RC_BACKDATE 카피북 (copybook / record header). + * 소급 대사: 지정 매입을 과거 영업일자로 소급하여 대사 로그를 MATCHED 로 기록. + */ +#ifndef RC_BACKDATE_H +#define RC_BACKDATE_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_backdate_rec_t; + +void RC_BACKDATE(TPSVCINFO *p); + +#endif /* RC_BACKDATE_H */ diff --git a/app/src/rc/svc/RC_BACKDATE.pgc b/app/src/rc/svc/RC_BACKDATE.pgc new file mode 100644 index 0000000..9412f08 --- /dev/null +++ b/app/src/rc/svc/RC_BACKDATE.pgc @@ -0,0 +1,34 @@ +/* + * RC_BACKDATE.pgc - rc 모듈 서비스 RC_BACKDATE (one service = one file). + * 소급 대사: 지정 매입을 과거 영업일자로 소급하여 대사 로그를 MATCHED 로 기록. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_BACKDATE.h" + +/* 110. RC_BACKDATE - 소급 대사: 지정 매입을 과거 영업일자로 소급하여 대사 로그를 MATCHED 로 기록. */ +void RC_BACKDATE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid, h_amt; char h_asof[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; + + h_pid = getl(b, T_PURCHASE_ID); + { char c[16]; gets_(b, T_STR1, c, sizeof(c)); + strncpy(h_asof, c, sizeof(h_asof)-1); h_asof[sizeof(h_asof)-1] = 0; } + EXEC SQL SELECT coalesce(net,0) INTO :h_amt FROM purchase WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0 && sqlca.sqlcode != 100) FAIL(b); + EXEC SQL SELECT nextval('rc_match_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_match_log (match_id, purchase_id, match_type, amount, status, biz_date) + VALUES (:h_id, :h_pid, 'BACKDATE', :h_amt, 'MATCHED', :h_asof); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ID1, h_id); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_BRANDDIFF.h b/app/src/rc/svc/RC_BRANDDIFF.h new file mode 100644 index 0000000..58532fa --- /dev/null +++ b/app/src/rc/svc/RC_BRANDDIFF.h @@ -0,0 +1,18 @@ +/* + * RC_BRANDDIFF.h - rc 서비스 RC_BRANDDIFF 카피북 (copybook / record header). + * 브랜드별 차이 분석: 업종(category) 단위 매입 총액과 수수료 합계 비율을 점검. + */ +#ifndef RC_BRANDDIFF_H +#define RC_BRANDDIFF_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_branddiff_rec_t; + +void RC_BRANDDIFF(TPSVCINFO *p); + +#endif /* RC_BRANDDIFF_H */ diff --git a/app/src/rc/svc/RC_BRANDDIFF.pgc b/app/src/rc/svc/RC_BRANDDIFF.pgc new file mode 100644 index 0000000..b48e307 --- /dev/null +++ b/app/src/rc/svc/RC_BRANDDIFF.pgc @@ -0,0 +1,32 @@ +/* + * RC_BRANDDIFF.pgc - rc 모듈 서비스 RC_BRANDDIFF (one service = one file). + * 브랜드별 차이 분석: 업종(category) 단위 매입 총액과 수수료 합계 비율을 점검. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_BRANDDIFF.h" + +/* 92. RC_BRANDDIFF - 브랜드별 차이 분석: 업종(category) 단위 매입 총액과 수수료 합계 비율을 점검. */ +void RC_BRANDDIFF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_gross, h_fee; char h_cat[32]; + 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; + + { char c[32]; gets_(b, T_CATEGORY, c, sizeof(c)); + strncpy(h_cat, c, sizeof(h_cat)-1); h_cat[sizeof(h_cat)-1] = 0; } + EXEC SQL SELECT coalesce(sum(amount),0), coalesce(sum(fee),0) INTO :h_gross, :h_fee + FROM purchase WHERE biz_date = :h_bizdate AND category = :h_cat; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_GROSS, h_gross); + setl(b, T_FEE, h_fee); + setl(b, T_RC, (h_gross > 0) ? (h_fee * 10000 / h_gross) : 0); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_BRANDRECON.h b/app/src/rc/svc/RC_BRANDRECON.h new file mode 100644 index 0000000..a50e449 --- /dev/null +++ b/app/src/rc/svc/RC_BRANDRECON.h @@ -0,0 +1,18 @@ +/* + * RC_BRANDRECON.h - rc 서비스 RC_BRANDRECON 카피북 (copybook / record header). + * 브랜드별 대사: 업종/브랜드(category) 단위로 당일 매입 총액과 건수를 집계. + */ +#ifndef RC_BRANDRECON_H +#define RC_BRANDRECON_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_brandrecon_rec_t; + +void RC_BRANDRECON(TPSVCINFO *p); + +#endif /* RC_BRANDRECON_H */ diff --git a/app/src/rc/svc/RC_BRANDRECON.pgc b/app/src/rc/svc/RC_BRANDRECON.pgc new file mode 100644 index 0000000..4fd2750 --- /dev/null +++ b/app/src/rc/svc/RC_BRANDRECON.pgc @@ -0,0 +1,31 @@ +/* + * RC_BRANDRECON.pgc - rc 모듈 서비스 RC_BRANDRECON (one service = one file). + * 브랜드별 대사: 업종/브랜드(category) 단위로 당일 매입 총액과 건수를 집계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_BRANDRECON.h" + +/* 86. RC_BRANDRECON - 브랜드별 대사: 업종/브랜드(category) 단위로 당일 매입 총액과 건수를 집계. */ +void RC_BRANDRECON(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_gross, h_cnt; char h_cat[32]; + 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; + + { char c[32]; gets_(b, T_CATEGORY, c, sizeof(c)); + strncpy(h_cat, c, sizeof(h_cat)-1); h_cat[sizeof(h_cat)-1] = 0; } + EXEC SQL SELECT coalesce(sum(amount),0), count(*) INTO :h_gross, :h_cnt + FROM purchase WHERE biz_date = :h_bizdate AND category = :h_cat; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_GROSS, h_gross); + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_CANCMATCH.h b/app/src/rc/svc/RC_CANCMATCH.h new file mode 100644 index 0000000..54061da --- /dev/null +++ b/app/src/rc/svc/RC_CANCMATCH.h @@ -0,0 +1,18 @@ +/* + * RC_CANCMATCH.h - rc 서비스 RC_CANCMATCH 카피북 (copybook / record header). + * 취소분 대사: 당일 취소(ac_cancel) 금액 합계와 취소 건수를 유형 무관하게 집계. + */ +#ifndef RC_CANCMATCH_H +#define RC_CANCMATCH_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_cancmatch_rec_t; + +void RC_CANCMATCH(TPSVCINFO *p); + +#endif /* RC_CANCMATCH_H */ diff --git a/app/src/rc/svc/RC_CANCMATCH.pgc b/app/src/rc/svc/RC_CANCMATCH.pgc new file mode 100644 index 0000000..364f677 --- /dev/null +++ b/app/src/rc/svc/RC_CANCMATCH.pgc @@ -0,0 +1,29 @@ +/* + * RC_CANCMATCH.pgc - rc 모듈 서비스 RC_CANCMATCH (one service = one file). + * 취소분 대사: 당일 취소(ac_cancel) 금액 합계와 취소 건수를 유형 무관하게 집계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_CANCMATCH.h" + +/* 122. RC_CANCMATCH - 취소분 대사: 당일 취소(ac_cancel) 금액 합계와 취소 건수를 유형 무관하게 집계. */ +void RC_CANCMATCH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_amt; + 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(amount),0) INTO :h_cnt, :h_amt + FROM ac_cancel WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + setl(b, T_AMOUNT, h_amt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_CARRYFWD.h b/app/src/rc/svc/RC_CARRYFWD.h new file mode 100644 index 0000000..d3b49cb --- /dev/null +++ b/app/src/rc/svc/RC_CARRYFWD.h @@ -0,0 +1,18 @@ +/* + * RC_CARRYFWD.h - rc 서비스 RC_CARRYFWD 카피북 (copybook / record header). + * 익일 이월 처리: 당일 미해결 예외를 지정 익일자로 복제 등록하여 이월시킨다. + */ +#ifndef RC_CARRYFWD_H +#define RC_CARRYFWD_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_carryfwd_rec_t; + +void RC_CARRYFWD(TPSVCINFO *p); + +#endif /* RC_CARRYFWD_H */ diff --git a/app/src/rc/svc/RC_CARRYFWD.pgc b/app/src/rc/svc/RC_CARRYFWD.pgc new file mode 100644 index 0000000..1ed327b --- /dev/null +++ b/app/src/rc/svc/RC_CARRYFWD.pgc @@ -0,0 +1,43 @@ +/* + * RC_CARRYFWD.pgc - rc 모듈 서비스 RC_CARRYFWD (one service = one file). + * 익일 이월 처리: 당일 미해결 예외를 지정 익일자로 복제 등록하여 이월시킨다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_CARRYFWD.h" + +/* 109. RC_CARRYFWD - 익일 이월 처리: 당일 미해결 예외를 지정 익일자로 복제 등록하여 이월시킨다. */ +void RC_CARRYFWD(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid, h_cnt = 0; char h_next[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; + + { char c[16]; gets_(b, T_STR1, c, sizeof(c)); + strncpy(h_next, c, sizeof(h_next)-1); h_next[sizeof(h_next)-1] = 0; } + EXEC SQL DECLARE cur_fwd CURSOR FOR + SELECT purchase_id FROM rc_exception + WHERE biz_date = :h_bizdate AND resolved = false; + EXEC SQL OPEN cur_fwd; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_fwd INTO :h_pid; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_fwd; FAIL(b); } + EXEC SQL SELECT nextval('rc_exc_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_exception (exc_id, purchase_id, reason, biz_date) + VALUES (:h_id, :h_pid, 'CARRY_FWD', :h_next); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_fwd; FAIL(b); } + h_cnt++; + } + EXEC SQL CLOSE cur_fwd; + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_CARRYIN.h b/app/src/rc/svc/RC_CARRYIN.h new file mode 100644 index 0000000..239fcd8 --- /dev/null +++ b/app/src/rc/svc/RC_CARRYIN.h @@ -0,0 +1,18 @@ +/* + * RC_CARRYIN.h - rc 서비스 RC_CARRYIN 카피북 (copybook / record header). + * 전일 이월 대사: 전 영업일의 미해결 예외 건수를 조회해 당일 이월 대상으로 반환. + */ +#ifndef RC_CARRYIN_H +#define RC_CARRYIN_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_carryin_rec_t; + +void RC_CARRYIN(TPSVCINFO *p); + +#endif /* RC_CARRYIN_H */ diff --git a/app/src/rc/svc/RC_CARRYIN.pgc b/app/src/rc/svc/RC_CARRYIN.pgc new file mode 100644 index 0000000..3c16b80 --- /dev/null +++ b/app/src/rc/svc/RC_CARRYIN.pgc @@ -0,0 +1,30 @@ +/* + * RC_CARRYIN.pgc - rc 모듈 서비스 RC_CARRYIN (one service = one file). + * 전일 이월 대사: 전 영업일의 미해결 예외 건수를 조회해 당일 이월 대상으로 반환. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_CARRYIN.h" + +/* 108. RC_CARRYIN - 전일 이월 대사: 전 영업일의 미해결 예외 건수를 조회해 당일 이월 대상으로 반환. */ +void RC_CARRYIN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt; char h_prev[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; + + { char c[16]; gets_(b, T_STR1, c, sizeof(c)); + strncpy(h_prev, c, sizeof(h_prev)-1); h_prev[sizeof(h_prev)-1] = 0; } + EXEC SQL SELECT count(*) INTO :h_cnt FROM rc_exception + WHERE biz_date = :h_prev AND resolved = false; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_CHANDIFF.h b/app/src/rc/svc/RC_CHANDIFF.h new file mode 100644 index 0000000..dab34d2 --- /dev/null +++ b/app/src/rc/svc/RC_CHANDIFF.h @@ -0,0 +1,18 @@ +/* + * RC_CHANDIFF.h - rc 서비스 RC_CHANDIFF 카피북 (copybook / record header). + * 채널별 차이 분석: 지정 채널의 매입 순액과 원장 SETTLE 합계 차이를 rc_diff 에 기록. + */ +#ifndef RC_CHANDIFF_H +#define RC_CHANDIFF_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_chandiff_rec_t; + +void RC_CHANDIFF(TPSVCINFO *p); + +#endif /* RC_CHANDIFF_H */ diff --git a/app/src/rc/svc/RC_CHANDIFF.pgc b/app/src/rc/svc/RC_CHANDIFF.pgc new file mode 100644 index 0000000..2b240be --- /dev/null +++ b/app/src/rc/svc/RC_CHANDIFF.pgc @@ -0,0 +1,38 @@ +/* + * RC_CHANDIFF.pgc - rc 모듈 서비스 RC_CHANDIFF (one service = one file). + * 채널별 차이 분석: 지정 채널의 매입 순액과 원장 SETTLE 합계 차이를 rc_diff 에 기록. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_CHANDIFF.h" + +/* 90. RC_CHANDIFF - 채널별 차이 분석: 지정 채널의 매입 순액과 원장 SETTLE 합계 차이를 rc_diff 에 기록. */ +void RC_CHANDIFF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_exp, h_act; char h_chan[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; + + { char c[16]; gets_(b, T_CHANNEL, c, sizeof(c)); + strncpy(h_chan, c, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0; } + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_exp FROM purchase + WHERE biz_date = :h_bizdate AND channel = :h_chan; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(l.amount),0) INTO :h_act FROM ledger l + JOIN purchase p ON p.purchase_id = l.purchase_id + WHERE l.biz_date = :h_bizdate AND l.entry_type = 'SETTLE' AND p.channel = :h_chan; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT nextval('rc_diff_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_diff (diff_id, expected_amt, actual_amt, diff_amt, diff_kind, biz_date) + VALUES (:h_id, :h_exp, :h_act, :h_exp - :h_act, 'AMT', :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_DELTA, h_exp - h_act); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_CHANRECON.h b/app/src/rc/svc/RC_CHANRECON.h new file mode 100644 index 0000000..56fb7fc --- /dev/null +++ b/app/src/rc/svc/RC_CHANRECON.h @@ -0,0 +1,18 @@ +/* + * RC_CHANRECON.h - rc 서비스 RC_CHANRECON 카피북 (copybook / record header). + * 채널별 대사: 지정 채널의 당일 매입 건수와 대사완료(M/S) 건수를 대조. + */ +#ifndef RC_CHANRECON_H +#define RC_CHANRECON_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_chanrecon_rec_t; + +void RC_CHANRECON(TPSVCINFO *p); + +#endif /* RC_CHANRECON_H */ diff --git a/app/src/rc/svc/RC_CHANRECON.pgc b/app/src/rc/svc/RC_CHANRECON.pgc new file mode 100644 index 0000000..9f1309e --- /dev/null +++ b/app/src/rc/svc/RC_CHANRECON.pgc @@ -0,0 +1,34 @@ +/* + * RC_CHANRECON.pgc - rc 모듈 서비스 RC_CHANRECON (one service = one file). + * 채널별 대사: 지정 채널의 당일 매입 건수와 대사완료(M/S) 건수를 대조. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_CHANRECON.h" + +/* 84. RC_CHANRECON - 채널별 대사: 지정 채널의 당일 매입 건수와 대사완료(M/S) 건수를 대조. */ +void RC_CHANRECON(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_total, h_done; char h_chan[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; + + { char c[16]; gets_(b, T_CHANNEL, c, sizeof(c)); + strncpy(h_chan, c, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0; } + EXEC SQL SELECT count(*) INTO :h_total FROM purchase + WHERE biz_date = :h_bizdate AND channel = :h_chan; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_done FROM purchase + WHERE biz_date = :h_bizdate AND channel = :h_chan AND status IN ('M','S'); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_total); + setl(b, T_ARG1, h_done); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_DEPGAP.h b/app/src/rc/svc/RC_DEPGAP.h new file mode 100644 index 0000000..6df2e33 --- /dev/null +++ b/app/src/rc/svc/RC_DEPGAP.h @@ -0,0 +1,18 @@ +/* + * RC_DEPGAP.h - rc 서비스 RC_DEPGAP 카피북 (copybook / record header). + * 입금 누락 감지: 매입은 있으나 RECON 원장이 없는 미입금 건수를 집계. + */ +#ifndef RC_DEPGAP_H +#define RC_DEPGAP_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_depgap_rec_t; + +void RC_DEPGAP(TPSVCINFO *p); + +#endif /* RC_DEPGAP_H */ diff --git a/app/src/rc/svc/RC_DEPGAP.pgc b/app/src/rc/svc/RC_DEPGAP.pgc new file mode 100644 index 0000000..6a151f6 --- /dev/null +++ b/app/src/rc/svc/RC_DEPGAP.pgc @@ -0,0 +1,30 @@ +/* + * RC_DEPGAP.pgc - rc 모듈 서비스 RC_DEPGAP (one service = one file). + * 입금 누락 감지: 매입은 있으나 RECON 원장이 없는 미입금 건수를 집계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_DEPGAP.h" + +/* 82. RC_DEPGAP - 입금 누락 감지: 매입은 있으나 RECON 원장이 없는 미입금 건수를 집계. */ +void RC_DEPGAP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt; + 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(*) INTO :h_cnt FROM purchase p + WHERE p.biz_date = :h_bizdate + AND NOT EXISTS (SELECT 1 FROM ledger l + WHERE l.purchase_id = p.purchase_id AND l.entry_type = 'RECON'); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_DEPPAY.h b/app/src/rc/svc/RC_DEPPAY.h new file mode 100644 index 0000000..2be89e0 --- /dev/null +++ b/app/src/rc/svc/RC_DEPPAY.h @@ -0,0 +1,18 @@ +/* + * RC_DEPPAY.h - rc 서비스 RC_DEPPAY 카피북 (copybook / record header). + * 입금-지급 축 대사: 원장 XFER 이체 합계와 정산 순액 합계의 지급 정합성을 대조. + */ +#ifndef RC_DEPPAY_H +#define RC_DEPPAY_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_deppay_rec_t; + +void RC_DEPPAY(TPSVCINFO *p); + +#endif /* RC_DEPPAY_H */ diff --git a/app/src/rc/svc/RC_DEPPAY.pgc b/app/src/rc/svc/RC_DEPPAY.pgc new file mode 100644 index 0000000..5638d98 --- /dev/null +++ b/app/src/rc/svc/RC_DEPPAY.pgc @@ -0,0 +1,33 @@ +/* + * RC_DEPPAY.pgc - rc 모듈 서비스 RC_DEPPAY (one service = one file). + * 입금-지급 축 대사: 원장 XFER 이체 합계와 정산 순액 합계의 지급 정합성을 대조. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_DEPPAY.h" + +/* 78. RC_DEPPAY - 입금-지급 축 대사: 원장 XFER 이체 합계와 정산 순액 합계의 지급 정합성을 대조. */ +void RC_DEPPAY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_xfer, h_settle; + 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(amount),0) INTO :h_xfer FROM ledger + WHERE biz_date = :h_bizdate AND entry_type = 'XFER'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_settle FROM settlement + WHERE biz_date = :h_bizdate AND status = 'SETTLED'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_xfer); + setl(b, T_GROSS, h_settle); + setl(b, T_DELTA, h_xfer - h_settle); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_DIFFRANK.h b/app/src/rc/svc/RC_DIFFRANK.h new file mode 100644 index 0000000..8d0dcb2 --- /dev/null +++ b/app/src/rc/svc/RC_DIFFRANK.h @@ -0,0 +1,18 @@ +/* + * RC_DIFFRANK.h - rc 서비스 RC_DIFFRANK 카피북 (copybook / record header). + * 차이 순위 분석: 당일 절대 차이금액이 가장 큰 매입 1건과 그 차액을 반환. + */ +#ifndef RC_DIFFRANK_H +#define RC_DIFFRANK_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_diffrank_rec_t; + +void RC_DIFFRANK(TPSVCINFO *p); + +#endif /* RC_DIFFRANK_H */ diff --git a/app/src/rc/svc/RC_DIFFRANK.pgc b/app/src/rc/svc/RC_DIFFRANK.pgc new file mode 100644 index 0000000..c1e6562 --- /dev/null +++ b/app/src/rc/svc/RC_DIFFRANK.pgc @@ -0,0 +1,31 @@ +/* + * RC_DIFFRANK.pgc - rc 모듈 서비스 RC_DIFFRANK (one service = one file). + * 차이 순위 분석: 당일 절대 차이금액이 가장 큰 매입 1건과 그 차액을 반환. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_DIFFRANK.h" + +/* 100. RC_DIFFRANK - 차이 순위 분석: 당일 절대 차이금액이 가장 큰 매입 1건과 그 차액을 반환. */ +void RC_DIFFRANK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pid = 0, h_max = 0; + 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(purchase_id,0), coalesce(abs(diff_amt),0) + INTO :h_pid, :h_max FROM rc_diff + WHERE biz_date = :h_bizdate + ORDER BY abs(diff_amt) DESC LIMIT 1; + if (sqlca.sqlcode < 0 && sqlca.sqlcode != 100) FAIL(b); + setl(b, T_PURCHASE_ID, h_pid); + setl(b, T_DELTA, h_max); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_EDCRECON.h b/app/src/rc/svc/RC_EDCRECON.h new file mode 100644 index 0000000..1d2f2e4 --- /dev/null +++ b/app/src/rc/svc/RC_EDCRECON.h @@ -0,0 +1,18 @@ +/* + * RC_EDCRECON.h - rc 서비스 RC_EDCRECON 카피북 (copybook / record header). + * EDC 채널 대사: EDC 단말 매입의 순액 합계와 미대사 건수를 함께 반환. + */ +#ifndef RC_EDCRECON_H +#define RC_EDCRECON_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_edcrecon_rec_t; + +void RC_EDCRECON(TPSVCINFO *p); + +#endif /* RC_EDCRECON_H */ diff --git a/app/src/rc/svc/RC_EDCRECON.pgc b/app/src/rc/svc/RC_EDCRECON.pgc new file mode 100644 index 0000000..5537dd6 --- /dev/null +++ b/app/src/rc/svc/RC_EDCRECON.pgc @@ -0,0 +1,32 @@ +/* + * RC_EDCRECON.pgc - rc 모듈 서비스 RC_EDCRECON (one service = one file). + * EDC 채널 대사: EDC 단말 매입의 순액 합계와 미대사 건수를 함께 반환. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_EDCRECON.h" + +/* 89. RC_EDCRECON - EDC 채널 대사: EDC 단말 매입의 순액 합계와 미대사 건수를 함께 반환. */ +void RC_EDCRECON(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_net, h_open; + 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_net FROM purchase + WHERE biz_date = :h_bizdate AND channel = 'EDC'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_open FROM purchase + WHERE biz_date = :h_bizdate AND channel = 'EDC' AND status = 'A'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_NET, h_net); + setl(b, T_ARG1, h_open); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_EXCNOTIFY.h b/app/src/rc/svc/RC_EXCNOTIFY.h new file mode 100644 index 0000000..8ec7b04 --- /dev/null +++ b/app/src/rc/svc/RC_EXCNOTIFY.h @@ -0,0 +1,18 @@ +/* + * RC_EXCNOTIFY.h - rc 서비스 RC_EXCNOTIFY 카피북 (copybook / record header). + * 대사 예외 알림: 당일 미해결 예외 건수를 집계해 임계 초과 시 rc_alert 경보를 발생. + */ +#ifndef RC_EXCNOTIFY_H +#define RC_EXCNOTIFY_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_excnotify_rec_t; + +void RC_EXCNOTIFY(TPSVCINFO *p); + +#endif /* RC_EXCNOTIFY_H */ diff --git a/app/src/rc/svc/RC_EXCNOTIFY.pgc b/app/src/rc/svc/RC_EXCNOTIFY.pgc new file mode 100644 index 0000000..7f69861 --- /dev/null +++ b/app/src/rc/svc/RC_EXCNOTIFY.pgc @@ -0,0 +1,34 @@ +/* + * RC_EXCNOTIFY.pgc - rc 모듈 서비스 RC_EXCNOTIFY (one service = one file). + * 대사 예외 알림: 당일 미해결 예외 건수를 집계해 임계 초과 시 rc_alert 경보를 발생. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_EXCNOTIFY.h" + +/* 131. RC_EXCNOTIFY - 대사 예외 알림: 당일 미해결 예외 건수를 집계해 임계 초과 시 rc_alert 경보를 발생. */ +void RC_EXCNOTIFY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_cnt; + 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(*) INTO :h_cnt FROM rc_exception + WHERE biz_date = :h_bizdate AND resolved = false; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) { + EXEC SQL SELECT nextval('rc_alert_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_alert (alert_id, lvl, message, biz_date) + VALUES (:h_id, 'WARN', 'UNRESOLVED_EXC', :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + } + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_FEEBRK.h b/app/src/rc/svc/RC_FEEBRK.h new file mode 100644 index 0000000..fc6ea7f --- /dev/null +++ b/app/src/rc/svc/RC_FEEBRK.h @@ -0,0 +1,18 @@ +/* + * RC_FEEBRK.h - rc 서비스 RC_FEEBRK 카피북 (copybook / record header). + * 수수료 내역 대사: 당일 매입 수수료 원계와 수수료조정 누계를 비교해 순수수료 산출. + */ +#ifndef RC_FEEBRK_H +#define RC_FEEBRK_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_feebrk_rec_t; + +void RC_FEEBRK(TPSVCINFO *p); + +#endif /* RC_FEEBRK_H */ diff --git a/app/src/rc/svc/RC_FEEBRK.pgc b/app/src/rc/svc/RC_FEEBRK.pgc new file mode 100644 index 0000000..4ff21fb --- /dev/null +++ b/app/src/rc/svc/RC_FEEBRK.pgc @@ -0,0 +1,31 @@ +/* + * RC_FEEBRK.pgc - rc 모듈 서비스 RC_FEEBRK (one service = one file). + * 수수료 내역 대사: 당일 매입 수수료 원계와 수수료조정 누계를 비교해 순수수료 산출. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_FEEBRK.h" + +/* 124. RC_FEEBRK - 수수료 내역 대사: 당일 매입 수수료 원계와 수수료조정 누계를 비교해 순수수료 산출. */ +void RC_FEEBRK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_fee, h_adj; + 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(fee),0) INTO :h_fee FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(delta),0) INTO :h_adj FROM ac_fee_adj WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_FEE, h_fee); + setl(b, T_DELTA, h_adj); + setl(b, T_ARG1, h_fee + h_adj); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_FIXDUP.h b/app/src/rc/svc/RC_FIXDUP.h new file mode 100644 index 0000000..942d891 --- /dev/null +++ b/app/src/rc/svc/RC_FIXDUP.h @@ -0,0 +1,18 @@ +/* + * RC_FIXDUP.h - rc 서비스 RC_FIXDUP 카피북 (copybook / record header). + * 중복 자동보정: 동일 매입에 SETTLE 원장이 2건 이상 중복된 매입 건수를 탐지. + */ +#ifndef RC_FIXDUP_H +#define RC_FIXDUP_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_fixdup_rec_t; + +void RC_FIXDUP(TPSVCINFO *p); + +#endif /* RC_FIXDUP_H */ diff --git a/app/src/rc/svc/RC_FIXDUP.pgc b/app/src/rc/svc/RC_FIXDUP.pgc new file mode 100644 index 0000000..a0f7f4e --- /dev/null +++ b/app/src/rc/svc/RC_FIXDUP.pgc @@ -0,0 +1,30 @@ +/* + * RC_FIXDUP.pgc - rc 모듈 서비스 RC_FIXDUP (one service = one file). + * 중복 자동보정: 동일 매입에 SETTLE 원장이 2건 이상 중복된 매입 건수를 탐지. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_FIXDUP.h" + +/* 115. RC_FIXDUP - 중복 자동보정: 동일 매입에 SETTLE 원장이 2건 이상 중복된 매입 건수를 탐지. */ +void RC_FIXDUP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt; + 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(*) INTO :h_cnt FROM ( + SELECT purchase_id FROM ledger + WHERE biz_date = :h_bizdate AND entry_type = 'SETTLE' + GROUP BY purchase_id HAVING count(*) > 1) d; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_FIXFEE.h b/app/src/rc/svc/RC_FIXFEE.h new file mode 100644 index 0000000..913ad05 --- /dev/null +++ b/app/src/rc/svc/RC_FIXFEE.h @@ -0,0 +1,18 @@ +/* + * RC_FIXFEE.h - rc 서비스 RC_FIXFEE 카피북 (copybook / record header). + * 수수료 자동보정: 매입 수수료가 MDR 이론치와 다르면 차이를 조정 델타로 반환. + */ +#ifndef RC_FIXFEE_H +#define RC_FIXFEE_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_fixfee_rec_t; + +void RC_FIXFEE(TPSVCINFO *p); + +#endif /* RC_FIXFEE_H */ diff --git a/app/src/rc/svc/RC_FIXFEE.pgc b/app/src/rc/svc/RC_FIXFEE.pgc new file mode 100644 index 0000000..ffeefbd --- /dev/null +++ b/app/src/rc/svc/RC_FIXFEE.pgc @@ -0,0 +1,34 @@ +/* + * RC_FIXFEE.pgc - rc 모듈 서비스 RC_FIXFEE (one service = one file). + * 수수료 자동보정: 매입 수수료가 MDR 이론치와 다르면 차이를 조정 델타로 반환. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_FIXFEE.h" + +/* 113. RC_FIXFEE - 수수료 자동보정: 매입 수수료가 MDR 이론치와 다르면 차이를 조정 델타로 반환. */ +void RC_FIXFEE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pid, h_fee, h_amt, h_bps, h_calc; + 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; + + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL SELECT p.fee, p.amount, coalesce(m.mdr_bps,0) + INTO :h_fee, :h_amt, :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); + h_calc = h_amt * h_bps / 10000; + setl(b, T_FEE, h_fee); + setl(b, T_ARG1, h_calc); + setl(b, T_DELTA, h_calc - h_fee); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_FIXFX.h b/app/src/rc/svc/RC_FIXFX.h new file mode 100644 index 0000000..354a333 --- /dev/null +++ b/app/src/rc/svc/RC_FIXFX.h @@ -0,0 +1,18 @@ +/* + * RC_FIXFX.h - rc 서비스 RC_FIXFX 카피북 (copybook / record header). + * 외화 자동보정: ac_fx 의 원화환산액과 원통화*환율 이론치의 차이를 산출. + */ +#ifndef RC_FIXFX_H +#define RC_FIXFX_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_fixfx_rec_t; + +void RC_FIXFX(TPSVCINFO *p); + +#endif /* RC_FIXFX_H */ diff --git a/app/src/rc/svc/RC_FIXFX.pgc b/app/src/rc/svc/RC_FIXFX.pgc new file mode 100644 index 0000000..2a5f55a --- /dev/null +++ b/app/src/rc/svc/RC_FIXFX.pgc @@ -0,0 +1,32 @@ +/* + * RC_FIXFX.pgc - rc 모듈 서비스 RC_FIXFX (one service = one file). + * 외화 자동보정: ac_fx 의 원화환산액과 원통화*환율 이론치의 차이를 산출. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_FIXFX.h" + +/* 114. RC_FIXFX - 외화 자동보정: ac_fx 의 원화환산액과 원통화*환율 이론치의 차이를 산출. */ +void RC_FIXFX(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pid, h_krw, h_fx, h_rate, h_calc; + 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; + + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL SELECT coalesce(krw_amount,0), coalesce(fx_amount,0), coalesce(rate_bps,0) + INTO :h_krw, :h_fx, :h_rate FROM ac_fx WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0 && sqlca.sqlcode != 100) FAIL(b); + h_calc = h_fx * h_rate / 10000; + setl(b, T_ARG1, h_krw); + setl(b, T_ARG2, h_calc); + setl(b, T_DELTA, h_krw - h_calc); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_FIXROUND.h b/app/src/rc/svc/RC_FIXROUND.h new file mode 100644 index 0000000..f26d8aa --- /dev/null +++ b/app/src/rc/svc/RC_FIXROUND.h @@ -0,0 +1,18 @@ +/* + * RC_FIXROUND.h - rc 서비스 RC_FIXROUND 카피북 (copybook / record header). + * 반올림 자동보정: 절대차 1 이하 단수 차이를 수수료조정(ac_fee_adj)으로 상계 기록. + */ +#ifndef RC_FIXROUND_H +#define RC_FIXROUND_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_fixround_rec_t; + +void RC_FIXROUND(TPSVCINFO *p); + +#endif /* RC_FIXROUND_H */ diff --git a/app/src/rc/svc/RC_FIXROUND.pgc b/app/src/rc/svc/RC_FIXROUND.pgc new file mode 100644 index 0000000..a02a577 --- /dev/null +++ b/app/src/rc/svc/RC_FIXROUND.pgc @@ -0,0 +1,41 @@ +/* + * RC_FIXROUND.pgc - rc 모듈 서비스 RC_FIXROUND (one service = one file). + * 반올림 자동보정: 절대차 1 이하 단수 차이를 수수료조정(ac_fee_adj)으로 상계 기록. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_FIXROUND.h" + +/* 112. RC_FIXROUND - 반올림 자동보정: 절대차 1 이하 단수 차이를 수수료조정(ac_fee_adj)으로 상계 기록. */ +void RC_FIXROUND(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid, h_diff, h_cnt = 0; + 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 cur_fr CURSOR FOR + SELECT purchase_id, diff_amt FROM rc_diff + WHERE biz_date = :h_bizdate AND abs(diff_amt) <= 1 AND diff_amt <> 0; + EXEC SQL OPEN cur_fr; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH cur_fr INTO :h_pid, :h_diff; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_fr; FAIL(b); } + EXEC SQL SELECT nextval('ac_feeadj_seq') INTO :h_id; + EXEC SQL INSERT INTO ac_fee_adj (adj_id, purchase_id, delta, reason, biz_date) + VALUES (:h_id, :h_pid, :h_diff, 'ROUND_FIX', :h_bizdate); + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cur_fr; FAIL(b); } + h_cnt++; + } + EXEC SQL CLOSE cur_fr; + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_FIXSIGN.h b/app/src/rc/svc/RC_FIXSIGN.h new file mode 100644 index 0000000..d43ccb9 --- /dev/null +++ b/app/src/rc/svc/RC_FIXSIGN.h @@ -0,0 +1,18 @@ +/* + * RC_FIXSIGN.h - rc 서비스 RC_FIXSIGN 카피북 (copybook / record header). + * 부호 자동보정: 부호 반전 rc_diff 라인의 차액 부호를 정정하여 재기록. + */ +#ifndef RC_FIXSIGN_H +#define RC_FIXSIGN_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_fixsign_rec_t; + +void RC_FIXSIGN(TPSVCINFO *p); + +#endif /* RC_FIXSIGN_H */ diff --git a/app/src/rc/svc/RC_FIXSIGN.pgc b/app/src/rc/svc/RC_FIXSIGN.pgc new file mode 100644 index 0000000..e2bca96 --- /dev/null +++ b/app/src/rc/svc/RC_FIXSIGN.pgc @@ -0,0 +1,30 @@ +/* + * RC_FIXSIGN.pgc - rc 모듈 서비스 RC_FIXSIGN (one service = one file). + * 부호 자동보정: 부호 반전 rc_diff 라인의 차액 부호를 정정하여 재기록. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_FIXSIGN.h" + +/* 116. RC_FIXSIGN - 부호 자동보정: 부호 반전 rc_diff 라인의 차액 부호를 정정하여 재기록. */ +void RC_FIXSIGN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_upd; + 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 UPDATE rc_diff SET diff_amt = expected_amt - actual_amt + WHERE biz_date = :h_bizdate + AND diff_amt <> expected_amt - actual_amt; + if (sqlca.sqlcode < 0) FAIL(b); + h_upd = sqlca.sqlerrd[2]; + setl(b, T_COUNT, h_upd); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_FXDIFF.h b/app/src/rc/svc/RC_FXDIFF.h new file mode 100644 index 0000000..70f1711 --- /dev/null +++ b/app/src/rc/svc/RC_FXDIFF.h @@ -0,0 +1,18 @@ +/* + * RC_FXDIFF.h - rc 서비스 RC_FXDIFF 카피북 (copybook / record header). + * 외화 환산 차이: 당일 ac_fx 원화환산 합계와 원통화*환율 이론합계의 차이를 집계. + */ +#ifndef RC_FXDIFF_H +#define RC_FXDIFF_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_fxdiff_rec_t; + +void RC_FXDIFF(TPSVCINFO *p); + +#endif /* RC_FXDIFF_H */ diff --git a/app/src/rc/svc/RC_FXDIFF.pgc b/app/src/rc/svc/RC_FXDIFF.pgc new file mode 100644 index 0000000..1359043 --- /dev/null +++ b/app/src/rc/svc/RC_FXDIFF.pgc @@ -0,0 +1,30 @@ +/* + * RC_FXDIFF.pgc - rc 모듈 서비스 RC_FXDIFF (one service = one file). + * 외화 환산 차이: 당일 ac_fx 원화환산 합계와 원통화*환율 이론합계의 차이를 집계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_FXDIFF.h" + +/* 125. RC_FXDIFF - 외화 환산 차이: 당일 ac_fx 원화환산 합계와 원통화*환율 이론합계의 차이를 집계. */ +void RC_FXDIFF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_krw, h_calc; + 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(krw_amount),0), coalesce(sum(fx_amount * rate_bps / 10000),0) + INTO :h_krw, :h_calc FROM ac_fx WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_krw); + setl(b, T_ARG2, h_calc); + setl(b, T_DELTA, h_krw - h_calc); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_HOLDCLR.h b/app/src/rc/svc/RC_HOLDCLR.h new file mode 100644 index 0000000..947f719 --- /dev/null +++ b/app/src/rc/svc/RC_HOLDCLR.h @@ -0,0 +1,18 @@ +/* + * RC_HOLDCLR.h - rc 서비스 RC_HOLDCLR 카피북 (copybook / record header). + * 대사 보류 해제: 보류(H) 매입을 접수(A)로 되돌리고 관련 예외를 해결 처리. + */ +#ifndef RC_HOLDCLR_H +#define RC_HOLDCLR_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_holdclr_rec_t; + +void RC_HOLDCLR(TPSVCINFO *p); + +#endif /* RC_HOLDCLR_H */ diff --git a/app/src/rc/svc/RC_HOLDCLR.pgc b/app/src/rc/svc/RC_HOLDCLR.pgc new file mode 100644 index 0000000..d1b3c0f --- /dev/null +++ b/app/src/rc/svc/RC_HOLDCLR.pgc @@ -0,0 +1,32 @@ +/* + * RC_HOLDCLR.pgc - rc 모듈 서비스 RC_HOLDCLR (one service = one file). + * 대사 보류 해제: 보류(H) 매입을 접수(A)로 되돌리고 관련 예외를 해결 처리. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_HOLDCLR.h" + +/* 107. RC_HOLDCLR - 대사 보류 해제: 보류(H) 매입을 접수(A)로 되돌리고 관련 예외를 해결 처리. */ +void RC_HOLDCLR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pid; + 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; + + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL UPDATE purchase SET status = 'A', updated_at = now() + WHERE purchase_id = :h_pid AND status = 'H'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL UPDATE rc_exception SET resolved = true + WHERE purchase_id = :h_pid AND resolved = false; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, sqlca.sqlerrd[2]); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_HOLDLIST.h b/app/src/rc/svc/RC_HOLDLIST.h new file mode 100644 index 0000000..b741152 --- /dev/null +++ b/app/src/rc/svc/RC_HOLDLIST.h @@ -0,0 +1,18 @@ +/* + * RC_HOLDLIST.h - rc 서비스 RC_HOLDLIST 카피북 (copybook / record header). + * 보류 목록 현황: 당일 보류(H) 상태 매입의 건수와 순액 합계를 반환. + */ +#ifndef RC_HOLDLIST_H +#define RC_HOLDLIST_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_holdlist_rec_t; + +void RC_HOLDLIST(TPSVCINFO *p); + +#endif /* RC_HOLDLIST_H */ diff --git a/app/src/rc/svc/RC_HOLDLIST.pgc b/app/src/rc/svc/RC_HOLDLIST.pgc new file mode 100644 index 0000000..26fdc04 --- /dev/null +++ b/app/src/rc/svc/RC_HOLDLIST.pgc @@ -0,0 +1,29 @@ +/* + * RC_HOLDLIST.pgc - rc 모듈 서비스 RC_HOLDLIST (one service = one file). + * 보류 목록 현황: 당일 보류(H) 상태 매입의 건수와 순액 합계를 반환. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_HOLDLIST.h" + +/* 111. RC_HOLDLIST - 보류 목록 현황: 당일 보류(H) 상태 매입의 건수와 순액 합계를 반환. */ +void RC_HOLDLIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_net; + 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_net + FROM purchase WHERE biz_date = :h_bizdate AND status = 'H'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + setl(b, T_NET, h_net); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_HOLDSET.h b/app/src/rc/svc/RC_HOLDSET.h new file mode 100644 index 0000000..13d5f26 --- /dev/null +++ b/app/src/rc/svc/RC_HOLDSET.h @@ -0,0 +1,18 @@ +/* + * RC_HOLDSET.h - rc 서비스 RC_HOLDSET 카피북 (copybook / record header). + * 대사 보류 설정: 지정 매입 상태를 보류(H)로 변경하고 보류 예외를 사유와 함께 등록. + */ +#ifndef RC_HOLDSET_H +#define RC_HOLDSET_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_holdset_rec_t; + +void RC_HOLDSET(TPSVCINFO *p); + +#endif /* RC_HOLDSET_H */ diff --git a/app/src/rc/svc/RC_HOLDSET.pgc b/app/src/rc/svc/RC_HOLDSET.pgc new file mode 100644 index 0000000..66c4aba --- /dev/null +++ b/app/src/rc/svc/RC_HOLDSET.pgc @@ -0,0 +1,35 @@ +/* + * RC_HOLDSET.pgc - rc 모듈 서비스 RC_HOLDSET (one service = one file). + * 대사 보류 설정: 지정 매입 상태를 보류(H)로 변경하고 보류 예외를 사유와 함께 등록. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_HOLDSET.h" + +/* 106. RC_HOLDSET - 대사 보류 설정: 지정 매입 상태를 보류(H)로 변경하고 보류 예외를 사유와 함께 등록. */ +void RC_HOLDSET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid; char h_rsn[64]; + 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; + + h_pid = getl(b, T_PURCHASE_ID); + { char c[64]; gets_(b, T_REASON, c, sizeof(c)); + strncpy(h_rsn, c, sizeof(h_rsn)-1); h_rsn[sizeof(h_rsn)-1] = 0; } + EXEC SQL UPDATE purchase SET status = 'H', updated_at = now() + WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT nextval('rc_exc_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_exception (exc_id, purchase_id, reason, biz_date) + VALUES (:h_id, :h_pid, :h_rsn, :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ID1, h_id); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_ISSDIFF.h b/app/src/rc/svc/RC_ISSDIFF.h new file mode 100644 index 0000000..5d69e42 --- /dev/null +++ b/app/src/rc/svc/RC_ISSDIFF.h @@ -0,0 +1,18 @@ +/* + * RC_ISSDIFF.h - rc 서비스 RC_ISSDIFF 카피북 (copybook / record header). + * 발급사별 차이 분석: 발급사 단위 매입 순액과 정산 순액 차이를 계산해 반환. + */ +#ifndef RC_ISSDIFF_H +#define RC_ISSDIFF_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_issdiff_rec_t; + +void RC_ISSDIFF(TPSVCINFO *p); + +#endif /* RC_ISSDIFF_H */ diff --git a/app/src/rc/svc/RC_ISSDIFF.pgc b/app/src/rc/svc/RC_ISSDIFF.pgc new file mode 100644 index 0000000..4d75223 --- /dev/null +++ b/app/src/rc/svc/RC_ISSDIFF.pgc @@ -0,0 +1,36 @@ +/* + * RC_ISSDIFF.pgc - rc 모듈 서비스 RC_ISSDIFF (one service = one file). + * 발급사별 차이 분석: 발급사 단위 매입 순액과 정산 순액 차이를 계산해 반환. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_ISSDIFF.h" + +/* 91. RC_ISSDIFF - 발급사별 차이 분석: 발급사 단위 매입 순액과 정산 순액 차이를 계산해 반환. */ +void RC_ISSDIFF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_exp, h_act; char h_iss[32]; + 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; + + { char c[32]; gets_(b, T_ISSUER, c, sizeof(c)); + strncpy(h_iss, c, sizeof(h_iss)-1); h_iss[sizeof(h_iss)-1] = 0; } + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_exp FROM purchase + WHERE biz_date = :h_bizdate AND issuer = :h_iss; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(s.net),0) INTO :h_act FROM settlement s + JOIN purchase p ON p.purchase_id = s.purchase_id + WHERE s.biz_date = :h_bizdate AND p.issuer = :h_iss; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_exp); + setl(b, T_ARG2, h_act); + setl(b, T_DELTA, h_exp - h_act); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_ISSRECON.h b/app/src/rc/svc/RC_ISSRECON.h new file mode 100644 index 0000000..f3a680a --- /dev/null +++ b/app/src/rc/svc/RC_ISSRECON.h @@ -0,0 +1,18 @@ +/* + * RC_ISSRECON.h - rc 서비스 RC_ISSRECON 카피북 (copybook / record header). + * 발급사별 대사: 지정 발급사의 당일 매입 순액과 정산 순액을 발급사 단위로 대조. + */ +#ifndef RC_ISSRECON_H +#define RC_ISSRECON_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_issrecon_rec_t; + +void RC_ISSRECON(TPSVCINFO *p); + +#endif /* RC_ISSRECON_H */ diff --git a/app/src/rc/svc/RC_ISSRECON.pgc b/app/src/rc/svc/RC_ISSRECON.pgc new file mode 100644 index 0000000..3dc2faf --- /dev/null +++ b/app/src/rc/svc/RC_ISSRECON.pgc @@ -0,0 +1,36 @@ +/* + * RC_ISSRECON.pgc - rc 모듈 서비스 RC_ISSRECON (one service = one file). + * 발급사별 대사: 지정 발급사의 당일 매입 순액과 정산 순액을 발급사 단위로 대조. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_ISSRECON.h" + +/* 85. RC_ISSRECON - 발급사별 대사: 지정 발급사의 당일 매입 순액과 정산 순액을 발급사 단위로 대조. */ +void RC_ISSRECON(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pnet, h_snet; char h_iss[32]; + 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; + + { char c[32]; gets_(b, T_ISSUER, c, sizeof(c)); + strncpy(h_iss, c, sizeof(h_iss)-1); h_iss[sizeof(h_iss)-1] = 0; } + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_pnet FROM purchase + WHERE biz_date = :h_bizdate AND issuer = :h_iss; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(s.net),0) INTO :h_snet FROM settlement s + JOIN purchase p ON p.purchase_id = s.purchase_id + WHERE s.biz_date = :h_bizdate AND p.issuer = :h_iss; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_pnet); + setl(b, T_ARG2, h_snet); + setl(b, T_DELTA, h_pnet - h_snet); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_KINDSPLIT.h b/app/src/rc/svc/RC_KINDSPLIT.h new file mode 100644 index 0000000..d0d5b6f --- /dev/null +++ b/app/src/rc/svc/RC_KINDSPLIT.h @@ -0,0 +1,18 @@ +/* + * RC_KINDSPLIT.h - rc 서비스 RC_KINDSPLIT 카피북 (copybook / record header). + * 차이 유형 분류: 당일 rc_diff 를 금액(AMT)/건수(CNT) 유형으로 나누어 각각 집계. + */ +#ifndef RC_KINDSPLIT_H +#define RC_KINDSPLIT_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_kindsplit_rec_t; + +void RC_KINDSPLIT(TPSVCINFO *p); + +#endif /* RC_KINDSPLIT_H */ diff --git a/app/src/rc/svc/RC_KINDSPLIT.pgc b/app/src/rc/svc/RC_KINDSPLIT.pgc new file mode 100644 index 0000000..081903f --- /dev/null +++ b/app/src/rc/svc/RC_KINDSPLIT.pgc @@ -0,0 +1,33 @@ +/* + * RC_KINDSPLIT.pgc - rc 모듈 서비스 RC_KINDSPLIT (one service = one file). + * 차이 유형 분류: 당일 rc_diff 를 금액(AMT)/건수(CNT) 유형으로 나누어 각각 집계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_KINDSPLIT.h" + +/* 99. RC_KINDSPLIT - 차이 유형 분류: 당일 rc_diff 를 금액(AMT)/건수(CNT) 유형으로 나누어 각각 집계. */ +void RC_KINDSPLIT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_amt, h_cnt; + 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(*) INTO :h_amt FROM rc_diff + WHERE biz_date = :h_bizdate AND diff_kind = 'AMT'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_cnt FROM rc_diff + WHERE biz_date = :h_bizdate AND diff_kind = 'CNT'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_amt); + setl(b, T_ARG2, h_cnt); + setl(b, T_COUNT, h_amt + h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_LEDGERBAL.h b/app/src/rc/svc/RC_LEDGERBAL.h new file mode 100644 index 0000000..1b5ca51 --- /dev/null +++ b/app/src/rc/svc/RC_LEDGERBAL.h @@ -0,0 +1,18 @@ +/* + * RC_LEDGERBAL.h - rc 서비스 RC_LEDGERBAL 카피북 (copybook / record header). + * 원장 축 잔액 대사: 당일 RECON/SETTLE/XFER 기표 합계의 순잔액이 0 인지 확인. + */ +#ifndef RC_LEDGERBAL_H +#define RC_LEDGERBAL_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_ledgerbal_rec_t; + +void RC_LEDGERBAL(TPSVCINFO *p); + +#endif /* RC_LEDGERBAL_H */ diff --git a/app/src/rc/svc/RC_LEDGERBAL.pgc b/app/src/rc/svc/RC_LEDGERBAL.pgc new file mode 100644 index 0000000..887f192 --- /dev/null +++ b/app/src/rc/svc/RC_LEDGERBAL.pgc @@ -0,0 +1,37 @@ +/* + * RC_LEDGERBAL.pgc - rc 모듈 서비스 RC_LEDGERBAL (one service = one file). + * 원장 축 잔액 대사: 당일 RECON/SETTLE/XFER 기표 합계의 순잔액이 0 인지 확인. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_LEDGERBAL.h" + +/* 80. RC_LEDGERBAL - 원장 축 잔액 대사: 당일 RECON/SETTLE/XFER 기표 합계의 순잔액이 0 인지 확인. */ +void RC_LEDGERBAL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_recon, h_settle, h_xfer; + 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(amount),0) INTO :h_recon FROM ledger + WHERE biz_date = :h_bizdate AND entry_type = 'RECON'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_settle FROM ledger + WHERE biz_date = :h_bizdate AND entry_type = 'SETTLE'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_xfer FROM ledger + WHERE biz_date = :h_bizdate AND entry_type = 'XFER'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_recon); + setl(b, T_ARG2, h_settle); + setl(b, T_ARG3, h_xfer); + setl(b, T_DELTA, h_recon - h_settle - h_xfer); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_MANCANCEL.h b/app/src/rc/svc/RC_MANCANCEL.h new file mode 100644 index 0000000..31afda9 --- /dev/null +++ b/app/src/rc/svc/RC_MANCANCEL.h @@ -0,0 +1,18 @@ +/* + * RC_MANCANCEL.h - rc 서비스 RC_MANCANCEL 카피북 (copybook / record header). + * 수작업 신청 취소: 아직 확정되지 않은 수작업 HOLD 신청 로그를 삭제. + */ +#ifndef RC_MANCANCEL_H +#define RC_MANCANCEL_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_mancancel_rec_t; + +void RC_MANCANCEL(TPSVCINFO *p); + +#endif /* RC_MANCANCEL_H */ diff --git a/app/src/rc/svc/RC_MANCANCEL.pgc b/app/src/rc/svc/RC_MANCANCEL.pgc new file mode 100644 index 0000000..64ad1b3 --- /dev/null +++ b/app/src/rc/svc/RC_MANCANCEL.pgc @@ -0,0 +1,30 @@ +/* + * RC_MANCANCEL.pgc - rc 모듈 서비스 RC_MANCANCEL (one service = one file). + * 수작업 신청 취소: 아직 확정되지 않은 수작업 HOLD 신청 로그를 삭제. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_MANCANCEL.h" + +/* 104. RC_MANCANCEL - 수작업 신청 취소: 아직 확정되지 않은 수작업 HOLD 신청 로그를 삭제. */ +void RC_MANCANCEL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pid, h_del; + 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; + + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL DELETE FROM rc_match_log + WHERE purchase_id = :h_pid AND match_type = 'MANUAL' AND status = 'HOLD'; + if (sqlca.sqlcode < 0) FAIL(b); + h_del = sqlca.sqlerrd[2]; + setl(b, T_COUNT, h_del); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_MANLIST.h b/app/src/rc/svc/RC_MANLIST.h new file mode 100644 index 0000000..e1e39d9 --- /dev/null +++ b/app/src/rc/svc/RC_MANLIST.h @@ -0,0 +1,18 @@ +/* + * RC_MANLIST.h - rc 서비스 RC_MANLIST 카피북 (copybook / record header). + * 수작업 신청 현황: 당일 수작업 대사 로그를 상태별(HOLD/확정)로 나누어 건수 집계. + */ +#ifndef RC_MANLIST_H +#define RC_MANLIST_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_manlist_rec_t; + +void RC_MANLIST(TPSVCINFO *p); + +#endif /* RC_MANLIST_H */ diff --git a/app/src/rc/svc/RC_MANLIST.pgc b/app/src/rc/svc/RC_MANLIST.pgc new file mode 100644 index 0000000..c1df853 --- /dev/null +++ b/app/src/rc/svc/RC_MANLIST.pgc @@ -0,0 +1,32 @@ +/* + * RC_MANLIST.pgc - rc 모듈 서비스 RC_MANLIST (one service = one file). + * 수작업 신청 현황: 당일 수작업 대사 로그를 상태별(HOLD/확정)로 나누어 건수 집계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_MANLIST.h" + +/* 105. RC_MANLIST - 수작업 신청 현황: 당일 수작업 대사 로그를 상태별(HOLD/확정)로 나누어 건수 집계. */ +void RC_MANLIST(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_hold, h_conf; + 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(*) INTO :h_hold FROM rc_match_log + WHERE biz_date = :h_bizdate AND match_type = 'MANUAL' AND status = 'HOLD'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_conf FROM rc_match_log + WHERE biz_date = :h_bizdate AND match_type = 'MANUAL' AND status = 'CONFIRMED'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_hold); + setl(b, T_ARG2, h_conf); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_MANNG.h b/app/src/rc/svc/RC_MANNG.h new file mode 100644 index 0000000..a2b8888 --- /dev/null +++ b/app/src/rc/svc/RC_MANNG.h @@ -0,0 +1,18 @@ +/* + * RC_MANNG.h - rc 서비스 RC_MANNG 카피북 (copybook / record header). + * 수작업 대사 반려: 해당 매입의 수작업 HOLD 로그를 ROLLBACK 으로 반려 처리. + */ +#ifndef RC_MANNG_H +#define RC_MANNG_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_manng_rec_t; + +void RC_MANNG(TPSVCINFO *p); + +#endif /* RC_MANNG_H */ diff --git a/app/src/rc/svc/RC_MANNG.pgc b/app/src/rc/svc/RC_MANNG.pgc new file mode 100644 index 0000000..b6b81ef --- /dev/null +++ b/app/src/rc/svc/RC_MANNG.pgc @@ -0,0 +1,30 @@ +/* + * RC_MANNG.pgc - rc 모듈 서비스 RC_MANNG (one service = one file). + * 수작업 대사 반려: 해당 매입의 수작업 HOLD 로그를 ROLLBACK 으로 반려 처리. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_MANNG.h" + +/* 103. RC_MANNG - 수작업 대사 반려: 해당 매입의 수작업 HOLD 로그를 ROLLBACK 으로 반려 처리. */ +void RC_MANNG(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pid, h_upd; + 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; + + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL UPDATE rc_match_log SET status = 'ROLLBACK', updated_at = now() + WHERE purchase_id = :h_pid AND match_type = 'MANUAL' AND status = 'HOLD'; + if (sqlca.sqlcode < 0) FAIL(b); + h_upd = sqlca.sqlerrd[2]; + setl(b, T_COUNT, h_upd); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_MANOK.h b/app/src/rc/svc/RC_MANOK.h new file mode 100644 index 0000000..bd1c21e --- /dev/null +++ b/app/src/rc/svc/RC_MANOK.h @@ -0,0 +1,18 @@ +/* + * RC_MANOK.h - rc 서비스 RC_MANOK 카피북 (copybook / record header). + * 수작업 대사 확정: 해당 매입의 수작업 HOLD 로그를 CONFIRMED 로 승인 처리. + */ +#ifndef RC_MANOK_H +#define RC_MANOK_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_manok_rec_t; + +void RC_MANOK(TPSVCINFO *p); + +#endif /* RC_MANOK_H */ diff --git a/app/src/rc/svc/RC_MANOK.pgc b/app/src/rc/svc/RC_MANOK.pgc new file mode 100644 index 0000000..1a20271 --- /dev/null +++ b/app/src/rc/svc/RC_MANOK.pgc @@ -0,0 +1,30 @@ +/* + * RC_MANOK.pgc - rc 모듈 서비스 RC_MANOK (one service = one file). + * 수작업 대사 확정: 해당 매입의 수작업 HOLD 로그를 CONFIRMED 로 승인 처리. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_MANOK.h" + +/* 102. RC_MANOK - 수작업 대사 확정: 해당 매입의 수작업 HOLD 로그를 CONFIRMED 로 승인 처리. */ +void RC_MANOK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pid, h_upd; + 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; + + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL UPDATE rc_match_log SET status = 'CONFIRMED', updated_at = now() + WHERE purchase_id = :h_pid AND match_type = 'MANUAL' AND status = 'HOLD'; + if (sqlca.sqlcode < 0) FAIL(b); + h_upd = sqlca.sqlerrd[2]; + setl(b, T_COUNT, h_upd); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_MANREQ.h b/app/src/rc/svc/RC_MANREQ.h new file mode 100644 index 0000000..7a76c6a --- /dev/null +++ b/app/src/rc/svc/RC_MANREQ.h @@ -0,0 +1,18 @@ +/* + * RC_MANREQ.h - rc 서비스 RC_MANREQ 카피북 (copybook / record header). + * 수작업 대사 신청: 지정 매입에 대해 수작업 대사 요청을 HOLD 상태 로그로 등록. + */ +#ifndef RC_MANREQ_H +#define RC_MANREQ_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_manreq_rec_t; + +void RC_MANREQ(TPSVCINFO *p); + +#endif /* RC_MANREQ_H */ diff --git a/app/src/rc/svc/RC_MANREQ.pgc b/app/src/rc/svc/RC_MANREQ.pgc new file mode 100644 index 0000000..f163022 --- /dev/null +++ b/app/src/rc/svc/RC_MANREQ.pgc @@ -0,0 +1,32 @@ +/* + * RC_MANREQ.pgc - rc 모듈 서비스 RC_MANREQ (one service = one file). + * 수작업 대사 신청: 지정 매입에 대해 수작업 대사 요청을 HOLD 상태 로그로 등록. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_MANREQ.h" + +/* 101. RC_MANREQ - 수작업 대사 신청: 지정 매입에 대해 수작업 대사 요청을 HOLD 상태 로그로 등록. */ +void RC_MANREQ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid, h_amt; + 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; + + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL SELECT coalesce(net,0) INTO :h_amt FROM purchase WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0 && sqlca.sqlcode != 100) FAIL(b); + EXEC SQL SELECT nextval('rc_match_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_match_log (match_id, purchase_id, match_type, amount, status, biz_date) + VALUES (:h_id, :h_pid, 'MANUAL', :h_amt, 'HOLD', :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ID1, h_id); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_MGRPDIFF.h b/app/src/rc/svc/RC_MGRPDIFF.h new file mode 100644 index 0000000..a0656d5 --- /dev/null +++ b/app/src/rc/svc/RC_MGRPDIFF.h @@ -0,0 +1,18 @@ +/* + * RC_MGRPDIFF.h - rc 서비스 RC_MGRPDIFF 카피북 (copybook / record header). + * 가맹점그룹별 차이 분석: 가맹점 접두 그룹의 매입 순액과 정산 순액 차액을 집계. + */ +#ifndef RC_MGRPDIFF_H +#define RC_MGRPDIFF_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_mgrpdiff_rec_t; + +void RC_MGRPDIFF(TPSVCINFO *p); + +#endif /* RC_MGRPDIFF_H */ diff --git a/app/src/rc/svc/RC_MGRPDIFF.pgc b/app/src/rc/svc/RC_MGRPDIFF.pgc new file mode 100644 index 0000000..6e3c56b --- /dev/null +++ b/app/src/rc/svc/RC_MGRPDIFF.pgc @@ -0,0 +1,33 @@ +/* + * RC_MGRPDIFF.pgc - rc 모듈 서비스 RC_MGRPDIFF (one service = one file). + * 가맹점그룹별 차이 분석: 가맹점 접두 그룹의 매입 순액과 정산 순액 차액을 집계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_MGRPDIFF.h" + +/* 93. RC_MGRPDIFF - 가맹점그룹별 차이 분석: 가맹점 접두 그룹의 매입 순액과 정산 순액 차액을 집계. */ +void RC_MGRPDIFF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_exp, h_act; char h_grp[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; + + { char c[16]; gets_(b, T_KEY1, c, sizeof(c)); + strncpy(h_grp, c, sizeof(h_grp)-1); h_grp[sizeof(h_grp)-1] = 0; } + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_exp FROM purchase + WHERE biz_date = :h_bizdate AND merchant_id LIKE :h_grp || '%'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_act FROM settlement + WHERE biz_date = :h_bizdate AND merchant_id LIKE :h_grp || '%'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_DELTA, h_exp - h_act); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_MGRPRECON.h b/app/src/rc/svc/RC_MGRPRECON.h new file mode 100644 index 0000000..4408b9e --- /dev/null +++ b/app/src/rc/svc/RC_MGRPRECON.h @@ -0,0 +1,18 @@ +/* + * RC_MGRPRECON.h - rc 서비스 RC_MGRPRECON 카피북 (copybook / record header). + * 가맹점그룹별 대사: 가맹점 ID 접두 그룹(prefix) 단위로 매입 순액을 묶어 대사. + */ +#ifndef RC_MGRPRECON_H +#define RC_MGRPRECON_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_mgrprecon_rec_t; + +void RC_MGRPRECON(TPSVCINFO *p); + +#endif /* RC_MGRPRECON_H */ diff --git a/app/src/rc/svc/RC_MGRPRECON.pgc b/app/src/rc/svc/RC_MGRPRECON.pgc new file mode 100644 index 0000000..54fc002 --- /dev/null +++ b/app/src/rc/svc/RC_MGRPRECON.pgc @@ -0,0 +1,30 @@ +/* + * RC_MGRPRECON.pgc - rc 모듈 서비스 RC_MGRPRECON (one service = one file). + * 가맹점그룹별 대사: 가맹점 ID 접두 그룹(prefix) 단위로 매입 순액을 묶어 대사. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_MGRPRECON.h" + +/* 87. RC_MGRPRECON - 가맹점그룹별 대사: 가맹점 ID 접두 그룹(prefix) 단위로 매입 순액을 묶어 대사. */ +void RC_MGRPRECON(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_net; char h_grp[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; + + { char c[16]; gets_(b, T_KEY1, c, sizeof(c)); + strncpy(h_grp, c, sizeof(h_grp)-1); h_grp[sizeof(h_grp)-1] = 0; } + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_net FROM purchase + WHERE biz_date = :h_bizdate AND merchant_id LIKE :h_grp || '%'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_NET, h_net); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_OVERAMT.h b/app/src/rc/svc/RC_OVERAMT.h new file mode 100644 index 0000000..36b8683 --- /dev/null +++ b/app/src/rc/svc/RC_OVERAMT.h @@ -0,0 +1,18 @@ +/* + * RC_OVERAMT.h - rc 서비스 RC_OVERAMT 카피북 (copybook / record header). + * 과다 차이 분석: 당일 rc_diff 중 실제가 기대를 초과(diff_amt<0)한 과다 건수/합계. + */ +#ifndef RC_OVERAMT_H +#define RC_OVERAMT_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_overamt_rec_t; + +void RC_OVERAMT(TPSVCINFO *p); + +#endif /* RC_OVERAMT_H */ diff --git a/app/src/rc/svc/RC_OVERAMT.pgc b/app/src/rc/svc/RC_OVERAMT.pgc new file mode 100644 index 0000000..3e454ea --- /dev/null +++ b/app/src/rc/svc/RC_OVERAMT.pgc @@ -0,0 +1,29 @@ +/* + * RC_OVERAMT.pgc - rc 모듈 서비스 RC_OVERAMT (one service = one file). + * 과다 차이 분석: 당일 rc_diff 중 실제가 기대를 초과(diff_amt<0)한 과다 건수/합계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_OVERAMT.h" + +/* 94. RC_OVERAMT - 과다 차이 분석: 당일 rc_diff 중 실제가 기대를 초과(diff_amt<0)한 과다 건수/합계. */ +void RC_OVERAMT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_sum; + 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(diff_amt),0) INTO :h_cnt, :h_sum + FROM rc_diff WHERE biz_date = :h_bizdate AND diff_amt < 0; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + setl(b, T_DELTA, h_sum); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_PARTDEP.h b/app/src/rc/svc/RC_PARTDEP.h new file mode 100644 index 0000000..20c2529 --- /dev/null +++ b/app/src/rc/svc/RC_PARTDEP.h @@ -0,0 +1,18 @@ +/* + * RC_PARTDEP.h - rc 서비스 RC_PARTDEP 카피북 (copybook / record header). + * 부분 입금 대사: 매입 순액보다 RECON 입금 누계가 작은 부분입금 건수를 집계. + */ +#ifndef RC_PARTDEP_H +#define RC_PARTDEP_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_partdep_rec_t; + +void RC_PARTDEP(TPSVCINFO *p); + +#endif /* RC_PARTDEP_H */ diff --git a/app/src/rc/svc/RC_PARTDEP.pgc b/app/src/rc/svc/RC_PARTDEP.pgc new file mode 100644 index 0000000..3d297d3 --- /dev/null +++ b/app/src/rc/svc/RC_PARTDEP.pgc @@ -0,0 +1,30 @@ +/* + * RC_PARTDEP.pgc - rc 모듈 서비스 RC_PARTDEP (one service = one file). + * 부분 입금 대사: 매입 순액보다 RECON 입금 누계가 작은 부분입금 건수를 집계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_PARTDEP.h" + +/* 121. RC_PARTDEP - 부분 입금 대사: 매입 순액보다 RECON 입금 누계가 작은 부분입금 건수를 집계. */ +void RC_PARTDEP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt; + 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(*) INTO :h_cnt FROM purchase p + WHERE p.biz_date = :h_bizdate + AND coalesce((SELECT sum(amount) FROM ledger l + WHERE l.purchase_id = p.purchase_id AND l.entry_type = 'RECON'),0) < p.net; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_PAYGAP.h b/app/src/rc/svc/RC_PAYGAP.h new file mode 100644 index 0000000..e370e30 --- /dev/null +++ b/app/src/rc/svc/RC_PAYGAP.h @@ -0,0 +1,18 @@ +/* + * RC_PAYGAP.h - rc 서비스 RC_PAYGAP 카피북 (copybook / record header). + * 지급 누락 감지: 대사완료(M) 매입 중 정산 레코드가 없는 미지급 건수를 반환. + */ +#ifndef RC_PAYGAP_H +#define RC_PAYGAP_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_paygap_rec_t; + +void RC_PAYGAP(TPSVCINFO *p); + +#endif /* RC_PAYGAP_H */ diff --git a/app/src/rc/svc/RC_PAYGAP.pgc b/app/src/rc/svc/RC_PAYGAP.pgc new file mode 100644 index 0000000..98ad19f --- /dev/null +++ b/app/src/rc/svc/RC_PAYGAP.pgc @@ -0,0 +1,29 @@ +/* + * RC_PAYGAP.pgc - rc 모듈 서비스 RC_PAYGAP (one service = one file). + * 지급 누락 감지: 대사완료(M) 매입 중 정산 레코드가 없는 미지급 건수를 반환. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_PAYGAP.h" + +/* 83. RC_PAYGAP - 지급 누락 감지: 대사완료(M) 매입 중 정산 레코드가 없는 미지급 건수를 반환. */ +void RC_PAYGAP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt; + 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(*) INTO :h_cnt FROM purchase p + WHERE 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); + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_PAYLEDGER.h b/app/src/rc/svc/RC_PAYLEDGER.h new file mode 100644 index 0000000..cd73867 --- /dev/null +++ b/app/src/rc/svc/RC_PAYLEDGER.h @@ -0,0 +1,18 @@ +/* + * RC_PAYLEDGER.h - rc 서비스 RC_PAYLEDGER 카피북 (copybook / record header). + * 지급-원장 축 대사: 정산 순액과 원장 SETTLE 기표 합계가 일치하는지 검증하고 차액 반환. + */ +#ifndef RC_PAYLEDGER_H +#define RC_PAYLEDGER_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_payledger_rec_t; + +void RC_PAYLEDGER(TPSVCINFO *p); + +#endif /* RC_PAYLEDGER_H */ diff --git a/app/src/rc/svc/RC_PAYLEDGER.pgc b/app/src/rc/svc/RC_PAYLEDGER.pgc new file mode 100644 index 0000000..6bbbefd --- /dev/null +++ b/app/src/rc/svc/RC_PAYLEDGER.pgc @@ -0,0 +1,33 @@ +/* + * RC_PAYLEDGER.pgc - rc 모듈 서비스 RC_PAYLEDGER (one service = one file). + * 지급-원장 축 대사: 정산 순액과 원장 SETTLE 기표 합계가 일치하는지 검증하고 차액 반환. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_PAYLEDGER.h" + +/* 79. RC_PAYLEDGER - 지급-원장 축 대사: 정산 순액과 원장 SETTLE 기표 합계가 일치하는지 검증하고 차액 반환. */ +void RC_PAYLEDGER(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_settle, h_ledger; + 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_settle FROM settlement + WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_ledger FROM ledger + WHERE biz_date = :h_bizdate AND entry_type = 'SETTLE'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_GROSS, h_settle); + setl(b, T_ARG1, h_ledger); + setl(b, T_DELTA, h_settle - h_ledger); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_POSRECON.h b/app/src/rc/svc/RC_POSRECON.h new file mode 100644 index 0000000..84371eb --- /dev/null +++ b/app/src/rc/svc/RC_POSRECON.h @@ -0,0 +1,18 @@ +/* + * RC_POSRECON.h - rc 서비스 RC_POSRECON 카피북 (copybook / record header). + * POS 채널 대사: POS 채널 매입의 대사완료율을 건수 기준으로 산출(백분율). + */ +#ifndef RC_POSRECON_H +#define RC_POSRECON_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_posrecon_rec_t; + +void RC_POSRECON(TPSVCINFO *p); + +#endif /* RC_POSRECON_H */ diff --git a/app/src/rc/svc/RC_POSRECON.pgc b/app/src/rc/svc/RC_POSRECON.pgc new file mode 100644 index 0000000..4855eee --- /dev/null +++ b/app/src/rc/svc/RC_POSRECON.pgc @@ -0,0 +1,33 @@ +/* + * RC_POSRECON.pgc - rc 모듈 서비스 RC_POSRECON (one service = one file). + * POS 채널 대사: POS 채널 매입의 대사완료율을 건수 기준으로 산출(백분율). + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_POSRECON.h" + +/* 88. RC_POSRECON - POS 채널 대사: POS 채널 매입의 대사완료율을 건수 기준으로 산출(백분율). */ +void RC_POSRECON(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_total, h_done, h_rate; + 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(*) INTO :h_total FROM purchase + WHERE biz_date = :h_bizdate AND channel = 'POS'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_done FROM purchase + WHERE biz_date = :h_bizdate AND channel = 'POS' AND status IN ('M','S'); + if (sqlca.sqlcode < 0) FAIL(b); + h_rate = (h_total > 0) ? (h_done * 100 / h_total) : 0; + setl(b, T_COUNT, h_total); + setl(b, T_RC, h_rate); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_RATECHAN.h b/app/src/rc/svc/RC_RATECHAN.h new file mode 100644 index 0000000..ade7297 --- /dev/null +++ b/app/src/rc/svc/RC_RATECHAN.h @@ -0,0 +1,18 @@ +/* + * RC_RATECHAN.h - rc 서비스 RC_RATECHAN 카피북 (copybook / record header). + * 채널별 대사율: 지정 채널의 당일 매입 대비 대사완료 비율을 백분율로 산출. + */ +#ifndef RC_RATECHAN_H +#define RC_RATECHAN_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_ratechan_rec_t; + +void RC_RATECHAN(TPSVCINFO *p); + +#endif /* RC_RATECHAN_H */ diff --git a/app/src/rc/svc/RC_RATECHAN.pgc b/app/src/rc/svc/RC_RATECHAN.pgc new file mode 100644 index 0000000..a02a80e --- /dev/null +++ b/app/src/rc/svc/RC_RATECHAN.pgc @@ -0,0 +1,34 @@ +/* + * RC_RATECHAN.pgc - rc 모듈 서비스 RC_RATECHAN (one service = one file). + * 채널별 대사율: 지정 채널의 당일 매입 대비 대사완료 비율을 백분율로 산출. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_RATECHAN.h" + +/* 129. RC_RATECHAN - 채널별 대사율: 지정 채널의 당일 매입 대비 대사완료 비율을 백분율로 산출. */ +void RC_RATECHAN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_total, h_done; char h_chan[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; + + { char c[16]; gets_(b, T_CHANNEL, c, sizeof(c)); + strncpy(h_chan, c, sizeof(h_chan)-1); h_chan[sizeof(h_chan)-1] = 0; } + EXEC SQL SELECT count(*) INTO :h_total FROM purchase + WHERE biz_date = :h_bizdate AND channel = :h_chan; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_done FROM purchase + WHERE biz_date = :h_bizdate AND channel = :h_chan AND status IN ('M','S'); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_total); + setl(b, T_RC, (h_total > 0) ? (h_done * 100 / h_total) : 0); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_RATEDAY.h b/app/src/rc/svc/RC_RATEDAY.h new file mode 100644 index 0000000..a738045 --- /dev/null +++ b/app/src/rc/svc/RC_RATEDAY.h @@ -0,0 +1,18 @@ +/* + * RC_RATEDAY.h - rc 서비스 RC_RATEDAY 카피북 (copybook / record header). + * 일별 대사율: 당일 매입 대비 대사완료 건수 비율을 백분율로 산출. + */ +#ifndef RC_RATEDAY_H +#define RC_RATEDAY_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_rateday_rec_t; + +void RC_RATEDAY(TPSVCINFO *p); + +#endif /* RC_RATEDAY_H */ diff --git a/app/src/rc/svc/RC_RATEDAY.pgc b/app/src/rc/svc/RC_RATEDAY.pgc new file mode 100644 index 0000000..566b5c1 --- /dev/null +++ b/app/src/rc/svc/RC_RATEDAY.pgc @@ -0,0 +1,30 @@ +/* + * RC_RATEDAY.pgc - rc 모듈 서비스 RC_RATEDAY (one service = one file). + * 일별 대사율: 당일 매입 대비 대사완료 건수 비율을 백분율로 산출. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_RATEDAY.h" + +/* 126. RC_RATEDAY - 일별 대사율: 당일 매입 대비 대사완료 건수 비율을 백분율로 산출. */ +void RC_RATEDAY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_total, h_done; + 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(*) INTO :h_total FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_done FROM purchase + WHERE biz_date = :h_bizdate AND status IN ('M','S'); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_RC, (h_total > 0) ? (h_done * 100 / h_total) : 0); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_RATEMERCH.h b/app/src/rc/svc/RC_RATEMERCH.h new file mode 100644 index 0000000..e74eb1f --- /dev/null +++ b/app/src/rc/svc/RC_RATEMERCH.h @@ -0,0 +1,18 @@ +/* + * RC_RATEMERCH.h - rc 서비스 RC_RATEMERCH 카피북 (copybook / record header). + * 가맹점별 대사율: 지정 가맹점의 당일 매입 대비 대사완료 비율을 백분율로 산출. + */ +#ifndef RC_RATEMERCH_H +#define RC_RATEMERCH_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_ratemerch_rec_t; + +void RC_RATEMERCH(TPSVCINFO *p); + +#endif /* RC_RATEMERCH_H */ diff --git a/app/src/rc/svc/RC_RATEMERCH.pgc b/app/src/rc/svc/RC_RATEMERCH.pgc new file mode 100644 index 0000000..27c40c4 --- /dev/null +++ b/app/src/rc/svc/RC_RATEMERCH.pgc @@ -0,0 +1,34 @@ +/* + * RC_RATEMERCH.pgc - rc 모듈 서비스 RC_RATEMERCH (one service = one file). + * 가맹점별 대사율: 지정 가맹점의 당일 매입 대비 대사완료 비율을 백분율로 산출. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_RATEMERCH.h" + +/* 130. RC_RATEMERCH - 가맹점별 대사율: 지정 가맹점의 당일 매입 대비 대사완료 비율을 백분율로 산출. */ +void RC_RATEMERCH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_total, h_done; char h_mid[64]; + 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; + + { char c[64]; gets_(b, T_MERCHANT, c, sizeof(c)); + strncpy(h_mid, c, sizeof(h_mid)-1); h_mid[sizeof(h_mid)-1] = 0; } + EXEC SQL SELECT count(*) INTO :h_total FROM purchase + WHERE biz_date = :h_bizdate AND merchant_id = :h_mid; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_done FROM purchase + WHERE biz_date = :h_bizdate AND merchant_id = :h_mid AND status IN ('M','S'); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_total); + setl(b, T_RC, (h_total > 0) ? (h_done * 100 / h_total) : 0); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_RATEMON.h b/app/src/rc/svc/RC_RATEMON.h new file mode 100644 index 0000000..9666678 --- /dev/null +++ b/app/src/rc/svc/RC_RATEMON.h @@ -0,0 +1,18 @@ +/* + * RC_RATEMON.h - rc 서비스 RC_RATEMON 카피북 (copybook / record header). + * 월별 대사율: 기준일이 속한 달 전체 매입 대비 대사완료 비율을 백분율로 산출. + */ +#ifndef RC_RATEMON_H +#define RC_RATEMON_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_ratemon_rec_t; + +void RC_RATEMON(TPSVCINFO *p); + +#endif /* RC_RATEMON_H */ diff --git a/app/src/rc/svc/RC_RATEMON.pgc b/app/src/rc/svc/RC_RATEMON.pgc new file mode 100644 index 0000000..4e630ff --- /dev/null +++ b/app/src/rc/svc/RC_RATEMON.pgc @@ -0,0 +1,33 @@ +/* + * RC_RATEMON.pgc - rc 모듈 서비스 RC_RATEMON (one service = one file). + * 월별 대사율: 기준일이 속한 달 전체 매입 대비 대사완료 비율을 백분율로 산출. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_RATEMON.h" + +/* 128. RC_RATEMON - 월별 대사율: 기준일이 속한 달 전체 매입 대비 대사완료 비율을 백분율로 산출. */ +void RC_RATEMON(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_total, h_done; + 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(*) INTO :h_total FROM purchase + WHERE date_trunc('month', biz_date) = date_trunc('month', :h_bizdate::date); + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_done FROM purchase + WHERE date_trunc('month', biz_date) = date_trunc('month', :h_bizdate::date) + AND status IN ('M','S'); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_total); + setl(b, T_RC, (h_total > 0) ? (h_done * 100 / h_total) : 0); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_RATEWEEK.h b/app/src/rc/svc/RC_RATEWEEK.h new file mode 100644 index 0000000..c029d3b --- /dev/null +++ b/app/src/rc/svc/RC_RATEWEEK.h @@ -0,0 +1,18 @@ +/* + * RC_RATEWEEK.h - rc 서비스 RC_RATEWEEK 카피북 (copybook / record header). + * 주별 대사율: 기준일 포함 최근 7영업일 매입 대비 대사완료 비율을 백분율로 산출. + */ +#ifndef RC_RATEWEEK_H +#define RC_RATEWEEK_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_rateweek_rec_t; + +void RC_RATEWEEK(TPSVCINFO *p); + +#endif /* RC_RATEWEEK_H */ diff --git a/app/src/rc/svc/RC_RATEWEEK.pgc b/app/src/rc/svc/RC_RATEWEEK.pgc new file mode 100644 index 0000000..81ba660 --- /dev/null +++ b/app/src/rc/svc/RC_RATEWEEK.pgc @@ -0,0 +1,33 @@ +/* + * RC_RATEWEEK.pgc - rc 모듈 서비스 RC_RATEWEEK (one service = one file). + * 주별 대사율: 기준일 포함 최근 7영업일 매입 대비 대사완료 비율을 백분율로 산출. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_RATEWEEK.h" + +/* 127. RC_RATEWEEK - 주별 대사율: 기준일 포함 최근 7영업일 매입 대비 대사완료 비율을 백분율로 산출. */ +void RC_RATEWEEK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_total, h_done; + 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(*) INTO :h_total FROM purchase + WHERE biz_date > :h_bizdate::date - 7 AND biz_date <= :h_bizdate::date; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_done FROM purchase + WHERE biz_date > :h_bizdate::date - 7 AND biz_date <= :h_bizdate::date + AND status IN ('M','S'); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_total); + setl(b, T_RC, (h_total > 0) ? (h_done * 100 / h_total) : 0); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_RECORR.h b/app/src/rc/svc/RC_RECORR.h new file mode 100644 index 0000000..0193262 --- /dev/null +++ b/app/src/rc/svc/RC_RECORR.h @@ -0,0 +1,18 @@ +/* + * RC_RECORR.h - rc 서비스 RC_RECORR 카피북 (copybook / record header). + * 정정후 재대사: 수수료조정이 반영된 매입을 대상으로 순액 재검증 후 로그를 남긴다. + */ +#ifndef RC_RECORR_H +#define RC_RECORR_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_recorr_rec_t; + +void RC_RECORR(TPSVCINFO *p); + +#endif /* RC_RECORR_H */ diff --git a/app/src/rc/svc/RC_RECORR.pgc b/app/src/rc/svc/RC_RECORR.pgc new file mode 100644 index 0000000..5321172 --- /dev/null +++ b/app/src/rc/svc/RC_RECORR.pgc @@ -0,0 +1,34 @@ +/* + * RC_RECORR.pgc - rc 모듈 서비스 RC_RECORR (one service = one file). + * 정정후 재대사: 수수료조정이 반영된 매입을 대상으로 순액 재검증 후 로그를 남긴다. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_RECORR.h" + +/* 119. RC_RECORR - 정정후 재대사: 수수료조정이 반영된 매입을 대상으로 순액 재검증 후 로그를 남긴다. */ +void RC_RECORR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid, h_net, h_adj; + 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; + + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL SELECT coalesce(net,0) INTO :h_net FROM purchase WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0 && sqlca.sqlcode != 100) FAIL(b); + EXEC SQL SELECT coalesce(sum(delta),0) INTO :h_adj FROM ac_fee_adj WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT nextval('rc_match_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_match_log (match_id, purchase_id, match_type, amount, status, biz_date) + VALUES (:h_id, :h_pid, 'RECORR', :h_net + :h_adj, 'MATCHED', :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_NET, h_net + h_adj); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_REDIFF.h b/app/src/rc/svc/RC_REDIFF.h new file mode 100644 index 0000000..a30dbe8 --- /dev/null +++ b/app/src/rc/svc/RC_REDIFF.h @@ -0,0 +1,18 @@ +/* + * RC_REDIFF.h - rc 서비스 RC_REDIFF 카피북 (copybook / record header). + * 차이발생 재대사: 지정 매입의 기대 순액과 SETTLE 실적 차이를 재산출해 rc_diff 갱신. + */ +#ifndef RC_REDIFF_H +#define RC_REDIFF_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_rediff_rec_t; + +void RC_REDIFF(TPSVCINFO *p); + +#endif /* RC_REDIFF_H */ diff --git a/app/src/rc/svc/RC_REDIFF.pgc b/app/src/rc/svc/RC_REDIFF.pgc new file mode 100644 index 0000000..215bf80 --- /dev/null +++ b/app/src/rc/svc/RC_REDIFF.pgc @@ -0,0 +1,35 @@ +/* + * RC_REDIFF.pgc - rc 모듈 서비스 RC_REDIFF (one service = one file). + * 차이발생 재대사: 지정 매입의 기대 순액과 SETTLE 실적 차이를 재산출해 rc_diff 갱신. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_REDIFF.h" + +/* 117. RC_REDIFF - 차이발생 재대사: 지정 매입의 기대 순액과 SETTLE 실적 차이를 재산출해 rc_diff 갱신. */ +void RC_REDIFF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_id, h_pid, h_exp, h_act; + 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; + + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL SELECT coalesce(net,0) INTO :h_exp 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_act FROM ledger + WHERE purchase_id = :h_pid AND entry_type = 'SETTLE'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT nextval('rc_diff_seq') INTO :h_id; + EXEC SQL INSERT INTO rc_diff (diff_id, purchase_id, expected_amt, actual_amt, diff_amt, diff_kind, biz_date) + VALUES (:h_id, :h_pid, :h_exp, :h_act, :h_exp - :h_act, 'AMT', :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_DELTA, h_exp - h_act); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_REFULL.h b/app/src/rc/svc/RC_REFULL.h new file mode 100644 index 0000000..1cdc497 --- /dev/null +++ b/app/src/rc/svc/RC_REFULL.h @@ -0,0 +1,18 @@ +/* + * RC_REFULL.h - rc 서비스 RC_REFULL 카피북 (copybook / record header). + * 전체 재대사: 당일 전체 매입 대비 대사완료(M/S) 비율을 재계산해 진척률로 반환. + */ +#ifndef RC_REFULL_H +#define RC_REFULL_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_refull_rec_t; + +void RC_REFULL(TPSVCINFO *p); + +#endif /* RC_REFULL_H */ diff --git a/app/src/rc/svc/RC_REFULL.pgc b/app/src/rc/svc/RC_REFULL.pgc new file mode 100644 index 0000000..9aaef36 --- /dev/null +++ b/app/src/rc/svc/RC_REFULL.pgc @@ -0,0 +1,32 @@ +/* + * RC_REFULL.pgc - rc 모듈 서비스 RC_REFULL (one service = one file). + * 전체 재대사: 당일 전체 매입 대비 대사완료(M/S) 비율을 재계산해 진척률로 반환. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_REFULL.h" + +/* 120. RC_REFULL - 전체 재대사: 당일 전체 매입 대비 대사완료(M/S) 비율을 재계산해 진척률로 반환. */ +void RC_REFULL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_total, h_done; + 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(*) INTO :h_total FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_done FROM purchase + WHERE biz_date = :h_bizdate AND status IN ('M','S'); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_total); + setl(b, T_ARG1, h_done); + setl(b, T_RC, (h_total > 0) ? (h_done * 100 / h_total) : 0); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_REFUNDREC.h b/app/src/rc/svc/RC_REFUNDREC.h new file mode 100644 index 0000000..b92fcc6 --- /dev/null +++ b/app/src/rc/svc/RC_REFUNDREC.h @@ -0,0 +1,18 @@ +/* + * RC_REFUNDREC.h - rc 서비스 RC_REFUNDREC 카피북 (copybook / record header). + * 환불분 대사: 취소 유형이 REFUND 인 환불 금액과 건수를 별도로 대사 집계. + */ +#ifndef RC_REFUNDREC_H +#define RC_REFUNDREC_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_refundrec_rec_t; + +void RC_REFUNDREC(TPSVCINFO *p); + +#endif /* RC_REFUNDREC_H */ diff --git a/app/src/rc/svc/RC_REFUNDREC.pgc b/app/src/rc/svc/RC_REFUNDREC.pgc new file mode 100644 index 0000000..6042020 --- /dev/null +++ b/app/src/rc/svc/RC_REFUNDREC.pgc @@ -0,0 +1,29 @@ +/* + * RC_REFUNDREC.pgc - rc 모듈 서비스 RC_REFUNDREC (one service = one file). + * 환불분 대사: 취소 유형이 REFUND 인 환불 금액과 건수를 별도로 대사 집계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_REFUNDREC.h" + +/* 123. RC_REFUNDREC - 환불분 대사: 취소 유형이 REFUND 인 환불 금액과 건수를 별도로 대사 집계. */ +void RC_REFUNDREC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_amt; + 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(amount),0) INTO :h_cnt, :h_amt + FROM ac_cancel WHERE biz_date = :h_bizdate AND cancel_type = 'REFUND'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + setl(b, T_AMOUNT, h_amt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_RELATE.h b/app/src/rc/svc/RC_RELATE.h new file mode 100644 index 0000000..a2d0691 --- /dev/null +++ b/app/src/rc/svc/RC_RELATE.h @@ -0,0 +1,18 @@ +/* + * RC_RELATE.h - rc 서비스 RC_RELATE 카피북 (copybook / record header). + * 지연도착 재대사: 원장 기표가 매입일보다 늦게 도착한 지연 건을 재대사 대상으로 집계. + */ +#ifndef RC_RELATE_H +#define RC_RELATE_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_relate_rec_t; + +void RC_RELATE(TPSVCINFO *p); + +#endif /* RC_RELATE_H */ diff --git a/app/src/rc/svc/RC_RELATE.pgc b/app/src/rc/svc/RC_RELATE.pgc new file mode 100644 index 0000000..958d1f0 --- /dev/null +++ b/app/src/rc/svc/RC_RELATE.pgc @@ -0,0 +1,29 @@ +/* + * RC_RELATE.pgc - rc 모듈 서비스 RC_RELATE (one service = one file). + * 지연도착 재대사: 원장 기표가 매입일보다 늦게 도착한 지연 건을 재대사 대상으로 집계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_RELATE.h" + +/* 118. RC_RELATE - 지연도착 재대사: 원장 기표가 매입일보다 늦게 도착한 지연 건을 재대사 대상으로 집계. */ +void RC_RELATE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt; + 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(DISTINCT p.purchase_id) INTO :h_cnt FROM purchase p + JOIN ledger l ON l.purchase_id = p.purchase_id + WHERE p.biz_date = :h_bizdate AND l.biz_date > p.biz_date; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_ROUNDDIFF.h b/app/src/rc/svc/RC_ROUNDDIFF.h new file mode 100644 index 0000000..b478d0a --- /dev/null +++ b/app/src/rc/svc/RC_ROUNDDIFF.h @@ -0,0 +1,18 @@ +/* + * RC_ROUNDDIFF.h - rc 서비스 RC_ROUNDDIFF 카피북 (copybook / record header). + * 반올림 차이 분석: 절대차가 1 이하인 단수 차이 건을 반올림 오차로 분류해 집계. + */ +#ifndef RC_ROUNDDIFF_H +#define RC_ROUNDDIFF_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_rounddiff_rec_t; + +void RC_ROUNDDIFF(TPSVCINFO *p); + +#endif /* RC_ROUNDDIFF_H */ diff --git a/app/src/rc/svc/RC_ROUNDDIFF.pgc b/app/src/rc/svc/RC_ROUNDDIFF.pgc new file mode 100644 index 0000000..3704033 --- /dev/null +++ b/app/src/rc/svc/RC_ROUNDDIFF.pgc @@ -0,0 +1,28 @@ +/* + * RC_ROUNDDIFF.pgc - rc 모듈 서비스 RC_ROUNDDIFF (one service = one file). + * 반올림 차이 분석: 절대차가 1 이하인 단수 차이 건을 반올림 오차로 분류해 집계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_ROUNDDIFF.h" + +/* 96. RC_ROUNDDIFF - 반올림 차이 분석: 절대차가 1 이하인 단수 차이 건을 반올림 오차로 분류해 집계. */ +void RC_ROUNDDIFF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt; + 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(*) INTO :h_cnt FROM rc_diff + WHERE biz_date = :h_bizdate AND abs(diff_amt) <= 1 AND diff_amt <> 0; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_SIGNDIFF.h b/app/src/rc/svc/RC_SIGNDIFF.h new file mode 100644 index 0000000..3becc57 --- /dev/null +++ b/app/src/rc/svc/RC_SIGNDIFF.h @@ -0,0 +1,18 @@ +/* + * RC_SIGNDIFF.h - rc 서비스 RC_SIGNDIFF 카피북 (copybook / record header). + * 부호 오류 차이 분석: 기대와 실제의 부호가 서로 반대인 rc_diff 라인을 검출. + */ +#ifndef RC_SIGNDIFF_H +#define RC_SIGNDIFF_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_signdiff_rec_t; + +void RC_SIGNDIFF(TPSVCINFO *p); + +#endif /* RC_SIGNDIFF_H */ diff --git a/app/src/rc/svc/RC_SIGNDIFF.pgc b/app/src/rc/svc/RC_SIGNDIFF.pgc new file mode 100644 index 0000000..c9686eb --- /dev/null +++ b/app/src/rc/svc/RC_SIGNDIFF.pgc @@ -0,0 +1,29 @@ +/* + * RC_SIGNDIFF.pgc - rc 모듈 서비스 RC_SIGNDIFF (one service = one file). + * 부호 오류 차이 분석: 기대와 실제의 부호가 서로 반대인 rc_diff 라인을 검출. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_SIGNDIFF.h" + +/* 98. RC_SIGNDIFF - 부호 오류 차이 분석: 기대와 실제의 부호가 서로 반대인 rc_diff 라인을 검출. */ +void RC_SIGNDIFF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt; + 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(*) INTO :h_cnt FROM rc_diff + WHERE biz_date = :h_bizdate + AND ((expected_amt > 0 AND actual_amt < 0) OR (expected_amt < 0 AND actual_amt > 0)); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_TIMEDIFF.h b/app/src/rc/svc/RC_TIMEDIFF.h new file mode 100644 index 0000000..40f11c5 --- /dev/null +++ b/app/src/rc/svc/RC_TIMEDIFF.h @@ -0,0 +1,18 @@ +/* + * RC_TIMEDIFF.h - rc 서비스 RC_TIMEDIFF 카피북 (copybook / record header). + * 기표시점 차이 분석: 매입 영업일과 원장 기표 영업일이 다른 이월/선기표 건수를 집계. + */ +#ifndef RC_TIMEDIFF_H +#define RC_TIMEDIFF_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_timediff_rec_t; + +void RC_TIMEDIFF(TPSVCINFO *p); + +#endif /* RC_TIMEDIFF_H */ diff --git a/app/src/rc/svc/RC_TIMEDIFF.pgc b/app/src/rc/svc/RC_TIMEDIFF.pgc new file mode 100644 index 0000000..7ada11f --- /dev/null +++ b/app/src/rc/svc/RC_TIMEDIFF.pgc @@ -0,0 +1,29 @@ +/* + * RC_TIMEDIFF.pgc - rc 모듈 서비스 RC_TIMEDIFF (one service = one file). + * 기표시점 차이 분석: 매입 영업일과 원장 기표 영업일이 다른 이월/선기표 건수를 집계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_TIMEDIFF.h" + +/* 97. RC_TIMEDIFF - 기표시점 차이 분석: 매입 영업일과 원장 기표 영업일이 다른 이월/선기표 건수를 집계. */ +void RC_TIMEDIFF(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt; + 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(*) INTO :h_cnt FROM purchase p + JOIN ledger l ON l.purchase_id = p.purchase_id + WHERE p.biz_date = :h_bizdate AND l.biz_date <> p.biz_date; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_TRAILQRY.h b/app/src/rc/svc/RC_TRAILQRY.h new file mode 100644 index 0000000..ddba0e6 --- /dev/null +++ b/app/src/rc/svc/RC_TRAILQRY.h @@ -0,0 +1,18 @@ +/* + * RC_TRAILQRY.h - rc 서비스 RC_TRAILQRY 카피북 (copybook / record header). + * 대사 감사추적 조회: 지정 매입의 전체 대사 로그 이력 건수와 최종 로그 ID 를 반환. + */ +#ifndef RC_TRAILQRY_H +#define RC_TRAILQRY_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_trailqry_rec_t; + +void RC_TRAILQRY(TPSVCINFO *p); + +#endif /* RC_TRAILQRY_H */ diff --git a/app/src/rc/svc/RC_TRAILQRY.pgc b/app/src/rc/svc/RC_TRAILQRY.pgc new file mode 100644 index 0000000..7acda82 --- /dev/null +++ b/app/src/rc/svc/RC_TRAILQRY.pgc @@ -0,0 +1,30 @@ +/* + * RC_TRAILQRY.pgc - rc 모듈 서비스 RC_TRAILQRY (one service = one file). + * 대사 감사추적 조회: 지정 매입의 전체 대사 로그 이력 건수와 최종 로그 ID 를 반환. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_TRAILQRY.h" + +/* 133. RC_TRAILQRY - 대사 감사추적 조회: 지정 매입의 전체 대사 로그 이력 건수와 최종 로그 ID 를 반환. */ +void RC_TRAILQRY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pid, h_cnt, h_last = 0; + 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; + + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL SELECT count(*), coalesce(max(match_id),0) INTO :h_cnt, :h_last + FROM rc_match_log WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + setl(b, T_ID1, h_last); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/rc/svc/RC_UNDERAMT.h b/app/src/rc/svc/RC_UNDERAMT.h new file mode 100644 index 0000000..c8be13f --- /dev/null +++ b/app/src/rc/svc/RC_UNDERAMT.h @@ -0,0 +1,18 @@ +/* + * RC_UNDERAMT.h - rc 서비스 RC_UNDERAMT 카피북 (copybook / record header). + * 과소 차이 분석: 당일 rc_diff 중 실제가 기대에 미달(diff_amt>0)한 과소 건수/합계. + */ +#ifndef RC_UNDERAMT_H +#define RC_UNDERAMT_H + +#include "rc_svc.h" + +typedef struct { + long purchase_id; + long amount; + char biz_date[16]; +} rc_rc_underamt_rec_t; + +void RC_UNDERAMT(TPSVCINFO *p); + +#endif /* RC_UNDERAMT_H */ diff --git a/app/src/rc/svc/RC_UNDERAMT.pgc b/app/src/rc/svc/RC_UNDERAMT.pgc new file mode 100644 index 0000000..b662d28 --- /dev/null +++ b/app/src/rc/svc/RC_UNDERAMT.pgc @@ -0,0 +1,29 @@ +/* + * RC_UNDERAMT.pgc - rc 모듈 서비스 RC_UNDERAMT (one service = one file). + * 과소 차이 분석: 당일 rc_diff 중 실제가 기대에 미달(diff_amt>0)한 과소 건수/합계. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by rc_svr. + */ +#include "RC_UNDERAMT.h" + +/* 95. RC_UNDERAMT - 과소 차이 분석: 당일 rc_diff 중 실제가 기대에 미달(diff_amt>0)한 과소 건수/합계. */ +void RC_UNDERAMT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_sum; + 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(diff_amt),0) INTO :h_cnt, :h_sum + FROM rc_diff WHERE biz_date = :h_bizdate AND diff_amt > 0; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + setl(b, T_DELTA, h_sum); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/batch/st_baddebt_batch.pgc b/app/src/st/batch/st_baddebt_batch.pgc new file mode 100644 index 0000000..8a5a07e --- /dev/null +++ b/app/src/st/batch/st_baddebt_batch.pgc @@ -0,0 +1,56 @@ +/* + * st_baddebt_batch.pgc - 대손 상각 배치 (XA 배치). + * 기준일 이전 지급일의 장기 보류(HELD) 미수 지급을 커서로 합산해 대손 규모를 산출한 + * 뒤, 해당 지급을 일괄 취소(CANCELLED)하고 대손 상각 음(-) 조정을 1건 기록한다. + * 사용법: st_baddebt_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "st_dbio.h" +#include "st_baddebt_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + st_baddebt_rec_t rec; + memset(&rec, 0, sizeof(rec)); + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long 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 baddebt_bc CURSOR FOR + SELECT net_amount FROM st_payment + WHERE status = 'HELD' AND pay_date <= :h_bizdate; + EXEC SQL OPEN baddebt_bc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH baddebt_bc INTO :h_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE baddebt_bc; tpabort(0); return 1; } + rec.held_cnt++; rec.writeoff += h_net; + } + EXEC SQL CLOSE baddebt_bc; + + if (rec.held_cnt > 0) { + EXEC SQL UPDATE st_payment SET status = 'CANCELLED' + WHERE status = 'HELD' AND pay_date <= :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "UPD FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + rec.adj_id = stdb_insert_adjust(0, 0, -rec.writeoff, "BADDEBT", bizdate); + if (rec.adj_id < 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 held_cnt=%ld writeoff=%ld adj=%ld\n", + ST_BADDEBT_TAG, bizdate, rec.held_cnt, rec.writeoff, rec.adj_id); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/st/batch/st_biweekly_batch.pgc b/app/src/st/batch/st_biweekly_batch.pgc new file mode 100644 index 0000000..b9a6737 --- /dev/null +++ b/app/src/st/batch/st_biweekly_batch.pgc @@ -0,0 +1,62 @@ +/* + * st_biweekly_batch.pgc - 격주 정산 지급 예정 배치 (XA 배치). + * 당일 정산을 가맹점별 net 으로 집계하고, 영업일 기준 10일 뒤(약 2주) 지급일을 미리 + * 계산해 지급 원장(st_payment)에 지급 예정(SCHEDULED)으로 등록한다. + * 사용법: st_biweekly_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "st_dbio.h" +#include "acq_common.h" +#include "st_biweekly_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + st_biweekly_rec_t rec; + char cur[16], nxt[16], paydate[16]; + int i; + memset(&rec, 0, sizeof(rec)); + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_mid[64]; + long h_net; + EXEC SQL END DECLARE SECTION; + + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strncpy(cur, bizdate, sizeof(cur)-1); cur[sizeof(cur)-1] = 0; + for (i = 0; i < 10; i++) { /* 지급일 = 영업일 +10 */ + if (acq_next_bizday(cur, nxt) < 0) { fprintf(stderr, "DATE FAIL\n"); return 1; } + strncpy(cur, nxt, sizeof(cur)-1); cur[sizeof(cur)-1] = 0; + } + strncpy(paydate, cur, sizeof(paydate)-1); paydate[sizeof(paydate)-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 biweekly_bc CURSOR FOR + SELECT merchant_id, coalesce(sum(net),0) FROM settlement + WHERE biz_date = :h_bizdate GROUP BY merchant_id ORDER BY merchant_id; + EXEC SQL OPEN biweekly_bc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + long pay; + EXEC SQL FETCH biweekly_bc INTO :h_mid, :h_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE biweekly_bc; tpabort(0); return 1; } + if (h_net <= 0) continue; + pay = stdb_insert_payment(h_mid, h_net, paydate, "SCHEDULED", bizdate); + if (pay < 0) { EXEC SQL CLOSE biweekly_bc; tpabort(0); return 1; } + rec.merchants++; rec.sched_total += h_net; rec.pay_last = pay; + printf(" SCHED %-8s net=%ld paydate=%s\n", h_mid, h_net, paydate); + } + EXEC SQL CLOSE biweekly_bc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> %s COMMIT: bizdate=%s paydate=%s merchants=%ld sched_total=%ld\n", + ST_BIWEEKLY_TAG, bizdate, paydate, rec.merchants, rec.sched_total); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/st/batch/st_chargeback_batch.pgc b/app/src/st/batch/st_chargeback_batch.pgc new file mode 100644 index 0000000..3694198 --- /dev/null +++ b/app/src/st/batch/st_chargeback_batch.pgc @@ -0,0 +1,47 @@ +/* + * st_chargeback_batch.pgc - 차지백 회수 정산 배치 (XA 배치). + * 당일 정산 조정 중 차지백(reason=CHARGEBACK) 건을 집계해 회수 규모를 산출하고, + * 당일 지급 예정(SCHEDULED)을 일괄 보류(HELD)로 전환해 회수 대상을 잠근다. + * 사용법: st_chargeback_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "st_dbio.h" +#include "st_chargeback_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + st_chargeback_rec_t rec; + memset(&rec, 0, sizeof(rec)); + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_sum; + 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(*), coalesce(sum(delta),0) INTO :h_cnt, :h_sum + FROM st_adjust WHERE biz_date = :h_bizdate AND reason = 'CHARGEBACK'; + if (sqlca.sqlcode < 0) { fprintf(stderr, "SUM FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + rec.cb_cnt = h_cnt; rec.cb_total = h_sum; + + if (h_cnt > 0) { + EXEC SQL UPDATE st_payment SET status = 'HELD' + WHERE biz_date = :h_bizdate AND status = 'SCHEDULED'; + if (sqlca.sqlcode < 0) { fprintf(stderr, "UPD FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + rec.held_upd = sqlca.sqlerrd[2]; + } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> %s COMMIT: bizdate=%s cb_cnt=%ld cb_total=%ld held_upd=%ld\n", + ST_CHARGEBACK_TAG, bizdate, rec.cb_cnt, rec.cb_total, rec.held_upd); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/st/batch/st_gradestat_batch.pgc b/app/src/st/batch/st_gradestat_batch.pgc new file mode 100644 index 0000000..abf65dd --- /dev/null +++ b/app/src/st/batch/st_gradestat_batch.pgc @@ -0,0 +1,47 @@ +/* + * st_gradestat_batch.pgc - 가맹점 등급별 통계 배치 (XA 배치). + * 가맹점을 요율(mdr_bps) 등급으로 묶어 등급별 가맹점 수를 커서로 집계하고 최다 등급 + * 요율을 산출해 등급 분포를 출력한다 (집계 전용). + * 사용법: st_gradestat_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "st_dbio.h" +#include "st_gradestat_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + st_gradestat_rec_t rec; + long topcnt = 0; + memset(&rec, 0, sizeof(rec)); + EXEC SQL BEGIN DECLARE SECTION; + long h_bps, h_cnt; + EXEC SQL END DECLARE SECTION; + + if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; } + if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; } + + EXEC SQL DECLARE gradestat_bc CURSOR FOR + SELECT mdr_bps, count(*) FROM merchant GROUP BY mdr_bps ORDER BY mdr_bps; + EXEC SQL OPEN gradestat_bc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH gradestat_bc INTO :h_bps, :h_cnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE gradestat_bc; tpabort(0); return 1; } + rec.grades++; rec.merchants += h_cnt; + if (h_cnt > topcnt) { topcnt = h_cnt; rec.top_bps = h_bps; } + printf(" GRADE bps=%ld count=%ld\n", h_bps, h_cnt); + } + EXEC SQL CLOSE gradestat_bc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> %s COMMIT: bizdate=%s grades=%ld merchants=%ld top_bps=%ld\n", + ST_GRADESTAT_TAG, bizdate, rec.grades, rec.merchants, rec.top_bps); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/st/batch/st_grpalloc_batch.pgc b/app/src/st/batch/st_grpalloc_batch.pgc new file mode 100644 index 0000000..ef5d879 --- /dev/null +++ b/app/src/st/batch/st_grpalloc_batch.pgc @@ -0,0 +1,53 @@ +/* + * st_grpalloc_batch.pgc - 그룹 통합정산 배분 배치 (XA 배치). + * 가맹점 ID 앞 2자리를 그룹 키로 묶어 당일 net 을 그룹별로 집계하고, 각 그룹의 대표 + * 지급처로 통합 지급 예정(st_payment)을 등록한다. + * 사용법: st_grpalloc_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "st_dbio.h" +#include "st_grpalloc_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + st_grpalloc_rec_t rec; + memset(&rec, 0, sizeof(rec)); + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_grp[8]; + long 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 grpalloc_bc CURSOR FOR + SELECT left(merchant_id, 2), coalesce(sum(net_amount),0) FROM st_merch_settle + WHERE biz_date = :h_bizdate GROUP BY left(merchant_id, 2) ORDER BY 1; + EXEC SQL OPEN grpalloc_bc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + long pay; + EXEC SQL FETCH grpalloc_bc INTO :h_grp, :h_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE grpalloc_bc; tpabort(0); return 1; } + if (h_net <= 0) continue; + pay = stdb_insert_payment(h_grp, h_net, bizdate, "SCHEDULED", bizdate); + if (pay < 0) { EXEC SQL CLOSE grpalloc_bc; tpabort(0); return 1; } + rec.groups++; rec.alloc_total += h_net; rec.pay_last = pay; + printf(" GRP %-4s net=%ld (pay=%ld)\n", h_grp, h_net, pay); + } + EXEC SQL CLOSE grpalloc_bc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> %s COMMIT: bizdate=%s groups=%ld alloc_total=%ld\n", + ST_GRPALLOC_TAG, bizdate, rec.groups, rec.alloc_total); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/st/batch/st_instant_batch.pgc b/app/src/st/batch/st_instant_batch.pgc new file mode 100644 index 0000000..316f666 --- /dev/null +++ b/app/src/st/batch/st_instant_batch.pgc @@ -0,0 +1,53 @@ +/* + * st_instant_batch.pgc - 즉시정산 지급 배치 (XA 배치). + * 당일 정산(settlement)을 가맹점별 net 으로 집계해 지급 원장(st_payment)에 즉시 + * 지급 완료(PAID) 건으로 등록한다 (SCHEDULED 단계 생략). + * 사용법: st_instant_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "st_dbio.h" +#include "st_instant_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + st_instant_rec_t rec; + memset(&rec, 0, sizeof(rec)); + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_mid[64]; + long 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 instant_bc CURSOR FOR + SELECT merchant_id, coalesce(sum(net),0) FROM settlement + WHERE biz_date = :h_bizdate GROUP BY merchant_id ORDER BY merchant_id; + EXEC SQL OPEN instant_bc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + long pay; + EXEC SQL FETCH instant_bc INTO :h_mid, :h_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE instant_bc; tpabort(0); return 1; } + if (h_net <= 0) continue; + pay = stdb_insert_payment(h_mid, h_net, bizdate, "PAID", bizdate); + if (pay < 0) { EXEC SQL CLOSE instant_bc; tpabort(0); return 1; } + rec.paid_cnt++; rec.paid_total += h_net; rec.pay_last = pay; + printf(" PAID %-8s net=%ld (pay=%ld)\n", h_mid, h_net, pay); + } + EXEC SQL CLOSE instant_bc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> %s COMMIT: bizdate=%s paid_cnt=%ld paid_total=%ld\n", + ST_INSTANT_TAG, bizdate, rec.paid_cnt, rec.paid_total); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/st/batch/st_rebate_batch.pgc b/app/src/st/batch/st_rebate_batch.pgc new file mode 100644 index 0000000..7ad3f4d --- /dev/null +++ b/app/src/st/batch/st_rebate_batch.pgc @@ -0,0 +1,54 @@ +/* + * st_rebate_batch.pgc - 리베이트 정산 배치 (XA 배치). + * 당일 가맹점 집계(st_merch_settle)를 커서로 순회하며 부과 수수료의 5%를 리베이트로 + * 산정해 정산 조정(st_adjust)에 양(+) 금액으로 적립한다. + * 사용법: st_rebate_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "st_dbio.h" +#include "st_rebate_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + st_rebate_rec_t rec; + memset(&rec, 0, sizeof(rec)); + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_mid[64]; + long h_fee; + 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 rebate_bc CURSOR FOR + SELECT merchant_id, fee_amount FROM st_merch_settle + WHERE biz_date = :h_bizdate AND fee_amount > 0 ORDER BY merchant_id; + EXEC SQL OPEN rebate_bc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + long rebate, adj; + EXEC SQL FETCH rebate_bc INTO :h_mid, :h_fee; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE rebate_bc; tpabort(0); return 1; } + rebate = h_fee * 500 / 10000; /* 수수료 5% 리베이트 */ + if (rebate <= 0) continue; + adj = stdb_insert_adjust(0, 0, rebate, "REBATE", bizdate); + if (adj < 0) { EXEC SQL CLOSE rebate_bc; tpabort(0); return 1; } + rec.merchants++; rec.rebate_total += rebate; rec.adj_last = adj; + printf(" REBATE %-8s fee=%ld -> %ld (adj=%ld)\n", h_mid, h_fee, rebate, adj); + } + EXEC SQL CLOSE rebate_bc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> %s COMMIT: bizdate=%s merchants=%ld rebate_total=%ld\n", + ST_REBATE_TAG, bizdate, rec.merchants, rec.rebate_total); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/st/batch/st_recvage_batch.pgc b/app/src/st/batch/st_recvage_batch.pgc new file mode 100644 index 0000000..ff51e4a --- /dev/null +++ b/app/src/st/batch/st_recvage_batch.pgc @@ -0,0 +1,45 @@ +/* + * st_recvage_batch.pgc - 미수금 연령 분석 배치 (XA 배치). + * 보류(HELD) 지급을 기준일 대비 지급일 경과에 따라 연령 구간(정상/연체)으로 나누어 + * 건수와 금액을 집계, 미수금 노후화 규모를 출력한다 (집계 전용). + * 사용법: st_recvage_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "st_dbio.h" +#include "st_recvage_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + st_recvage_rec_t rec; + memset(&rec, 0, sizeof(rec)); + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_sum, h_overdue; + 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(*), coalesce(sum(net_amount),0) INTO :h_cnt, :h_sum + FROM st_payment WHERE status = 'HELD'; + if (sqlca.sqlcode < 0) { fprintf(stderr, "SUM FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + rec.held_cnt = h_cnt; rec.recv_total = h_sum; + + EXEC SQL SELECT count(*) INTO :h_overdue FROM st_payment + WHERE status = 'HELD' AND pay_date < :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OVERDUE FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + rec.oldest = h_overdue; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> %s COMMIT: bizdate=%s held_cnt=%ld recv_total=%ld overdue_cnt=%ld\n", + ST_RECVAGE_TAG, bizdate, rec.held_cnt, rec.recv_total, rec.oldest); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/st/batch/st_slabfee_batch.pgc b/app/src/st/batch/st_slabfee_batch.pgc new file mode 100644 index 0000000..b2efbe4 --- /dev/null +++ b/app/src/st/batch/st_slabfee_batch.pgc @@ -0,0 +1,62 @@ +/* + * st_slabfee_batch.pgc - 슬라브(누진 구간) 요율 배치 (XA 배치). + * 당일 매입 총액을 기준으로 MDR 요율표의 구간을 커서로 순회하며, 금액이 각 구간에 + * 걸치는 부분에만 해당 요율을 적용하는 누진(슬라브) 방식으로 예상 수수료를 합산한다. + * 사용법: st_slabfee_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "st_dbio.h" +#include "st_slabfee_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + st_slabfee_rec_t rec; + long prev_min = -1, prev_bps = 0; + memset(&rec, 0, sizeof(rec)); + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[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]\n", sqlca.sqlcode); tpabort(0); return 1; } + rec.day_gross = h_gross; + + EXEC SQL DECLARE slabfee_bc CURSOR FOR + SELECT band_min, rate_bps FROM st_fee_rate WHERE fee_type = 'MDR' ORDER BY band_min; + EXEC SQL OPEN slabfee_bc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH slabfee_bc INTO :h_min, :h_bps; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE slabfee_bc; tpabort(0); return 1; } + if (prev_min >= 0 && h_gross > prev_min) { + long top = (h_gross < h_min) ? h_gross : h_min; + long seg = (top - prev_min) * prev_bps / 10000; + rec.slab_fee += seg; rec.segs++; + printf(" SLAB [%ld,%ld) bps=%ld -> %ld\n", prev_min, h_min, prev_bps, seg); + } + prev_min = h_min; prev_bps = h_bps; + } + EXEC SQL CLOSE slabfee_bc; + if (prev_min >= 0 && h_gross > prev_min) { + long seg = (h_gross - prev_min) * prev_bps / 10000; + rec.slab_fee += seg; rec.segs++; + } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> %s COMMIT: bizdate=%s gross=%ld segs=%ld slab_fee=%ld\n", + ST_SLABFEE_TAG, bizdate, rec.day_gross, rec.segs, rec.slab_fee); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/st/batch/st_vatdetail_batch.pgc b/app/src/st/batch/st_vatdetail_batch.pgc new file mode 100644 index 0000000..40dfb96 --- /dev/null +++ b/app/src/st/batch/st_vatdetail_batch.pgc @@ -0,0 +1,55 @@ +/* + * st_vatdetail_batch.pgc - 부가세 명세 생성 배치 (XA 배치). + * 당일 가맹점별 수수료 상세(MDR/VAN/RELAY) 합계를 커서로 집계해 공급가액으로 보고 + * 부가세(10%)를 산정, 정산 상세(st_settle_dtl)에 VAT 라인으로 기록한다. + * 사용법: st_vatdetail_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "st_dbio.h" +#include "acq_common.h" +#include "st_vatdetail_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + st_vatdetail_rec_t rec; + memset(&rec, 0, sizeof(rec)); + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_mid[64]; + long h_supply; + 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 vatdetail_bc CURSOR FOR + SELECT merchant_id, coalesce(sum(amount),0) FROM st_settle_dtl + WHERE biz_date = :h_bizdate AND fee_type IN ('MDR', 'VAN', 'RELAY') + GROUP BY merchant_id ORDER BY merchant_id; + EXEC SQL OPEN vatdetail_bc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + long vat, dtl; + EXEC SQL FETCH vatdetail_bc INTO :h_mid, :h_supply; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE vatdetail_bc; tpabort(0); return 1; } + vat = acq_vat(h_supply); + dtl = stdb_insert_fee_dtl(0, 0, h_mid, "VAT", vat, bizdate); + if (dtl < 0) { EXEC SQL CLOSE vatdetail_bc; tpabort(0); return 1; } + rec.merchants++; rec.supply_total += h_supply; rec.vat_total += vat; + printf(" VAT %-8s supply=%ld vat=%ld\n", h_mid, h_supply, vat); + } + EXEC SQL CLOSE vatdetail_bc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> %s COMMIT: bizdate=%s merchants=%ld supply=%ld vat=%ld\n", + ST_VATDETAIL_TAG, bizdate, rec.merchants, rec.supply_total, rec.vat_total); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/st/batch/st_whtdetail_batch.pgc b/app/src/st/batch/st_whtdetail_batch.pgc new file mode 100644 index 0000000..6e4b349 --- /dev/null +++ b/app/src/st/batch/st_whtdetail_batch.pgc @@ -0,0 +1,53 @@ +/* + * st_whtdetail_batch.pgc - 원천세 명세 집계 배치 (XA 배치). + * 당일 가맹점별 정률(PCT) 수수료 합계를 커서로 집계해 원천징수세(3.3%)를 산정하고 + * 명세로 출력한다 (집계/출력 전용, 원장 미기록). + * 사용법: st_whtdetail_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "st_dbio.h" +#include "acq_common.h" +#include "st_whtdetail_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + st_whtdetail_rec_t rec; + memset(&rec, 0, sizeof(rec)); + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_mid[64]; + long h_fee; + 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 whtdetail_bc CURSOR FOR + SELECT merchant_id, coalesce(sum(amount),0) FROM st_settle_dtl + WHERE biz_date = :h_bizdate AND fee_type = 'PCT' + GROUP BY merchant_id ORDER BY merchant_id; + EXEC SQL OPEN whtdetail_bc; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + long wht; + EXEC SQL FETCH whtdetail_bc INTO :h_mid, :h_fee; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE whtdetail_bc; tpabort(0); return 1; } + wht = acq_wht_amt(h_fee, 330); /* 3.3% 원천 */ + rec.merchants++; rec.fee_total += h_fee; rec.wht_total += wht; + printf(" WHT %-8s fee=%ld wht=%ld\n", h_mid, h_fee, wht); + } + EXEC SQL CLOSE whtdetail_bc; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> %s COMMIT: bizdate=%s merchants=%ld fee=%ld wht=%ld\n", + ST_WHTDETAIL_TAG, bizdate, rec.merchants, rec.fee_total, rec.wht_total); + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/st/dbio/st_baddebt_batch.h b/app/src/st/dbio/st_baddebt_batch.h new file mode 100644 index 0000000..407633c --- /dev/null +++ b/app/src/st/dbio/st_baddebt_batch.h @@ -0,0 +1,6 @@ +/* st_baddebt_batch.h - st 대손 상각 배치 copybook. */ +#ifndef ST_BADDEBT_BATCH_H +#define ST_BADDEBT_BATCH_H +#define ST_BADDEBT_TAG "st_baddebt_batch" +typedef struct { long held_cnt; long writeoff; long adj_id; } st_baddebt_rec_t; +#endif diff --git a/app/src/st/dbio/st_biweekly_batch.h b/app/src/st/dbio/st_biweekly_batch.h new file mode 100644 index 0000000..c9151c6 --- /dev/null +++ b/app/src/st/dbio/st_biweekly_batch.h @@ -0,0 +1,6 @@ +/* st_biweekly_batch.h - st 격주 정산 지급 예정 배치 copybook. */ +#ifndef ST_BIWEEKLY_BATCH_H +#define ST_BIWEEKLY_BATCH_H +#define ST_BIWEEKLY_TAG "st_biweekly_batch" +typedef struct { long merchants; long sched_total; long pay_last; } st_biweekly_rec_t; +#endif diff --git a/app/src/st/dbio/st_chargeback_batch.h b/app/src/st/dbio/st_chargeback_batch.h new file mode 100644 index 0000000..c3e2444 --- /dev/null +++ b/app/src/st/dbio/st_chargeback_batch.h @@ -0,0 +1,6 @@ +/* st_chargeback_batch.h - st 차지백 회수 정산 배치 copybook. */ +#ifndef ST_CHARGEBACK_BATCH_H +#define ST_CHARGEBACK_BATCH_H +#define ST_CHARGEBACK_TAG "st_chargeback_batch" +typedef struct { long cb_cnt; long cb_total; long held_upd; } st_chargeback_rec_t; +#endif diff --git a/app/src/st/dbio/st_gradestat_batch.h b/app/src/st/dbio/st_gradestat_batch.h new file mode 100644 index 0000000..93d86e3 --- /dev/null +++ b/app/src/st/dbio/st_gradestat_batch.h @@ -0,0 +1,6 @@ +/* st_gradestat_batch.h - st 가맹점 등급별 통계 배치 copybook. */ +#ifndef ST_GRADESTAT_BATCH_H +#define ST_GRADESTAT_BATCH_H +#define ST_GRADESTAT_TAG "st_gradestat_batch" +typedef struct { long grades; long merchants; long top_bps; } st_gradestat_rec_t; +#endif diff --git a/app/src/st/dbio/st_grpalloc_batch.h b/app/src/st/dbio/st_grpalloc_batch.h new file mode 100644 index 0000000..665d0a6 --- /dev/null +++ b/app/src/st/dbio/st_grpalloc_batch.h @@ -0,0 +1,6 @@ +/* st_grpalloc_batch.h - st 그룹 통합정산 배분 배치 copybook. */ +#ifndef ST_GRPALLOC_BATCH_H +#define ST_GRPALLOC_BATCH_H +#define ST_GRPALLOC_TAG "st_grpalloc_batch" +typedef struct { long groups; long alloc_total; long pay_last; } st_grpalloc_rec_t; +#endif diff --git a/app/src/st/dbio/st_instant_batch.h b/app/src/st/dbio/st_instant_batch.h new file mode 100644 index 0000000..07911e3 --- /dev/null +++ b/app/src/st/dbio/st_instant_batch.h @@ -0,0 +1,6 @@ +/* st_instant_batch.h - st 즉시정산 지급 배치 copybook. */ +#ifndef ST_INSTANT_BATCH_H +#define ST_INSTANT_BATCH_H +#define ST_INSTANT_TAG "st_instant_batch" +typedef struct { long paid_cnt; long paid_total; long pay_last; } st_instant_rec_t; +#endif diff --git a/app/src/st/dbio/st_rebate_batch.h b/app/src/st/dbio/st_rebate_batch.h new file mode 100644 index 0000000..6647169 --- /dev/null +++ b/app/src/st/dbio/st_rebate_batch.h @@ -0,0 +1,6 @@ +/* st_rebate_batch.h - st 리베이트 정산 배치 copybook. */ +#ifndef ST_REBATE_BATCH_H +#define ST_REBATE_BATCH_H +#define ST_REBATE_TAG "st_rebate_batch" +typedef struct { long merchants; long rebate_total; long adj_last; } st_rebate_rec_t; +#endif diff --git a/app/src/st/dbio/st_recvage_batch.h b/app/src/st/dbio/st_recvage_batch.h new file mode 100644 index 0000000..76d0d3f --- /dev/null +++ b/app/src/st/dbio/st_recvage_batch.h @@ -0,0 +1,6 @@ +/* st_recvage_batch.h - st 미수금 연령 분석 배치 copybook. */ +#ifndef ST_RECVAGE_BATCH_H +#define ST_RECVAGE_BATCH_H +#define ST_RECVAGE_TAG "st_recvage_batch" +typedef struct { long held_cnt; long recv_total; long oldest; } st_recvage_rec_t; +#endif diff --git a/app/src/st/dbio/st_slabfee_batch.h b/app/src/st/dbio/st_slabfee_batch.h new file mode 100644 index 0000000..a9d67aa --- /dev/null +++ b/app/src/st/dbio/st_slabfee_batch.h @@ -0,0 +1,6 @@ +/* st_slabfee_batch.h - st 슬라브(누진 구간) 요율 배치 copybook. */ +#ifndef ST_SLABFEE_BATCH_H +#define ST_SLABFEE_BATCH_H +#define ST_SLABFEE_TAG "st_slabfee_batch" +typedef struct { long day_gross; long segs; long slab_fee; } st_slabfee_rec_t; +#endif diff --git a/app/src/st/dbio/st_vatdetail_batch.h b/app/src/st/dbio/st_vatdetail_batch.h new file mode 100644 index 0000000..19f87a5 --- /dev/null +++ b/app/src/st/dbio/st_vatdetail_batch.h @@ -0,0 +1,6 @@ +/* st_vatdetail_batch.h - st 부가세 명세 생성 배치 copybook. */ +#ifndef ST_VATDETAIL_BATCH_H +#define ST_VATDETAIL_BATCH_H +#define ST_VATDETAIL_TAG "st_vatdetail_batch" +typedef struct { long merchants; long supply_total; long vat_total; } st_vatdetail_rec_t; +#endif diff --git a/app/src/st/dbio/st_whtdetail_batch.h b/app/src/st/dbio/st_whtdetail_batch.h new file mode 100644 index 0000000..baa376f --- /dev/null +++ b/app/src/st/dbio/st_whtdetail_batch.h @@ -0,0 +1,6 @@ +/* st_whtdetail_batch.h - st 원천세 명세 집계 배치 copybook. */ +#ifndef ST_WHTDETAIL_BATCH_H +#define ST_WHTDETAIL_BATCH_H +#define ST_WHTDETAIL_TAG "st_whtdetail_batch" +typedef struct { long merchants; long fee_total; long wht_total; } st_whtdetail_rec_t; +#endif diff --git a/app/src/st/st_svr.pgc b/app/src/st/st_svr.pgc index cd3c969..dbf3c67 100644 --- a/app/src/st/st_svr.pgc +++ b/app/src/st/st_svr.pgc @@ -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 75 online services now lives in its own file + * business logic. Each of st's 133 online services now lives in its own file * (app/src/st/svc/.pgc) with its own copybook header * (app/src/st/svc/.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 75 online st(정산/수수료) services. + * st_svr: ONE binary that tpadvertise()s ALL 133 online st(정산/수수료) services. * SETTLE keeps the proven behavior: its own XA branch writes the settlement + * settle ledger rows. Terminal service of the 매입 chain. */ @@ -93,6 +93,64 @@ #include "ST_PAYRETRY.h" #include "ST_LIMITCHK.h" #include "ST_LEDGERSNAP.h" +#include "ST_FLATFEE.h" +#include "ST_PCTFEE.h" +#include "ST_SLABFEE.h" +#include "ST_BANDSIM.h" +#include "ST_RATECARD.h" +#include "ST_RATECMP.h" +#include "ST_TIERFEE.h" +#include "ST_FLATVSPCT.h" +#include "ST_RATEMAX.h" +#include "ST_RATEFLOOR.h" +#include "ST_PROMOAPPLY.h" +#include "ST_EVENTFEE.h" +#include "ST_PARTNERSET.h" +#include "ST_COUPONADJ.h" +#include "ST_CASHBACK.h" +#include "ST_MKTGSHARE.h" +#include "ST_CYCLED3.h" +#include "ST_INSTANTPAY.h" +#include "ST_BIWEEKLY.h" +#include "ST_CYCLEQTR.h" +#include "ST_CYCLESET.h" +#include "ST_NEXTPAYDT.h" +#include "ST_PARTPAY.h" +#include "ST_REPARTIAL.h" +#include "ST_VOIDRECALC.h" +#include "ST_REFUNDCALC.h" +#include "ST_CHARGEBACK.h" +#include "ST_ADJREVERSE.h" +#include "ST_REBATE.h" +#include "ST_VOLREBATE.h" +#include "ST_LOYALTY.h" +#include "ST_GROWTHINC.h" +#include "ST_REFERRAL.h" +#include "ST_DEPOSITSET.h" +#include "ST_RECVAGE.h" +#include "ST_BADDEBT.h" +#include "ST_DEBTNET.h" +#include "ST_COLLATERAL.h" +#include "ST_RECVSTAT.h" +#include "ST_VATDETAIL.h" +#include "ST_WHTDETAIL.h" +#include "ST_TAXSUMMARY.h" +#include "ST_SUPPLYCALC.h" +#include "ST_VATADJUST.h" +#include "ST_PRECONFIRM.h" +#include "ST_LOCKBATCH.h" +#include "ST_UNLOCK.h" +#include "ST_APPROVEALL.h" +#include "ST_WORKFLOW.h" +#include "ST_GRPROLLUP.h" +#include "ST_GRPALLOC.h" +#include "ST_CHAINSET.h" +#include "ST_DISPUTERSV.h" +#include "ST_APPEALSTAT.h" +#include "ST_VANDETAIL.h" +#include "ST_RELAYSPLIT.h" +#include "ST_GRADESTAT.h" +#include "ST_MONTHSTAT.h" /* ----- advertise table: {service name, function} ---------------------- */ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { @@ -134,6 +192,35 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"ST_EDIRECON", ST_EDIRECON}, {"ST_UNPAIDSUM", ST_UNPAIDSUM}, {"ST_PAYRETRY", ST_PAYRETRY}, {"ST_LIMITCHK", ST_LIMITCHK}, {"ST_LEDGERSNAP", ST_LEDGERSNAP}, + {"ST_FLATFEE", ST_FLATFEE}, {"ST_PCTFEE", ST_PCTFEE}, + {"ST_SLABFEE", ST_SLABFEE}, {"ST_BANDSIM", ST_BANDSIM}, + {"ST_RATECARD", ST_RATECARD}, {"ST_RATECMP", ST_RATECMP}, + {"ST_TIERFEE", ST_TIERFEE}, {"ST_FLATVSPCT", ST_FLATVSPCT}, + {"ST_RATEMAX", ST_RATEMAX}, {"ST_RATEFLOOR", ST_RATEFLOOR}, + {"ST_PROMOAPPLY", ST_PROMOAPPLY}, {"ST_EVENTFEE", ST_EVENTFEE}, + {"ST_PARTNERSET", ST_PARTNERSET}, {"ST_COUPONADJ", ST_COUPONADJ}, + {"ST_CASHBACK", ST_CASHBACK}, {"ST_MKTGSHARE", ST_MKTGSHARE}, + {"ST_CYCLED3", ST_CYCLED3}, {"ST_INSTANTPAY", ST_INSTANTPAY}, + {"ST_BIWEEKLY", ST_BIWEEKLY}, {"ST_CYCLEQTR", ST_CYCLEQTR}, + {"ST_CYCLESET", ST_CYCLESET}, {"ST_NEXTPAYDT", ST_NEXTPAYDT}, + {"ST_PARTPAY", ST_PARTPAY}, {"ST_REPARTIAL", ST_REPARTIAL}, + {"ST_VOIDRECALC", ST_VOIDRECALC}, {"ST_REFUNDCALC", ST_REFUNDCALC}, + {"ST_CHARGEBACK", ST_CHARGEBACK}, {"ST_ADJREVERSE", ST_ADJREVERSE}, + {"ST_REBATE", ST_REBATE}, {"ST_VOLREBATE", ST_VOLREBATE}, + {"ST_LOYALTY", ST_LOYALTY}, {"ST_GROWTHINC", ST_GROWTHINC}, + {"ST_REFERRAL", ST_REFERRAL}, {"ST_DEPOSITSET", ST_DEPOSITSET}, + {"ST_RECVAGE", ST_RECVAGE}, {"ST_BADDEBT", ST_BADDEBT}, + {"ST_DEBTNET", ST_DEBTNET}, {"ST_COLLATERAL", ST_COLLATERAL}, + {"ST_RECVSTAT", ST_RECVSTAT}, {"ST_VATDETAIL", ST_VATDETAIL}, + {"ST_WHTDETAIL", ST_WHTDETAIL}, {"ST_TAXSUMMARY", ST_TAXSUMMARY}, + {"ST_SUPPLYCALC", ST_SUPPLYCALC}, {"ST_VATADJUST", ST_VATADJUST}, + {"ST_PRECONFIRM", ST_PRECONFIRM}, {"ST_LOCKBATCH", ST_LOCKBATCH}, + {"ST_UNLOCK", ST_UNLOCK}, {"ST_APPROVEALL", ST_APPROVEALL}, + {"ST_WORKFLOW", ST_WORKFLOW}, {"ST_GRPROLLUP", ST_GRPROLLUP}, + {"ST_GRPALLOC", ST_GRPALLOC}, {"ST_CHAINSET", ST_CHAINSET}, + {"ST_DISPUTERSV", ST_DISPUTERSV}, {"ST_APPEALSTAT", ST_APPEALSTAT}, + {"ST_VANDETAIL", ST_VANDETAIL}, {"ST_RELAYSPLIT", ST_RELAYSPLIT}, + {"ST_GRADESTAT", ST_GRADESTAT}, {"ST_MONTHSTAT", ST_MONTHSTAT}, {NULL, NULL} }; diff --git a/app/src/st/svc/ST_ADJREVERSE.h b/app/src/st/svc/ST_ADJREVERSE.h new file mode 100644 index 0000000..829b102 --- /dev/null +++ b/app/src/st/svc/ST_ADJREVERSE.h @@ -0,0 +1,19 @@ +/* + * ST_ADJREVERSE.h - st 서비스 ST_ADJREVERSE 카피북 (copybook / record header). + * 조정 취소(역분개). + */ +#ifndef ST_ADJREVERSE_H +#define ST_ADJREVERSE_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_adjreverse_rec_t; + +void ST_ADJREVERSE(TPSVCINFO *p); + +#endif /* ST_ADJREVERSE_H */ diff --git a/app/src/st/svc/ST_ADJREVERSE.pgc b/app/src/st/svc/ST_ADJREVERSE.pgc new file mode 100644 index 0000000..dbab2fc --- /dev/null +++ b/app/src/st/svc/ST_ADJREVERSE.pgc @@ -0,0 +1,30 @@ +/* + * ST_ADJREVERSE.pgc - st 모듈 서비스 ST_ADJREVERSE (one service = one file). + * 조정 취소(역분개): 기존 정산 조정(T_ADJ) 행을 조회해 동일 금액의 반대 부호 조정을 + * 새로 기록함으로써 앞선 조정을 상쇄한다. + */ +#include "ST_ADJREVERSE.h" + +/* 103. ST_ADJREVERSE - 기존 조정을 반대부호로 상쇄. */ +void ST_ADJREVERSE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long newadj; + EXEC SQL BEGIN DECLARE SECTION; + long h_adj, h_delta, h_sid, h_pid; + EXEC SQL END DECLARE SECTION; + h_adj = getl(b, T_ADJ); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + EXEC SQL SELECT delta, settlement_id, purchase_id INTO :h_delta, :h_sid, :h_pid + FROM st_adjust WHERE adj_id = :h_adj; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + newadj = stdb_insert_adjust(h_sid, h_pid, -h_delta, "REVERSE", bizdate); + if (newadj < 0) FAIL(b); + setl(b, T_ADJ, newadj); + setl(b, T_DELTA, -h_delta); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_APPEALSTAT.h b/app/src/st/svc/ST_APPEALSTAT.h new file mode 100644 index 0000000..d9f0dfa --- /dev/null +++ b/app/src/st/svc/ST_APPEALSTAT.h @@ -0,0 +1,19 @@ +/* + * ST_APPEALSTAT.h - st 서비스 ST_APPEALSTAT 카피북 (copybook / record header). + * 이의신청 통계. + */ +#ifndef ST_APPEALSTAT_H +#define ST_APPEALSTAT_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_appealstat_rec_t; + +void ST_APPEALSTAT(TPSVCINFO *p); + +#endif /* ST_APPEALSTAT_H */ diff --git a/app/src/st/svc/ST_APPEALSTAT.pgc b/app/src/st/svc/ST_APPEALSTAT.pgc new file mode 100644 index 0000000..7225092 --- /dev/null +++ b/app/src/st/svc/ST_APPEALSTAT.pgc @@ -0,0 +1,28 @@ +/* + * ST_APPEALSTAT.pgc - st 모듈 서비스 ST_APPEALSTAT (one service = one file). + * 이의신청 통계: 정산 조정 중 이의신청 관련(reason LIKE 'DISPUTE%') 건수와 청구 + * 금액 합계를 집계해 이의신청 규모를 산출한다 (조회 전용). + */ +#include "ST_APPEALSTAT.h" + +/* 129. ST_APPEALSTAT - 이의신청 조정 건수/금액 통계. */ +void ST_APPEALSTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_sum; + 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(delta),0) INTO :h_cnt, :h_sum + FROM st_adjust WHERE biz_date = :h_bizdate AND reason LIKE 'DISPUTE%'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + setl(b, T_DELTA, h_sum); + setl(b, T_RC, (h_cnt > 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_APPROVEALL.h b/app/src/st/svc/ST_APPROVEALL.h new file mode 100644 index 0000000..73cba61 --- /dev/null +++ b/app/src/st/svc/ST_APPROVEALL.h @@ -0,0 +1,19 @@ +/* + * ST_APPROVEALL.h - st 서비스 ST_APPROVEALL 카피북 (copybook / record header). + * 정산 일괄 승인. + */ +#ifndef ST_APPROVEALL_H +#define ST_APPROVEALL_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_approveall_rec_t; + +void ST_APPROVEALL(TPSVCINFO *p); + +#endif /* ST_APPROVEALL_H */ diff --git a/app/src/st/svc/ST_APPROVEALL.pgc b/app/src/st/svc/ST_APPROVEALL.pgc new file mode 100644 index 0000000..4b621a3 --- /dev/null +++ b/app/src/st/svc/ST_APPROVEALL.pgc @@ -0,0 +1,28 @@ +/* + * ST_APPROVEALL.pgc - st 모듈 서비스 ST_APPROVEALL (one service = one file). + * 정산 일괄 승인: 당일 지급 예정(SCHEDULED) 전 건을 한 번에 지급 완료(PAID)로 + * 승인 처리한다. 승인된 지급 건수를 반환한다. + */ +#include "ST_APPROVEALL.h" + +/* 123. ST_APPROVEALL - 당일 지급 예정 일괄 승인(->PAID). */ +void ST_APPROVEALL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long approved; + 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 UPDATE st_payment SET status = 'PAID' + WHERE biz_date = :h_bizdate AND status = 'SCHEDULED'; + if (sqlca.sqlcode < 0) FAIL(b); + approved = sqlca.sqlerrd[2]; + setl(b, T_COUNT, approved); + setl(b, T_RC, (approved > 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_BADDEBT.h b/app/src/st/svc/ST_BADDEBT.h new file mode 100644 index 0000000..89f8759 --- /dev/null +++ b/app/src/st/svc/ST_BADDEBT.h @@ -0,0 +1,19 @@ +/* + * ST_BADDEBT.h - st 서비스 ST_BADDEBT 카피북 (copybook / record header). + * 대손 상각. + */ +#ifndef ST_BADDEBT_H +#define ST_BADDEBT_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_baddebt_rec_t; + +void ST_BADDEBT(TPSVCINFO *p); + +#endif /* ST_BADDEBT_H */ diff --git a/app/src/st/svc/ST_BADDEBT.pgc b/app/src/st/svc/ST_BADDEBT.pgc new file mode 100644 index 0000000..5a34266 --- /dev/null +++ b/app/src/st/svc/ST_BADDEBT.pgc @@ -0,0 +1,44 @@ +/* + * ST_BADDEBT.pgc - st 모듈 서비스 ST_BADDEBT (one service = one file). + * 대손 상각: 특정 가맹점의 장기 보류(HELD) 지급 건을 커서로 순회해 총 미수액을 + * 산출하고, 대손 상각 음(-) 조정 기록 + 해당 지급 취소(CANCELLED) 처리한다. + */ +#include "ST_BADDEBT.h" + +/* 111. ST_BADDEBT - 장기 미수 대손 상각(상계+취소). */ +void ST_BADDEBT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + long total = 0, cnt = 0, adj; + EXEC SQL BEGIN DECLARE SECTION; + long 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 DECLARE baddebt_c CURSOR FOR + SELECT net_amount FROM st_payment + WHERE merchant_id = :h_merch AND status = 'HELD' AND pay_date <= :h_bizdate; + EXEC SQL OPEN baddebt_c; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH baddebt_c INTO :h_net; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE baddebt_c; FAIL(b); } + total += h_net; cnt++; + } + EXEC SQL CLOSE baddebt_c; + if (cnt == 0) { setl(b, T_RC, 1); OK(b); } + adj = stdb_insert_adjust(0, 0, -total, "BADDEBT", bizdate); + if (adj < 0) FAIL(b); + if (stdb_update_payment_status(merch, "HELD", "CANCELLED", bizdate) < 0) FAIL(b); + setl(b, T_ADJ, adj); + setl(b, T_COUNT, cnt); + setl(b, T_DELTA, -total); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_BANDSIM.h b/app/src/st/svc/ST_BANDSIM.h new file mode 100644 index 0000000..ecb7030 --- /dev/null +++ b/app/src/st/svc/ST_BANDSIM.h @@ -0,0 +1,19 @@ +/* + * ST_BANDSIM.h - st 서비스 ST_BANDSIM 카피북 (copybook / record header). + * 구간별 요율 시뮬레이션. + */ +#ifndef ST_BANDSIM_H +#define ST_BANDSIM_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_bandsim_rec_t; + +void ST_BANDSIM(TPSVCINFO *p); + +#endif /* ST_BANDSIM_H */ diff --git a/app/src/st/svc/ST_BANDSIM.pgc b/app/src/st/svc/ST_BANDSIM.pgc new file mode 100644 index 0000000..b8b1d5e --- /dev/null +++ b/app/src/st/svc/ST_BANDSIM.pgc @@ -0,0 +1,41 @@ +/* + * ST_BANDSIM.pgc - st 모듈 서비스 ST_BANDSIM (one service = one file). + * 구간별 요율 시뮬레이션: 입력 금액에 적용 가능한(band_min <= amount) 모든 구간 중 + * 최고 요율 구간을 찾아 예상 수수료를 산출한다 (조회 전용, 최대치 시나리오). + */ +#include "ST_BANDSIM.h" + +/* 79. ST_BANDSIM - 적용가능 구간 중 최고 요율 시뮬. */ +void ST_BANDSIM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char ftype[16]; + long amount = getl(b, T_AMOUNT), maxbps = 0, hits = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_min, h_bps, h_amt; + 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; + h_amt = amount; + EXEC SQL DECLARE bandsim_c CURSOR FOR + SELECT band_min, rate_bps FROM st_fee_rate + WHERE fee_type = :h_ftype AND band_min <= :h_amt ORDER BY rate_bps DESC; + EXEC SQL OPEN bandsim_c; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH bandsim_c INTO :h_min, :h_bps; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE bandsim_c; FAIL(b); } + if (hits == 0) maxbps = h_bps; /* rate_bps DESC: 첫 행이 최고 요율 */ + hits++; + } + EXEC SQL CLOSE bandsim_c; + if (hits == 0) { setl(b, T_RC, -1); OK(b); } + setl(b, T_COUNT, hits); + setl(b, T_RC, maxbps); + setl(b, T_FEE, acq_fee(amount, maxbps)); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_BIWEEKLY.h b/app/src/st/svc/ST_BIWEEKLY.h new file mode 100644 index 0000000..14b02b6 --- /dev/null +++ b/app/src/st/svc/ST_BIWEEKLY.h @@ -0,0 +1,19 @@ +/* + * ST_BIWEEKLY.h - st 서비스 ST_BIWEEKLY 카피북 (copybook / record header). + * 격주 정산. + */ +#ifndef ST_BIWEEKLY_H +#define ST_BIWEEKLY_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_biweekly_rec_t; + +void ST_BIWEEKLY(TPSVCINFO *p); + +#endif /* ST_BIWEEKLY_H */ diff --git a/app/src/st/svc/ST_BIWEEKLY.pgc b/app/src/st/svc/ST_BIWEEKLY.pgc new file mode 100644 index 0000000..7b2c4c6 --- /dev/null +++ b/app/src/st/svc/ST_BIWEEKLY.pgc @@ -0,0 +1,39 @@ +/* + * ST_BIWEEKLY.pgc - st 모듈 서비스 ST_BIWEEKLY (one service = one file). + * 격주 정산: 가맹점 당일 정산 net 합계를 집계하고 영업일 기준 10일 뒤(약 2주) + * 지급일로 지급 예정 등록한다. + */ +#include "ST_BIWEEKLY.h" + +/* 94. ST_BIWEEKLY - 2주 주기 net 합계 지급 예정. */ +void ST_BIWEEKLY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16], cur[16], nxt[16]; + long pay; + int i; + 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; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_sum <= 0) { setl(b, T_RC, 1); OK(b); } + strncpy(cur, bizdate, sizeof(cur)-1); cur[sizeof(cur)-1] = 0; + for (i = 0; i < 10; i++) { /* 영업일 +10 (약 2주) */ + if (acq_next_bizday(cur, nxt) < 0) FAIL(b); + strncpy(cur, nxt, sizeof(cur)-1); cur[sizeof(cur)-1] = 0; + } + pay = stdb_insert_payment(merch, h_sum, cur, "SCHEDULED", bizdate); + if (pay < 0) FAIL(b); + setl(b, T_ID1, pay); + setl(b, T_NET, h_sum); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_CASHBACK.h b/app/src/st/svc/ST_CASHBACK.h new file mode 100644 index 0000000..22e0f9c --- /dev/null +++ b/app/src/st/svc/ST_CASHBACK.h @@ -0,0 +1,19 @@ +/* + * ST_CASHBACK.h - st 서비스 ST_CASHBACK 카피북 (copybook / record header). + * 캐시백 정산. + */ +#ifndef ST_CASHBACK_H +#define ST_CASHBACK_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_cashback_rec_t; + +void ST_CASHBACK(TPSVCINFO *p); + +#endif /* ST_CASHBACK_H */ diff --git a/app/src/st/svc/ST_CASHBACK.pgc b/app/src/st/svc/ST_CASHBACK.pgc new file mode 100644 index 0000000..609f581 --- /dev/null +++ b/app/src/st/svc/ST_CASHBACK.pgc @@ -0,0 +1,26 @@ +/* + * ST_CASHBACK.pgc - st 모듈 서비스 ST_CASHBACK (one service = one file). + * 캐시백 정산: 거래 금액에 캐시백율(T_ARG1 bps)을 적용한 캐시백을 가맹점 지급 + * 예정(st_payment)으로 등록한다 (판촉성 지급). + */ +#include "ST_CASHBACK.h" + +/* 90. ST_CASHBACK - 캐시백 지급 예정 등록. */ +void ST_CASHBACK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + long amount = getl(b, T_AMOUNT), bps = getl(b, T_ARG1), cash, pay; + gets_(b, T_MERCHANT, merch, sizeof(merch)); if (merch[0] == 0) strcpy(merch, "M0000"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bps <= 0) bps = 100; /* 기본 1% 캐시백 */ + cash = acq_fee(amount, bps); + if (cash <= 0) { setl(b, T_RC, 1); OK(b); } + pay = stdb_insert_payment(merch, cash, bizdate, "SCHEDULED", bizdate); + if (pay < 0) FAIL(b); + setl(b, T_ID1, pay); + setl(b, T_AMOUNT, cash); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_CHAINSET.h b/app/src/st/svc/ST_CHAINSET.h new file mode 100644 index 0000000..d5ef5b9 --- /dev/null +++ b/app/src/st/svc/ST_CHAINSET.h @@ -0,0 +1,19 @@ +/* + * ST_CHAINSET.h - st 서비스 ST_CHAINSET 카피북 (copybook / record header). + * 체인점 통합 정산. + */ +#ifndef ST_CHAINSET_H +#define ST_CHAINSET_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_chainset_rec_t; + +void ST_CHAINSET(TPSVCINFO *p); + +#endif /* ST_CHAINSET_H */ diff --git a/app/src/st/svc/ST_CHAINSET.pgc b/app/src/st/svc/ST_CHAINSET.pgc new file mode 100644 index 0000000..08cc8f2 --- /dev/null +++ b/app/src/st/svc/ST_CHAINSET.pgc @@ -0,0 +1,36 @@ +/* + * ST_CHAINSET.pgc - st 모듈 서비스 ST_CHAINSET (one service = one file). + * 체인점 통합 정산: 업종/분류(T_CATEGORY)로 묶인 체인 매입의 정산 net 을 조인 집계해 + * 대표 지급처(T_MERCHANT)로 통합 지급 예정 등록한다. + */ +#include "ST_CHAINSET.h" + +/* 127. ST_CHAINSET - 업종 체인 통합 net 지급. */ +void ST_CHAINSET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], cat[32], bizdate[16]; + long pay; + EXEC SQL BEGIN DECLARE SECTION; + long h_sum, h_cnt; + char h_cat[32], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_MERCHANT, merch, sizeof(merch)); if (merch[0] == 0) strcpy(merch, "CHAIN"); + gets_(b, T_CATEGORY, cat, sizeof(cat)); if (cat[0] == 0) strcpy(cat, "GEN"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + strncpy(h_cat, cat, sizeof(h_cat)-1); h_cat[sizeof(h_cat)-1] = 0; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(s.net),0), count(*) INTO :h_sum, :h_cnt + FROM settlement s JOIN purchase p ON p.purchase_id = s.purchase_id + WHERE p.category = :h_cat AND s.biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_sum <= 0) { setl(b, T_RC, 1); OK(b); } + pay = stdb_insert_payment(merch, h_sum, bizdate, "SCHEDULED", bizdate); + if (pay < 0) FAIL(b); + setl(b, T_ID1, pay); + setl(b, T_COUNT, h_cnt); + setl(b, T_NET, h_sum); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_CHARGEBACK.h b/app/src/st/svc/ST_CHARGEBACK.h new file mode 100644 index 0000000..54b4706 --- /dev/null +++ b/app/src/st/svc/ST_CHARGEBACK.h @@ -0,0 +1,19 @@ +/* + * ST_CHARGEBACK.h - st 서비스 ST_CHARGEBACK 카피북 (copybook / record header). + * 차지백 정산 처리. + */ +#ifndef ST_CHARGEBACK_H +#define ST_CHARGEBACK_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_chargeback_rec_t; + +void ST_CHARGEBACK(TPSVCINFO *p); + +#endif /* ST_CHARGEBACK_H */ diff --git a/app/src/st/svc/ST_CHARGEBACK.pgc b/app/src/st/svc/ST_CHARGEBACK.pgc new file mode 100644 index 0000000..5b1813f --- /dev/null +++ b/app/src/st/svc/ST_CHARGEBACK.pgc @@ -0,0 +1,28 @@ +/* + * ST_CHARGEBACK.pgc - st 모듈 서비스 ST_CHARGEBACK (one service = one file). + * 차지백 정산 처리: 차지백 금액(T_AMOUNT)을 음(-)의 정산 조정으로 회수하고 해당 + * 가맹점 지급을 보류(HELD) 상태로 전환한다. + */ +#include "ST_CHARGEBACK.h" + +/* 102. ST_CHARGEBACK - 차지백 회수 조정 + 지급 보류. */ +void ST_CHARGEBACK(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), adj; + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (amount <= 0) { setl(b, T_RC, 1); OK(b); } + adj = stdb_insert_adjust(sid, pid, -amount, "CHARGEBACK", bizdate); + if (adj < 0) FAIL(b); + if (merch[0] != 0) + if (stdb_update_payment_status(merch, "SCHEDULED", "HELD", bizdate) < 0) FAIL(b); + setl(b, T_ADJ, adj); + setl(b, T_DELTA, -amount); + Bchg(b, T_STATUS, 0, "CB", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_COLLATERAL.h b/app/src/st/svc/ST_COLLATERAL.h new file mode 100644 index 0000000..b17396e --- /dev/null +++ b/app/src/st/svc/ST_COLLATERAL.h @@ -0,0 +1,19 @@ +/* + * ST_COLLATERAL.h - st 서비스 ST_COLLATERAL 카피북 (copybook / record header). + * 담보/보증금 잔액. + */ +#ifndef ST_COLLATERAL_H +#define ST_COLLATERAL_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_collateral_rec_t; + +void ST_COLLATERAL(TPSVCINFO *p); + +#endif /* ST_COLLATERAL_H */ diff --git a/app/src/st/svc/ST_COLLATERAL.pgc b/app/src/st/svc/ST_COLLATERAL.pgc new file mode 100644 index 0000000..de94d96 --- /dev/null +++ b/app/src/st/svc/ST_COLLATERAL.pgc @@ -0,0 +1,28 @@ +/* + * ST_COLLATERAL.pgc - st 모듈 서비스 ST_COLLATERAL (one service = one file). + * 담보/보증금 잔액: 정산 상세의 DEPOSIT 라인을 합산해 가맹점 누적 보증금(담보) + * 잔액과 예치 건수를 조회한다 (조회 전용). + */ +#include "ST_COLLATERAL.h" + +/* 113. ST_COLLATERAL - 가맹점 누적 보증금 잔액 조회. */ +void ST_COLLATERAL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + EXEC SQL BEGIN DECLARE SECTION; + long h_bal, h_cnt; + 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 coalesce(sum(amount),0), count(*) INTO :h_bal, :h_cnt + FROM st_settle_dtl WHERE merchant_id = :h_merch AND fee_type = 'DEPOSIT'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_AMOUNT, h_bal); + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, (h_bal > 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_COUPONADJ.h b/app/src/st/svc/ST_COUPONADJ.h new file mode 100644 index 0000000..8d97236 --- /dev/null +++ b/app/src/st/svc/ST_COUPONADJ.h @@ -0,0 +1,19 @@ +/* + * ST_COUPONADJ.h - st 서비스 ST_COUPONADJ 카피북 (copybook / record header). + * 쿠폰 조정 정산. + */ +#ifndef ST_COUPONADJ_H +#define ST_COUPONADJ_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_couponadj_rec_t; + +void ST_COUPONADJ(TPSVCINFO *p); + +#endif /* ST_COUPONADJ_H */ diff --git a/app/src/st/svc/ST_COUPONADJ.pgc b/app/src/st/svc/ST_COUPONADJ.pgc new file mode 100644 index 0000000..7e2f84e --- /dev/null +++ b/app/src/st/svc/ST_COUPONADJ.pgc @@ -0,0 +1,26 @@ +/* + * ST_COUPONADJ.pgc - st 모듈 서비스 ST_COUPONADJ (one service = one file). + * 쿠폰 조정 정산: 발급 쿠폰 금액(T_ARG1)을 가맹점 부담분으로 양(+)의 정산 조정에 + * 기록해 정산에 반영한다 (쿠폰 사용액 정산). + */ +#include "ST_COUPONADJ.h" + +/* 89. ST_COUPONADJ - 쿠폰 사용액 정산 조정(가산). */ +void ST_COUPONADJ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], reason[64], rbuf[96]; + long sid = getl(b, T_SETTLE_ID), pid = getl(b, T_PURCHASE_ID); + long coupon = getl(b, T_ARG1), adj; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_STR1, reason, sizeof(reason)); if (reason[0] == 0) strcpy(reason, "CPN"); + if (coupon <= 0) { setl(b, T_RC, 1); OK(b); } + snprintf(rbuf, sizeof(rbuf), "COUPON:%s", reason); + adj = stdb_insert_adjust(sid, pid, coupon, rbuf, bizdate); + if (adj < 0) FAIL(b); + setl(b, T_ADJ, adj); + setl(b, T_DELTA, coupon); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_CYCLED3.h b/app/src/st/svc/ST_CYCLED3.h new file mode 100644 index 0000000..1d8f8cb --- /dev/null +++ b/app/src/st/svc/ST_CYCLED3.h @@ -0,0 +1,19 @@ +/* + * ST_CYCLED3.h - st 서비스 ST_CYCLED3 카피북 (copybook / record header). + * D+3 정산 주기. + */ +#ifndef ST_CYCLED3_H +#define ST_CYCLED3_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_cycled3_rec_t; + +void ST_CYCLED3(TPSVCINFO *p); + +#endif /* ST_CYCLED3_H */ diff --git a/app/src/st/svc/ST_CYCLED3.pgc b/app/src/st/svc/ST_CYCLED3.pgc new file mode 100644 index 0000000..5705456 --- /dev/null +++ b/app/src/st/svc/ST_CYCLED3.pgc @@ -0,0 +1,29 @@ +/* + * ST_CYCLED3.pgc - st 모듈 서비스 ST_CYCLED3 (one service = one file). + * D+3 정산 주기: 영업일(T_BIZDATE)로부터 영업일 기준 3일 뒤 지급일을 산출해 + * (주말 스킵) 정산 net 을 해당 지급일로 지급 예정 등록한다. + */ +#include "ST_CYCLED3.h" + +/* 92. ST_CYCLED3 - D+3 영업일 지급 예정 등록. */ +void ST_CYCLED3(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16], cur[16], nxt[16]; + long net = getl(b, T_NET), pay; + int i; + gets_(b, T_MERCHANT, merch, sizeof(merch)); if (merch[0] == 0) strcpy(merch, "M0000"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + strncpy(cur, bizdate, sizeof(cur)-1); cur[sizeof(cur)-1] = 0; + for (i = 0; i < 3; i++) { /* 영업일 +3 (주말 스킵) */ + if (acq_next_bizday(cur, nxt) < 0) FAIL(b); + strncpy(cur, nxt, sizeof(cur)-1); cur[sizeof(cur)-1] = 0; + } + pay = stdb_insert_payment(merch, net, cur, "SCHEDULED", bizdate); + if (pay < 0) FAIL(b); + setl(b, T_ID1, pay); + Bchg(b, T_STR1, 0, cur, 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_CYCLEQTR.h b/app/src/st/svc/ST_CYCLEQTR.h new file mode 100644 index 0000000..d06b3ab --- /dev/null +++ b/app/src/st/svc/ST_CYCLEQTR.h @@ -0,0 +1,19 @@ +/* + * ST_CYCLEQTR.h - st 서비스 ST_CYCLEQTR 카피북 (copybook / record header). + * 분기 정산 주기. + */ +#ifndef ST_CYCLEQTR_H +#define ST_CYCLEQTR_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_cycleqtr_rec_t; + +void ST_CYCLEQTR(TPSVCINFO *p); + +#endif /* ST_CYCLEQTR_H */ diff --git a/app/src/st/svc/ST_CYCLEQTR.pgc b/app/src/st/svc/ST_CYCLEQTR.pgc new file mode 100644 index 0000000..bf217a3 --- /dev/null +++ b/app/src/st/svc/ST_CYCLEQTR.pgc @@ -0,0 +1,34 @@ +/* + * ST_CYCLEQTR.pgc - st 모듈 서비스 ST_CYCLEQTR (one service = one file). + * 분기 정산 주기: 기준일(T_BIZDATE) 이하의 미지급 정산 net 을 누적 합계로 집계해 + * 분기 통합 지급 예정으로 등록한다 (누적 정산). + */ +#include "ST_CYCLEQTR.h" + +/* 95. ST_CYCLEQTR - 기준일까지 누적 net 분기 지급. */ +void ST_CYCLEQTR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + long pay; + EXEC SQL BEGIN DECLARE SECTION; + long h_sum, h_cnt; + 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; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_sum <= 0) { setl(b, T_RC, 1); OK(b); } + pay = stdb_insert_payment(merch, h_sum, bizdate, "SCHEDULED", bizdate); + if (pay < 0) FAIL(b); + setl(b, T_ID1, pay); + setl(b, T_COUNT, h_cnt); + setl(b, T_NET, h_sum); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_CYCLESET.h b/app/src/st/svc/ST_CYCLESET.h new file mode 100644 index 0000000..9ac4271 --- /dev/null +++ b/app/src/st/svc/ST_CYCLESET.h @@ -0,0 +1,19 @@ +/* + * ST_CYCLESET.h - st 서비스 ST_CYCLESET 카피북 (copybook / record header). + * 정산 주기 설정 조회. + */ +#ifndef ST_CYCLESET_H +#define ST_CYCLESET_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_cycleset_rec_t; + +void ST_CYCLESET(TPSVCINFO *p); + +#endif /* ST_CYCLESET_H */ diff --git a/app/src/st/svc/ST_CYCLESET.pgc b/app/src/st/svc/ST_CYCLESET.pgc new file mode 100644 index 0000000..053935e --- /dev/null +++ b/app/src/st/svc/ST_CYCLESET.pgc @@ -0,0 +1,32 @@ +/* + * ST_CYCLESET.pgc - st 모듈 서비스 ST_CYCLESET (one service = one file). + * 정산 주기 설정 조회: 가맹점 한도(daily_limit)를 기준으로 적용 정산 주기 코드 + * (대형=D1 / 중형=D2 / 소형=WEEK)를 파생해 반환한다 (조회 전용). + */ +#include "ST_CYCLESET.h" + +/* 96. ST_CYCLESET - 가맹점 한도 기반 정산주기 코드 파생. */ +void ST_CYCLESET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], cyc[8]; + EXEC SQL BEGIN DECLARE SECTION; + long h_limit; + char h_merch[64], h_status[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 daily_limit, status INTO :h_limit, :h_status FROM merchant + WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (h_limit >= 300000000) strcpy(cyc, "D1"); + else if (h_limit >= 100000000) strcpy(cyc, "D2"); + else strcpy(cyc, "WEEK"); + setl(b, T_AMOUNT, h_limit); + Bchg(b, T_STATUS, 0, h_status, 0L); + Bchg(b, T_STR1, 0, cyc, 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_DEBTNET.h b/app/src/st/svc/ST_DEBTNET.h new file mode 100644 index 0000000..d8ae641 --- /dev/null +++ b/app/src/st/svc/ST_DEBTNET.h @@ -0,0 +1,19 @@ +/* + * ST_DEBTNET.h - st 서비스 ST_DEBTNET 카피북 (copybook / record header). + * 채권 상계 netting. + */ +#ifndef ST_DEBTNET_H +#define ST_DEBTNET_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_debtnet_rec_t; + +void ST_DEBTNET(TPSVCINFO *p); + +#endif /* ST_DEBTNET_H */ diff --git a/app/src/st/svc/ST_DEBTNET.pgc b/app/src/st/svc/ST_DEBTNET.pgc new file mode 100644 index 0000000..f8f5eb7 --- /dev/null +++ b/app/src/st/svc/ST_DEBTNET.pgc @@ -0,0 +1,34 @@ +/* + * ST_DEBTNET.pgc - st 모듈 서비스 ST_DEBTNET (one service = one file). + * 채권 상계: 가맹점의 지급 예정(SCHEDULED) 채무와 보류(HELD) 미수 채권을 각각 + * 집계해 상계 후 순 지급/회수 포지션을 계산한다 (조회 전용). + */ +#include "ST_DEBTNET.h" + +/* 112. ST_DEBTNET - 지급 채무 대 미수 채권 상계. */ +void ST_DEBTNET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + long position; + EXEC SQL BEGIN DECLARE SECTION; + long h_payable, h_recv; + 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 coalesce(sum(net_amount),0) INTO :h_payable FROM st_payment + WHERE merchant_id = :h_merch AND status = 'SCHEDULED'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(net_amount),0) INTO :h_recv FROM st_payment + WHERE merchant_id = :h_merch AND status = 'HELD'; + if (sqlca.sqlcode < 0) FAIL(b); + position = h_payable - h_recv; + setl(b, T_AMT1, h_payable); + setl(b, T_AMT2, h_recv); + setl(b, T_NET, position); + setl(b, T_RC, (position >= 0) ? 0 : 1); /* 1 = 순 회수 포지션 */ + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_DEPOSITSET.h b/app/src/st/svc/ST_DEPOSITSET.h new file mode 100644 index 0000000..6bb3329 --- /dev/null +++ b/app/src/st/svc/ST_DEPOSITSET.h @@ -0,0 +1,19 @@ +/* + * ST_DEPOSITSET.h - st 서비스 ST_DEPOSITSET 카피북 (copybook / record header). + * 보증금 설정/차감. + */ +#ifndef ST_DEPOSITSET_H +#define ST_DEPOSITSET_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_depositset_rec_t; + +void ST_DEPOSITSET(TPSVCINFO *p); + +#endif /* ST_DEPOSITSET_H */ diff --git a/app/src/st/svc/ST_DEPOSITSET.pgc b/app/src/st/svc/ST_DEPOSITSET.pgc new file mode 100644 index 0000000..7759f4d --- /dev/null +++ b/app/src/st/svc/ST_DEPOSITSET.pgc @@ -0,0 +1,26 @@ +/* + * ST_DEPOSITSET.pgc - st 모듈 서비스 ST_DEPOSITSET (one service = one file). + * 보증금 설정/차감: 가맹점 보증금 예치액(T_ARG1)을 정산 상세에 DEPOSIT 라인으로 + * 적립 기록하고 정산 net 에서 차감한 잔여액을 반환한다. + */ +#include "ST_DEPOSITSET.h" + +/* 109. ST_DEPOSITSET - 보증금 예치 기록 + net 차감. */ +void ST_DEPOSITSET(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 net = getl(b, T_NET), deposit = getl(b, T_ARG1), dtl; + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (deposit <= 0) { setl(b, T_RC, 1); OK(b); } + if (deposit > net) deposit = net; + dtl = stdb_insert_fee_dtl(sid, pid, merch, "DEPOSIT", deposit, bizdate); + if (dtl < 0) FAIL(b); + setl(b, T_AMOUNT, deposit); + setl(b, T_NET, net - deposit); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_DISPUTERSV.h b/app/src/st/svc/ST_DISPUTERSV.h new file mode 100644 index 0000000..aa5a609 --- /dev/null +++ b/app/src/st/svc/ST_DISPUTERSV.h @@ -0,0 +1,19 @@ +/* + * ST_DISPUTERSV.h - st 서비스 ST_DISPUTERSV 카피북 (copybook / record header). + * 이의신청 결과 반영. + */ +#ifndef ST_DISPUTERSV_H +#define ST_DISPUTERSV_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_disputersv_rec_t; + +void ST_DISPUTERSV(TPSVCINFO *p); + +#endif /* ST_DISPUTERSV_H */ diff --git a/app/src/st/svc/ST_DISPUTERSV.pgc b/app/src/st/svc/ST_DISPUTERSV.pgc new file mode 100644 index 0000000..a232b69 --- /dev/null +++ b/app/src/st/svc/ST_DISPUTERSV.pgc @@ -0,0 +1,29 @@ +/* + * ST_DISPUTERSV.pgc - st 모듈 서비스 ST_DISPUTERSV (one service = one file). + * 이의신청 결과 반영: 판정(T_STATUS: A=인용/R=기각)에 따라 인용 시 청구액을 양(+)의 + * 조정으로 반영하고 지급을 재개(HELD->SCHEDULED), 기각 시 보류를 지급(HELD->PAID)한다. + */ +#include "ST_DISPUTERSV.h" + +/* 128. ST_DISPUTERSV - 이의신청 인용/기각 후속 처리. */ +void ST_DISPUTERSV(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16], decision[8]; + long pid = getl(b, T_PURCHASE_ID), claimed = getl(b, T_DELTA), adj = 0; + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_STATUS, decision, sizeof(decision)); if (decision[0] == 0) strcpy(decision, "R"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (decision[0] == 'A') { /* 인용: 청구액 반영 + 지급 재개 */ + adj = stdb_insert_adjust(0, pid, claimed, "DISPUTE_OK", bizdate); + if (adj < 0) FAIL(b); + if (stdb_update_payment_status(merch, "HELD", "SCHEDULED", bizdate) < 0) FAIL(b); + } else { /* 기각: 보류 지급 확정 */ + if (stdb_update_payment_status(merch, "HELD", "PAID", bizdate) < 0) FAIL(b); + } + setl(b, T_ADJ, adj); + Bchg(b, T_STATUS, 0, (decision[0] == 'A') ? "OK" : "NG", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_EVENTFEE.h b/app/src/st/svc/ST_EVENTFEE.h new file mode 100644 index 0000000..6439a8b --- /dev/null +++ b/app/src/st/svc/ST_EVENTFEE.h @@ -0,0 +1,19 @@ +/* + * ST_EVENTFEE.h - st 서비스 ST_EVENTFEE 카피북 (copybook / record header). + * 이벤트 수수료 감면. + */ +#ifndef ST_EVENTFEE_H +#define ST_EVENTFEE_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_eventfee_rec_t; + +void ST_EVENTFEE(TPSVCINFO *p); + +#endif /* ST_EVENTFEE_H */ diff --git a/app/src/st/svc/ST_EVENTFEE.pgc b/app/src/st/svc/ST_EVENTFEE.pgc new file mode 100644 index 0000000..3540e18 --- /dev/null +++ b/app/src/st/svc/ST_EVENTFEE.pgc @@ -0,0 +1,26 @@ +/* + * ST_EVENTFEE.pgc - st 모듈 서비스 ST_EVENTFEE (one service = one file). + * 이벤트 수수료 감면: 이벤트 기간 거래에 대해 정상 요율의 절반을 적용한 감면 + * 수수료를 산출하고 정산 상세에 EVENT 라인으로 기록한다. + */ +#include "ST_EVENTFEE.h" + +/* 87. ST_EVENTFEE - 이벤트 반값 수수료 감면 기록. */ +void ST_EVENTFEE(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), bps = getl(b, T_ARG1), fee, dtl; + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bps <= 0) bps = 25; + fee = acq_fee(amount, bps) / 2; /* 이벤트 반값 */ + dtl = stdb_insert_fee_dtl(sid, pid, merch, "EVENT", fee, bizdate); + if (dtl < 0) FAIL(b); + setl(b, T_FEE, fee); + setl(b, T_RC, bps / 2); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_FLATFEE.h b/app/src/st/svc/ST_FLATFEE.h new file mode 100644 index 0000000..df77620 --- /dev/null +++ b/app/src/st/svc/ST_FLATFEE.h @@ -0,0 +1,19 @@ +/* + * ST_FLATFEE.h - st 서비스 ST_FLATFEE 카피북 (copybook / record header). + * 정액 수수료 적용. + */ +#ifndef ST_FLATFEE_H +#define ST_FLATFEE_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_flat_rec_t; + +void ST_FLATFEE(TPSVCINFO *p); + +#endif /* ST_FLATFEE_H */ diff --git a/app/src/st/svc/ST_FLATFEE.pgc b/app/src/st/svc/ST_FLATFEE.pgc new file mode 100644 index 0000000..fb8c077 --- /dev/null +++ b/app/src/st/svc/ST_FLATFEE.pgc @@ -0,0 +1,25 @@ +/* + * ST_FLATFEE.pgc - st 모듈 서비스 ST_FLATFEE (one service = one file). + * 정액 수수료 적용: 거래 건마다 고정 금액 수수료를 정산 상세(st_settle_dtl)에 + * FLAT 라인으로 기록한다. 요율(bps)과 무관한 정액 과금. + */ +#include "ST_FLATFEE.h" + +/* 76. ST_FLATFEE - 정액 수수료 적용 (고정액 정산 상세 기록). */ +void ST_FLATFEE(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 flat = getl(b, T_ARG1), dtl; + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (flat <= 0) flat = 500; /* 기본 정액 500원 */ + dtl = stdb_insert_fee_dtl(sid, pid, merch, "FLAT", flat, bizdate); + if (dtl < 0) FAIL(b); + setl(b, T_FEE, flat); + setl(b, T_ID1, dtl); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_FLATVSPCT.h b/app/src/st/svc/ST_FLATVSPCT.h new file mode 100644 index 0000000..908cfed --- /dev/null +++ b/app/src/st/svc/ST_FLATVSPCT.h @@ -0,0 +1,19 @@ +/* + * ST_FLATVSPCT.h - st 서비스 ST_FLATVSPCT 카피북 (copybook / record header). + * 정액 대 정률 수수료 비교. + */ +#ifndef ST_FLATVSPCT_H +#define ST_FLATVSPCT_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_flatvspct_rec_t; + +void ST_FLATVSPCT(TPSVCINFO *p); + +#endif /* ST_FLATVSPCT_H */ diff --git a/app/src/st/svc/ST_FLATVSPCT.pgc b/app/src/st/svc/ST_FLATVSPCT.pgc new file mode 100644 index 0000000..92403cf --- /dev/null +++ b/app/src/st/svc/ST_FLATVSPCT.pgc @@ -0,0 +1,26 @@ +/* + * ST_FLATVSPCT.pgc - st 모듈 서비스 ST_FLATVSPCT (one service = one file). + * 정액 대 정률 비교: 정액(T_ARG1)과 정률(금액 x T_ARG2 bps)을 각각 계산해 더 저렴한 + * 과금 방식을 판정하고 채택 수수료/방식코드를 반환한다 (조회 전용). + */ +#include "ST_FLATVSPCT.h" + +/* 83. ST_FLATVSPCT - 정액/정률 중 저가 방식 선택. */ +void ST_FLATVSPCT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long amount = getl(b, T_AMOUNT), flat = getl(b, T_ARG1), bps = getl(b, T_ARG2); + long pct, chosen, mode; + if (flat <= 0) flat = 500; + if (bps <= 0) bps = 25; + pct = acq_fee(amount, bps); + if (flat <= pct) { chosen = flat; mode = 0; } /* 0 = 정액 채택 */ + else { chosen = pct; mode = 1; } /* 1 = 정률 채택 */ + setl(b, T_AMT1, flat); + setl(b, T_AMT2, pct); + setl(b, T_FEE, chosen); + setl(b, T_RC, mode); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_GRADESTAT.h b/app/src/st/svc/ST_GRADESTAT.h new file mode 100644 index 0000000..8c0c6a7 --- /dev/null +++ b/app/src/st/svc/ST_GRADESTAT.h @@ -0,0 +1,19 @@ +/* + * ST_GRADESTAT.h - st 서비스 ST_GRADESTAT 카피북 (copybook / record header). + * 가맹점 등급별 통계. + */ +#ifndef ST_GRADESTAT_H +#define ST_GRADESTAT_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_gradestat_rec_t; + +void ST_GRADESTAT(TPSVCINFO *p); + +#endif /* ST_GRADESTAT_H */ diff --git a/app/src/st/svc/ST_GRADESTAT.pgc b/app/src/st/svc/ST_GRADESTAT.pgc new file mode 100644 index 0000000..6620d85 --- /dev/null +++ b/app/src/st/svc/ST_GRADESTAT.pgc @@ -0,0 +1,35 @@ +/* + * ST_GRADESTAT.pgc - st 모듈 서비스 ST_GRADESTAT (one service = one file). + * 가맹점 등급별 통계: 가맹점 요율(mdr_bps) 그룹별 가맹점 수를 커서로 집계해 등급 + * 분포와 최다 등급 요율을 산출한다 (조회 전용). + */ +#include "ST_GRADESTAT.h" + +/* 132. ST_GRADESTAT - mdr_bps 등급별 가맹점 분포. */ +void ST_GRADESTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long grades = 0, total = 0, topcnt = 0, topbps = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_bps, h_cnt; + EXEC SQL END DECLARE SECTION; + EXEC SQL DECLARE gradestat_c CURSOR FOR + SELECT mdr_bps, count(*) FROM merchant GROUP BY mdr_bps ORDER BY mdr_bps; + EXEC SQL OPEN gradestat_c; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH gradestat_c INTO :h_bps, :h_cnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE gradestat_c; FAIL(b); } + grades++; total += h_cnt; + if (h_cnt > topcnt) { topcnt = h_cnt; topbps = h_bps; } + } + EXEC SQL CLOSE gradestat_c; + setl(b, T_COUNT, grades); + setl(b, T_ARG1, total); + setl(b, T_ARG2, topcnt); + setl(b, T_RC, topbps); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_GROWTHINC.h b/app/src/st/svc/ST_GROWTHINC.h new file mode 100644 index 0000000..ef52843 --- /dev/null +++ b/app/src/st/svc/ST_GROWTHINC.h @@ -0,0 +1,19 @@ +/* + * ST_GROWTHINC.h - st 서비스 ST_GROWTHINC 카피북 (copybook / record header). + * 성장 인센티브(전월 대비). + */ +#ifndef ST_GROWTHINC_H +#define ST_GROWTHINC_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_growthinc_rec_t; + +void ST_GROWTHINC(TPSVCINFO *p); + +#endif /* ST_GROWTHINC_H */ diff --git a/app/src/st/svc/ST_GROWTHINC.pgc b/app/src/st/svc/ST_GROWTHINC.pgc new file mode 100644 index 0000000..2f48ce7 --- /dev/null +++ b/app/src/st/svc/ST_GROWTHINC.pgc @@ -0,0 +1,44 @@ +/* + * ST_GROWTHINC.pgc - st 모듈 서비스 ST_GROWTHINC (one service = one file). + * 성장 인센티브: 당일 매출과 비교일(T_STR1) 매출을 비교해 증가분(성장분)에 대해서만 + * 인센티브 요율을 적용한 보상을 조정으로 기록한다. + */ +#include "ST_GROWTHINC.h" + +/* 107. ST_GROWTHINC - 전기 대비 매출 성장분 인센티브. */ +void ST_GROWTHINC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16], prevdate[16]; + long bps = getl(b, T_ARG1), growth, inc, adj; + EXEC SQL BEGIN DECLARE SECTION; + long h_now, h_prev; + char h_merch[64], h_now_d[16], h_prev_d[16]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_STR1, prevdate, sizeof(prevdate)); + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + strncpy(h_now_d, bizdate, sizeof(h_now_d)-1); h_now_d[sizeof(h_now_d)-1] = 0; + strncpy(h_prev_d, prevdate, sizeof(h_prev_d)-1); h_prev_d[sizeof(h_prev_d)-1] = 0; + if (bps <= 0) bps = 30; + EXEC SQL SELECT coalesce(gross_amount,0) INTO :h_now FROM st_merch_settle + WHERE merchant_id = :h_merch AND biz_date = :h_now_d; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlcode == 100) h_now = 0; + EXEC SQL SELECT coalesce(gross_amount,0) INTO :h_prev FROM st_merch_settle + WHERE merchant_id = :h_merch AND biz_date = :h_prev_d; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlcode == 100) h_prev = 0; + growth = h_now - h_prev; + if (growth <= 0) { setl(b, T_RC, 1); setl(b, T_DELTA, 0); OK(b); } + inc = acq_fee(growth, bps); + adj = stdb_insert_adjust(0, 0, inc, "GROWTH", bizdate); + if (adj < 0) FAIL(b); + setl(b, T_ADJ, adj); + setl(b, T_AMT1, growth); + setl(b, T_DELTA, inc); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_GRPALLOC.h b/app/src/st/svc/ST_GRPALLOC.h new file mode 100644 index 0000000..6bf5ecf --- /dev/null +++ b/app/src/st/svc/ST_GRPALLOC.h @@ -0,0 +1,19 @@ +/* + * ST_GRPALLOC.h - st 서비스 ST_GRPALLOC 카피북 (copybook / record header). + * 그룹 정산 배분. + */ +#ifndef ST_GRPALLOC_H +#define ST_GRPALLOC_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_grpalloc_rec_t; + +void ST_GRPALLOC(TPSVCINFO *p); + +#endif /* ST_GRPALLOC_H */ diff --git a/app/src/st/svc/ST_GRPALLOC.pgc b/app/src/st/svc/ST_GRPALLOC.pgc new file mode 100644 index 0000000..a5d41d2 --- /dev/null +++ b/app/src/st/svc/ST_GRPALLOC.pgc @@ -0,0 +1,50 @@ +/* + * ST_GRPALLOC.pgc - st 모듈 서비스 ST_GRPALLOC (one service = one file). + * 그룹 정산 배분: 그룹 통합 지급총액(T_AMOUNT)을 구성원의 net 비중에 따라 안분해 + * 각 구성원에게 지급 예정(st_payment)으로 등록한다. + */ +#include "ST_GRPALLOC.h" + +/* 126. ST_GRPALLOC - net 비중 안분 후 구성원별 지급 등록. */ +void ST_GRPALLOC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char prefix[16], like[24], bizdate[16]; + long alloc = getl(b, T_AMOUNT), allocated = 0, members = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_base, h_mnet; + char h_like[24], h_bizdate[16], h_mid[64]; + 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 SELECT coalesce(sum(net_amount),0) INTO :h_base FROM st_merch_settle + WHERE merchant_id LIKE :h_like AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_base <= 0) { setl(b, T_RC, 1); OK(b); } + EXEC SQL DECLARE grpalloc_c CURSOR FOR + SELECT merchant_id, net_amount FROM st_merch_settle + WHERE merchant_id LIKE :h_like AND biz_date = :h_bizdate ORDER BY merchant_id; + EXEC SQL OPEN grpalloc_c; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + char mid[64]; + long share, pay; + EXEC SQL FETCH grpalloc_c INTO :h_mid, :h_mnet; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE grpalloc_c; FAIL(b); } + strncpy(mid, h_mid, sizeof(mid)-1); mid[sizeof(mid)-1] = 0; + share = alloc * h_mnet / h_base; /* net 비중 안분 */ + pay = stdb_insert_payment(mid, share, bizdate, "SCHEDULED", bizdate); + if (pay < 0) { EXEC SQL CLOSE grpalloc_c; FAIL(b); } + allocated += share; members++; + } + EXEC SQL CLOSE grpalloc_c; + setl(b, T_COUNT, members); + setl(b, T_AMOUNT, allocated); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_GRPROLLUP.h b/app/src/st/svc/ST_GRPROLLUP.h new file mode 100644 index 0000000..4eb15c9 --- /dev/null +++ b/app/src/st/svc/ST_GRPROLLUP.h @@ -0,0 +1,19 @@ +/* + * ST_GRPROLLUP.h - st 서비스 ST_GRPROLLUP 카피북 (copybook / record header). + * 가맹점 그룹 집계 rollup. + */ +#ifndef ST_GRPROLLUP_H +#define ST_GRPROLLUP_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_grprollup_rec_t; + +void ST_GRPROLLUP(TPSVCINFO *p); + +#endif /* ST_GRPROLLUP_H */ diff --git a/app/src/st/svc/ST_GRPROLLUP.pgc b/app/src/st/svc/ST_GRPROLLUP.pgc new file mode 100644 index 0000000..8a41861 --- /dev/null +++ b/app/src/st/svc/ST_GRPROLLUP.pgc @@ -0,0 +1,42 @@ +/* + * ST_GRPROLLUP.pgc - st 모듈 서비스 ST_GRPROLLUP (one service = one file). + * 가맹점 그룹 집계 rollup: prefix(T_KEY1) 로 묶인 그룹 구성원의 당일 매출/수수료/net + * 을 커서로 순회 합산해 그룹 통합 집계를 반환한다 (조회 전용). + */ +#include "ST_GRPROLLUP.h" + +/* 125. ST_GRPROLLUP - 그룹 구성원 매출/수수료/net rollup. */ +void ST_GRPROLLUP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char prefix[16], like[24], bizdate[16]; + long members = 0, tg = 0, tf = 0, tn = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_g, h_f, h_n; + char 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 grproll_c CURSOR FOR + SELECT gross_amount, fee_amount, net_amount FROM st_merch_settle + WHERE merchant_id LIKE :h_like AND biz_date = :h_bizdate ORDER BY merchant_id; + EXEC SQL OPEN grproll_c; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH grproll_c INTO :h_g, :h_f, :h_n; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE grproll_c; FAIL(b); } + members++; tg += h_g; tf += h_f; tn += h_n; + } + EXEC SQL CLOSE grproll_c; + setl(b, T_COUNT, members); + setl(b, T_GROSS, tg); + setl(b, T_FEE, tf); + setl(b, T_NET, tn); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_INSTANTPAY.h b/app/src/st/svc/ST_INSTANTPAY.h new file mode 100644 index 0000000..cf8c461 --- /dev/null +++ b/app/src/st/svc/ST_INSTANTPAY.h @@ -0,0 +1,19 @@ +/* + * ST_INSTANTPAY.h - st 서비스 ST_INSTANTPAY 카피북 (copybook / record header). + * 즉시 정산 지급. + */ +#ifndef ST_INSTANTPAY_H +#define ST_INSTANTPAY_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_instantpay_rec_t; + +void ST_INSTANTPAY(TPSVCINFO *p); + +#endif /* ST_INSTANTPAY_H */ diff --git a/app/src/st/svc/ST_INSTANTPAY.pgc b/app/src/st/svc/ST_INSTANTPAY.pgc new file mode 100644 index 0000000..9d2d597 --- /dev/null +++ b/app/src/st/svc/ST_INSTANTPAY.pgc @@ -0,0 +1,24 @@ +/* + * ST_INSTANTPAY.pgc - st 모듈 서비스 ST_INSTANTPAY (one service = one file). + * 즉시 정산 지급: 정산 net 을 당일 지급으로 즉시 확정(status=PAID)해 지급 + * 원장(st_payment)에 기록한다 (SCHEDULED 단계 없이 즉시 완료). + */ +#include "ST_INSTANTPAY.h" + +/* 93. ST_INSTANTPAY - 즉시 지급(PAID) 등록. */ +void ST_INSTANTPAY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + long net = getl(b, T_NET), pay; + gets_(b, T_MERCHANT, merch, sizeof(merch)); if (merch[0] == 0) strcpy(merch, "M0000"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (net <= 0) { setl(b, T_RC, 1); OK(b); } + pay = stdb_insert_payment(merch, net, bizdate, "PAID", bizdate); + if (pay < 0) FAIL(b); + setl(b, T_ID1, pay); + Bchg(b, T_STATUS, 0, "PAID", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_LOCKBATCH.h b/app/src/st/svc/ST_LOCKBATCH.h new file mode 100644 index 0000000..1b9230f --- /dev/null +++ b/app/src/st/svc/ST_LOCKBATCH.h @@ -0,0 +1,19 @@ +/* + * ST_LOCKBATCH.h - st 서비스 ST_LOCKBATCH 카피북 (copybook / record header). + * 정산 배치 잠금. + */ +#ifndef ST_LOCKBATCH_H +#define ST_LOCKBATCH_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_lockbatch_rec_t; + +void ST_LOCKBATCH(TPSVCINFO *p); + +#endif /* ST_LOCKBATCH_H */ diff --git a/app/src/st/svc/ST_LOCKBATCH.pgc b/app/src/st/svc/ST_LOCKBATCH.pgc new file mode 100644 index 0000000..1c5c983 --- /dev/null +++ b/app/src/st/svc/ST_LOCKBATCH.pgc @@ -0,0 +1,28 @@ +/* + * ST_LOCKBATCH.pgc - st 모듈 서비스 ST_LOCKBATCH (one service = one file). + * 정산 배치 잠금: 당일 확정된 정산(status=SETTLED)을 일괄 LOCKED 로 전환해 이후 + * 조정/재계산을 차단한다. 잠금된 건수를 반환한다. + */ +#include "ST_LOCKBATCH.h" + +/* 121. ST_LOCKBATCH - 당일 정산 일괄 잠금(SETTLED->LOCKED). */ +void ST_LOCKBATCH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long locked; + 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 UPDATE settlement SET status = 'LOCKED' + WHERE biz_date = :h_bizdate AND status = 'SETTLED'; + if (sqlca.sqlcode < 0) FAIL(b); + locked = sqlca.sqlerrd[2]; + setl(b, T_COUNT, locked); + setl(b, T_RC, (locked > 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_LOYALTY.h b/app/src/st/svc/ST_LOYALTY.h new file mode 100644 index 0000000..dc43011 --- /dev/null +++ b/app/src/st/svc/ST_LOYALTY.h @@ -0,0 +1,19 @@ +/* + * ST_LOYALTY.h - st 서비스 ST_LOYALTY 카피북 (copybook / record header). + * 로열티 인센티브. + */ +#ifndef ST_LOYALTY_H +#define ST_LOYALTY_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_loyalty_rec_t; + +void ST_LOYALTY(TPSVCINFO *p); + +#endif /* ST_LOYALTY_H */ diff --git a/app/src/st/svc/ST_LOYALTY.pgc b/app/src/st/svc/ST_LOYALTY.pgc new file mode 100644 index 0000000..7fcbca8 --- /dev/null +++ b/app/src/st/svc/ST_LOYALTY.pgc @@ -0,0 +1,36 @@ +/* + * ST_LOYALTY.pgc - st 모듈 서비스 ST_LOYALTY (one service = one file). + * 로열티 인센티브: 가맹점의 전체 기간 누적 매출(st_merch_settle 전 일자 합계)에 + * 소액 요율을 적용한 장기 거래 로열티 보상을 조정으로 기록한다. + */ +#include "ST_LOYALTY.h" + +/* 106. ST_LOYALTY - 누적 매출 기반 로열티 보상. */ +void ST_LOYALTY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + long bps = getl(b, T_ARG1), bonus, adj; + EXEC SQL BEGIN DECLARE SECTION; + long h_total, h_days; + 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; + if (bps <= 0) bps = 5; /* 기본 0.05% 로열티 */ + EXEC SQL SELECT coalesce(sum(gross_amount),0), count(*) + INTO :h_total, :h_days FROM st_merch_settle WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_total <= 0) { setl(b, T_RC, 1); OK(b); } + bonus = acq_fee(h_total, bps); + adj = stdb_insert_adjust(0, 0, bonus, "LOYALTY", bizdate); + if (adj < 0) FAIL(b); + setl(b, T_ADJ, adj); + setl(b, T_GROSS, h_total); + setl(b, T_COUNT, h_days); + setl(b, T_DELTA, bonus); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_MKTGSHARE.h b/app/src/st/svc/ST_MKTGSHARE.h new file mode 100644 index 0000000..9923fa8 --- /dev/null +++ b/app/src/st/svc/ST_MKTGSHARE.h @@ -0,0 +1,19 @@ +/* + * ST_MKTGSHARE.h - st 서비스 ST_MKTGSHARE 카피북 (copybook / record header). + * 마케팅 분담금 정산. + */ +#ifndef ST_MKTGSHARE_H +#define ST_MKTGSHARE_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_mktgshare_rec_t; + +void ST_MKTGSHARE(TPSVCINFO *p); + +#endif /* ST_MKTGSHARE_H */ diff --git a/app/src/st/svc/ST_MKTGSHARE.pgc b/app/src/st/svc/ST_MKTGSHARE.pgc new file mode 100644 index 0000000..da110ed --- /dev/null +++ b/app/src/st/svc/ST_MKTGSHARE.pgc @@ -0,0 +1,35 @@ +/* + * ST_MKTGSHARE.pgc - st 모듈 서비스 ST_MKTGSHARE (one service = one file). + * 마케팅 분담금 정산: 가맹점 일별 집계(st_merch_settle)의 총매출에 분담율 + * (T_ARG1 bps)을 적용한 마케팅 분담금을 정산 상세 MKTG 라인으로 기록한다. + */ +#include "ST_MKTGSHARE.h" + +/* 91. ST_MKTGSHARE - 매출 기반 마케팅 분담금 부과. */ +void ST_MKTGSHARE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + long bps = getl(b, T_ARG1), share, dtl; + EXEC SQL BEGIN DECLARE SECTION; + long h_gross; + 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; + if (bps <= 0) bps = 50; /* 기본 0.5% 분담 */ + EXEC SQL SELECT coalesce(gross_amount,0) INTO :h_gross 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_gross <= 0) { setl(b, T_RC, 1); OK(b); } + share = acq_fee(h_gross, bps); + dtl = stdb_insert_fee_dtl(0, 0, merch, "MKTG", share, bizdate); + if (dtl < 0) FAIL(b); + setl(b, T_GROSS, h_gross); + setl(b, T_FEE, share); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_MONTHSTAT.h b/app/src/st/svc/ST_MONTHSTAT.h new file mode 100644 index 0000000..9537852 --- /dev/null +++ b/app/src/st/svc/ST_MONTHSTAT.h @@ -0,0 +1,19 @@ +/* + * ST_MONTHSTAT.h - st 서비스 ST_MONTHSTAT 카피북 (copybook / record header). + * 월별 정산 통계. + */ +#ifndef ST_MONTHSTAT_H +#define ST_MONTHSTAT_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_monthstat_rec_t; + +void ST_MONTHSTAT(TPSVCINFO *p); + +#endif /* ST_MONTHSTAT_H */ diff --git a/app/src/st/svc/ST_MONTHSTAT.pgc b/app/src/st/svc/ST_MONTHSTAT.pgc new file mode 100644 index 0000000..df49fcc --- /dev/null +++ b/app/src/st/svc/ST_MONTHSTAT.pgc @@ -0,0 +1,29 @@ +/* + * ST_MONTHSTAT.pgc - st 모듈 서비스 ST_MONTHSTAT (one service = one file). + * 월별 정산 통계: 대상 월(T_STR1, YYYY-MM)에 속하는 정산의 건수/net 합계/최대 net 을 + * 월 단위로 집계해 반환한다 (조회 전용). + */ +#include "ST_MONTHSTAT.h" + +/* 133. ST_MONTHSTAT - 월(YYYY-MM) 정산 집계. */ +void ST_MONTHSTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char month[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_sum, h_max; + char h_month[16]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_STR1, month, sizeof(month)); if (month[0] == 0) strcpy(month, "2026-07"); + strncpy(h_month, month, sizeof(h_month)-1); h_month[sizeof(h_month)-1] = 0; + EXEC SQL SELECT count(*), coalesce(sum(net),0), coalesce(max(net),0) + INTO :h_cnt, :h_sum, :h_max FROM settlement + WHERE to_char(biz_date, 'YYYY-MM') = :h_month; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_sum); + setl(b, T_AMT1, h_max); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_NEXTPAYDT.h b/app/src/st/svc/ST_NEXTPAYDT.h new file mode 100644 index 0000000..041841e --- /dev/null +++ b/app/src/st/svc/ST_NEXTPAYDT.h @@ -0,0 +1,19 @@ +/* + * ST_NEXTPAYDT.h - st 서비스 ST_NEXTPAYDT 카피북 (copybook / record header). + * 다음 지급 영업일 계산. + */ +#ifndef ST_NEXTPAYDT_H +#define ST_NEXTPAYDT_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_nextpaydt_rec_t; + +void ST_NEXTPAYDT(TPSVCINFO *p); + +#endif /* ST_NEXTPAYDT_H */ diff --git a/app/src/st/svc/ST_NEXTPAYDT.pgc b/app/src/st/svc/ST_NEXTPAYDT.pgc new file mode 100644 index 0000000..d2d1c9c --- /dev/null +++ b/app/src/st/svc/ST_NEXTPAYDT.pgc @@ -0,0 +1,21 @@ +/* + * ST_NEXTPAYDT.pgc - st 모듈 서비스 ST_NEXTPAYDT (one service = one file). + * 다음 지급 영업일 계산: 입력 영업일의 다음 영업일(주말 스킵)을 공통 유틸 + * (acq_next_bizday)로 계산해 지급 예정일 문자열로 반환한다 (조회 전용, 무DB). + */ +#include "ST_NEXTPAYDT.h" + +/* 97. ST_NEXTPAYDT - 다음 영업일(지급일) 계산. */ +void ST_NEXTPAYDT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], nxt[16]; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) { setl(b, T_RC, 1); OK(b); } + if (acq_next_bizday(bizdate, nxt) < 0) { setl(b, T_RC, -1); FAIL(b); } + Bchg(b, T_STR1, 0, nxt, 0L); + setl(b, T_RC, 0); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_PARTNERSET.h b/app/src/st/svc/ST_PARTNERSET.h new file mode 100644 index 0000000..4620c44 --- /dev/null +++ b/app/src/st/svc/ST_PARTNERSET.h @@ -0,0 +1,19 @@ +/* + * ST_PARTNERSET.h - st 서비스 ST_PARTNERSET 카피북 (copybook / record header). + * 제휴사 정산 배분. + */ +#ifndef ST_PARTNERSET_H +#define ST_PARTNERSET_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_partnerset_rec_t; + +void ST_PARTNERSET(TPSVCINFO *p); + +#endif /* ST_PARTNERSET_H */ diff --git a/app/src/st/svc/ST_PARTNERSET.pgc b/app/src/st/svc/ST_PARTNERSET.pgc new file mode 100644 index 0000000..6965a64 --- /dev/null +++ b/app/src/st/svc/ST_PARTNERSET.pgc @@ -0,0 +1,26 @@ +/* + * ST_PARTNERSET.pgc - st 모듈 서비스 ST_PARTNERSET (one service = one file). + * 제휴사 정산 배분: 정산 net 금액에서 제휴 지분율(T_ARG1 bps)만큼을 제휴사 + * (T_STR1) 몫으로 떼어 지급 예정(st_payment)으로 등록한다. + */ +#include "ST_PARTNERSET.h" + +/* 88. ST_PARTNERSET - 제휴사 지분 지급 예정 등록. */ +void ST_PARTNERSET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char partner[64], bizdate[16]; + long net = getl(b, T_NET), bps = getl(b, T_ARG1), share, pay; + gets_(b, T_STR1, partner, sizeof(partner)); if (partner[0] == 0) strcpy(partner, "PARTNER"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bps <= 0) bps = 2000; /* 기본 제휴 지분 20% */ + share = acq_fee(net, bps); + if (share <= 0) { setl(b, T_RC, 1); OK(b); } + pay = stdb_insert_payment(partner, share, bizdate, "SCHEDULED", bizdate); + if (pay < 0) FAIL(b); + setl(b, T_ID1, pay); + setl(b, T_AMOUNT, share); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_PARTPAY.h b/app/src/st/svc/ST_PARTPAY.h new file mode 100644 index 0000000..4ddcfeb --- /dev/null +++ b/app/src/st/svc/ST_PARTPAY.h @@ -0,0 +1,19 @@ +/* + * ST_PARTPAY.h - st 서비스 ST_PARTPAY 카피북 (copybook / record header). + * 부분 지급 처리. + */ +#ifndef ST_PARTPAY_H +#define ST_PARTPAY_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_partpay_rec_t; + +void ST_PARTPAY(TPSVCINFO *p); + +#endif /* ST_PARTPAY_H */ diff --git a/app/src/st/svc/ST_PARTPAY.pgc b/app/src/st/svc/ST_PARTPAY.pgc new file mode 100644 index 0000000..549a6df --- /dev/null +++ b/app/src/st/svc/ST_PARTPAY.pgc @@ -0,0 +1,26 @@ +/* + * ST_PARTPAY.pgc - st 모듈 서비스 ST_PARTPAY (one service = one file). + * 부분 지급 처리: 정산 net 중 일부(T_ARG1)만 당일 지급(PAID)으로 확정하고 + * 잔여 지급액을 함께 반환한다 (분할 지급). + */ +#include "ST_PARTPAY.h" + +/* 98. ST_PARTPAY - net 일부 부분 지급(PAID). */ +void ST_PARTPAY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + long net = getl(b, T_NET), part = getl(b, T_ARG1), remain, pay; + gets_(b, T_MERCHANT, merch, sizeof(merch)); if (merch[0] == 0) strcpy(merch, "M0000"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (part <= 0 || part > net) part = net; + remain = net - part; + pay = stdb_insert_payment(merch, part, bizdate, "PAID", bizdate); + if (pay < 0) FAIL(b); + setl(b, T_ID1, pay); + setl(b, T_AMOUNT, part); + setl(b, T_NET, remain); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_PCTFEE.h b/app/src/st/svc/ST_PCTFEE.h new file mode 100644 index 0000000..7af2732 --- /dev/null +++ b/app/src/st/svc/ST_PCTFEE.h @@ -0,0 +1,19 @@ +/* + * ST_PCTFEE.h - st 서비스 ST_PCTFEE 카피북 (copybook / record header). + * 정률 수수료 계산. + */ +#ifndef ST_PCTFEE_H +#define ST_PCTFEE_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_pct_rec_t; + +void ST_PCTFEE(TPSVCINFO *p); + +#endif /* ST_PCTFEE_H */ diff --git a/app/src/st/svc/ST_PCTFEE.pgc b/app/src/st/svc/ST_PCTFEE.pgc new file mode 100644 index 0000000..c75ab17 --- /dev/null +++ b/app/src/st/svc/ST_PCTFEE.pgc @@ -0,0 +1,27 @@ +/* + * ST_PCTFEE.pgc - st 모듈 서비스 ST_PCTFEE (one service = one file). + * 정률 수수료 계산: 거래 금액에 입력 요율(bps, T_ARG1)을 곱해(acq_fee) 수수료를 + * 산출하고 정산 상세에 PCT 라인으로 기록한다. + */ +#include "ST_PCTFEE.h" + +/* 77. ST_PCTFEE - 정률 수수료 계산 (금액 x bps). */ +void ST_PCTFEE(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), bps = getl(b, T_ARG1), fee, dtl; + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bps <= 0) bps = 25; /* 기본 요율 0.25% */ + fee = acq_fee(amount, bps); + dtl = stdb_insert_fee_dtl(sid, pid, merch, "PCT", fee, bizdate); + if (dtl < 0) FAIL(b); + setl(b, T_FEE, fee); + setl(b, T_RC, bps); + setl(b, T_ID1, dtl); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_PRECONFIRM.h b/app/src/st/svc/ST_PRECONFIRM.h new file mode 100644 index 0000000..c5a6515 --- /dev/null +++ b/app/src/st/svc/ST_PRECONFIRM.h @@ -0,0 +1,19 @@ +/* + * ST_PRECONFIRM.h - st 서비스 ST_PRECONFIRM 카피북 (copybook / record header). + * 정산 확정 전 검증. + */ +#ifndef ST_PRECONFIRM_H +#define ST_PRECONFIRM_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_preconfirm_rec_t; + +void ST_PRECONFIRM(TPSVCINFO *p); + +#endif /* ST_PRECONFIRM_H */ diff --git a/app/src/st/svc/ST_PRECONFIRM.pgc b/app/src/st/svc/ST_PRECONFIRM.pgc new file mode 100644 index 0000000..db25964 --- /dev/null +++ b/app/src/st/svc/ST_PRECONFIRM.pgc @@ -0,0 +1,31 @@ +/* + * ST_PRECONFIRM.pgc - st 모듈 서비스 ST_PRECONFIRM (one service = one file). + * 정산 확정 전 검증: 당일 지급 중 보류(HELD) 건이 남아있는지 확인해 확정 가능 + * 여부를 판정한다. 보류가 없으면 확정 가능(ready)으로 반환한다 (조회 전용). + */ +#include "ST_PRECONFIRM.h" + +/* 120. ST_PRECONFIRM - 보류 잔여 여부로 확정 가능 판정. */ +void ST_PRECONFIRM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_held, h_sched; + 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(*) INTO :h_held FROM st_payment + WHERE biz_date = :h_bizdate AND status = 'HELD'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_sched FROM st_payment + WHERE biz_date = :h_bizdate AND status = 'SCHEDULED'; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_AMT1, h_held); + setl(b, T_AMT2, h_sched); + setl(b, T_RC, (h_held == 0) ? 0 : 1); /* 0 = 확정 가능 */ + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_PROMOAPPLY.h b/app/src/st/svc/ST_PROMOAPPLY.h new file mode 100644 index 0000000..aac973a --- /dev/null +++ b/app/src/st/svc/ST_PROMOAPPLY.h @@ -0,0 +1,19 @@ +/* + * ST_PROMOAPPLY.h - st 서비스 ST_PROMOAPPLY 카피북 (copybook / record header). + * 프로모션 할인 적용. + */ +#ifndef ST_PROMOAPPLY_H +#define ST_PROMOAPPLY_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_promoapply_rec_t; + +void ST_PROMOAPPLY(TPSVCINFO *p); + +#endif /* ST_PROMOAPPLY_H */ diff --git a/app/src/st/svc/ST_PROMOAPPLY.pgc b/app/src/st/svc/ST_PROMOAPPLY.pgc new file mode 100644 index 0000000..904d7a8 --- /dev/null +++ b/app/src/st/svc/ST_PROMOAPPLY.pgc @@ -0,0 +1,27 @@ +/* + * ST_PROMOAPPLY.pgc - st 모듈 서비스 ST_PROMOAPPLY (one service = one file). + * 프로모션 할인 적용: 산정된 수수료(T_FEE)에 프로모션 할인율(T_ARG1 bps)을 적용해 + * 할인액을 계산하고 음(-)의 정산 조정으로 기록한다 (수수료 감액). + */ +#include "ST_PROMOAPPLY.h" + +/* 86. ST_PROMOAPPLY - 수수료 프로모션 할인(음수 조정). */ +void ST_PROMOAPPLY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long sid = getl(b, T_SETTLE_ID), pid = getl(b, T_PURCHASE_ID); + long fee = getl(b, T_FEE), bps = getl(b, T_ARG1), disc, adj; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bps <= 0) bps = 1000; /* 기본 10% 할인 */ + disc = acq_fee(fee, bps); + if (disc <= 0) { setl(b, T_RC, 1); OK(b); } + adj = stdb_insert_adjust(sid, pid, -disc, "PROMO", bizdate); + if (adj < 0) FAIL(b); + setl(b, T_ADJ, adj); + setl(b, T_DELTA, -disc); + setl(b, T_NET, fee - disc); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_RATECARD.h b/app/src/st/svc/ST_RATECARD.h new file mode 100644 index 0000000..9cc8110 --- /dev/null +++ b/app/src/st/svc/ST_RATECARD.h @@ -0,0 +1,19 @@ +/* + * ST_RATECARD.h - st 서비스 ST_RATECARD 카피북 (copybook / record header). + * 요율표 전체 조회(요율 카드). + */ +#ifndef ST_RATECARD_H +#define ST_RATECARD_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_ratecard_rec_t; + +void ST_RATECARD(TPSVCINFO *p); + +#endif /* ST_RATECARD_H */ diff --git a/app/src/st/svc/ST_RATECARD.pgc b/app/src/st/svc/ST_RATECARD.pgc new file mode 100644 index 0000000..5f92920 --- /dev/null +++ b/app/src/st/svc/ST_RATECARD.pgc @@ -0,0 +1,29 @@ +/* + * ST_RATECARD.pgc - st 모듈 서비스 ST_RATECARD (one service = one file). + * 요율 카드 조회: 특정 수수료 유형의 등록 구간 개수와 최소/최대 요율을 집계해 + * 요율표 요약(요율 카드)을 반환한다 (조회 전용). + */ +#include "ST_RATECARD.h" + +/* 80. ST_RATECARD - 요율표 요약(구간수/최소/최대 bps). */ +void ST_RATECARD(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char ftype[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_min, h_max; + 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 SELECT count(*), coalesce(min(rate_bps),0), coalesce(max(rate_bps),0) + INTO :h_cnt, :h_min, :h_max FROM st_fee_rate WHERE fee_type = :h_ftype; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt == 0) { setl(b, T_RC, 1); OK(b); } + setl(b, T_COUNT, h_cnt); + setl(b, T_AMT1, h_min); + setl(b, T_AMT2, h_max); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_RATECMP.h b/app/src/st/svc/ST_RATECMP.h new file mode 100644 index 0000000..d926a48 --- /dev/null +++ b/app/src/st/svc/ST_RATECMP.h @@ -0,0 +1,19 @@ +/* + * ST_RATECMP.h - st 서비스 ST_RATECMP 카피북 (copybook / record header). + * 요율 유형 비교. + */ +#ifndef ST_RATECMP_H +#define ST_RATECMP_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_ratecmp_rec_t; + +void ST_RATECMP(TPSVCINFO *p); + +#endif /* ST_RATECMP_H */ diff --git a/app/src/st/svc/ST_RATECMP.pgc b/app/src/st/svc/ST_RATECMP.pgc new file mode 100644 index 0000000..0a17287 --- /dev/null +++ b/app/src/st/svc/ST_RATECMP.pgc @@ -0,0 +1,26 @@ +/* + * ST_RATECMP.pgc - st 모듈 서비스 ST_RATECMP (one service = one file). + * 요율 유형 비교: 두 수수료 유형(T_STR1/T_STR2)의 입력 금액 적용 요율을 + * dbio(stdb_lookup_rate)로 각각 조회해 요율 차이를 계산한다 (조회 전용). + */ +#include "ST_RATECMP.h" + +/* 81. ST_RATECMP - 두 요율 유형 적용 bps 비교. */ +void ST_RATECMP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char ta[16], tb[16]; + long amount = getl(b, T_AMOUNT), ra, rb; + gets_(b, T_STR1, ta, sizeof(ta)); if (ta[0] == 0) strcpy(ta, "MDR"); + gets_(b, T_STR2, tb, sizeof(tb)); if (tb[0] == 0) strcpy(tb, "VAN"); + ra = stdb_lookup_rate(ta, amount); + rb = stdb_lookup_rate(tb, amount); + if (ra < 0 || rb < 0) FAIL(b); + setl(b, T_ARG1, ra); + setl(b, T_ARG2, rb); + setl(b, T_DELTA, ra - rb); + setl(b, T_FEE, acq_fee(amount, ra) - acq_fee(amount, rb)); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_RATEFLOOR.h b/app/src/st/svc/ST_RATEFLOOR.h new file mode 100644 index 0000000..52d4459 --- /dev/null +++ b/app/src/st/svc/ST_RATEFLOOR.h @@ -0,0 +1,19 @@ +/* + * ST_RATEFLOOR.h - st 서비스 ST_RATEFLOOR 카피북 (copybook / record header). + * 요율 하한 적용. + */ +#ifndef ST_RATEFLOOR_H +#define ST_RATEFLOOR_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_ratefloor_rec_t; + +void ST_RATEFLOOR(TPSVCINFO *p); + +#endif /* ST_RATEFLOOR_H */ diff --git a/app/src/st/svc/ST_RATEFLOOR.pgc b/app/src/st/svc/ST_RATEFLOOR.pgc new file mode 100644 index 0000000..6f6d4b4 --- /dev/null +++ b/app/src/st/svc/ST_RATEFLOOR.pgc @@ -0,0 +1,29 @@ +/* + * ST_RATEFLOOR.pgc - st 모듈 서비스 ST_RATEFLOOR (one service = one file). + * 요율 하한 적용: 정률 수수료(금액 x bps)가 하한액(T_ARG2)에 미달하면 하한으로 + * 끌어올려 정산 상세에 FLRFEE 라인으로 기록한다. + */ +#include "ST_RATEFLOOR.h" + +/* 85. ST_RATEFLOOR - 정률 수수료 하한 보정 후 기록. */ +void ST_RATEFLOOR(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), bps = getl(b, T_ARG1), floor = getl(b, T_ARG2); + long fee, lifted = 0, dtl; + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bps <= 0) bps = 25; + if (floor <= 0) floor = 100; + fee = acq_fee(amount, bps); + if (fee < floor) { fee = floor; lifted = 1; } + dtl = stdb_insert_fee_dtl(sid, pid, merch, "FLRFEE", fee, bizdate); + if (dtl < 0) FAIL(b); + setl(b, T_FEE, fee); + setl(b, T_RC, lifted); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_RATEMAX.h b/app/src/st/svc/ST_RATEMAX.h new file mode 100644 index 0000000..4492a9e --- /dev/null +++ b/app/src/st/svc/ST_RATEMAX.h @@ -0,0 +1,19 @@ +/* + * ST_RATEMAX.h - st 서비스 ST_RATEMAX 카피북 (copybook / record header). + * 요율 상한 적용. + */ +#ifndef ST_RATEMAX_H +#define ST_RATEMAX_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_ratemax_rec_t; + +void ST_RATEMAX(TPSVCINFO *p); + +#endif /* ST_RATEMAX_H */ diff --git a/app/src/st/svc/ST_RATEMAX.pgc b/app/src/st/svc/ST_RATEMAX.pgc new file mode 100644 index 0000000..e16279e --- /dev/null +++ b/app/src/st/svc/ST_RATEMAX.pgc @@ -0,0 +1,29 @@ +/* + * ST_RATEMAX.pgc - st 모듈 서비스 ST_RATEMAX (one service = one file). + * 요율 상한 적용: 정률 수수료(금액 x bps)를 계산하되 상한액(T_ARG2)을 초과하면 + * 상한으로 절삭해 정산 상세에 CAPFEE 라인으로 기록한다. + */ +#include "ST_RATEMAX.h" + +/* 84. ST_RATEMAX - 정률 수수료 상한 절삭 후 기록. */ +void ST_RATEMAX(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), bps = getl(b, T_ARG1), cap = getl(b, T_ARG2); + long fee, capped = 0, dtl; + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bps <= 0) bps = 25; + if (cap <= 0) cap = 100000; + fee = acq_fee(amount, bps); + if (fee > cap) { fee = cap; capped = 1; } + dtl = stdb_insert_fee_dtl(sid, pid, merch, "CAPFEE", fee, bizdate); + if (dtl < 0) FAIL(b); + setl(b, T_FEE, fee); + setl(b, T_RC, capped); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_REBATE.h b/app/src/st/svc/ST_REBATE.h new file mode 100644 index 0000000..10d1a32 --- /dev/null +++ b/app/src/st/svc/ST_REBATE.h @@ -0,0 +1,19 @@ +/* + * ST_REBATE.h - st 서비스 ST_REBATE 카피북 (copybook / record header). + * 리베이트 정산. + */ +#ifndef ST_REBATE_H +#define ST_REBATE_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_rebate_rec_t; + +void ST_REBATE(TPSVCINFO *p); + +#endif /* ST_REBATE_H */ diff --git a/app/src/st/svc/ST_REBATE.pgc b/app/src/st/svc/ST_REBATE.pgc new file mode 100644 index 0000000..e95053e --- /dev/null +++ b/app/src/st/svc/ST_REBATE.pgc @@ -0,0 +1,26 @@ +/* + * ST_REBATE.pgc - st 모듈 서비스 ST_REBATE (one service = one file). + * 리베이트 정산: 부과 수수료(T_FEE)의 일정 비율(T_ARG1 bps)을 가맹점에 돌려주는 + * 리베이트로 산정해 양(+)의 정산 조정으로 기록한다. + */ +#include "ST_REBATE.h" + +/* 104. ST_REBATE - 수수료 기반 리베이트 환급 조정. */ +void ST_REBATE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long sid = getl(b, T_SETTLE_ID), pid = getl(b, T_PURCHASE_ID); + long fee = getl(b, T_FEE), bps = getl(b, T_ARG1), rebate, adj; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bps <= 0) bps = 500; /* 기본 리베이트 5% */ + rebate = acq_fee(fee, bps); + if (rebate <= 0) { setl(b, T_RC, 1); OK(b); } + adj = stdb_insert_adjust(sid, pid, rebate, "REBATE", bizdate); + if (adj < 0) FAIL(b); + setl(b, T_ADJ, adj); + setl(b, T_DELTA, rebate); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_RECVAGE.h b/app/src/st/svc/ST_RECVAGE.h new file mode 100644 index 0000000..ec13f23 --- /dev/null +++ b/app/src/st/svc/ST_RECVAGE.h @@ -0,0 +1,19 @@ +/* + * ST_RECVAGE.h - st 서비스 ST_RECVAGE 카피북 (copybook / record header). + * 미수금 연령 분석. + */ +#ifndef ST_RECVAGE_H +#define ST_RECVAGE_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_recvage_rec_t; + +void ST_RECVAGE(TPSVCINFO *p); + +#endif /* ST_RECVAGE_H */ diff --git a/app/src/st/svc/ST_RECVAGE.pgc b/app/src/st/svc/ST_RECVAGE.pgc new file mode 100644 index 0000000..23f9b1a --- /dev/null +++ b/app/src/st/svc/ST_RECVAGE.pgc @@ -0,0 +1,28 @@ +/* + * ST_RECVAGE.pgc - st 모듈 서비스 ST_RECVAGE (one service = one file). + * 미수금 연령 분석: 기준일(T_BIZDATE) 이전 지급일의 보류(HELD) 지급 건수와 금액을 + * 집계해 연체 미수 규모를 산출한다 (조회 전용). + */ +#include "ST_RECVAGE.h" + +/* 110. ST_RECVAGE - 기준일 이전 보류 미수금 집계. */ +void ST_RECVAGE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_sum; + 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_amount),0) INTO :h_cnt, :h_sum + FROM st_payment WHERE status = 'HELD' AND pay_date <= :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + setl(b, T_GROSS, h_sum); + setl(b, T_RC, (h_cnt > 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_RECVSTAT.h b/app/src/st/svc/ST_RECVSTAT.h new file mode 100644 index 0000000..535cf4f --- /dev/null +++ b/app/src/st/svc/ST_RECVSTAT.h @@ -0,0 +1,19 @@ +/* + * ST_RECVSTAT.h - st 서비스 ST_RECVSTAT 카피북 (copybook / record header). + * 미수금 통계. + */ +#ifndef ST_RECVSTAT_H +#define ST_RECVSTAT_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_recvstat_rec_t; + +void ST_RECVSTAT(TPSVCINFO *p); + +#endif /* ST_RECVSTAT_H */ diff --git a/app/src/st/svc/ST_RECVSTAT.pgc b/app/src/st/svc/ST_RECVSTAT.pgc new file mode 100644 index 0000000..4e25fad --- /dev/null +++ b/app/src/st/svc/ST_RECVSTAT.pgc @@ -0,0 +1,40 @@ +/* + * ST_RECVSTAT.pgc - st 모듈 서비스 ST_RECVSTAT (one service = one file). + * 미수금 통계: 지급(st_payment) 상태별 건수와 금액을 커서로 순회 집계해 보류/예정/ + * 완료 분포를 산출하고 보류(HELD) 비중을 반환한다 (조회 전용). + */ +#include "ST_RECVSTAT.h" + +/* 114. ST_RECVSTAT - 지급 상태별 분포 통계. */ +void ST_RECVSTAT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long groups = 0, grand = 0, held = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_sum; + char h_status[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 recvstat_c CURSOR FOR + SELECT status, count(*), coalesce(sum(net_amount),0) FROM st_payment + WHERE biz_date = :h_bizdate GROUP BY status ORDER BY status; + EXEC SQL OPEN recvstat_c; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH recvstat_c INTO :h_status, :h_cnt, :h_sum; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE recvstat_c; FAIL(b); } + groups++; + grand += h_sum; + if (strcmp(h_status, "HELD") == 0) held = h_sum; + } + EXEC SQL CLOSE recvstat_c; + setl(b, T_COUNT, groups); + setl(b, T_GROSS, grand); + setl(b, T_AMT1, held); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_REFERRAL.h b/app/src/st/svc/ST_REFERRAL.h new file mode 100644 index 0000000..ec896d7 --- /dev/null +++ b/app/src/st/svc/ST_REFERRAL.h @@ -0,0 +1,19 @@ +/* + * ST_REFERRAL.h - st 서비스 ST_REFERRAL 카피북 (copybook / record header). + * 추천 인센티브. + */ +#ifndef ST_REFERRAL_H +#define ST_REFERRAL_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_referral_rec_t; + +void ST_REFERRAL(TPSVCINFO *p); + +#endif /* ST_REFERRAL_H */ diff --git a/app/src/st/svc/ST_REFERRAL.pgc b/app/src/st/svc/ST_REFERRAL.pgc new file mode 100644 index 0000000..d4322e8 --- /dev/null +++ b/app/src/st/svc/ST_REFERRAL.pgc @@ -0,0 +1,36 @@ +/* + * ST_REFERRAL.pgc - st 모듈 서비스 ST_REFERRAL (one service = one file). + * 추천 인센티브: 추천으로 유치된 가맹점 수(prefix T_KEY1 매칭 건수)에 건당 보상액 + * (T_ARG1)을 곱해 추천 가맹점(T_MERCHANT)에 지급 예정으로 등록한다. + */ +#include "ST_REFERRAL.h" + +/* 108. ST_REFERRAL - 추천 유치 건당 보상 지급. */ +void ST_REFERRAL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], prefix[16], like[24], bizdate[16]; + long per = getl(b, T_ARG1), bonus, pay; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt; + char h_like[24]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_MERCHANT, merch, sizeof(merch)); if (merch[0] == 0) strcpy(merch, "M0000"); + gets_(b, T_KEY1, prefix, sizeof(prefix)); if (prefix[0] == 0) strcpy(prefix, "M0"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (per <= 0) per = 10000; /* 건당 1만원 */ + snprintf(like, sizeof(like), "%s%%", prefix); + strncpy(h_like, like, sizeof(h_like)-1); h_like[sizeof(h_like)-1] = 0; + EXEC SQL SELECT count(*) INTO :h_cnt FROM merchant WHERE merchant_id LIKE :h_like; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt <= 0) { setl(b, T_RC, 1); OK(b); } + bonus = h_cnt * per; + pay = stdb_insert_payment(merch, bonus, bizdate, "SCHEDULED", bizdate); + if (pay < 0) FAIL(b); + setl(b, T_ID1, pay); + setl(b, T_COUNT, h_cnt); + setl(b, T_AMOUNT, bonus); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_REFUNDCALC.h b/app/src/st/svc/ST_REFUNDCALC.h new file mode 100644 index 0000000..a4c5683 --- /dev/null +++ b/app/src/st/svc/ST_REFUNDCALC.h @@ -0,0 +1,19 @@ +/* + * ST_REFUNDCALC.h - st 서비스 ST_REFUNDCALC 카피북 (copybook / record header). + * 환불 재계산. + */ +#ifndef ST_REFUNDCALC_H +#define ST_REFUNDCALC_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_refundcalc_rec_t; + +void ST_REFUNDCALC(TPSVCINFO *p); + +#endif /* ST_REFUNDCALC_H */ diff --git a/app/src/st/svc/ST_REFUNDCALC.pgc b/app/src/st/svc/ST_REFUNDCALC.pgc new file mode 100644 index 0000000..e768654 --- /dev/null +++ b/app/src/st/svc/ST_REFUNDCALC.pgc @@ -0,0 +1,27 @@ +/* + * ST_REFUNDCALC.pgc - st 모듈 서비스 ST_REFUNDCALC (one service = one file). + * 환불 재계산: 환불액(T_ARG1)에 비례한 수수료 환급분을 계산해 정산 상세에 + * REFUND 라인(음수 금액)으로 기록한다. + */ +#include "ST_REFUNDCALC.h" + +/* 101. ST_REFUNDCALC - 환불 비례 수수료 환급 기록. */ +void ST_REFUNDCALC(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 refund = getl(b, T_ARG1), bps = getl(b, T_ARG2), back, dtl; + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bps <= 0) bps = 25; + if (refund <= 0) { setl(b, T_RC, 1); OK(b); } + back = acq_fee(refund, bps); + dtl = stdb_insert_fee_dtl(sid, pid, merch, "REFUND", -back, bizdate); + if (dtl < 0) FAIL(b); + setl(b, T_FEE, -back); + setl(b, T_ID1, dtl); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_RELAYSPLIT.h b/app/src/st/svc/ST_RELAYSPLIT.h new file mode 100644 index 0000000..2034f93 --- /dev/null +++ b/app/src/st/svc/ST_RELAYSPLIT.h @@ -0,0 +1,19 @@ +/* + * ST_RELAYSPLIT.h - st 서비스 ST_RELAYSPLIT 카피북 (copybook / record header). + * 중계수수료 분배. + */ +#ifndef ST_RELAYSPLIT_H +#define ST_RELAYSPLIT_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_relaysplit_rec_t; + +void ST_RELAYSPLIT(TPSVCINFO *p); + +#endif /* ST_RELAYSPLIT_H */ diff --git a/app/src/st/svc/ST_RELAYSPLIT.pgc b/app/src/st/svc/ST_RELAYSPLIT.pgc new file mode 100644 index 0000000..7c57a4d --- /dev/null +++ b/app/src/st/svc/ST_RELAYSPLIT.pgc @@ -0,0 +1,29 @@ +/* + * ST_RELAYSPLIT.pgc - st 모듈 서비스 ST_RELAYSPLIT (one service = one file). + * 중계수수료 분배: 중계 수수료(T_FEE)를 지분율(T_ARG1 bps)에 따라 중계사 몫과 + * 매입사 몫으로 나누어 각각 정산 상세 RELAYA/RELAYB 라인으로 기록한다. + */ +#include "ST_RELAYSPLIT.h" + +/* 131. ST_RELAYSPLIT - 중계수수료 2자 분배 기록. */ +void ST_RELAYSPLIT(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 relay = getl(b, T_FEE), bps = getl(b, T_ARG1), part_a, part_b, da, db; + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bps <= 0) bps = 6000; /* 중계사 60% 기본 */ + part_a = acq_fee(relay, bps); + part_b = relay - part_a; + da = stdb_insert_fee_dtl(sid, pid, merch, "RELAYA", part_a, bizdate); + if (da < 0) FAIL(b); + db = stdb_insert_fee_dtl(sid, pid, merch, "RELAYB", part_b, bizdate); + if (db < 0) FAIL(b); + setl(b, T_AMT1, part_a); + setl(b, T_AMT2, part_b); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_REPARTIAL.h b/app/src/st/svc/ST_REPARTIAL.h new file mode 100644 index 0000000..1914833 --- /dev/null +++ b/app/src/st/svc/ST_REPARTIAL.h @@ -0,0 +1,19 @@ +/* + * ST_REPARTIAL.h - st 서비스 ST_REPARTIAL 카피북 (copybook / record header). + * 부분 재정산. + */ +#ifndef ST_REPARTIAL_H +#define ST_REPARTIAL_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_repartial_rec_t; + +void ST_REPARTIAL(TPSVCINFO *p); + +#endif /* ST_REPARTIAL_H */ diff --git a/app/src/st/svc/ST_REPARTIAL.pgc b/app/src/st/svc/ST_REPARTIAL.pgc new file mode 100644 index 0000000..6e2df73 --- /dev/null +++ b/app/src/st/svc/ST_REPARTIAL.pgc @@ -0,0 +1,31 @@ +/* + * ST_REPARTIAL.pgc - st 모듈 서비스 ST_REPARTIAL (one service = one file). + * 부분 재정산: 부분취소액(T_DELTA)만큼 줄어든 잔여 거래금액으로 수수료를 재계산해 + * 원 수수료 대비 감소분을 음(-)의 정산 조정으로 반영한다. + */ +#include "ST_REPARTIAL.h" + +/* 99. ST_REPARTIAL - 부분취소 후 수수료 재계산 조정. */ +void ST_REPARTIAL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long sid = getl(b, T_SETTLE_ID), pid = getl(b, T_PURCHASE_ID); + long amount = getl(b, T_AMOUNT), cancel = getl(b, T_DELTA), bps = getl(b, T_ARG1); + long oldfee, newfee, diff, adj; + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bps <= 0) bps = 25; + if (cancel < 0) cancel = 0; + if (cancel > amount) cancel = amount; + oldfee = acq_fee(amount, bps); + newfee = acq_fee(amount - cancel, bps); + diff = newfee - oldfee; /* 음수: 수수료 감소 */ + adj = stdb_insert_adjust(sid, pid, diff, "REPARTIAL", bizdate); + if (adj < 0) FAIL(b); + setl(b, T_ADJ, adj); + setl(b, T_FEE, newfee); + setl(b, T_DELTA, diff); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_SLABFEE.h b/app/src/st/svc/ST_SLABFEE.h new file mode 100644 index 0000000..08a48a4 --- /dev/null +++ b/app/src/st/svc/ST_SLABFEE.h @@ -0,0 +1,19 @@ +/* + * ST_SLABFEE.h - st 서비스 ST_SLABFEE 카피북 (copybook / record header). + * 슬라브(누진 구간) 요율 계산. + */ +#ifndef ST_SLABFEE_H +#define ST_SLABFEE_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_slab_rec_t; + +void ST_SLABFEE(TPSVCINFO *p); + +#endif /* ST_SLABFEE_H */ diff --git a/app/src/st/svc/ST_SLABFEE.pgc b/app/src/st/svc/ST_SLABFEE.pgc new file mode 100644 index 0000000..eee6472 --- /dev/null +++ b/app/src/st/svc/ST_SLABFEE.pgc @@ -0,0 +1,46 @@ +/* + * ST_SLABFEE.pgc - st 모듈 서비스 ST_SLABFEE (one service = one file). + * 슬라브(누진 구간) 요율 계산: 요율표의 각 구간에 대해 금액이 걸치는 부분에만 + * 해당 구간 요율을 적용하는 누진 방식으로 수수료를 합산한다 (조회 전용). + */ +#include "ST_SLABFEE.h" + +/* 78. ST_SLABFEE - 슬라브 누진 요율 수수료 합산. */ +void ST_SLABFEE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char ftype[16]; + long amount = getl(b, T_AMOUNT), slab = 0, segs = 0; + long prev_min = -1, prev_bps = 0; + 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 slab_c CURSOR FOR + SELECT band_min, rate_bps FROM st_fee_rate WHERE fee_type = :h_ftype ORDER BY band_min; + EXEC SQL OPEN slab_c; + if (sqlca.sqlcode < 0) FAIL(b); + for (;;) { + EXEC SQL FETCH slab_c INTO :h_min, :h_bps; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE slab_c; FAIL(b); } + if (prev_min >= 0 && amount > prev_min) { /* 이전 구간 [prev_min, h_min) 부분 과금 */ + long top = (amount < h_min) ? amount : h_min; + slab += acq_fee(top - prev_min, prev_bps); + segs++; + } + prev_min = h_min; prev_bps = h_bps; + } + EXEC SQL CLOSE slab_c; + if (prev_min >= 0 && amount > prev_min) { /* 마지막 구간 [prev_min, +inf) */ + slab += acq_fee(amount - prev_min, prev_bps); + segs++; + } + setl(b, T_FEE, slab); + setl(b, T_COUNT, segs); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_SUPPLYCALC.h b/app/src/st/svc/ST_SUPPLYCALC.h new file mode 100644 index 0000000..a251ab0 --- /dev/null +++ b/app/src/st/svc/ST_SUPPLYCALC.h @@ -0,0 +1,19 @@ +/* + * ST_SUPPLYCALC.h - st 서비스 ST_SUPPLYCALC 카피북 (copybook / record header). + * 공급가액 역산. + */ +#ifndef ST_SUPPLYCALC_H +#define ST_SUPPLYCALC_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_supplycalc_rec_t; + +void ST_SUPPLYCALC(TPSVCINFO *p); + +#endif /* ST_SUPPLYCALC_H */ diff --git a/app/src/st/svc/ST_SUPPLYCALC.pgc b/app/src/st/svc/ST_SUPPLYCALC.pgc new file mode 100644 index 0000000..b9823e4 --- /dev/null +++ b/app/src/st/svc/ST_SUPPLYCALC.pgc @@ -0,0 +1,22 @@ +/* + * ST_SUPPLYCALC.pgc - st 모듈 서비스 ST_SUPPLYCALC (one service = one file). + * 공급가액 역산: 부가세 포함 총액(T_AMOUNT)에서 공급가액과 부가세를 분리 + * 역산한다 (총액 = 공급가액 x 1.1 가정, 무DB 계산). + */ +#include "ST_SUPPLYCALC.h" + +/* 118. ST_SUPPLYCALC - VAT 포함 총액에서 공급가액 역산. */ +void ST_SUPPLYCALC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long gross = getl(b, T_AMOUNT), supply, vat; + if (gross <= 0) { setl(b, T_RC, 1); OK(b); } + supply = gross * 10 / 11; /* 총액 / 1.1 */ + vat = gross - supply; + setl(b, T_GROSS, gross); + setl(b, T_NET, supply); + setl(b, T_TAX, vat); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_TAXSUMMARY.h b/app/src/st/svc/ST_TAXSUMMARY.h new file mode 100644 index 0000000..067e262 --- /dev/null +++ b/app/src/st/svc/ST_TAXSUMMARY.h @@ -0,0 +1,19 @@ +/* + * ST_TAXSUMMARY.h - st 서비스 ST_TAXSUMMARY 카피북 (copybook / record header). + * 세금 요약(VAT+WHT). + */ +#ifndef ST_TAXSUMMARY_H +#define ST_TAXSUMMARY_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_taxsummary_rec_t; + +void ST_TAXSUMMARY(TPSVCINFO *p); + +#endif /* ST_TAXSUMMARY_H */ diff --git a/app/src/st/svc/ST_TAXSUMMARY.pgc b/app/src/st/svc/ST_TAXSUMMARY.pgc new file mode 100644 index 0000000..cb94804 --- /dev/null +++ b/app/src/st/svc/ST_TAXSUMMARY.pgc @@ -0,0 +1,24 @@ +/* + * ST_TAXSUMMARY.pgc - st 모듈 서비스 ST_TAXSUMMARY (one service = one file). + * 세금 요약: 입력 수수료(T_FEE)를 공급가액으로 부가세(acq_vat)와 원천세 + * (acq_wht_amt)를 동시에 산정해 총 세금과 세후 순액을 반환한다 (조회 전용, 무DB). + */ +#include "ST_TAXSUMMARY.h" + +/* 117. ST_TAXSUMMARY - 부가세+원천세 통합 요약. */ +void ST_TAXSUMMARY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long fee = getl(b, T_FEE), whtbps = getl(b, T_ARG1), vat, wht, tax; + if (whtbps <= 0) whtbps = 330; + vat = acq_vat(fee); + wht = acq_wht_amt(fee, whtbps); + tax = vat + wht; + setl(b, T_AMT1, vat); + setl(b, T_AMT2, wht); + setl(b, T_TAX, tax); + setl(b, T_NET, fee - tax); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_TIERFEE.h b/app/src/st/svc/ST_TIERFEE.h new file mode 100644 index 0000000..b7bd3b8 --- /dev/null +++ b/app/src/st/svc/ST_TIERFEE.h @@ -0,0 +1,19 @@ +/* + * ST_TIERFEE.h - st 서비스 ST_TIERFEE 카피북 (copybook / record header). + * 구간별 정률 수수료 적용. + */ +#ifndef ST_TIERFEE_H +#define ST_TIERFEE_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_tierfee_rec_t; + +void ST_TIERFEE(TPSVCINFO *p); + +#endif /* ST_TIERFEE_H */ diff --git a/app/src/st/svc/ST_TIERFEE.pgc b/app/src/st/svc/ST_TIERFEE.pgc new file mode 100644 index 0000000..fb0e821 --- /dev/null +++ b/app/src/st/svc/ST_TIERFEE.pgc @@ -0,0 +1,32 @@ +/* + * ST_TIERFEE.pgc - st 모듈 서비스 ST_TIERFEE (one service = one file). + * 구간별 정률 적용: 입력 금액에 해당하는 최상위 구간(band_min <= amount)의 요율을 + * 단건 조회로 선택해 정률 수수료를 산출한다 (조회 전용). + */ +#include "ST_TIERFEE.h" + +/* 82. ST_TIERFEE - 해당 구간 요율로 정률 수수료 산출. */ +void ST_TIERFEE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char ftype[16]; + long amount = getl(b, T_AMOUNT), fee; + EXEC SQL BEGIN DECLARE SECTION; + long h_bps, h_amt; + 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; + h_amt = amount; + EXEC SQL SELECT rate_bps INTO :h_bps FROM st_fee_rate + WHERE fee_type = :h_ftype AND band_min <= :h_amt + ORDER BY band_min DESC LIMIT 1; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); setl(b, T_FEE, 0); OK(b); } + fee = acq_fee(amount, h_bps); + setl(b, T_RC, h_bps); + setl(b, T_FEE, fee); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_UNLOCK.h b/app/src/st/svc/ST_UNLOCK.h new file mode 100644 index 0000000..54e1fce --- /dev/null +++ b/app/src/st/svc/ST_UNLOCK.h @@ -0,0 +1,19 @@ +/* + * ST_UNLOCK.h - st 서비스 ST_UNLOCK 카피북 (copybook / record header). + * 정산 잠금 해제. + */ +#ifndef ST_UNLOCK_H +#define ST_UNLOCK_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_unlock_rec_t; + +void ST_UNLOCK(TPSVCINFO *p); + +#endif /* ST_UNLOCK_H */ diff --git a/app/src/st/svc/ST_UNLOCK.pgc b/app/src/st/svc/ST_UNLOCK.pgc new file mode 100644 index 0000000..5e79bf2 --- /dev/null +++ b/app/src/st/svc/ST_UNLOCK.pgc @@ -0,0 +1,28 @@ +/* + * ST_UNLOCK.pgc - st 모듈 서비스 ST_UNLOCK (one service = one file). + * 정산 잠금 해제: 잠금된 정산(status=LOCKED)을 다시 SETTLED 로 되돌려 조정/재계산을 + * 허용한다. 해제된 건수를 반환한다. + */ +#include "ST_UNLOCK.h" + +/* 122. ST_UNLOCK - 정산 잠금 해제(LOCKED->SETTLED). */ +void ST_UNLOCK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long freed; + 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 UPDATE settlement SET status = 'SETTLED' + WHERE biz_date = :h_bizdate AND status = 'LOCKED'; + if (sqlca.sqlcode < 0) FAIL(b); + freed = sqlca.sqlerrd[2]; + setl(b, T_COUNT, freed); + setl(b, T_RC, (freed > 0) ? 0 : 1); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_VANDETAIL.h b/app/src/st/svc/ST_VANDETAIL.h new file mode 100644 index 0000000..a4f3677 --- /dev/null +++ b/app/src/st/svc/ST_VANDETAIL.h @@ -0,0 +1,19 @@ +/* + * ST_VANDETAIL.h - st 서비스 ST_VANDETAIL 카피북 (copybook / record header). + * VAN 수수료 세분 명세. + */ +#ifndef ST_VANDETAIL_H +#define ST_VANDETAIL_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_vandetail_rec_t; + +void ST_VANDETAIL(TPSVCINFO *p); + +#endif /* ST_VANDETAIL_H */ diff --git a/app/src/st/svc/ST_VANDETAIL.pgc b/app/src/st/svc/ST_VANDETAIL.pgc new file mode 100644 index 0000000..dbe3a61 --- /dev/null +++ b/app/src/st/svc/ST_VANDETAIL.pgc @@ -0,0 +1,30 @@ +/* + * ST_VANDETAIL.pgc - st 모듈 서비스 ST_VANDETAIL (one service = one file). + * VAN 수수료 세분 명세: 당일 정산 상세 중 VAN 라인의 건수/합계/평균을 집계해 + * VAN 중계 수수료 명세를 반환한다 (조회 전용). + */ +#include "ST_VANDETAIL.h" + +/* 130. ST_VANDETAIL - VAN 수수료 건수/합계/평균 명세. */ +void ST_VANDETAIL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long avg; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_sum; + 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(amount),0) INTO :h_cnt, :h_sum + FROM st_settle_dtl WHERE biz_date = :h_bizdate AND fee_type = 'VAN'; + if (sqlca.sqlcode < 0) FAIL(b); + avg = (h_cnt > 0) ? h_sum / h_cnt : 0; + setl(b, T_COUNT, h_cnt); + setl(b, T_FEE, h_sum); + setl(b, T_AMT1, avg); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_VATADJUST.h b/app/src/st/svc/ST_VATADJUST.h new file mode 100644 index 0000000..013afd8 --- /dev/null +++ b/app/src/st/svc/ST_VATADJUST.h @@ -0,0 +1,19 @@ +/* + * ST_VATADJUST.h - st 서비스 ST_VATADJUST 카피북 (copybook / record header). + * 부가세 조정. + */ +#ifndef ST_VATADJUST_H +#define ST_VATADJUST_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_vatadjust_rec_t; + +void ST_VATADJUST(TPSVCINFO *p); + +#endif /* ST_VATADJUST_H */ diff --git a/app/src/st/svc/ST_VATADJUST.pgc b/app/src/st/svc/ST_VATADJUST.pgc new file mode 100644 index 0000000..dadce75 --- /dev/null +++ b/app/src/st/svc/ST_VATADJUST.pgc @@ -0,0 +1,27 @@ +/* + * ST_VATADJUST.pgc - st 모듈 서비스 ST_VATADJUST (one service = one file). + * 부가세 조정: 조정된 공급가액(T_ARG1)에 대한 재산정 부가세(acq_vat)를 정산 상세에 + * VAT 라인으로 재기록하고 기존 대비 차이를 반환한다. + */ +#include "ST_VATADJUST.h" + +/* 119. ST_VATADJUST - 공급가액 조정 재부가세 기록. */ +void ST_VATADJUST(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 supply = getl(b, T_ARG1), oldvat = getl(b, T_TAX), newvat, dtl; + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (supply <= 0) { setl(b, T_RC, 1); OK(b); } + newvat = acq_vat(supply); + dtl = stdb_insert_fee_dtl(sid, pid, merch, "VAT", newvat, bizdate); + if (dtl < 0) FAIL(b); + setl(b, T_TAX, newvat); + setl(b, T_DELTA, newvat - oldvat); + setl(b, T_ID1, dtl); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_VATDETAIL.h b/app/src/st/svc/ST_VATDETAIL.h new file mode 100644 index 0000000..e2347b5 --- /dev/null +++ b/app/src/st/svc/ST_VATDETAIL.h @@ -0,0 +1,19 @@ +/* + * ST_VATDETAIL.h - st 서비스 ST_VATDETAIL 카피북 (copybook / record header). + * 부가세 명세. + */ +#ifndef ST_VATDETAIL_H +#define ST_VATDETAIL_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_vatdetail_rec_t; + +void ST_VATDETAIL(TPSVCINFO *p); + +#endif /* ST_VATDETAIL_H */ diff --git a/app/src/st/svc/ST_VATDETAIL.pgc b/app/src/st/svc/ST_VATDETAIL.pgc new file mode 100644 index 0000000..c6bebe2 --- /dev/null +++ b/app/src/st/svc/ST_VATDETAIL.pgc @@ -0,0 +1,33 @@ +/* + * ST_VATDETAIL.pgc - st 모듈 서비스 ST_VATDETAIL (one service = one file). + * 부가세 명세: 가맹점 당일 수수료 상세(MDR/VAN/RELAY 합계)를 공급가액으로 보고 + * 부가세(공통 acq_vat, 10%)를 산출한 부가세 명세를 반환한다 (조회 전용). + */ +#include "ST_VATDETAIL.h" + +/* 115. ST_VATDETAIL - 수수료 합계 기준 부가세 명세. */ +void ST_VATDETAIL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + long vat; + EXEC SQL BEGIN DECLARE SECTION; + long h_feesum, h_cnt; + 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(amount),0), count(*) INTO :h_feesum, :h_cnt + FROM st_settle_dtl WHERE merchant_id = :h_merch AND biz_date = :h_bizdate + AND fee_type IN ('MDR', 'VAN', 'RELAY'); + if (sqlca.sqlcode < 0) FAIL(b); + vat = acq_vat(h_feesum); + setl(b, T_GROSS, h_feesum); + setl(b, T_TAX, vat); + setl(b, T_COUNT, h_cnt); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_VOIDRECALC.h b/app/src/st/svc/ST_VOIDRECALC.h new file mode 100644 index 0000000..e0fd92b --- /dev/null +++ b/app/src/st/svc/ST_VOIDRECALC.h @@ -0,0 +1,19 @@ +/* + * ST_VOIDRECALC.h - st 서비스 ST_VOIDRECALC 카피북 (copybook / record header). + * void 재계산. + */ +#ifndef ST_VOIDRECALC_H +#define ST_VOIDRECALC_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_voidrecalc_rec_t; + +void ST_VOIDRECALC(TPSVCINFO *p); + +#endif /* ST_VOIDRECALC_H */ diff --git a/app/src/st/svc/ST_VOIDRECALC.pgc b/app/src/st/svc/ST_VOIDRECALC.pgc new file mode 100644 index 0000000..29dd29f --- /dev/null +++ b/app/src/st/svc/ST_VOIDRECALC.pgc @@ -0,0 +1,27 @@ +/* + * ST_VOIDRECALC.pgc - st 모듈 서비스 ST_VOIDRECALC (one service = one file). + * void(전체 취소) 재계산: 매입 정산 net 전액을 역분개(음수 조정)하고 해당 + * 가맹점 지급 예정을 취소(CANCELLED) 처리한다. + */ +#include "ST_VOIDRECALC.h" + +/* 100. ST_VOIDRECALC - 전체 취소 역분개 + 지급 취소. */ +void ST_VOIDRECALC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], merch[64]; + long sid = getl(b, T_SETTLE_ID), pid = getl(b, T_PURCHASE_ID); + long net = getl(b, T_NET), adj; + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + adj = stdb_insert_adjust(sid, pid, -net, "VOID", bizdate); + if (adj < 0) FAIL(b); + if (merch[0] != 0) + if (stdb_update_payment_status(merch, "SCHEDULED", "CANCELLED", bizdate) < 0) FAIL(b); + setl(b, T_ADJ, adj); + setl(b, T_DELTA, -net); + Bchg(b, T_STATUS, 0, "V", 0L); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_VOLREBATE.h b/app/src/st/svc/ST_VOLREBATE.h new file mode 100644 index 0000000..662960d --- /dev/null +++ b/app/src/st/svc/ST_VOLREBATE.h @@ -0,0 +1,19 @@ +/* + * ST_VOLREBATE.h - st 서비스 ST_VOLREBATE 카피북 (copybook / record header). + * 거래량 리베이트. + */ +#ifndef ST_VOLREBATE_H +#define ST_VOLREBATE_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_volrebate_rec_t; + +void ST_VOLREBATE(TPSVCINFO *p); + +#endif /* ST_VOLREBATE_H */ diff --git a/app/src/st/svc/ST_VOLREBATE.pgc b/app/src/st/svc/ST_VOLREBATE.pgc new file mode 100644 index 0000000..924388e --- /dev/null +++ b/app/src/st/svc/ST_VOLREBATE.pgc @@ -0,0 +1,42 @@ +/* + * ST_VOLREBATE.pgc - st 모듈 서비스 ST_VOLREBATE (one service = one file). + * 거래량 리베이트: 가맹점 일별 집계의 거래 건수 구간에 따라 리베이트율을 차등 + * 적용해(건수 많을수록 우대) 매출 기준 리베이트를 조정으로 기록한다. + */ +#include "ST_VOLREBATE.h" + +/* 105. ST_VOLREBATE - 거래건수 구간별 리베이트. */ +void ST_VOLREBATE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + long bps, rebate, adj; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_gross; + 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(txn_count,0), coalesce(gross_amount,0) + INTO :h_cnt, :h_gross FROM st_merch_settle + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } + if (h_cnt >= 1000) bps = 100; + else if (h_cnt >= 500) bps = 50; + else if (h_cnt >= 100) bps = 20; + else bps = 0; + if (bps == 0) { setl(b, T_RC, 1); OK(b); } + rebate = acq_fee(h_gross, bps); + adj = stdb_insert_adjust(0, 0, rebate, "VOLREBATE", bizdate); + if (adj < 0) FAIL(b); + setl(b, T_ADJ, adj); + setl(b, T_COUNT, h_cnt); + setl(b, T_DELTA, rebate); + setl(b, T_RC, bps); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_WHTDETAIL.h b/app/src/st/svc/ST_WHTDETAIL.h new file mode 100644 index 0000000..695fe05 --- /dev/null +++ b/app/src/st/svc/ST_WHTDETAIL.h @@ -0,0 +1,19 @@ +/* + * ST_WHTDETAIL.h - st 서비스 ST_WHTDETAIL 카피북 (copybook / record header). + * 원천세 명세. + */ +#ifndef ST_WHTDETAIL_H +#define ST_WHTDETAIL_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_whtdetail_rec_t; + +void ST_WHTDETAIL(TPSVCINFO *p); + +#endif /* ST_WHTDETAIL_H */ diff --git a/app/src/st/svc/ST_WHTDETAIL.pgc b/app/src/st/svc/ST_WHTDETAIL.pgc new file mode 100644 index 0000000..24f0936 --- /dev/null +++ b/app/src/st/svc/ST_WHTDETAIL.pgc @@ -0,0 +1,33 @@ +/* + * ST_WHTDETAIL.pgc - st 모듈 서비스 ST_WHTDETAIL (one service = one file). + * 원천세 명세: 가맹점 당일 수수료 합계에 원천징수율(T_ARG1 bps)을 적용해 + * 원천세(공통 acq_wht_amt)를 산출한 원천세 명세를 반환한다 (조회 전용). + */ +#include "ST_WHTDETAIL.h" + +/* 116. ST_WHTDETAIL - 수수료 합계 기준 원천세 명세. */ +void ST_WHTDETAIL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + long bps = getl(b, T_ARG1), wht; + EXEC SQL BEGIN DECLARE SECTION; + long h_feesum; + 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; + if (bps <= 0) bps = 330; /* 기본 3.3% 원천 */ + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_feesum FROM st_settle_dtl + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND fee_type = 'PCT'; + if (sqlca.sqlcode < 0) FAIL(b); + wht = acq_wht_amt(h_feesum, bps); + setl(b, T_GROSS, h_feesum); + setl(b, T_TAX, wht); + setl(b, T_RC, bps); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/st/svc/ST_WORKFLOW.h b/app/src/st/svc/ST_WORKFLOW.h new file mode 100644 index 0000000..ac90f96 --- /dev/null +++ b/app/src/st/svc/ST_WORKFLOW.h @@ -0,0 +1,19 @@ +/* + * ST_WORKFLOW.h - st 서비스 ST_WORKFLOW 카피북 (copybook / record header). + * 정산 워크플로 상태 전이. + */ +#ifndef ST_WORKFLOW_H +#define ST_WORKFLOW_H + +#include "st_svc.h" + +typedef struct { + long in_amount; + long calc_fee; + long result_code; + long row_count; +} st_st_workflow_rec_t; + +void ST_WORKFLOW(TPSVCINFO *p); + +#endif /* ST_WORKFLOW_H */ diff --git a/app/src/st/svc/ST_WORKFLOW.pgc b/app/src/st/svc/ST_WORKFLOW.pgc new file mode 100644 index 0000000..8012d79 --- /dev/null +++ b/app/src/st/svc/ST_WORKFLOW.pgc @@ -0,0 +1,36 @@ +/* + * ST_WORKFLOW.pgc - st 모듈 서비스 ST_WORKFLOW (one service = one file). + * 정산 워크플로 상태 전이: 요청 전이(from=T_STR1 -> to=T_STR2)가 허용 전이표 + * (SETTLED->LOCKED->PAID)에 부합하는지 검증한 뒤에만 정산 상태를 갱신한다. + */ +#include "ST_WORKFLOW.h" + +/* 124. ST_WORKFLOW - 허용 전이 검증 후 정산 상태 갱신. */ +void ST_WORKFLOW(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char from[16], to[16], bizdate[16]; + int valid = 0; + long moved; + EXEC SQL BEGIN DECLARE SECTION; + char h_from[16], h_to[16], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_STR1, from, sizeof(from)); + gets_(b, T_STR2, to, sizeof(to)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (strcmp(from, "SETTLED") == 0 && strcmp(to, "LOCKED") == 0) valid = 1; + else if (strcmp(from, "LOCKED") == 0 && strcmp(to, "PAID") == 0) valid = 1; + if (!valid) { setl(b, T_RC, -1); OK(b); } + strncpy(h_from, from, sizeof(h_from)-1); h_from[sizeof(h_from)-1] = 0; + strncpy(h_to, to, sizeof(h_to)-1); h_to[sizeof(h_to)-1] = 0; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL UPDATE settlement SET status = :h_to + WHERE biz_date = :h_bizdate AND status = :h_from; + if (sqlca.sqlcode < 0) FAIL(b); + moved = sqlca.sqlerrd[2]; + setl(b, T_COUNT, moved); + setl(b, T_RC, 0); + OK(b); +} + +/* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/vl/batch/vl_axis_recon_batch.pgc b/app/src/vl/batch/vl_axis_recon_batch.pgc new file mode 100644 index 0000000..e26b1bc --- /dev/null +++ b/app/src/vl/batch/vl_axis_recon_batch.pgc @@ -0,0 +1,49 @@ +/* + * vl_axis_recon_batch.pgc - 정합성 축 매입-정산 대사 배치 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; the 검증 SELECT/집계 runs on that branch, then tpcommit drives XA 2PC. + * Usage: vl_axis_recon_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "vl_dbio.h" +#include "vl_axis_recon_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : VL_AXIS_DEF_DATE; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pnet, h_snet; + EXEC SQL END DECLARE SECTION; + vl_axis_rec_t rec; + + 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(net),0) INTO :h_pnet FROM purchase + WHERE biz_date = :h_bizdate AND status = 'S'; + if (sqlca.sqlcode < 0) { fprintf(stderr, "SEL1 FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_snet FROM settlement + WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "vl_axis_recon_batch FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + rec.pnet = h_pnet; rec.snet = h_snet; + if (h_pnet != h_snet) { + if (vldb_insert_suspect(0, VL_AXIS_RULE, "ERROR", "매입-정산 축 총액 불일치", h_bizdate) < 0) { + fprintf(stderr, "insert_suspect FAIL\n"); tpabort(0); return 1; + } + } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> vl_axis_recon_batch COMMIT: bizdate=%s 매입순액=%ld 정산순액=%ld delta=%ld\n", + bizdate, rec.pnet, rec.snet, rec.pnet - rec.snet); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/vl/batch/vl_cancelrate_batch.pgc b/app/src/vl/batch/vl_cancelrate_batch.pgc new file mode 100644 index 0000000..17c8255 --- /dev/null +++ b/app/src/vl/batch/vl_cancelrate_batch.pgc @@ -0,0 +1,48 @@ +/* + * vl_cancelrate_batch.pgc - 취소율 이상 스캔 배치 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; the 검증 SELECT/집계 runs on that branch, then tpcommit drives XA 2PC. + * Usage: vl_cancelrate_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "vl_dbio.h" +#include "vl_cancelrate_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pcnt, h_ccnt; + EXEC SQL END DECLARE SECTION; + vl_cancelrate_rec_t rec; + + 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_pcnt FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "SEL1 FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + EXEC SQL SELECT count(*) INTO :h_ccnt FROM ac_cancel WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "vl_cancelrate_batch FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + rec.pcnt = h_pcnt; rec.ccnt = h_ccnt; + rec.pct = (h_pcnt > 0) ? (h_ccnt * 100 / h_pcnt) : 0; + if (rec.pct >= VL_CXLRATE_THR) { + if (vldb_insert_suspect(0, VL_CXLRATE_RULE, "WARN", "취소율 과다", h_bizdate) < 0) { + tpabort(0); return 1; + } + } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> vl_cancelrate_batch COMMIT: bizdate=%s 매입=%ld 취소=%ld 취소율=%ld%%\n", + bizdate, rec.pcnt, rec.ccnt, rec.pct); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/vl/batch/vl_datefut_batch.pgc b/app/src/vl/batch/vl_datefut_batch.pgc new file mode 100644 index 0000000..5bd1f41 --- /dev/null +++ b/app/src/vl/batch/vl_datefut_batch.pgc @@ -0,0 +1,45 @@ +/* + * vl_datefut_batch.pgc - 미래일자 매입 스캔 배치 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; the 검증 SELECT/집계 runs on that branch, then tpcommit drives XA 2PC. + * Usage: vl_datefut_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "vl_dbio.h" +#include "vl_datefut_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_first; + EXEC SQL END DECLARE SECTION; + vl_datefut_rec_t rec; + + 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(*), coalesce(min(purchase_id),0) INTO :h_cnt, :h_first + FROM purchase WHERE biz_date > current_date; + if (sqlca.sqlcode < 0) { fprintf(stderr, "vl_datefut_batch FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + rec.cnt = h_cnt; rec.first_id = h_first; + if (h_cnt > 0) { + if (vldb_insert_suspect(h_first, VL_DATEFUT_RULE, "ERROR", "미래일자 매입", h_bizdate) < 0) { + tpabort(0); return 1; + } + } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> vl_datefut_batch COMMIT: bizdate=%s 미래일자매입=%ld\n", bizdate, rec.cnt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/vl/batch/vl_dupappr_batch.pgc b/app/src/vl/batch/vl_dupappr_batch.pgc new file mode 100644 index 0000000..e343a74 --- /dev/null +++ b/app/src/vl/batch/vl_dupappr_batch.pgc @@ -0,0 +1,52 @@ +/* + * vl_dupappr_batch.pgc - 중복 승인 스캔 배치 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; the 검증 SELECT/집계 runs on that branch, then tpcommit drives XA 2PC. + * Usage: vl_dupappr_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "vl_dbio.h" +#include "vl_dupappr_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pid, h_cnt; + EXEC SQL END DECLARE SECTION; + vl_dupappr_rec_t rec; rec.groups = 0; + + 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 dac CURSOR FOR + SELECT purchase_id, count(*) FROM approval + WHERE approved = true GROUP BY purchase_id HAVING count(*) > 1; + EXEC SQL OPEN dac; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH dac INTO :h_pid, :h_cnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE dac; tpabort(0); return 1; } + if (vldb_insert_suspect(h_pid, VL_DUPAPPR_RULE, "WARN", "중복 승인 의심", h_bizdate) < 0) { + EXEC SQL CLOSE dac; tpabort(0); return 1; + } + rec.groups++; + } + EXEC SQL CLOSE dac; + if (sqlca.sqlcode < 0) { fprintf(stderr, "vl_dupappr_batch 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(">>> vl_dupappr_batch COMMIT: bizdate=%s 중복승인그룹=%ld\n", bizdate, rec.groups); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/vl/batch/vl_negfee_batch.pgc b/app/src/vl/batch/vl_negfee_batch.pgc new file mode 100644 index 0000000..42ba111 --- /dev/null +++ b/app/src/vl/batch/vl_negfee_batch.pgc @@ -0,0 +1,45 @@ +/* + * vl_negfee_batch.pgc - 음수 수수료 매입 스캔 배치 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; the 검증 SELECT/집계 runs on that branch, then tpcommit drives XA 2PC. + * Usage: vl_negfee_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "vl_dbio.h" +#include "vl_negfee_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_first; + EXEC SQL END DECLARE SECTION; + vl_negfee_rec_t rec; + + 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(*), coalesce(min(purchase_id),0) INTO :h_cnt, :h_first + FROM purchase WHERE biz_date = :h_bizdate AND fee < 0; + if (sqlca.sqlcode < 0) { fprintf(stderr, "vl_negfee_batch FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + rec.cnt = h_cnt; rec.first_id = h_first; + if (h_cnt > 0) { + if (vldb_insert_suspect(h_first, VL_NEGFEE_RULE, "ERROR", "음수 수수료 매입", h_bizdate) < 0) { + tpabort(0); return 1; + } + } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> vl_negfee_batch COMMIT: bizdate=%s 음수수수료=%ld 최소매입ID=%ld\n", bizdate, rec.cnt, rec.first_id); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/vl/batch/vl_outlier_batch.pgc b/app/src/vl/batch/vl_outlier_batch.pgc new file mode 100644 index 0000000..e504613 --- /dev/null +++ b/app/src/vl/batch/vl_outlier_batch.pgc @@ -0,0 +1,54 @@ +/* + * vl_outlier_batch.pgc - 금액 이상치 매입 스캔 배치 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; the 검증 SELECT/집계 runs on that branch, then tpcommit drives XA 2PC. + * Usage: vl_outlier_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "vl_dbio.h" +#include "vl_outlier_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pid, h_amt; + EXEC SQL END DECLARE SECTION; + vl_outlier_rec_t rec; rec.outliers = 0; + + 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 dou CURSOR FOR + SELECT purchase_id, amount FROM purchase + WHERE biz_date = :h_bizdate + AND amount > 4 * (SELECT coalesce(avg(amount),0) FROM purchase + WHERE biz_date = :h_bizdate); + EXEC SQL OPEN dou; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH dou INTO :h_pid, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE dou; tpabort(0); return 1; } + if (vldb_insert_suspect(h_pid, VL_OUTLIER_RULE, "WARN", "금액 이상치 매입", h_bizdate) < 0) { + EXEC SQL CLOSE dou; tpabort(0); return 1; + } + rec.outliers++; + } + EXEC SQL CLOSE dou; + if (sqlca.sqlcode < 0) { fprintf(stderr, "vl_outlier_batch 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(">>> vl_outlier_batch COMMIT: bizdate=%s 이상치=%ld\n", bizdate, rec.outliers); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/vl/batch/vl_overlimit_batch.pgc b/app/src/vl/batch/vl_overlimit_batch.pgc new file mode 100644 index 0000000..18a322b --- /dev/null +++ b/app/src/vl/batch/vl_overlimit_batch.pgc @@ -0,0 +1,54 @@ +/* + * vl_overlimit_batch.pgc - 가맹점 한도위반 스캔 배치 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; the 검증 SELECT/집계 runs on that branch, then tpcommit drives XA 2PC. + * Usage: vl_overlimit_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "vl_dbio.h" +#include "vl_overlimit_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_merch[64]; + long h_sum; + EXEC SQL END DECLARE SECTION; + vl_overlimit_rec_t rec; rec.merchants = 0; + + 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 dol CURSOR FOR + SELECT p.merchant_id, sum(p.amount) FROM purchase p + JOIN merchant m ON m.merchant_id = p.merchant_id + WHERE p.biz_date = :h_bizdate + GROUP BY p.merchant_id HAVING sum(p.amount) > max(m.daily_limit); + EXEC SQL OPEN dol; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH dol INTO :h_merch, :h_sum; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE dol; tpabort(0); return 1; } + if (vldb_insert_suspect(0, VL_OVRLIM_RULE, "WARN", "가맹점 일한도 초과", h_bizdate) < 0) { + EXEC SQL CLOSE dol; tpabort(0); return 1; + } + rec.merchants++; + } + EXEC SQL CLOSE dol; + if (sqlca.sqlcode < 0) { fprintf(stderr, "vl_overlimit_batch 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(">>> vl_overlimit_batch COMMIT: bizdate=%s 한도초과가맹점=%ld\n", bizdate, rec.merchants); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/vl/batch/vl_purnoappr_batch.pgc b/app/src/vl/batch/vl_purnoappr_batch.pgc new file mode 100644 index 0000000..f03d49d --- /dev/null +++ b/app/src/vl/batch/vl_purnoappr_batch.pgc @@ -0,0 +1,53 @@ +/* + * vl_purnoappr_batch.pgc - 무승인 매입 스캔 배치 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; the 검증 SELECT/집계 runs on that branch, then tpcommit drives XA 2PC. + * Usage: vl_purnoappr_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "vl_dbio.h" +#include "vl_purnoappr_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pid; + EXEC SQL END DECLARE SECTION; + vl_purnoappr_rec_t rec; rec.unapproved = 0; + + 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 dna CURSOR FOR + SELECT p.purchase_id FROM purchase p + WHERE p.biz_date = :h_bizdate AND p.status <> 'C' + AND NOT EXISTS (SELECT 1 FROM approval a WHERE a.purchase_id = p.purchase_id); + EXEC SQL OPEN dna; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH dna INTO :h_pid; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE dna; tpabort(0); return 1; } + if (vldb_insert_suspect(h_pid, VL_NOAPPR_RULE, "WARN", "무승인 매입", h_bizdate) < 0) { + EXEC SQL CLOSE dna; tpabort(0); return 1; + } + rec.unapproved++; + } + EXEC SQL CLOSE dna; + if (sqlca.sqlcode < 0) { fprintf(stderr, "vl_purnoappr_batch 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(">>> vl_purnoappr_batch COMMIT: bizdate=%s 무승인매입=%ld\n", bizdate, rec.unapproved); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/vl/batch/vl_riappr_batch.pgc b/app/src/vl/batch/vl_riappr_batch.pgc new file mode 100644 index 0000000..b1122c8 --- /dev/null +++ b/app/src/vl/batch/vl_riappr_batch.pgc @@ -0,0 +1,45 @@ +/* + * vl_riappr_batch.pgc - 승인 참조무결성 스캔 배치 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; the 검증 SELECT/집계 runs on that branch, then tpcommit drives XA 2PC. + * Usage: vl_riappr_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "vl_dbio.h" +#include "vl_riappr_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_orphan; + EXEC SQL END DECLARE SECTION; + vl_riappr_rec_t rec; + + 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_orphan FROM approval a + WHERE NOT EXISTS (SELECT 1 FROM purchase p WHERE p.purchase_id = a.purchase_id); + if (sqlca.sqlcode < 0) { fprintf(stderr, "vl_riappr_batch FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + rec.orphans = h_orphan; + if (h_orphan > 0) { + if (vldb_insert_suspect(0, VL_RIAPPR_RULE, "CRIT", "승인 참조무결성 위반", h_bizdate) < 0) { + tpabort(0); return 1; + } + } + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> vl_riappr_batch COMMIT: bizdate=%s 고아승인=%ld\n", bizdate, rec.orphans); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/vl/batch/vl_sumtxn_batch.pgc b/app/src/vl/batch/vl_sumtxn_batch.pgc new file mode 100644 index 0000000..6a7e651 --- /dev/null +++ b/app/src/vl/batch/vl_sumtxn_batch.pgc @@ -0,0 +1,57 @@ +/* + * vl_sumtxn_batch.pgc - 집계-원천 건수 대사 배치 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; the 검증 SELECT/집계 runs on that branch, then tpcommit drives XA 2PC. + * Usage: vl_sumtxn_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "vl_dbio.h" +#include "vl_sumtxn_batch.h" + +int main(int argc, char **argv) +{ + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_merch[64]; + long h_scnt, h_pcnt; + EXEC SQL END DECLARE SECTION; + vl_sumtxn_rec_t rec; rec.mismatches = 0; + + 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 dsm CURSOR FOR + SELECT ms.merchant_id, ms.txn_count, + (SELECT count(*) FROM purchase p + WHERE p.merchant_id = ms.merchant_id AND p.biz_date = ms.biz_date + AND p.status <> 'C') + FROM merchant_summary ms WHERE ms.biz_date = :h_bizdate; + EXEC SQL OPEN dsm; + if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; } + for (;;) { + EXEC SQL FETCH dsm INTO :h_merch, :h_scnt, :h_pcnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE dsm; tpabort(0); return 1; } + if (h_scnt != h_pcnt) { + if (vldb_insert_suspect(0, VL_SUMCNT_RULE, "WARN", "집계-원천 건수 불일치", h_bizdate) < 0) { + EXEC SQL CLOSE dsm; tpabort(0); return 1; + } + rec.mismatches++; + } + } + EXEC SQL CLOSE dsm; + if (sqlca.sqlcode < 0) { fprintf(stderr, "vl_sumtxn_batch 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(">>> vl_sumtxn_batch COMMIT: bizdate=%s 건수불일치=%ld\n", bizdate, rec.mismatches); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/vl/batch/vl_suspesc_batch.pgc b/app/src/vl/batch/vl_suspesc_batch.pgc new file mode 100644 index 0000000..f7c5182 --- /dev/null +++ b/app/src/vl/batch/vl_suspesc_batch.pgc @@ -0,0 +1,41 @@ +/* + * vl_suspesc_batch.pgc - 장기 미결 의심거래 자동 상향 배치 (XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction; the 검증 SELECT/집계 runs on that branch, then tpcommit drives XA 2PC. + * Usage: vl_suspesc_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "vl_dbio.h" +#include "vl_suspesc_batch.h" + +int main(int argc, char **argv) +{ + long age = (argc > 2) ? atol(argv[2]) : VL_SUSPESC_AGE; + const char *bizdate = (argc > 1) ? argv[1] : "2026-07-19"; (void)bizdate; + EXEC SQL BEGIN DECLARE SECTION; + long h_age, h_rows; + EXEC SQL END DECLARE SECTION; + vl_suspesc_rec_t rec; + h_age = (age > 0) ? age : VL_SUSPESC_AGE; + + 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 vl_suspect SET severity = 'ERROR' + WHERE status = 'OPEN' AND severity = 'WARN' + AND biz_date < current_date - CAST(:h_age AS int); + if (sqlca.sqlcode < 0) { fprintf(stderr, "vl_suspesc_batch FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; } + h_rows = (long)sqlca.sqlerrd[2]; + rec.escalated = h_rows; + + if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; } + printf(">>> vl_suspesc_batch COMMIT: 경과일>%ld 자동상향=%ld건\n", h_age, rec.escalated); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/vl/dbio/vl_axis_recon_batch.h b/app/src/vl/dbio/vl_axis_recon_batch.h new file mode 100644 index 0000000..de03b9d --- /dev/null +++ b/app/src/vl/dbio/vl_axis_recon_batch.h @@ -0,0 +1,12 @@ +/* + * vl_axis_recon_batch.h - vl vl_axis_recon_batch 배치 전용 카피북 (copybook). 정합성 축 매입-정산 대사 배치 + * 축 대사 결과 레코드 + 적재 규칙 코드 상수. Included by vl_axis_recon_batch.pgc. + */ +#ifndef VL_AXIS_RECON_BATCH_H +#define VL_AXIS_RECON_BATCH_H +#define VL_AXIS_RULE "AXIS_MIS" +#define VL_AXIS_DEF_DATE "2026-07-19" + +typedef struct { long pnet; long snet; } vl_axis_rec_t; + +#endif /* VL_AXIS_RECON_BATCH_H */ diff --git a/app/src/vl/dbio/vl_cancelrate_batch.h b/app/src/vl/dbio/vl_cancelrate_batch.h new file mode 100644 index 0000000..b9e60db --- /dev/null +++ b/app/src/vl/dbio/vl_cancelrate_batch.h @@ -0,0 +1,12 @@ +/* + * vl_cancelrate_batch.h - vl vl_cancelrate_batch 배치 전용 카피북 (copybook). 취소율 이상 스캔 배치 + * 취소율 산출 레코드 + 임계율/적재 규칙 코드 상수. Included by vl_cancelrate_batch.pgc. + */ +#ifndef VL_CANCELRATE_BATCH_H +#define VL_CANCELRATE_BATCH_H +#define VL_CXLRATE_RULE "CXLRATE" +#define VL_CXLRATE_THR 10 + +typedef struct { long pcnt; long ccnt; long pct; } vl_cancelrate_rec_t; + +#endif /* VL_CANCELRATE_BATCH_H */ diff --git a/app/src/vl/dbio/vl_datefut_batch.h b/app/src/vl/dbio/vl_datefut_batch.h new file mode 100644 index 0000000..1cbc25e --- /dev/null +++ b/app/src/vl/dbio/vl_datefut_batch.h @@ -0,0 +1,11 @@ +/* + * vl_datefut_batch.h - vl vl_datefut_batch 배치 전용 카피북 (copybook). 미래일자 매입 스캔 배치 + * 미래일자 매입 검출 레코드 + 적재 규칙 코드 상수. Included by vl_datefut_batch.pgc. + */ +#ifndef VL_DATEFUT_BATCH_H +#define VL_DATEFUT_BATCH_H +#define VL_DATEFUT_RULE "DATEFUT" + +typedef struct { long cnt; long first_id; } vl_datefut_rec_t; + +#endif /* VL_DATEFUT_BATCH_H */ diff --git a/app/src/vl/dbio/vl_dupappr_batch.h b/app/src/vl/dbio/vl_dupappr_batch.h new file mode 100644 index 0000000..aa07b09 --- /dev/null +++ b/app/src/vl/dbio/vl_dupappr_batch.h @@ -0,0 +1,11 @@ +/* + * vl_dupappr_batch.h - vl vl_dupappr_batch 배치 전용 카피북 (copybook). 중복 승인 스캔 배치 + * 중복 승인 그룹 카운트 레코드 + 적재 규칙 코드 상수. Included by vl_dupappr_batch.pgc. + */ +#ifndef VL_DUPAPPR_BATCH_H +#define VL_DUPAPPR_BATCH_H +#define VL_DUPAPPR_RULE "DUPAPPR" + +typedef struct { long groups; } vl_dupappr_rec_t; + +#endif /* VL_DUPAPPR_BATCH_H */ diff --git a/app/src/vl/dbio/vl_negfee_batch.h b/app/src/vl/dbio/vl_negfee_batch.h new file mode 100644 index 0000000..b4f5bf6 --- /dev/null +++ b/app/src/vl/dbio/vl_negfee_batch.h @@ -0,0 +1,11 @@ +/* + * vl_negfee_batch.h - vl vl_negfee_batch 배치 전용 카피북 (copybook). 음수 수수료 매입 스캔 배치 + * 음수 수수료 검출 레코드 + 적재 규칙 코드 상수. Included by vl_negfee_batch.pgc. + */ +#ifndef VL_NEGFEE_BATCH_H +#define VL_NEGFEE_BATCH_H +#define VL_NEGFEE_RULE "NEGFEE" + +typedef struct { long cnt; long first_id; } vl_negfee_rec_t; + +#endif /* VL_NEGFEE_BATCH_H */ diff --git a/app/src/vl/dbio/vl_outlier_batch.h b/app/src/vl/dbio/vl_outlier_batch.h new file mode 100644 index 0000000..161b140 --- /dev/null +++ b/app/src/vl/dbio/vl_outlier_batch.h @@ -0,0 +1,11 @@ +/* + * vl_outlier_batch.h - vl vl_outlier_batch 배치 전용 카피북 (copybook). 금액 이상치 매입 스캔 배치 + * 이상치 매입 카운트 레코드 + 적재 규칙 코드 상수. Included by vl_outlier_batch.pgc. + */ +#ifndef VL_OUTLIER_BATCH_H +#define VL_OUTLIER_BATCH_H +#define VL_OUTLIER_RULE "OUTLIER" + +typedef struct { long outliers; } vl_outlier_rec_t; + +#endif /* VL_OUTLIER_BATCH_H */ diff --git a/app/src/vl/dbio/vl_overlimit_batch.h b/app/src/vl/dbio/vl_overlimit_batch.h new file mode 100644 index 0000000..56dead6 --- /dev/null +++ b/app/src/vl/dbio/vl_overlimit_batch.h @@ -0,0 +1,11 @@ +/* + * vl_overlimit_batch.h - vl vl_overlimit_batch 배치 전용 카피북 (copybook). 가맹점 한도위반 스캔 배치 + * 한도 초과 가맹점 카운트 레코드 + 적재 규칙 코드 상수. Included by vl_overlimit_batch.pgc. + */ +#ifndef VL_OVERLIMIT_BATCH_H +#define VL_OVERLIMIT_BATCH_H +#define VL_OVRLIM_RULE "OVRLIM" + +typedef struct { long merchants; } vl_overlimit_rec_t; + +#endif /* VL_OVERLIMIT_BATCH_H */ diff --git a/app/src/vl/dbio/vl_purnoappr_batch.h b/app/src/vl/dbio/vl_purnoappr_batch.h new file mode 100644 index 0000000..bbc0082 --- /dev/null +++ b/app/src/vl/dbio/vl_purnoappr_batch.h @@ -0,0 +1,11 @@ +/* + * vl_purnoappr_batch.h - vl vl_purnoappr_batch 배치 전용 카피북 (copybook). 무승인 매입 스캔 배치 + * 무승인 매입 카운트 레코드 + 적재 규칙 코드 상수. Included by vl_purnoappr_batch.pgc. + */ +#ifndef VL_PURNOAPPR_BATCH_H +#define VL_PURNOAPPR_BATCH_H +#define VL_NOAPPR_RULE "NOAPPR" + +typedef struct { long unapproved; } vl_purnoappr_rec_t; + +#endif /* VL_PURNOAPPR_BATCH_H */ diff --git a/app/src/vl/dbio/vl_riappr_batch.h b/app/src/vl/dbio/vl_riappr_batch.h new file mode 100644 index 0000000..805b36e --- /dev/null +++ b/app/src/vl/dbio/vl_riappr_batch.h @@ -0,0 +1,11 @@ +/* + * vl_riappr_batch.h - vl vl_riappr_batch 배치 전용 카피북 (copybook). 승인 참조무결성 스캔 배치 + * 고아 승인 카운트 레코드 + 적재 규칙 코드 상수. Included by vl_riappr_batch.pgc. + */ +#ifndef VL_RIAPPR_BATCH_H +#define VL_RIAPPR_BATCH_H +#define VL_RIAPPR_RULE "RIAPPR" + +typedef struct { long orphans; } vl_riappr_rec_t; + +#endif /* VL_RIAPPR_BATCH_H */ diff --git a/app/src/vl/dbio/vl_sumtxn_batch.h b/app/src/vl/dbio/vl_sumtxn_batch.h new file mode 100644 index 0000000..455ea60 --- /dev/null +++ b/app/src/vl/dbio/vl_sumtxn_batch.h @@ -0,0 +1,11 @@ +/* + * vl_sumtxn_batch.h - vl vl_sumtxn_batch 배치 전용 카피북 (copybook). 집계-원천 건수 대사 배치 + * 건수 대사 불일치 카운트 레코드 + 적재 규칙 코드 상수. Included by vl_sumtxn_batch.pgc. + */ +#ifndef VL_SUMTXN_BATCH_H +#define VL_SUMTXN_BATCH_H +#define VL_SUMCNT_RULE "SUMCNT" + +typedef struct { long mismatches; } vl_sumtxn_rec_t; + +#endif /* VL_SUMTXN_BATCH_H */ diff --git a/app/src/vl/dbio/vl_suspesc_batch.h b/app/src/vl/dbio/vl_suspesc_batch.h new file mode 100644 index 0000000..7c17adc --- /dev/null +++ b/app/src/vl/dbio/vl_suspesc_batch.h @@ -0,0 +1,11 @@ +/* + * vl_suspesc_batch.h - vl vl_suspesc_batch 배치 전용 카피북 (copybook). 장기 미결 의심거래 자동 상향 배치 + * 자동 상향 건수 레코드 + 경과일 임계 상수. Included by vl_suspesc_batch.pgc. + */ +#ifndef VL_SUSPESC_BATCH_H +#define VL_SUSPESC_BATCH_H +#define VL_SUSPESC_AGE 3 + +typedef struct { long escalated; } vl_suspesc_rec_t; + +#endif /* VL_SUSPESC_BATCH_H */ diff --git a/app/src/vl/svc/VL_APPRLATE.h b/app/src/vl/svc/VL_APPRLATE.h new file mode 100644 index 0000000..182d42c --- /dev/null +++ b/app/src/vl/svc/VL_APPRLATE.h @@ -0,0 +1,12 @@ +/* + * VL_APPRLATE.h - vl 서비스 VL_APPRLATE 카피북 (copybook / record header). + * 사용 UBF 필드: T_PURCHASE_ID,T_ARG1,T_ARG2,T_RC + */ +#ifndef VL_APPRLATE_H +#define VL_APPRLATE_H + +#include "vl_svc.h" + +void VL_APPRLATE(TPSVCINFO *p); + +#endif /* VL_APPRLATE_H */ diff --git a/app/src/vl/svc/VL_APPRLATE.pgc b/app/src/vl/svc/VL_APPRLATE.pgc new file mode 100644 index 0000000..fed2ffb --- /dev/null +++ b/app/src/vl/svc/VL_APPRLATE.pgc @@ -0,0 +1,27 @@ +/* + * VL_APPRLATE.pgc - vl 모듈 서비스 VL_APPRLATE (one service = one file). + * 기능: 지연 승인 검출 건별 (승인시각-매입생성 경과초>T_ARG1 기본86400, RC 1=지연). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_APPRLATE.h" + +void VL_APPRLATE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, limit; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_secs; + EXEC SQL END DECLARE SECTION; + h_pid = getl(b, T_PURCHASE_ID); + limit = getl(b, T_ARG1); + if (limit <= 0) limit = 86400; + EXEC SQL SELECT coalesce(max(CAST(EXTRACT(EPOCH FROM (a.approved_at - p.created_at)) AS bigint)),0) + INTO :h_secs + FROM approval a JOIN purchase p ON p.purchase_id = a.purchase_id + WHERE a.purchase_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_secs > limit) rc = 1; /* 승인 지연 초과 */ + setl(b, T_ARG2, h_secs); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_APPRNOPUR.h b/app/src/vl/svc/VL_APPRNOPUR.h new file mode 100644 index 0000000..35cfd2d --- /dev/null +++ b/app/src/vl/svc/VL_APPRNOPUR.h @@ -0,0 +1,12 @@ +/* + * VL_APPRNOPUR.h - vl 서비스 VL_APPRNOPUR 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT,T_ID1,T_RC + */ +#ifndef VL_APPRNOPUR_H +#define VL_APPRNOPUR_H + +#include "vl_svc.h" + +void VL_APPRNOPUR(TPSVCINFO *p); + +#endif /* VL_APPRNOPUR_H */ diff --git a/app/src/vl/svc/VL_APPRNOPUR.pgc b/app/src/vl/svc/VL_APPRNOPUR.pgc new file mode 100644 index 0000000..2fc7dda --- /dev/null +++ b/app/src/vl/svc/VL_APPRNOPUR.pgc @@ -0,0 +1,25 @@ +/* + * VL_APPRNOPUR.pgc - vl 모듈 서비스 VL_APPRNOPUR (one service = one file). + * 기능: 미매입 승인 검출 (매입 원거래 없는 고아 승인 전수, RC 1=존재; 최소 승인ID 반환). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_APPRNOPUR.h" + +void VL_APPRNOPUR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_first; + EXEC SQL END DECLARE SECTION; + EXEC SQL SELECT count(*), coalesce(min(a.approval_id),0) INTO :h_cnt, :h_first + FROM approval a + WHERE NOT EXISTS (SELECT 1 FROM purchase p + WHERE p.purchase_id = a.purchase_id); + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 매입 없는 승인 존재 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_ID1, h_first); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_AXLEDGDC.h b/app/src/vl/svc/VL_AXLEDGDC.h new file mode 100644 index 0000000..2ecce99 --- /dev/null +++ b/app/src/vl/svc/VL_AXLEDGDC.h @@ -0,0 +1,12 @@ +/* + * VL_AXLEDGDC.h - vl 서비스 VL_AXLEDGDC 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_AMT1,T_AMT2,T_AMT3,T_RC + */ +#ifndef VL_AXLEDGDC_H +#define VL_AXLEDGDC_H + +#include "vl_svc.h" + +void VL_AXLEDGDC(TPSVCINFO *p); + +#endif /* VL_AXLEDGDC_H */ diff --git a/app/src/vl/svc/VL_AXLEDGDC.pgc b/app/src/vl/svc/VL_AXLEDGDC.pgc new file mode 100644 index 0000000..673f3ee --- /dev/null +++ b/app/src/vl/svc/VL_AXLEDGDC.pgc @@ -0,0 +1,32 @@ +/* + * VL_AXLEDGDC.pgc - vl 모듈 서비스 VL_AXLEDGDC (one service = one file). + * 기능: 정합성 축 검증 원장 대차 (RECON 차변 vs SETTLE 대변, RC 비트 1=대차불일치 2=지급대응 없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_AXLEDGDC.h" + +void VL_AXLEDGDC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_recon, h_settle, h_xfer; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT + coalesce(sum(CASE WHEN entry_type = 'RECON' THEN amount ELSE 0 END),0), + coalesce(sum(CASE WHEN entry_type = 'SETTLE' THEN amount ELSE 0 END),0), + coalesce(sum(CASE WHEN entry_type = 'XFER' THEN amount ELSE 0 END),0) + INTO :h_recon, :h_settle, :h_xfer + FROM ledger WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_recon != h_settle) rc |= 1; /* 차변-대변 불일치 */ + if (h_xfer > 0 && h_settle == 0) rc |= 2; /* 지급 있으나 정산분개 없음 */ + setl(b, T_AMT1, h_recon); + setl(b, T_AMT2, h_settle); + setl(b, T_AMT3, h_xfer); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_AXPURAPPR.h b/app/src/vl/svc/VL_AXPURAPPR.h new file mode 100644 index 0000000..f6c1dde --- /dev/null +++ b/app/src/vl/svc/VL_AXPURAPPR.h @@ -0,0 +1,12 @@ +/* + * VL_AXPURAPPR.h - vl 서비스 VL_AXPURAPPR 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_AMT1,T_AMT2,T_DELTA,T_RC + */ +#ifndef VL_AXPURAPPR_H +#define VL_AXPURAPPR_H + +#include "vl_svc.h" + +void VL_AXPURAPPR(TPSVCINFO *p); + +#endif /* VL_AXPURAPPR_H */ diff --git a/app/src/vl/svc/VL_AXPURAPPR.pgc b/app/src/vl/svc/VL_AXPURAPPR.pgc new file mode 100644 index 0000000..c6e730d --- /dev/null +++ b/app/src/vl/svc/VL_AXPURAPPR.pgc @@ -0,0 +1,32 @@ +/* + * VL_AXPURAPPR.pgc - vl 모듈 서비스 VL_AXPURAPPR (one service = one file). + * 기능: 정합성 축 검증 매입-승인 (일자별 총매입금액 vs 승인가결 매입금액 합, RC 1=불일치). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_AXPURAPPR.h" + +void VL_AXPURAPPR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pall, h_papp; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_pall FROM purchase + WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(p.amount),0) INTO :h_papp FROM purchase p + WHERE p.biz_date = :h_bizdate + AND EXISTS (SELECT 1 FROM approval a + WHERE a.purchase_id = p.purchase_id AND a.approved); + if (sqlca.sqlcode < 0) FAIL(b); + if (h_pall != h_papp) rc = 1; /* 매입-승인 축 불일치 */ + setl(b, T_AMT1, h_pall); + setl(b, T_AMT2, h_papp); + setl(b, T_DELTA, h_pall - h_papp); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_AXPURSETT.h b/app/src/vl/svc/VL_AXPURSETT.h new file mode 100644 index 0000000..845ce28 --- /dev/null +++ b/app/src/vl/svc/VL_AXPURSETT.h @@ -0,0 +1,12 @@ +/* + * VL_AXPURSETT.h - vl 서비스 VL_AXPURSETT 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_AMT1,T_AMT2,T_COUNT,T_DELTA,T_RC + */ +#ifndef VL_AXPURSETT_H +#define VL_AXPURSETT_H + +#include "vl_svc.h" + +void VL_AXPURSETT(TPSVCINFO *p); + +#endif /* VL_AXPURSETT_H */ diff --git a/app/src/vl/svc/VL_AXPURSETT.pgc b/app/src/vl/svc/VL_AXPURSETT.pgc new file mode 100644 index 0000000..de35b44 --- /dev/null +++ b/app/src/vl/svc/VL_AXPURSETT.pgc @@ -0,0 +1,31 @@ +/* + * VL_AXPURSETT.pgc - vl 모듈 서비스 VL_AXPURSETT (one service = one file). + * 기능: 정합성 축 검증 매입-정산 (정산대상 매입 순액 vs settlement 순액 합, RC 1=불일치). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_AXPURSETT.h" + +void VL_AXPURSETT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pnet, h_snet, h_scnt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_pnet FROM purchase + WHERE biz_date = :h_bizdate AND status IN ('S','M'); + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*), coalesce(sum(net),0) INTO :h_scnt, :h_snet + FROM settlement WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_pnet != h_snet) rc = 1; /* 매입-정산 축 불일치 */ + setl(b, T_AMT1, h_pnet); + setl(b, T_AMT2, h_snet); + setl(b, T_COUNT, h_scnt); + setl(b, T_DELTA, h_pnet - h_snet); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_AXSETTPAY.h b/app/src/vl/svc/VL_AXSETTPAY.h new file mode 100644 index 0000000..d94133e --- /dev/null +++ b/app/src/vl/svc/VL_AXSETTPAY.h @@ -0,0 +1,12 @@ +/* + * VL_AXSETTPAY.h - vl 서비스 VL_AXSETTPAY 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_AMT1,T_AMT2,T_DELTA,T_RC + */ +#ifndef VL_AXSETTPAY_H +#define VL_AXSETTPAY_H + +#include "vl_svc.h" + +void VL_AXSETTPAY(TPSVCINFO *p); + +#endif /* VL_AXSETTPAY_H */ diff --git a/app/src/vl/svc/VL_AXSETTPAY.pgc b/app/src/vl/svc/VL_AXSETTPAY.pgc new file mode 100644 index 0000000..5d98c19 --- /dev/null +++ b/app/src/vl/svc/VL_AXSETTPAY.pgc @@ -0,0 +1,30 @@ +/* + * VL_AXSETTPAY.pgc - vl 모듈 서비스 VL_AXSETTPAY (one service = one file). + * 기능: 정합성 축 검증 정산-지급 (settlement 순액 합 vs 원장 지급 XFER 합, RC 1=불일치). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_AXSETTPAY.h" + +void VL_AXSETTPAY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_snet, h_xamt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_snet FROM settlement + WHERE biz_date = :h_bizdate AND status = 'SETTLED'; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_xamt FROM ledger + WHERE biz_date = :h_bizdate AND entry_type = 'XFER'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_snet != h_xamt) rc = 1; /* 정산-지급 축 불일치 */ + setl(b, T_AMT1, h_snet); + setl(b, T_AMT2, h_xamt); + setl(b, T_DELTA, h_snet - h_xamt); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_CANCELRATE.h b/app/src/vl/svc/VL_CANCELRATE.h new file mode 100644 index 0000000..008d711 --- /dev/null +++ b/app/src/vl/svc/VL_CANCELRATE.h @@ -0,0 +1,12 @@ +/* + * VL_CANCELRATE.h - vl 서비스 VL_CANCELRATE 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_ARG1,T_ARG2,T_ARG3,T_COUNT,T_RC + */ +#ifndef VL_CANCELRATE_H +#define VL_CANCELRATE_H + +#include "vl_svc.h" + +void VL_CANCELRATE(TPSVCINFO *p); + +#endif /* VL_CANCELRATE_H */ diff --git a/app/src/vl/svc/VL_CANCELRATE.pgc b/app/src/vl/svc/VL_CANCELRATE.pgc new file mode 100644 index 0000000..09e1060 --- /dev/null +++ b/app/src/vl/svc/VL_CANCELRATE.pgc @@ -0,0 +1,31 @@ +/* + * VL_CANCELRATE.pgc - vl 모듈 서비스 VL_CANCELRATE (one service = one file). + * 기능: 취소율 검증 (취소 건수/매입 건수 비율>=T_ARG1% 기본10, RC 1=취소율 과다). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_CANCELRATE.h" + +void VL_CANCELRATE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, pct, thr; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_pcnt, h_ccnt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + thr = getl(b, T_ARG1); + if (thr <= 0) thr = 10; + EXEC SQL SELECT count(*) INTO :h_pcnt FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_ccnt FROM ac_cancel WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + pct = (h_pcnt > 0) ? (h_ccnt * 100 / h_pcnt) : 0; + if (pct >= thr) rc = 1; /* 취소율 과다 */ + setl(b, T_COUNT, h_pcnt); + setl(b, T_ARG2, h_ccnt); + setl(b, T_ARG3, pct); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_CATSKEW.h b/app/src/vl/svc/VL_CATSKEW.h new file mode 100644 index 0000000..a08bd7d --- /dev/null +++ b/app/src/vl/svc/VL_CATSKEW.h @@ -0,0 +1,12 @@ +/* + * VL_CATSKEW.h - vl 서비스 VL_CATSKEW 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_ARG1,T_ARG2,T_ARG3,T_COUNT,T_RC + */ +#ifndef VL_CATSKEW_H +#define VL_CATSKEW_H + +#include "vl_svc.h" + +void VL_CATSKEW(TPSVCINFO *p); + +#endif /* VL_CATSKEW_H */ diff --git a/app/src/vl/svc/VL_CATSKEW.pgc b/app/src/vl/svc/VL_CATSKEW.pgc new file mode 100644 index 0000000..c6a69bb --- /dev/null +++ b/app/src/vl/svc/VL_CATSKEW.pgc @@ -0,0 +1,33 @@ +/* + * VL_CATSKEW.pgc - vl 모듈 서비스 VL_CATSKEW (one service = one file). + * 기능: 업종 편중 탐지 (최다 업종 비중>=T_ARG1% 기본80, RC 1=편중). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_CATSKEW.h" + +void VL_CATSKEW(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, pct, thr; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_tot, h_top; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + thr = getl(b, T_ARG1); + if (thr <= 0) thr = 80; + EXEC SQL SELECT count(*) INTO :h_tot FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(max(c),0) INTO :h_top FROM ( + SELECT count(*) AS c FROM purchase + WHERE biz_date = :h_bizdate GROUP BY category) t; + if (sqlca.sqlcode < 0) FAIL(b); + pct = (h_tot > 0) ? (h_top * 100 / h_tot) : 0; + if (pct >= thr) rc = 1; /* 단일 업종 편중 */ + setl(b, T_COUNT, h_tot); + setl(b, T_ARG2, h_top); + setl(b, T_ARG3, pct); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_CHANSPLIT.h b/app/src/vl/svc/VL_CHANSPLIT.h new file mode 100644 index 0000000..2778d71 --- /dev/null +++ b/app/src/vl/svc/VL_CHANSPLIT.h @@ -0,0 +1,12 @@ +/* + * VL_CHANSPLIT.h - vl 서비스 VL_CHANSPLIT 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_ARG1,T_ARG2,T_ARG3,T_COUNT,T_RC + */ +#ifndef VL_CHANSPLIT_H +#define VL_CHANSPLIT_H + +#include "vl_svc.h" + +void VL_CHANSPLIT(TPSVCINFO *p); + +#endif /* VL_CHANSPLIT_H */ diff --git a/app/src/vl/svc/VL_CHANSPLIT.pgc b/app/src/vl/svc/VL_CHANSPLIT.pgc new file mode 100644 index 0000000..c3b1563 --- /dev/null +++ b/app/src/vl/svc/VL_CHANSPLIT.pgc @@ -0,0 +1,32 @@ +/* + * VL_CHANSPLIT.pgc - vl 모듈 서비스 VL_CHANSPLIT (one service = one file). + * 기능: 채널 분포 검증 (FOREIGN 채널 비중>=T_ARG1% 기본30, RC 1=편중). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_CHANSPLIT.h" + +void VL_CHANSPLIT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, pct, thr; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_tot, h_frn; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + thr = getl(b, T_ARG1); + if (thr <= 0) thr = 30; + EXEC SQL SELECT count(*), + coalesce(sum(CASE WHEN channel = 'FOREIGN' THEN 1 ELSE 0 END),0) + INTO :h_tot, :h_frn + FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + pct = (h_tot > 0) ? (h_frn * 100 / h_tot) : 0; + if (pct >= thr) rc = 1; /* 해외 채널 편중 */ + setl(b, T_COUNT, h_tot); + setl(b, T_ARG2, h_frn); + setl(b, T_ARG3, pct); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_DATEFUT.h b/app/src/vl/svc/VL_DATEFUT.h new file mode 100644 index 0000000..483c616 --- /dev/null +++ b/app/src/vl/svc/VL_DATEFUT.h @@ -0,0 +1,12 @@ +/* + * VL_DATEFUT.h - vl 서비스 VL_DATEFUT 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT,T_ID1,T_RC + */ +#ifndef VL_DATEFUT_H +#define VL_DATEFUT_H + +#include "vl_svc.h" + +void VL_DATEFUT(TPSVCINFO *p); + +#endif /* VL_DATEFUT_H */ diff --git a/app/src/vl/svc/VL_DATEFUT.pgc b/app/src/vl/svc/VL_DATEFUT.pgc new file mode 100644 index 0000000..de4f22a --- /dev/null +++ b/app/src/vl/svc/VL_DATEFUT.pgc @@ -0,0 +1,23 @@ +/* + * VL_DATEFUT.pgc - vl 모듈 서비스 VL_DATEFUT (one service = one file). + * 기능: 미래일자 매입 검출 (영업일이 오늘 이후인 매입 수, RC 1=존재). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_DATEFUT.h" + +void VL_DATEFUT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_first; + EXEC SQL END DECLARE SECTION; + EXEC SQL SELECT count(*), coalesce(min(purchase_id),0) INTO :h_cnt, :h_first + FROM purchase WHERE biz_date > current_date; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 미래일자 매입 존재 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_ID1, h_first); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_DATEGAP.h b/app/src/vl/svc/VL_DATEGAP.h new file mode 100644 index 0000000..01c2693 --- /dev/null +++ b/app/src/vl/svc/VL_DATEGAP.h @@ -0,0 +1,12 @@ +/* + * VL_DATEGAP.h - vl 서비스 VL_DATEGAP 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT,T_SETTLE_ID,T_RC + */ +#ifndef VL_DATEGAP_H +#define VL_DATEGAP_H + +#include "vl_svc.h" + +void VL_DATEGAP(TPSVCINFO *p); + +#endif /* VL_DATEGAP_H */ diff --git a/app/src/vl/svc/VL_DATEGAP.pgc b/app/src/vl/svc/VL_DATEGAP.pgc new file mode 100644 index 0000000..d9ba658 --- /dev/null +++ b/app/src/vl/svc/VL_DATEGAP.pgc @@ -0,0 +1,24 @@ +/* + * VL_DATEGAP.pgc - vl 모듈 서비스 VL_DATEGAP (one service = one file). + * 기능: 매입-정산 일자 역전 검출 (정산일<매입일 건수, RC 1=존재; 최소 정산ID 반환). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_DATEGAP.h" + +void VL_DATEGAP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_first; + EXEC SQL END DECLARE SECTION; + EXEC SQL SELECT count(*), coalesce(min(s.settlement_id),0) INTO :h_cnt, :h_first + FROM settlement s JOIN purchase p ON p.purchase_id = s.purchase_id + WHERE s.biz_date < p.biz_date; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 정산일이 매입일보다 앞섬 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_SETTLE_ID, h_first); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_DUPAPPR.h b/app/src/vl/svc/VL_DUPAPPR.h new file mode 100644 index 0000000..5720bf5 --- /dev/null +++ b/app/src/vl/svc/VL_DUPAPPR.h @@ -0,0 +1,12 @@ +/* + * VL_DUPAPPR.h - vl 서비스 VL_DUPAPPR 카피북 (copybook / record header). + * 사용 UBF 필드: T_PURCHASE_ID,T_BIZDATE,T_COUNT,T_RC + */ +#ifndef VL_DUPAPPR_H +#define VL_DUPAPPR_H + +#include "vl_svc.h" + +void VL_DUPAPPR(TPSVCINFO *p); + +#endif /* VL_DUPAPPR_H */ diff --git a/app/src/vl/svc/VL_DUPAPPR.pgc b/app/src/vl/svc/VL_DUPAPPR.pgc new file mode 100644 index 0000000..f90a1e0 --- /dev/null +++ b/app/src/vl/svc/VL_DUPAPPR.pgc @@ -0,0 +1,30 @@ +/* + * VL_DUPAPPR.pgc - vl 모듈 서비스 VL_DUPAPPR (one service = one file). + * 기능: 중복 승인 검출 건별 (동일 매입 승인가결 2건 이상, RC 1=중복; 검증로그 기록). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_DUPAPPR.h" + +void VL_DUPAPPR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_cnt; + EXEC SQL END DECLARE SECTION; + h_pid = getl(b, T_PURCHASE_ID); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (bizdate[0] == 0) strcpy(bizdate, "2026-07-19"); + EXEC SQL SELECT count(*) INTO :h_cnt FROM approval + WHERE purchase_id = :h_pid AND approved = true; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 1) { + rc = 1; /* 중복 승인 */ + if (vldb_insert_check_log("VL_DUPAPPR", h_pid, rc, "중복 승인 검출", bizdate) < 0) + FAIL(b); + } + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_DUPLEDGR.h b/app/src/vl/svc/VL_DUPLEDGR.h new file mode 100644 index 0000000..6a95a6f --- /dev/null +++ b/app/src/vl/svc/VL_DUPLEDGR.h @@ -0,0 +1,12 @@ +/* + * VL_DUPLEDGR.h - vl 서비스 VL_DUPLEDGR 카피북 (copybook / record header). + * 사용 UBF 필드: T_PURCHASE_ID,T_COUNT,T_RC + */ +#ifndef VL_DUPLEDGR_H +#define VL_DUPLEDGR_H + +#include "vl_svc.h" + +void VL_DUPLEDGR(TPSVCINFO *p); + +#endif /* VL_DUPLEDGR_H */ diff --git a/app/src/vl/svc/VL_DUPLEDGR.pgc b/app/src/vl/svc/VL_DUPLEDGR.pgc new file mode 100644 index 0000000..54f948b --- /dev/null +++ b/app/src/vl/svc/VL_DUPLEDGR.pgc @@ -0,0 +1,24 @@ +/* + * VL_DUPLEDGR.pgc - vl 모듈 서비스 VL_DUPLEDGR (one service = one file). + * 기능: 중복 원장 엔트리 검출 (동일 매입+엔트리유형 최대 그룹 건수>1, RC 1=중복). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_DUPLEDGR.h" + +void VL_DUPLEDGR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_maxdup; + EXEC SQL END DECLARE SECTION; + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL SELECT coalesce(max(c),0) INTO :h_maxdup FROM ( + SELECT count(*) AS c FROM ledger + WHERE purchase_id = :h_pid GROUP BY entry_type) t; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_maxdup > 1) rc = 1; /* 동일 유형 원장 중복 */ + setl(b, T_COUNT, h_maxdup); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_DUPPURCH.h b/app/src/vl/svc/VL_DUPPURCH.h new file mode 100644 index 0000000..6036432 --- /dev/null +++ b/app/src/vl/svc/VL_DUPPURCH.h @@ -0,0 +1,12 @@ +/* + * VL_DUPPURCH.h - vl 서비스 VL_DUPPURCH 카피북 (copybook / record header). + * 사용 UBF 필드: T_PURCHASE_ID,T_COUNT,T_RC + */ +#ifndef VL_DUPPURCH_H +#define VL_DUPPURCH_H + +#include "vl_svc.h" + +void VL_DUPPURCH(TPSVCINFO *p); + +#endif /* VL_DUPPURCH_H */ diff --git a/app/src/vl/svc/VL_DUPPURCH.pgc b/app/src/vl/svc/VL_DUPPURCH.pgc new file mode 100644 index 0000000..98fad9e --- /dev/null +++ b/app/src/vl/svc/VL_DUPPURCH.pgc @@ -0,0 +1,28 @@ +/* + * VL_DUPPURCH.pgc - vl 모듈 서비스 VL_DUPPURCH (one service = one file). + * 기능: 중복 매입 검출 건별 (동일 가맹점+금액+영업일의 타 매입 존재, RC 1=중복). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_DUPPURCH.h" + +void VL_DUPPURCH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_dups; + EXEC SQL END DECLARE SECTION; + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL SELECT count(*) INTO :h_dups FROM purchase p + WHERE p.purchase_id <> :h_pid + AND EXISTS (SELECT 1 FROM purchase q + WHERE q.purchase_id = :h_pid + AND q.merchant_id = p.merchant_id + AND q.amount = p.amount + AND q.biz_date = p.biz_date); + if (sqlca.sqlcode < 0) FAIL(b); + if (h_dups > 0) rc = 1; /* 동일 조건 중복 매입 */ + setl(b, T_COUNT, h_dups); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_DUPSETTMR.h b/app/src/vl/svc/VL_DUPSETTMR.h new file mode 100644 index 0000000..2f75305 --- /dev/null +++ b/app/src/vl/svc/VL_DUPSETTMR.h @@ -0,0 +1,12 @@ +/* + * VL_DUPSETTMR.h - vl 서비스 VL_DUPSETTMR 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT,T_BIZDATE,T_COUNT,T_RC + */ +#ifndef VL_DUPSETTMR_H +#define VL_DUPSETTMR_H + +#include "vl_svc.h" + +void VL_DUPSETTMR(TPSVCINFO *p); + +#endif /* VL_DUPSETTMR_H */ diff --git a/app/src/vl/svc/VL_DUPSETTMR.pgc b/app/src/vl/svc/VL_DUPSETTMR.pgc new file mode 100644 index 0000000..74de9db --- /dev/null +++ b/app/src/vl/svc/VL_DUPSETTMR.pgc @@ -0,0 +1,27 @@ +/* + * VL_DUPSETTMR.pgc - vl 모듈 서비스 VL_DUPSETTMR (one service = one file). + * 기능: 가맹점 중복 정산 검출 (동일 가맹점+영업일 settlement 2건 이상, RC 1=중복). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_DUPSETTMR.h" + +void VL_DUPSETTMR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_bizdate[16]; + long h_cnt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_MERCHANT, h_merch, sizeof(h_merch)); + if (h_merch[0] == 0) { userlog("VL_DUPSETTMR: T_MERCHANT 누락"); FAIL(b); } + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT count(*) INTO :h_cnt FROM settlement + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 1) rc = 1; /* 가맹점 일별 중복 정산 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_FEEHIGH.h b/app/src/vl/svc/VL_FEEHIGH.h new file mode 100644 index 0000000..c8e5f58 --- /dev/null +++ b/app/src/vl/svc/VL_FEEHIGH.h @@ -0,0 +1,12 @@ +/* + * VL_FEEHIGH.h - vl 서비스 VL_FEEHIGH 카피북 (copybook / record header). + * 사용 UBF 필드: T_PURCHASE_ID,T_FEE,T_ARG1,T_ARG2,T_RC + */ +#ifndef VL_FEEHIGH_H +#define VL_FEEHIGH_H + +#include "vl_svc.h" + +void VL_FEEHIGH(TPSVCINFO *p); + +#endif /* VL_FEEHIGH_H */ diff --git a/app/src/vl/svc/VL_FEEHIGH.pgc b/app/src/vl/svc/VL_FEEHIGH.pgc new file mode 100644 index 0000000..22dc5c1 --- /dev/null +++ b/app/src/vl/svc/VL_FEEHIGH.pgc @@ -0,0 +1,27 @@ +/* + * VL_FEEHIGH.pgc - vl 모듈 서비스 VL_FEEHIGH (one service = one file). + * 기능: 수수료 과다 검출 건별 (fee 실효요율 bps>T_ARG1 기본500, RC 1=과다 8=매입없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_FEEHIGH.h" + +void VL_FEEHIGH(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, bps; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_amt, h_fee; + EXEC SQL END DECLARE SECTION; + h_pid = getl(b, T_PURCHASE_ID); + bps = getl(b, T_ARG1); + if (bps <= 0) bps = 500; + EXEC SQL SELECT amount, fee INTO :h_amt, :h_fee FROM purchase + WHERE purchase_id = :h_pid; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 8); OK(b); } /* 8=매입 없음 */ + if (sqlca.sqlcode < 0) FAIL(b); + if (h_amt > 0 && h_fee * 10000 > h_amt * bps) rc = 1; /* 실효요율 초과 */ + setl(b, T_FEE, h_fee); + setl(b, T_ARG2, bps); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_ISSUERMIX.h b/app/src/vl/svc/VL_ISSUERMIX.h new file mode 100644 index 0000000..5115ead --- /dev/null +++ b/app/src/vl/svc/VL_ISSUERMIX.h @@ -0,0 +1,12 @@ +/* + * VL_ISSUERMIX.h - vl 서비스 VL_ISSUERMIX 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_ARG1,T_ARG2,T_ARG3,T_STR1,T_RC + */ +#ifndef VL_ISSUERMIX_H +#define VL_ISSUERMIX_H + +#include "vl_svc.h" + +void VL_ISSUERMIX(TPSVCINFO *p); + +#endif /* VL_ISSUERMIX_H */ diff --git a/app/src/vl/svc/VL_ISSUERMIX.pgc b/app/src/vl/svc/VL_ISSUERMIX.pgc new file mode 100644 index 0000000..d5cbb67 --- /dev/null +++ b/app/src/vl/svc/VL_ISSUERMIX.pgc @@ -0,0 +1,34 @@ +/* + * VL_ISSUERMIX.pgc - vl 모듈 서비스 VL_ISSUERMIX (one service = one file). + * 기능: 발급사 분포 이상 (최다 발급사 비중>=T_ARG1% 기본80, RC 1=편중 8=무거래). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_ISSUERMIX.h" + +void VL_ISSUERMIX(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, pct, thr; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_iss[64]; + long h_tot, h_top; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + thr = getl(b, T_ARG1); + if (thr <= 0) thr = 80; + EXEC SQL SELECT issuer, count(*) INTO :h_iss, :h_top FROM purchase + WHERE biz_date = :h_bizdate + GROUP BY issuer ORDER BY count(*) DESC LIMIT 1; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 8); OK(b); } /* 8=무거래 */ + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_tot FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + pct = (h_tot > 0) ? (h_top * 100 / h_tot) : 0; + if (pct >= thr) rc = 1; /* 단일 발급사 편중 */ + setl(b, T_ARG2, h_top); + setl(b, T_ARG3, pct); + Bchg(b, T_STR1, 0, h_iss, 0L); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_LIMITPCT.h b/app/src/vl/svc/VL_LIMITPCT.h new file mode 100644 index 0000000..04b7fad --- /dev/null +++ b/app/src/vl/svc/VL_LIMITPCT.h @@ -0,0 +1,12 @@ +/* + * VL_LIMITPCT.h - vl 서비스 VL_LIMITPCT 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT,T_BIZDATE,T_ARG1,T_ARG2,T_AMT1,T_RC + */ +#ifndef VL_LIMITPCT_H +#define VL_LIMITPCT_H + +#include "vl_svc.h" + +void VL_LIMITPCT(TPSVCINFO *p); + +#endif /* VL_LIMITPCT_H */ diff --git a/app/src/vl/svc/VL_LIMITPCT.pgc b/app/src/vl/svc/VL_LIMITPCT.pgc new file mode 100644 index 0000000..36807eb --- /dev/null +++ b/app/src/vl/svc/VL_LIMITPCT.pgc @@ -0,0 +1,34 @@ +/* + * VL_LIMITPCT.pgc - vl 모듈 서비스 VL_LIMITPCT (one service = one file). + * 기능: 한도 사용률 검증 (가맹점 일 매입합/한도 %가 T_ARG1 기본90 이상, RC 1=근접 8=가맹점없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_LIMITPCT.h" + +void VL_LIMITPCT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, pct, thr; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_bizdate[16]; + long h_sum, h_lim; + EXEC SQL END DECLARE SECTION; + gets_(b, T_MERCHANT, h_merch, sizeof(h_merch)); + if (h_merch[0] == 0) { userlog("VL_LIMITPCT: T_MERCHANT 누락"); FAIL(b); } + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + thr = getl(b, T_ARG1); + if (thr <= 0) thr = 90; + EXEC SQL SELECT daily_limit INTO :h_lim FROM merchant WHERE merchant_id = :h_merch; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 8); OK(b); } /* 8=가맹점 없음 */ + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_sum FROM purchase + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + pct = (h_lim > 0) ? (h_sum * 100 / h_lim) : 0; + if (pct >= thr) rc = 1; /* 한도 근접/초과 */ + setl(b, T_ARG2, pct); + setl(b, T_AMT1, h_sum); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_LONGOPEN.h b/app/src/vl/svc/VL_LONGOPEN.h new file mode 100644 index 0000000..29c8d67 --- /dev/null +++ b/app/src/vl/svc/VL_LONGOPEN.h @@ -0,0 +1,12 @@ +/* + * VL_LONGOPEN.h - vl 서비스 VL_LONGOPEN 카피북 (copybook / record header). + * 사용 UBF 필드: T_ARG1,T_ARG2,T_COUNT,T_ID1,T_RC + */ +#ifndef VL_LONGOPEN_H +#define VL_LONGOPEN_H + +#include "vl_svc.h" + +void VL_LONGOPEN(TPSVCINFO *p); + +#endif /* VL_LONGOPEN_H */ diff --git a/app/src/vl/svc/VL_LONGOPEN.pgc b/app/src/vl/svc/VL_LONGOPEN.pgc new file mode 100644 index 0000000..7ac1c15 --- /dev/null +++ b/app/src/vl/svc/VL_LONGOPEN.pgc @@ -0,0 +1,28 @@ +/* + * VL_LONGOPEN.pgc - vl 모듈 서비스 VL_LONGOPEN (one service = one file). + * 기능: 장기 미결 의심거래 검출 (OPEN 상태로 N일 초과 경과, 기준 T_ARG1 기본7, RC 1=존재). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_LONGOPEN.h" + +void VL_LONGOPEN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_days, h_cnt, h_oldest; + EXEC SQL END DECLARE SECTION; + h_days = getl(b, T_ARG1); + if (h_days <= 0) h_days = 7; + EXEC SQL SELECT count(*), coalesce(min(suspect_id),0) INTO :h_cnt, :h_oldest + FROM vl_suspect + WHERE status = 'OPEN' + AND biz_date < current_date - CAST(:h_days AS int); + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 장기 미결 의심 존재 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_ID1, h_oldest); + setl(b, T_ARG2, h_days); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_NEGFEE.h b/app/src/vl/svc/VL_NEGFEE.h new file mode 100644 index 0000000..a34b49c --- /dev/null +++ b/app/src/vl/svc/VL_NEGFEE.h @@ -0,0 +1,12 @@ +/* + * VL_NEGFEE.h - vl 서비스 VL_NEGFEE 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_COUNT,T_ID1,T_RC + */ +#ifndef VL_NEGFEE_H +#define VL_NEGFEE_H + +#include "vl_svc.h" + +void VL_NEGFEE(TPSVCINFO *p); + +#endif /* VL_NEGFEE_H */ diff --git a/app/src/vl/svc/VL_NEGFEE.pgc b/app/src/vl/svc/VL_NEGFEE.pgc new file mode 100644 index 0000000..fbb0abb --- /dev/null +++ b/app/src/vl/svc/VL_NEGFEE.pgc @@ -0,0 +1,26 @@ +/* + * VL_NEGFEE.pgc - vl 모듈 서비스 VL_NEGFEE (one service = one file). + * 기능: 음수 수수료 매입 검출 (일자별 fee<0 매입 수, RC 1=존재; 최소 매입ID 반환). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_NEGFEE.h" + +void VL_NEGFEE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_first; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT count(*), coalesce(min(purchase_id),0) INTO :h_cnt, :h_first + FROM purchase WHERE biz_date = :h_bizdate AND fee < 0; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 음수 수수료 매입 존재 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_ID1, h_first); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_NIGHTTXN.h b/app/src/vl/svc/VL_NIGHTTXN.h new file mode 100644 index 0000000..815baa1 --- /dev/null +++ b/app/src/vl/svc/VL_NIGHTTXN.h @@ -0,0 +1,12 @@ +/* + * VL_NIGHTTXN.h - vl 서비스 VL_NIGHTTXN 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_ARG1,T_ARG2,T_ARG3,T_COUNT,T_RC + */ +#ifndef VL_NIGHTTXN_H +#define VL_NIGHTTXN_H + +#include "vl_svc.h" + +void VL_NIGHTTXN(TPSVCINFO *p); + +#endif /* VL_NIGHTTXN_H */ diff --git a/app/src/vl/svc/VL_NIGHTTXN.pgc b/app/src/vl/svc/VL_NIGHTTXN.pgc new file mode 100644 index 0000000..bb5ea46 --- /dev/null +++ b/app/src/vl/svc/VL_NIGHTTXN.pgc @@ -0,0 +1,32 @@ +/* + * VL_NIGHTTXN.pgc - vl 모듈 서비스 VL_NIGHTTXN (one service = one file). + * 기능: 심야 거래 패턴 탐지 (0-6시 거래 비중>=T_ARG1% 기본20, RC 1=편중). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_NIGHTTXN.h" + +void VL_NIGHTTXN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, pct, thr; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_tot, h_night; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + thr = getl(b, T_ARG1); + if (thr <= 0) thr = 20; + EXEC SQL SELECT count(*), + coalesce(sum(CASE WHEN EXTRACT(HOUR FROM created_at) < 6 THEN 1 ELSE 0 END),0) + INTO :h_tot, :h_night + FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + pct = (h_tot > 0) ? (h_night * 100 / h_tot) : 0; + if (pct >= thr) rc = 1; /* 심야 거래 편중 */ + setl(b, T_COUNT, h_tot); + setl(b, T_ARG2, h_night); + setl(b, T_ARG3, pct); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_OUTLIER.h b/app/src/vl/svc/VL_OUTLIER.h new file mode 100644 index 0000000..0008f85 --- /dev/null +++ b/app/src/vl/svc/VL_OUTLIER.h @@ -0,0 +1,12 @@ +/* + * VL_OUTLIER.h - vl 서비스 VL_OUTLIER 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_ARG1,T_COUNT,T_RC + */ +#ifndef VL_OUTLIER_H +#define VL_OUTLIER_H + +#include "vl_svc.h" + +void VL_OUTLIER(TPSVCINFO *p); + +#endif /* VL_OUTLIER_H */ diff --git a/app/src/vl/svc/VL_OUTLIER.pgc b/app/src/vl/svc/VL_OUTLIER.pgc new file mode 100644 index 0000000..735c300 --- /dev/null +++ b/app/src/vl/svc/VL_OUTLIER.pgc @@ -0,0 +1,29 @@ +/* + * VL_OUTLIER.pgc - vl 모듈 서비스 VL_OUTLIER (one service = one file). + * 기능: 이상치 매입 탐지 (금액>당일 평균*배수 T_ARG1 기본4 건수, RC 1=존재). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_OUTLIER.h" + +void VL_OUTLIER(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_mult, h_cnt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + h_mult = getl(b, T_ARG1); + if (h_mult <= 0) h_mult = 4; + EXEC SQL SELECT count(*) INTO :h_cnt FROM purchase + WHERE biz_date = :h_bizdate + AND amount > :h_mult * (SELECT coalesce(avg(amount),0) + FROM purchase WHERE biz_date = :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 이상치 매입 존재 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_OVERLIMIT.h b/app/src/vl/svc/VL_OVERLIMIT.h new file mode 100644 index 0000000..cd1047d --- /dev/null +++ b/app/src/vl/svc/VL_OVERLIMIT.h @@ -0,0 +1,12 @@ +/* + * VL_OVERLIMIT.h - vl 서비스 VL_OVERLIMIT 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_COUNT,T_RC + */ +#ifndef VL_OVERLIMIT_H +#define VL_OVERLIMIT_H + +#include "vl_svc.h" + +void VL_OVERLIMIT(TPSVCINFO *p); + +#endif /* VL_OVERLIMIT_H */ diff --git a/app/src/vl/svc/VL_OVERLIMIT.pgc b/app/src/vl/svc/VL_OVERLIMIT.pgc new file mode 100644 index 0000000..464a6df --- /dev/null +++ b/app/src/vl/svc/VL_OVERLIMIT.pgc @@ -0,0 +1,29 @@ +/* + * VL_OVERLIMIT.pgc - vl 모듈 서비스 VL_OVERLIMIT (one service = one file). + * 기능: 가맹점 한도위반 검출 (일 매입합>일한도 가맹점 수, RC 1=존재). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_OVERLIMIT.h" + +void VL_OVERLIMIT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT count(*) INTO :h_cnt FROM ( + SELECT p.merchant_id + FROM purchase p JOIN merchant m ON m.merchant_id = p.merchant_id + WHERE p.biz_date = :h_bizdate + GROUP BY p.merchant_id + HAVING sum(p.amount) > max(m.daily_limit)) t; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 일한도 초과 가맹점 존재 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_PURNOAPPR.h b/app/src/vl/svc/VL_PURNOAPPR.h new file mode 100644 index 0000000..96f64f0 --- /dev/null +++ b/app/src/vl/svc/VL_PURNOAPPR.h @@ -0,0 +1,12 @@ +/* + * VL_PURNOAPPR.h - vl 서비스 VL_PURNOAPPR 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_COUNT,T_ID1,T_RC + */ +#ifndef VL_PURNOAPPR_H +#define VL_PURNOAPPR_H + +#include "vl_svc.h" + +void VL_PURNOAPPR(TPSVCINFO *p); + +#endif /* VL_PURNOAPPR_H */ diff --git a/app/src/vl/svc/VL_PURNOAPPR.pgc b/app/src/vl/svc/VL_PURNOAPPR.pgc new file mode 100644 index 0000000..5a44b3f --- /dev/null +++ b/app/src/vl/svc/VL_PURNOAPPR.pgc @@ -0,0 +1,29 @@ +/* + * VL_PURNOAPPR.pgc - vl 모듈 서비스 VL_PURNOAPPR (one service = one file). + * 기능: 무승인 매입 검출 (일자별 승인 전무 매입 수, 취소분 제외, RC 1=존재). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_PURNOAPPR.h" + +void VL_PURNOAPPR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_first; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT count(*), coalesce(min(p.purchase_id),0) INTO :h_cnt, :h_first + FROM purchase p + WHERE p.biz_date = :h_bizdate AND p.status <> 'C' + AND NOT EXISTS (SELECT 1 FROM approval a + WHERE a.purchase_id = p.purchase_id); + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 승인 없는 매입 존재 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_ID1, h_first); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_RIAPPR.h b/app/src/vl/svc/VL_RIAPPR.h new file mode 100644 index 0000000..aadb0ac --- /dev/null +++ b/app/src/vl/svc/VL_RIAPPR.h @@ -0,0 +1,12 @@ +/* + * VL_RIAPPR.h - vl 서비스 VL_RIAPPR 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT,T_RC + */ +#ifndef VL_RIAPPR_H +#define VL_RIAPPR_H + +#include "vl_svc.h" + +void VL_RIAPPR(TPSVCINFO *p); + +#endif /* VL_RIAPPR_H */ diff --git a/app/src/vl/svc/VL_RIAPPR.pgc b/app/src/vl/svc/VL_RIAPPR.pgc new file mode 100644 index 0000000..a9d13f2 --- /dev/null +++ b/app/src/vl/svc/VL_RIAPPR.pgc @@ -0,0 +1,23 @@ +/* + * VL_RIAPPR.pgc - vl 모듈 서비스 VL_RIAPPR (one service = one file). + * 기능: 참조무결성 승인 (취소/무효 매입에 승인가결 존재 건수, RC 1=위반). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_RIAPPR.h" + +void VL_RIAPPR(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt; + EXEC SQL END DECLARE SECTION; + EXEC SQL SELECT count(*) INTO :h_cnt FROM approval a + JOIN purchase p ON p.purchase_id = a.purchase_id + WHERE a.approved AND p.status IN ('C','V'); + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 취소/무효 매입 승인 존재 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_RICANCEL.h b/app/src/vl/svc/VL_RICANCEL.h new file mode 100644 index 0000000..be3639a --- /dev/null +++ b/app/src/vl/svc/VL_RICANCEL.h @@ -0,0 +1,12 @@ +/* + * VL_RICANCEL.h - vl 서비스 VL_RICANCEL 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_COUNT,T_CANCEL_ID,T_RC + */ +#ifndef VL_RICANCEL_H +#define VL_RICANCEL_H + +#include "vl_svc.h" + +void VL_RICANCEL(TPSVCINFO *p); + +#endif /* VL_RICANCEL_H */ diff --git a/app/src/vl/svc/VL_RICANCEL.pgc b/app/src/vl/svc/VL_RICANCEL.pgc new file mode 100644 index 0000000..d5cf1d8 --- /dev/null +++ b/app/src/vl/svc/VL_RICANCEL.pgc @@ -0,0 +1,29 @@ +/* + * VL_RICANCEL.pgc - vl 모듈 서비스 VL_RICANCEL (one service = one file). + * 기능: 참조무결성 취소 (일자별 매입 원거래 없는 취소 수, RC 1=위반; 최소 취소ID 반환). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_RICANCEL.h" + +void VL_RICANCEL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_first; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT count(*), coalesce(min(c.cancel_id),0) INTO :h_cnt, :h_first + FROM ac_cancel c + WHERE c.biz_date = :h_bizdate + AND NOT EXISTS (SELECT 1 FROM purchase p + WHERE p.purchase_id = c.purchase_id); + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 매입 없는 취소 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_CANCEL_ID, h_first); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_RIFX.h b/app/src/vl/svc/VL_RIFX.h new file mode 100644 index 0000000..0bd3dd9 --- /dev/null +++ b/app/src/vl/svc/VL_RIFX.h @@ -0,0 +1,12 @@ +/* + * VL_RIFX.h - vl 서비스 VL_RIFX 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT,T_AMT1,T_RC + */ +#ifndef VL_RIFX_H +#define VL_RIFX_H + +#include "vl_svc.h" + +void VL_RIFX(TPSVCINFO *p); + +#endif /* VL_RIFX_H */ diff --git a/app/src/vl/svc/VL_RIFX.pgc b/app/src/vl/svc/VL_RIFX.pgc new file mode 100644 index 0000000..dee53c5 --- /dev/null +++ b/app/src/vl/svc/VL_RIFX.pgc @@ -0,0 +1,25 @@ +/* + * VL_RIFX.pgc - vl 모듈 서비스 VL_RIFX (one service = one file). + * 기능: 참조무결성 해외 (매입 원거래 없는 해외 내역 수+환산합, RC 1=위반). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_RIFX.h" + +void VL_RIFX(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_amt; + EXEC SQL END DECLARE SECTION; + EXEC SQL SELECT count(*), coalesce(sum(f.krw_amount),0) INTO :h_cnt, :h_amt + FROM ac_fx f + WHERE NOT EXISTS (SELECT 1 FROM purchase p + WHERE p.purchase_id = f.purchase_id); + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 매입 없는 해외 내역 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_AMT1, h_amt); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_RIINSTALL.h b/app/src/vl/svc/VL_RIINSTALL.h new file mode 100644 index 0000000..1617e65 --- /dev/null +++ b/app/src/vl/svc/VL_RIINSTALL.h @@ -0,0 +1,12 @@ +/* + * VL_RIINSTALL.h - vl 서비스 VL_RIINSTALL 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT,T_RC + */ +#ifndef VL_RIINSTALL_H +#define VL_RIINSTALL_H + +#include "vl_svc.h" + +void VL_RIINSTALL(TPSVCINFO *p); + +#endif /* VL_RIINSTALL_H */ diff --git a/app/src/vl/svc/VL_RIINSTALL.pgc b/app/src/vl/svc/VL_RIINSTALL.pgc new file mode 100644 index 0000000..4d3b9c8 --- /dev/null +++ b/app/src/vl/svc/VL_RIINSTALL.pgc @@ -0,0 +1,22 @@ +/* + * VL_RIINSTALL.pgc - vl 모듈 서비스 VL_RIINSTALL (one service = one file). + * 기능: 참조무결성 할부 (매입 원거래 없는 할부 내역 수, NOT IN, RC 1=위반). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_RIINSTALL.h" + +void VL_RIINSTALL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt; + EXEC SQL END DECLARE SECTION; + EXEC SQL SELECT count(*) INTO :h_cnt FROM ac_install i + WHERE i.purchase_id NOT IN (SELECT purchase_id FROM purchase); + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 매입 없는 할부 내역 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_RIPAY.h b/app/src/vl/svc/VL_RIPAY.h new file mode 100644 index 0000000..1ced2c2 --- /dev/null +++ b/app/src/vl/svc/VL_RIPAY.h @@ -0,0 +1,12 @@ +/* + * VL_RIPAY.h - vl 서비스 VL_RIPAY 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT,T_ID1,T_RC + */ +#ifndef VL_RIPAY_H +#define VL_RIPAY_H + +#include "vl_svc.h" + +void VL_RIPAY(TPSVCINFO *p); + +#endif /* VL_RIPAY_H */ diff --git a/app/src/vl/svc/VL_RIPAY.pgc b/app/src/vl/svc/VL_RIPAY.pgc new file mode 100644 index 0000000..e54854e --- /dev/null +++ b/app/src/vl/svc/VL_RIPAY.pgc @@ -0,0 +1,26 @@ +/* + * VL_RIPAY.pgc - vl 모듈 서비스 VL_RIPAY (one service = one file). + * 기능: 참조무결성 지급 (지급 XFER 원장의 매입 원거래 부재 건수, RC 1=위반). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_RIPAY.h" + +void VL_RIPAY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_first; + EXEC SQL END DECLARE SECTION; + EXEC SQL SELECT count(*), coalesce(min(l.ledger_id),0) INTO :h_cnt, :h_first + FROM ledger l + WHERE l.entry_type = 'XFER' + AND NOT EXISTS (SELECT 1 FROM purchase p + WHERE p.purchase_id = l.purchase_id); + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 매입 없는 지급 원장 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_ID1, h_first); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_RITAXINV.h b/app/src/vl/svc/VL_RITAXINV.h new file mode 100644 index 0000000..5042a94 --- /dev/null +++ b/app/src/vl/svc/VL_RITAXINV.h @@ -0,0 +1,12 @@ +/* + * VL_RITAXINV.h - vl 서비스 VL_RITAXINV 카피북 (copybook / record header). + * 사용 UBF 필드: T_COUNT,T_RC + */ +#ifndef VL_RITAXINV_H +#define VL_RITAXINV_H + +#include "vl_svc.h" + +void VL_RITAXINV(TPSVCINFO *p); + +#endif /* VL_RITAXINV_H */ diff --git a/app/src/vl/svc/VL_RITAXINV.pgc b/app/src/vl/svc/VL_RITAXINV.pgc new file mode 100644 index 0000000..8d8112f --- /dev/null +++ b/app/src/vl/svc/VL_RITAXINV.pgc @@ -0,0 +1,23 @@ +/* + * VL_RITAXINV.pgc - vl 모듈 서비스 VL_RITAXINV (one service = one file). + * 기능: 참조무결성 세금계산서 (매입 원거래 없는 세금계산서 수, LEFT JOIN, RC 1=위반). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_RITAXINV.h" + +void VL_RITAXINV(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt; + EXEC SQL END DECLARE SECTION; + EXEC SQL SELECT count(*) INTO :h_cnt FROM ac_tax_invoice t + LEFT JOIN purchase p ON p.purchase_id = t.purchase_id + WHERE p.purchase_id IS NULL; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 매입 없는 세금계산서 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_ROUNDAMT.h b/app/src/vl/svc/VL_ROUNDAMT.h new file mode 100644 index 0000000..38423f2 --- /dev/null +++ b/app/src/vl/svc/VL_ROUNDAMT.h @@ -0,0 +1,12 @@ +/* + * VL_ROUNDAMT.h - vl 서비스 VL_ROUNDAMT 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_ARG1,T_ARG2,T_ARG3,T_COUNT,T_RC + */ +#ifndef VL_ROUNDAMT_H +#define VL_ROUNDAMT_H + +#include "vl_svc.h" + +void VL_ROUNDAMT(TPSVCINFO *p); + +#endif /* VL_ROUNDAMT_H */ diff --git a/app/src/vl/svc/VL_ROUNDAMT.pgc b/app/src/vl/svc/VL_ROUNDAMT.pgc new file mode 100644 index 0000000..a2abac1 --- /dev/null +++ b/app/src/vl/svc/VL_ROUNDAMT.pgc @@ -0,0 +1,32 @@ +/* + * VL_ROUNDAMT.pgc - vl 모듈 서비스 VL_ROUNDAMT (one service = one file). + * 기능: 라운드금액 패턴 탐지 (10만원 배수 매입 비중>=T_ARG1% 기본50, RC 1=편중). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_ROUNDAMT.h" + +void VL_ROUNDAMT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, pct, thr; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_tot, h_round; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + thr = getl(b, T_ARG1); + if (thr <= 0) thr = 50; + EXEC SQL SELECT count(*), + coalesce(sum(CASE WHEN amount % 100000 = 0 THEN 1 ELSE 0 END),0) + INTO :h_tot, :h_round + FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + pct = (h_tot > 0) ? (h_round * 100 / h_tot) : 0; + if (pct >= thr) rc = 1; /* 라운드금액 편중 */ + setl(b, T_COUNT, h_tot); + setl(b, T_ARG2, h_round); + setl(b, T_ARG3, pct); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_RULECOUNT.h b/app/src/vl/svc/VL_RULECOUNT.h new file mode 100644 index 0000000..ea62d64 --- /dev/null +++ b/app/src/vl/svc/VL_RULECOUNT.h @@ -0,0 +1,12 @@ +/* + * VL_RULECOUNT.h - vl 서비스 VL_RULECOUNT 카피북 (copybook / record header). + * 사용 UBF 필드: T_ARG1,T_ARG2,T_ARG3,T_ARG4,T_RC + */ +#ifndef VL_RULECOUNT_H +#define VL_RULECOUNT_H + +#include "vl_svc.h" + +void VL_RULECOUNT(TPSVCINFO *p); + +#endif /* VL_RULECOUNT_H */ diff --git a/app/src/vl/svc/VL_RULECOUNT.pgc b/app/src/vl/svc/VL_RULECOUNT.pgc new file mode 100644 index 0000000..a49c32f --- /dev/null +++ b/app/src/vl/svc/VL_RULECOUNT.pgc @@ -0,0 +1,30 @@ +/* + * VL_RULECOUNT.pgc - vl 모듈 서비스 VL_RULECOUNT (one service = one file). + * 기능: 검증규칙 심각도 분포 (INFO/WARN/ERROR/CRIT 건수, RC 1=CRIT 규칙 존재). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_RULECOUNT.h" + +void VL_RULECOUNT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_info, h_warn, h_err, h_crit; + EXEC SQL END DECLARE SECTION; + EXEC SQL SELECT + coalesce(sum(CASE WHEN severity = 'INFO' THEN 1 ELSE 0 END),0), + coalesce(sum(CASE WHEN severity = 'WARN' THEN 1 ELSE 0 END),0), + coalesce(sum(CASE WHEN severity = 'ERROR' THEN 1 ELSE 0 END),0), + coalesce(sum(CASE WHEN severity = 'CRIT' THEN 1 ELSE 0 END),0) + INTO :h_info, :h_warn, :h_err, :h_crit + FROM vl_rule; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_crit > 0) rc = 1; /* CRIT 규칙 존재 알림 */ + setl(b, T_ARG1, h_info); + setl(b, T_ARG2, h_warn); + setl(b, T_ARG3, h_err); + setl(b, T_ARG4, h_crit); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_RULEFIND.h b/app/src/vl/svc/VL_RULEFIND.h new file mode 100644 index 0000000..e78588c --- /dev/null +++ b/app/src/vl/svc/VL_RULEFIND.h @@ -0,0 +1,12 @@ +/* + * VL_RULEFIND.h - vl 서비스 VL_RULEFIND 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR3,T_ARG1,T_STR4,T_RC + */ +#ifndef VL_RULEFIND_H +#define VL_RULEFIND_H + +#include "vl_svc.h" + +void VL_RULEFIND(TPSVCINFO *p); + +#endif /* VL_RULEFIND_H */ diff --git a/app/src/vl/svc/VL_RULEFIND.pgc b/app/src/vl/svc/VL_RULEFIND.pgc new file mode 100644 index 0000000..661f475 --- /dev/null +++ b/app/src/vl/svc/VL_RULEFIND.pgc @@ -0,0 +1,24 @@ +/* + * VL_RULEFIND.pgc - vl 모듈 서비스 VL_RULEFIND (one service = one file). + * 기능: 검증규칙 단건 조회 (dbio vldb_get_rule 로 임계값/심각도 반환, RC 1=미존재). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_RULEFIND.h" + +void VL_RULEFIND(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char rule[32], sev[8]; + long thr = 0; + gets_(b, T_STR3, rule, sizeof(rule)); + if (rule[0] == 0) { userlog("VL_RULEFIND: T_STR3(rule_code) 누락"); FAIL(b); } + sev[0] = 0; + if (vldb_get_rule(rule, &thr, sev) < 0) { + setl(b, T_RC, 1); /* 규칙 미존재 */ + OK(b); + } + setl(b, T_ARG1, thr); + Bchg(b, T_STR4, 0, sev, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/vl/svc/VL_RULEON.h b/app/src/vl/svc/VL_RULEON.h new file mode 100644 index 0000000..4ffab11 --- /dev/null +++ b/app/src/vl/svc/VL_RULEON.h @@ -0,0 +1,12 @@ +/* + * VL_RULEON.h - vl 서비스 VL_RULEON 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR3,T_RC + */ +#ifndef VL_RULEON_H +#define VL_RULEON_H + +#include "vl_svc.h" + +void VL_RULEON(TPSVCINFO *p); + +#endif /* VL_RULEON_H */ diff --git a/app/src/vl/svc/VL_RULEON.pgc b/app/src/vl/svc/VL_RULEON.pgc new file mode 100644 index 0000000..77023dc --- /dev/null +++ b/app/src/vl/svc/VL_RULEON.pgc @@ -0,0 +1,25 @@ +/* + * VL_RULEON.pgc - vl 모듈 서비스 VL_RULEON (one service = one file). + * 기능: 검증규칙 활성화 (status ACTIVE 전환, RC 1=이미 활성 2=상태변경 실패 3=규칙없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_RULEON.h" + +void VL_RULEON(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_rule[32], h_st[16]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_STR3, h_rule, sizeof(h_rule)); + if (h_rule[0] == 0) { userlog("VL_RULEON: T_STR3(rule_code) 누락"); FAIL(b); } + EXEC SQL SELECT status INTO :h_st FROM vl_rule WHERE rule_code = :h_rule; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 3); OK(b); } /* 3=규칙 없음 */ + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_st, "ACTIVE") == 0) { setl(b, T_RC, 1); OK(b); } /* 이미 활성 */ + EXEC SQL UPDATE vl_rule SET status = 'ACTIVE', updated_at = now() + WHERE rule_code = :h_rule; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_RC, ((long)sqlca.sqlerrd[2] == 0) ? 2 : 0); + OK(b); +} diff --git a/app/src/vl/svc/VL_RULERENAME.h b/app/src/vl/svc/VL_RULERENAME.h new file mode 100644 index 0000000..0150832 --- /dev/null +++ b/app/src/vl/svc/VL_RULERENAME.h @@ -0,0 +1,12 @@ +/* + * VL_RULERENAME.h - vl 서비스 VL_RULERENAME 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR3,T_STR1,T_RC + */ +#ifndef VL_RULERENAME_H +#define VL_RULERENAME_H + +#include "vl_svc.h" + +void VL_RULERENAME(TPSVCINFO *p); + +#endif /* VL_RULERENAME_H */ diff --git a/app/src/vl/svc/VL_RULERENAME.pgc b/app/src/vl/svc/VL_RULERENAME.pgc new file mode 100644 index 0000000..a680706 --- /dev/null +++ b/app/src/vl/svc/VL_RULERENAME.pgc @@ -0,0 +1,25 @@ +/* + * VL_RULERENAME.pgc - vl 모듈 서비스 VL_RULERENAME (one service = one file). + * 기능: 검증규칙 명칭 변경 (rule_name UPDATE, 빈 값이면 기본명, RC 1=대상 규칙 없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_RULERENAME.h" + +void VL_RULERENAME(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_rule[32], h_name[64]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_STR3, h_rule, sizeof(h_rule)); + if (h_rule[0] == 0) { userlog("VL_RULERENAME: T_STR3(rule_code) 누락"); FAIL(b); } + gets_(b, T_STR1, h_name, sizeof(h_name)); + if (h_name[0] == 0) strcpy(h_name, "무명 검증 규칙"); + EXEC SQL UPDATE vl_rule SET rule_name = :h_name, updated_at = now() + WHERE rule_code = :h_rule; + if (sqlca.sqlcode < 0) FAIL(b); + if ((long)sqlca.sqlerrd[2] == 0) rc = 1; /* 대상 규칙 없음 */ + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_RULESEV.h b/app/src/vl/svc/VL_RULESEV.h new file mode 100644 index 0000000..cd52507 --- /dev/null +++ b/app/src/vl/svc/VL_RULESEV.h @@ -0,0 +1,12 @@ +/* + * VL_RULESEV.h - vl 서비스 VL_RULESEV 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR3,T_STR4,T_RC + */ +#ifndef VL_RULESEV_H +#define VL_RULESEV_H + +#include "vl_svc.h" + +void VL_RULESEV(TPSVCINFO *p); + +#endif /* VL_RULESEV_H */ diff --git a/app/src/vl/svc/VL_RULESEV.pgc b/app/src/vl/svc/VL_RULESEV.pgc new file mode 100644 index 0000000..fa3db66 --- /dev/null +++ b/app/src/vl/svc/VL_RULESEV.pgc @@ -0,0 +1,29 @@ +/* + * VL_RULESEV.pgc - vl 모듈 서비스 VL_RULESEV (one service = one file). + * 기능: 검증규칙 심각도 변경 (INFO/WARN/ERROR/CRIT 유효성 검사 후 UPDATE, RC 1=규칙없음 2=심각도오류). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_RULESEV.h" + +void VL_RULESEV(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_rule[32], h_sev[8]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_STR3, h_rule, sizeof(h_rule)); + if (h_rule[0] == 0) { userlog("VL_RULESEV: T_STR3(rule_code) 누락"); FAIL(b); } + gets_(b, T_STR4, h_sev, sizeof(h_sev)); + if (strcmp(h_sev, "INFO") != 0 && strcmp(h_sev, "WARN") != 0 && + strcmp(h_sev, "ERROR") != 0 && strcmp(h_sev, "CRIT") != 0) { + setl(b, T_RC, 2); /* 허용되지 않는 심각도 */ + OK(b); + } + EXEC SQL UPDATE vl_rule SET severity = :h_sev, updated_at = now() + WHERE rule_code = :h_rule; + if (sqlca.sqlcode < 0) FAIL(b); + if ((long)sqlca.sqlerrd[2] == 0) rc = 1; /* 대상 규칙 없음 */ + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_RULEVER.h b/app/src/vl/svc/VL_RULEVER.h new file mode 100644 index 0000000..2733b33 --- /dev/null +++ b/app/src/vl/svc/VL_RULEVER.h @@ -0,0 +1,12 @@ +/* + * VL_RULEVER.h - vl 서비스 VL_RULEVER 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR3,T_ARG1,T_ARG2,T_RC + */ +#ifndef VL_RULEVER_H +#define VL_RULEVER_H + +#include "vl_svc.h" + +void VL_RULEVER(TPSVCINFO *p); + +#endif /* VL_RULEVER_H */ diff --git a/app/src/vl/svc/VL_RULEVER.pgc b/app/src/vl/svc/VL_RULEVER.pgc new file mode 100644 index 0000000..ded716b --- /dev/null +++ b/app/src/vl/svc/VL_RULEVER.pgc @@ -0,0 +1,31 @@ +/* + * VL_RULEVER.pgc - vl 모듈 서비스 VL_RULEVER (one service = one file). + * 기능: 검증규칙 최신성 검사 (T_ARG1 기본30일 이내 갱신 여부, RC 1=노후 2=규칙없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_RULEVER.h" + +void VL_RULEVER(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_rule[32]; + long h_days, h_recent, h_exist; + EXEC SQL END DECLARE SECTION; + gets_(b, T_STR3, h_rule, sizeof(h_rule)); + if (h_rule[0] == 0) { userlog("VL_RULEVER: T_STR3(rule_code) 누락"); FAIL(b); } + h_days = getl(b, T_ARG1); + if (h_days <= 0) h_days = 30; + EXEC SQL SELECT count(*) INTO :h_exist FROM vl_rule WHERE rule_code = :h_rule; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_exist == 0) { setl(b, T_RC, 2); OK(b); } /* 2=규칙 없음 */ + EXEC SQL SELECT count(*) INTO :h_recent FROM vl_rule + WHERE rule_code = :h_rule + AND updated_at >= now() - CAST(:h_days AS int) * INTERVAL '1 day'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_recent == 0) rc = 1; /* 장기 미갱신 규칙 */ + setl(b, T_ARG2, h_days); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_SETTLREV.h b/app/src/vl/svc/VL_SETTLREV.h new file mode 100644 index 0000000..46b122e --- /dev/null +++ b/app/src/vl/svc/VL_SETTLREV.h @@ -0,0 +1,12 @@ +/* + * VL_SETTLREV.h - vl 서비스 VL_SETTLREV 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_COUNT,T_AMT1,T_RC + */ +#ifndef VL_SETTLREV_H +#define VL_SETTLREV_H + +#include "vl_svc.h" + +void VL_SETTLREV(TPSVCINFO *p); + +#endif /* VL_SETTLREV_H */ diff --git a/app/src/vl/svc/VL_SETTLREV.pgc b/app/src/vl/svc/VL_SETTLREV.pgc new file mode 100644 index 0000000..c01ea32 --- /dev/null +++ b/app/src/vl/svc/VL_SETTLREV.pgc @@ -0,0 +1,26 @@ +/* + * VL_SETTLREV.pgc - vl 모듈 서비스 VL_SETTLREV (one service = one file). + * 기능: 정산 역전 검출 (일자별 순액 음수 settlement 건수, RC 1=존재; 최악치 반환). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_SETTLREV.h" + +void VL_SETTLREV(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt, h_worst; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT count(*), coalesce(min(net),0) INTO :h_cnt, :h_worst + FROM settlement WHERE biz_date = :h_bizdate AND net < 0; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 순액 역전 정산 존재 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_AMT1, h_worst); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_SPIKEAMT.h b/app/src/vl/svc/VL_SPIKEAMT.h new file mode 100644 index 0000000..980d0fb --- /dev/null +++ b/app/src/vl/svc/VL_SPIKEAMT.h @@ -0,0 +1,12 @@ +/* + * VL_SPIKEAMT.h - vl 서비스 VL_SPIKEAMT 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_ARG1,T_AMT1,T_AMT2,T_COUNT,T_RC + */ +#ifndef VL_SPIKEAMT_H +#define VL_SPIKEAMT_H + +#include "vl_svc.h" + +void VL_SPIKEAMT(TPSVCINFO *p); + +#endif /* VL_SPIKEAMT_H */ diff --git a/app/src/vl/svc/VL_SPIKEAMT.pgc b/app/src/vl/svc/VL_SPIKEAMT.pgc new file mode 100644 index 0000000..d74a82a --- /dev/null +++ b/app/src/vl/svc/VL_SPIKEAMT.pgc @@ -0,0 +1,31 @@ +/* + * VL_SPIKEAMT.pgc - vl 모듈 서비스 VL_SPIKEAMT (one service = one file). + * 기능: 금액 급증 탐지 (당일 최대 매입>평균*배수 T_ARG1 기본5, RC 1=급증). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_SPIKEAMT.h" + +void VL_SPIKEAMT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, mult; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_max, h_avg, h_cnt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + mult = getl(b, T_ARG1); + if (mult <= 0) mult = 5; + EXEC SQL SELECT coalesce(max(amount),0), + coalesce(CAST(avg(amount) AS bigint),0), count(*) + INTO :h_max, :h_avg, :h_cnt + FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0 && h_max > h_avg * mult) rc = 1; /* 금액 급증 */ + setl(b, T_AMT1, h_max); + setl(b, T_AMT2, h_avg); + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_STATMIX.h b/app/src/vl/svc/VL_STATMIX.h new file mode 100644 index 0000000..caa4246 --- /dev/null +++ b/app/src/vl/svc/VL_STATMIX.h @@ -0,0 +1,12 @@ +/* + * VL_STATMIX.h - vl 서비스 VL_STATMIX 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_ARG1,T_ARG2,T_ARG3,T_COUNT,T_RC + */ +#ifndef VL_STATMIX_H +#define VL_STATMIX_H + +#include "vl_svc.h" + +void VL_STATMIX(TPSVCINFO *p); + +#endif /* VL_STATMIX_H */ diff --git a/app/src/vl/svc/VL_STATMIX.pgc b/app/src/vl/svc/VL_STATMIX.pgc new file mode 100644 index 0000000..b90d85c --- /dev/null +++ b/app/src/vl/svc/VL_STATMIX.pgc @@ -0,0 +1,32 @@ +/* + * VL_STATMIX.pgc - vl 모듈 서비스 VL_STATMIX (one service = one file). + * 기능: 상태 분포 검증 (취소 C 상태 비중>=T_ARG1% 기본15, RC 1=취소 과다). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_STATMIX.h" + +void VL_STATMIX(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, pct, thr; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_tot, h_cxl; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + thr = getl(b, T_ARG1); + if (thr <= 0) thr = 15; + EXEC SQL SELECT count(*), + coalesce(sum(CASE WHEN status = 'C' THEN 1 ELSE 0 END),0) + INTO :h_tot, :h_cxl + FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + pct = (h_tot > 0) ? (h_cxl * 100 / h_tot) : 0; + if (pct >= thr) rc = 1; /* 취소 상태 과다 */ + setl(b, T_COUNT, h_tot); + setl(b, T_ARG2, h_cxl); + setl(b, T_ARG3, pct); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_SUMFEECHK.h b/app/src/vl/svc/VL_SUMFEECHK.h new file mode 100644 index 0000000..0c27738 --- /dev/null +++ b/app/src/vl/svc/VL_SUMFEECHK.h @@ -0,0 +1,12 @@ +/* + * VL_SUMFEECHK.h - vl 서비스 VL_SUMFEECHK 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_COUNT,T_RC + */ +#ifndef VL_SUMFEECHK_H +#define VL_SUMFEECHK_H + +#include "vl_svc.h" + +void VL_SUMFEECHK(TPSVCINFO *p); + +#endif /* VL_SUMFEECHK_H */ diff --git a/app/src/vl/svc/VL_SUMFEECHK.pgc b/app/src/vl/svc/VL_SUMFEECHK.pgc new file mode 100644 index 0000000..58dab7c --- /dev/null +++ b/app/src/vl/svc/VL_SUMFEECHK.pgc @@ -0,0 +1,30 @@ +/* + * VL_SUMFEECHK.pgc - vl 모듈 서비스 VL_SUMFEECHK (one service = one file). + * 기능: 통계-원천 수수료 대사 (일자별 가맹점 수수료 집계 vs 원천 합 불일치 수, RC 1=존재). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_SUMFEECHK.h" + +void VL_SUMFEECHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT count(*) INTO :h_cnt FROM ( + SELECT ms.merchant_id + FROM merchant_summary ms + JOIN (SELECT merchant_id, coalesce(sum(fee),0) AS f FROM purchase + WHERE biz_date = :h_bizdate GROUP BY merchant_id) pv + ON pv.merchant_id = ms.merchant_id + WHERE ms.biz_date = :h_bizdate AND ms.fee_amount <> pv.f) t; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 수수료 집계 불일치 가맹점 존재 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_SUMGROSS.h b/app/src/vl/svc/VL_SUMGROSS.h new file mode 100644 index 0000000..24ea91e --- /dev/null +++ b/app/src/vl/svc/VL_SUMGROSS.h @@ -0,0 +1,12 @@ +/* + * VL_SUMGROSS.h - vl 서비스 VL_SUMGROSS 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT,T_BIZDATE,T_GROSS,T_AMT1,T_DELTA,T_RC + */ +#ifndef VL_SUMGROSS_H +#define VL_SUMGROSS_H + +#include "vl_svc.h" + +void VL_SUMGROSS(TPSVCINFO *p); + +#endif /* VL_SUMGROSS_H */ diff --git a/app/src/vl/svc/VL_SUMGROSS.pgc b/app/src/vl/svc/VL_SUMGROSS.pgc new file mode 100644 index 0000000..b95bcee --- /dev/null +++ b/app/src/vl/svc/VL_SUMGROSS.pgc @@ -0,0 +1,33 @@ +/* + * VL_SUMGROSS.pgc - vl 모듈 서비스 VL_SUMGROSS (one service = one file). + * 기능: 통계-원천 총액 대사 (merchant_summary 총액 vs 원천 매입금액 합, RC 1=불일치 8=통계없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_SUMGROSS.h" + +void VL_SUMGROSS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_bizdate[16]; + long h_sgross, h_pgross; + EXEC SQL END DECLARE SECTION; + gets_(b, T_MERCHANT, h_merch, sizeof(h_merch)); + if (h_merch[0] == 0) { userlog("VL_SUMGROSS: T_MERCHANT 누락"); FAIL(b); } + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT gross_amount INTO :h_sgross FROM merchant_summary + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 8); OK(b); } /* 8=통계 없음 */ + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_pgross FROM purchase + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status <> 'C'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_sgross != h_pgross) rc = 1; /* 집계-원천 총액 불일치 */ + setl(b, T_GROSS, h_pgross); + setl(b, T_AMT1, h_sgross); + setl(b, T_DELTA, h_sgross - h_pgross); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_SUMNETCHK.h b/app/src/vl/svc/VL_SUMNETCHK.h new file mode 100644 index 0000000..04e81fa --- /dev/null +++ b/app/src/vl/svc/VL_SUMNETCHK.h @@ -0,0 +1,12 @@ +/* + * VL_SUMNETCHK.h - vl 서비스 VL_SUMNETCHK 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT,T_BIZDATE,T_NET,T_AMT1,T_RC + */ +#ifndef VL_SUMNETCHK_H +#define VL_SUMNETCHK_H + +#include "vl_svc.h" + +void VL_SUMNETCHK(TPSVCINFO *p); + +#endif /* VL_SUMNETCHK_H */ diff --git a/app/src/vl/svc/VL_SUMNETCHK.pgc b/app/src/vl/svc/VL_SUMNETCHK.pgc new file mode 100644 index 0000000..b9ebf06 --- /dev/null +++ b/app/src/vl/svc/VL_SUMNETCHK.pgc @@ -0,0 +1,32 @@ +/* + * VL_SUMNETCHK.pgc - vl 모듈 서비스 VL_SUMNETCHK (one service = one file). + * 기능: 통계-원천 순액 대사 (merchant_summary 순액 vs 원천 매입순액 합, RC 1=불일치 8=통계없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_SUMNETCHK.h" + +void VL_SUMNETCHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_bizdate[16]; + long h_snet, h_pnet; + EXEC SQL END DECLARE SECTION; + gets_(b, T_MERCHANT, h_merch, sizeof(h_merch)); + if (h_merch[0] == 0) { userlog("VL_SUMNETCHK: T_MERCHANT 누락"); FAIL(b); } + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT net_amount INTO :h_snet FROM merchant_summary + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 8); OK(b); } /* 8=통계 없음 */ + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_pnet FROM purchase + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status <> 'C'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_snet != h_pnet) rc = 1; /* 집계-원천 순액 불일치 */ + setl(b, T_NET, h_pnet); + setl(b, T_AMT1, h_snet); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_SUMTXNCNT.h b/app/src/vl/svc/VL_SUMTXNCNT.h new file mode 100644 index 0000000..966850e --- /dev/null +++ b/app/src/vl/svc/VL_SUMTXNCNT.h @@ -0,0 +1,12 @@ +/* + * VL_SUMTXNCNT.h - vl 서비스 VL_SUMTXNCNT 카피북 (copybook / record header). + * 사용 UBF 필드: T_MERCHANT,T_BIZDATE,T_COUNT,T_ARG1,T_DELTA,T_RC + */ +#ifndef VL_SUMTXNCNT_H +#define VL_SUMTXNCNT_H + +#include "vl_svc.h" + +void VL_SUMTXNCNT(TPSVCINFO *p); + +#endif /* VL_SUMTXNCNT_H */ diff --git a/app/src/vl/svc/VL_SUMTXNCNT.pgc b/app/src/vl/svc/VL_SUMTXNCNT.pgc new file mode 100644 index 0000000..2188594 --- /dev/null +++ b/app/src/vl/svc/VL_SUMTXNCNT.pgc @@ -0,0 +1,33 @@ +/* + * VL_SUMTXNCNT.pgc - vl 모듈 서비스 VL_SUMTXNCNT (one service = one file). + * 기능: 통계-원천 건수 대사 (merchant_summary 건수 vs 원천 매입 건수, RC 1=불일치 8=통계없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_SUMTXNCNT.h" + +void VL_SUMTXNCNT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_bizdate[16]; + long h_scnt, h_pcnt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_MERCHANT, h_merch, sizeof(h_merch)); + if (h_merch[0] == 0) { userlog("VL_SUMTXNCNT: T_MERCHANT 누락"); FAIL(b); } + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT txn_count INTO :h_scnt FROM merchant_summary + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 8); OK(b); } /* 8=통계 없음 */ + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT count(*) INTO :h_pcnt FROM purchase + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status <> 'C'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_scnt != h_pcnt) rc = 1; /* 집계-원천 건수 불일치 */ + setl(b, T_COUNT, h_pcnt); + setl(b, T_ARG1, h_scnt); + setl(b, T_DELTA, h_scnt - h_pcnt); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_SUSPAGEAVG.h b/app/src/vl/svc/VL_SUSPAGEAVG.h new file mode 100644 index 0000000..a3b9e6f --- /dev/null +++ b/app/src/vl/svc/VL_SUSPAGEAVG.h @@ -0,0 +1,12 @@ +/* + * VL_SUSPAGEAVG.h - vl 서비스 VL_SUSPAGEAVG 카피북 (copybook / record header). + * 사용 UBF 필드: T_ARG1,T_ARG2,T_COUNT,T_RC + */ +#ifndef VL_SUSPAGEAVG_H +#define VL_SUSPAGEAVG_H + +#include "vl_svc.h" + +void VL_SUSPAGEAVG(TPSVCINFO *p); + +#endif /* VL_SUSPAGEAVG_H */ diff --git a/app/src/vl/svc/VL_SUSPAGEAVG.pgc b/app/src/vl/svc/VL_SUSPAGEAVG.pgc new file mode 100644 index 0000000..7ad98a7 --- /dev/null +++ b/app/src/vl/svc/VL_SUSPAGEAVG.pgc @@ -0,0 +1,27 @@ +/* + * VL_SUSPAGEAVG.pgc - vl 모듈 서비스 VL_SUSPAGEAVG (one service = one file). + * 기능: 의심거래 평균 경과일 (OPEN 평균 경과일>T_ARG1 기본5, RC 1=평균 경과 초과). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_SUSPAGEAVG.h" + +void VL_SUSPAGEAVG(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, thr; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_avg; + EXEC SQL END DECLARE SECTION; + thr = getl(b, T_ARG1); + if (thr <= 0) thr = 5; + EXEC SQL SELECT count(*), + coalesce(CAST(avg(current_date - biz_date) AS bigint),0) + INTO :h_cnt, :h_avg + FROM vl_suspect WHERE status = 'OPEN'; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0 && h_avg > thr) rc = 1; /* 평균 경과일 초과 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_ARG2, h_avg); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_SUSPASSIGN.h b/app/src/vl/svc/VL_SUSPASSIGN.h new file mode 100644 index 0000000..83fda28 --- /dev/null +++ b/app/src/vl/svc/VL_SUSPASSIGN.h @@ -0,0 +1,12 @@ +/* + * VL_SUSPASSIGN.h - vl 서비스 VL_SUSPASSIGN 카피북 (copybook / record header). + * 사용 UBF 필드: T_ID1,T_STR1,T_ARG1,T_RC + */ +#ifndef VL_SUSPASSIGN_H +#define VL_SUSPASSIGN_H + +#include "vl_svc.h" + +void VL_SUSPASSIGN(TPSVCINFO *p); + +#endif /* VL_SUSPASSIGN_H */ diff --git a/app/src/vl/svc/VL_SUSPASSIGN.pgc b/app/src/vl/svc/VL_SUSPASSIGN.pgc new file mode 100644 index 0000000..9ae52b4 --- /dev/null +++ b/app/src/vl/svc/VL_SUSPASSIGN.pgc @@ -0,0 +1,28 @@ +/* + * VL_SUSPASSIGN.pgc - vl 모듈 서비스 VL_SUSPASSIGN (one service = one file). + * 기능: 의심거래 담당 배정 (미종결 건 detail 에 담당자 기록, RC 1=대상없음/종결됨). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_SUSPASSIGN.h" + +void VL_SUSPASSIGN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, rows; + EXEC SQL BEGIN DECLARE SECTION; + long h_sid; + char h_who[32]; + EXEC SQL END DECLARE SECTION; + h_sid = getl(b, T_ID1); + gets_(b, T_STR1, h_who, sizeof(h_who)); + if (h_who[0] == 0) { userlog("VL_SUSPASSIGN: T_STR1(담당자) 누락"); FAIL(b); } + EXEC SQL UPDATE vl_suspect + SET detail = detail || ' [담당:' || :h_who || ']' + WHERE suspect_id = :h_sid AND status <> 'CLOSED'; + if (sqlca.sqlcode < 0) FAIL(b); + rows = (long)sqlca.sqlerrd[2]; + if (rows == 0) rc = 1; /* 종결됨 또는 미존재 */ + setl(b, T_ARG1, rows); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_SUSPBULK.h b/app/src/vl/svc/VL_SUSPBULK.h new file mode 100644 index 0000000..e629397 --- /dev/null +++ b/app/src/vl/svc/VL_SUSPBULK.h @@ -0,0 +1,12 @@ +/* + * VL_SUSPBULK.h - vl 서비스 VL_SUSPBULK 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_COUNT,T_RC + */ +#ifndef VL_SUSPBULK_H +#define VL_SUSPBULK_H + +#include "vl_svc.h" + +void VL_SUSPBULK(TPSVCINFO *p); + +#endif /* VL_SUSPBULK_H */ diff --git a/app/src/vl/svc/VL_SUSPBULK.pgc b/app/src/vl/svc/VL_SUSPBULK.pgc new file mode 100644 index 0000000..959684f --- /dev/null +++ b/app/src/vl/svc/VL_SUSPBULK.pgc @@ -0,0 +1,25 @@ +/* + * VL_SUSPBULK.pgc - vl 모듈 서비스 VL_SUSPBULK (one service = one file). + * 기능: 의심거래 일괄 확인 (일자별 OPEN 건 CONFIRMED 일괄 전환, RC 1=처리 대상 없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_SUSPBULK.h" + +void VL_SUSPBULK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, rows; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL UPDATE vl_suspect SET status = 'CONFIRMED' + WHERE biz_date = :h_bizdate AND status = 'OPEN'; + if (sqlca.sqlcode < 0) FAIL(b); + rows = (long)sqlca.sqlerrd[2]; + if (rows == 0) rc = 1; /* 확인 대상 OPEN 없음 */ + setl(b, T_COUNT, rows); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_SUSPESCAL.h b/app/src/vl/svc/VL_SUSPESCAL.h new file mode 100644 index 0000000..a093adb --- /dev/null +++ b/app/src/vl/svc/VL_SUSPESCAL.h @@ -0,0 +1,12 @@ +/* + * VL_SUSPESCAL.h - vl 서비스 VL_SUSPESCAL 카피북 (copybook / record header). + * 사용 UBF 필드: T_ID1,T_RC + */ +#ifndef VL_SUSPESCAL_H +#define VL_SUSPESCAL_H + +#include "vl_svc.h" + +void VL_SUSPESCAL(TPSVCINFO *p); + +#endif /* VL_SUSPESCAL_H */ diff --git a/app/src/vl/svc/VL_SUSPESCAL.pgc b/app/src/vl/svc/VL_SUSPESCAL.pgc new file mode 100644 index 0000000..1450292 --- /dev/null +++ b/app/src/vl/svc/VL_SUSPESCAL.pgc @@ -0,0 +1,26 @@ +/* + * VL_SUSPESCAL.pgc - vl 모듈 서비스 VL_SUSPESCAL (one service = one file). + * 기능: 의심거래 상향 (WARN→ERROR→CRIT 단계 승격, RC 1=상향불가 3=미존재). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_SUSPESCAL.h" + +void VL_SUSPESCAL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_sid; + char h_sev[8], h_new[8]; + EXEC SQL END DECLARE SECTION; + h_sid = getl(b, T_ID1); + EXEC SQL SELECT severity INTO :h_sev FROM vl_suspect WHERE suspect_id = :h_sid; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 3); OK(b); } /* 3=미존재 */ + if (sqlca.sqlcode < 0) FAIL(b); + if (strcmp(h_sev, "WARN") == 0) strcpy(h_new, "ERROR"); + else if (strcmp(h_sev, "ERROR") == 0) strcpy(h_new, "CRIT"); + else { setl(b, T_RC, 1); OK(b); } /* 더 상향 불가 */ + EXEC SQL UPDATE vl_suspect SET severity = :h_new WHERE suspect_id = :h_sid; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/vl/svc/VL_SUSPNOTE.h b/app/src/vl/svc/VL_SUSPNOTE.h new file mode 100644 index 0000000..19a6216 --- /dev/null +++ b/app/src/vl/svc/VL_SUSPNOTE.h @@ -0,0 +1,12 @@ +/* + * VL_SUSPNOTE.h - vl 서비스 VL_SUSPNOTE 카피북 (copybook / record header). + * 사용 UBF 필드: T_ID1,T_MSG,T_RC + */ +#ifndef VL_SUSPNOTE_H +#define VL_SUSPNOTE_H + +#include "vl_svc.h" + +void VL_SUSPNOTE(TPSVCINFO *p); + +#endif /* VL_SUSPNOTE_H */ diff --git a/app/src/vl/svc/VL_SUSPNOTE.pgc b/app/src/vl/svc/VL_SUSPNOTE.pgc new file mode 100644 index 0000000..14515e0 --- /dev/null +++ b/app/src/vl/svc/VL_SUSPNOTE.pgc @@ -0,0 +1,25 @@ +/* + * VL_SUSPNOTE.pgc - vl 모듈 서비스 VL_SUSPNOTE (one service = one file). + * 기능: 의심거래 코멘트 추가 (상태 무관 detail 에 메모 append, RC 1=대상 미존재). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_SUSPNOTE.h" + +void VL_SUSPNOTE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_sid; + char h_note[128]; + EXEC SQL END DECLARE SECTION; + h_sid = getl(b, T_ID1); + gets_(b, T_MSG, h_note, sizeof(h_note)); + if (h_note[0] == 0) { userlog("VL_SUSPNOTE: T_MSG(메모) 누락"); FAIL(b); } + EXEC SQL UPDATE vl_suspect SET detail = detail || ' | ' || :h_note + WHERE suspect_id = :h_sid; + if (sqlca.sqlcode < 0) FAIL(b); + if ((long)sqlca.sqlerrd[2] == 0) rc = 1; /* 대상 미존재 */ + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_SUSPRECHK.h b/app/src/vl/svc/VL_SUSPRECHK.h new file mode 100644 index 0000000..4fed6fe --- /dev/null +++ b/app/src/vl/svc/VL_SUSPRECHK.h @@ -0,0 +1,12 @@ +/* + * VL_SUSPRECHK.h - vl 서비스 VL_SUSPRECHK 카피북 (copybook / record header). + * 사용 UBF 필드: T_ID1,T_STR1,T_ARG1,T_RC + */ +#ifndef VL_SUSPRECHK_H +#define VL_SUSPRECHK_H + +#include "vl_svc.h" + +void VL_SUSPRECHK(TPSVCINFO *p); + +#endif /* VL_SUSPRECHK_H */ diff --git a/app/src/vl/svc/VL_SUSPRECHK.pgc b/app/src/vl/svc/VL_SUSPRECHK.pgc new file mode 100644 index 0000000..2f7b80d --- /dev/null +++ b/app/src/vl/svc/VL_SUSPRECHK.pgc @@ -0,0 +1,29 @@ +/* + * VL_SUSPRECHK.pgc - vl 모듈 서비스 VL_SUSPRECHK (one service = one file). + * 기능: 의심거래 재검증 (CONFIRMED→OPEN 재개, 재검증자 기록, RC 1=CONFIRMED 아님/미존재). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_SUSPRECHK.h" + +void VL_SUSPRECHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, rows; + EXEC SQL BEGIN DECLARE SECTION; + long h_sid; + char h_who[32]; + EXEC SQL END DECLARE SECTION; + h_sid = getl(b, T_ID1); + gets_(b, T_STR1, h_who, sizeof(h_who)); + if (h_who[0] == 0) strcpy(h_who, "MISC"); + EXEC SQL UPDATE vl_suspect + SET status = 'OPEN', + detail = detail || ' [재검증:' || :h_who || ']' + WHERE suspect_id = :h_sid AND status = 'CONFIRMED'; + if (sqlca.sqlcode < 0) FAIL(b); + rows = (long)sqlca.sqlerrd[2]; + if (rows == 0) rc = 1; /* CONFIRMED 아님 또는 미존재 */ + setl(b, T_ARG1, rows); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_SUSPTRIAGE.h b/app/src/vl/svc/VL_SUSPTRIAGE.h new file mode 100644 index 0000000..47246e9 --- /dev/null +++ b/app/src/vl/svc/VL_SUSPTRIAGE.h @@ -0,0 +1,12 @@ +/* + * VL_SUSPTRIAGE.h - vl 서비스 VL_SUSPTRIAGE 카피북 (copybook / record header). + * 사용 UBF 필드: T_ID1,T_STR4,T_ARG1,T_RC + */ +#ifndef VL_SUSPTRIAGE_H +#define VL_SUSPTRIAGE_H + +#include "vl_svc.h" + +void VL_SUSPTRIAGE(TPSVCINFO *p); + +#endif /* VL_SUSPTRIAGE_H */ diff --git a/app/src/vl/svc/VL_SUSPTRIAGE.pgc b/app/src/vl/svc/VL_SUSPTRIAGE.pgc new file mode 100644 index 0000000..b51f9e0 --- /dev/null +++ b/app/src/vl/svc/VL_SUSPTRIAGE.pgc @@ -0,0 +1,31 @@ +/* + * VL_SUSPTRIAGE.pgc - vl 모듈 서비스 VL_SUSPTRIAGE (one service = one file). + * 기능: 의심거래 분류 (OPEN 건 심각도 재지정, RC 1=대상없음/OPEN아님 2=심각도오류). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_SUSPTRIAGE.h" + +void VL_SUSPTRIAGE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0, rows; + EXEC SQL BEGIN DECLARE SECTION; + long h_sid; + char h_sev[8]; + EXEC SQL END DECLARE SECTION; + h_sid = getl(b, T_ID1); + gets_(b, T_STR4, h_sev, sizeof(h_sev)); + if (strcmp(h_sev, "INFO") != 0 && strcmp(h_sev, "WARN") != 0 && + strcmp(h_sev, "ERROR") != 0 && strcmp(h_sev, "CRIT") != 0) { + setl(b, T_RC, 2); /* 허용되지 않는 심각도 */ + OK(b); + } + EXEC SQL UPDATE vl_suspect SET severity = :h_sev + WHERE suspect_id = :h_sid AND status = 'OPEN'; + if (sqlca.sqlcode < 0) FAIL(b); + rows = (long)sqlca.sqlerrd[2]; + if (rows == 0) rc = 1; /* OPEN 아님 또는 미존재 */ + setl(b, T_ARG1, rows); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_THRCOMPARE.h b/app/src/vl/svc/VL_THRCOMPARE.h new file mode 100644 index 0000000..8941300 --- /dev/null +++ b/app/src/vl/svc/VL_THRCOMPARE.h @@ -0,0 +1,12 @@ +/* + * VL_THRCOMPARE.h - vl 서비스 VL_THRCOMPARE 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR3,T_ARG1,T_ARG2,T_STR4,T_RC + */ +#ifndef VL_THRCOMPARE_H +#define VL_THRCOMPARE_H + +#include "vl_svc.h" + +void VL_THRCOMPARE(TPSVCINFO *p); + +#endif /* VL_THRCOMPARE_H */ diff --git a/app/src/vl/svc/VL_THRCOMPARE.pgc b/app/src/vl/svc/VL_THRCOMPARE.pgc new file mode 100644 index 0000000..064548d --- /dev/null +++ b/app/src/vl/svc/VL_THRCOMPARE.pgc @@ -0,0 +1,26 @@ +/* + * VL_THRCOMPARE.pgc - vl 모듈 서비스 VL_THRCOMPARE (one service = one file). + * 기능: 임계값 비교 (실측 T_ARG1 vs 규칙 임계값, RC 1=초과 8=규칙없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_THRCOMPARE.h" + +void VL_THRCOMPARE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char rule[32], sev[8]; + long measured, thr = 0, rc = 0; + gets_(b, T_STR3, rule, sizeof(rule)); + if (rule[0] == 0) { userlog("VL_THRCOMPARE: T_STR3(rule_code) 누락"); FAIL(b); } + measured = getl(b, T_ARG1); + sev[0] = 0; + if (vldb_get_rule(rule, &thr, sev) < 0) { + setl(b, T_RC, 8); /* 규칙 없음 */ + OK(b); + } + if (measured > thr) rc = 1; /* 임계 초과 */ + setl(b, T_ARG2, thr); + Bchg(b, T_STR4, 0, sev, 0L); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_THRGET.h b/app/src/vl/svc/VL_THRGET.h new file mode 100644 index 0000000..c72df20 --- /dev/null +++ b/app/src/vl/svc/VL_THRGET.h @@ -0,0 +1,12 @@ +/* + * VL_THRGET.h - vl 서비스 VL_THRGET 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR3,T_ARG1,T_STR4,T_STR5,T_RC + */ +#ifndef VL_THRGET_H +#define VL_THRGET_H + +#include "vl_svc.h" + +void VL_THRGET(TPSVCINFO *p); + +#endif /* VL_THRGET_H */ diff --git a/app/src/vl/svc/VL_THRGET.pgc b/app/src/vl/svc/VL_THRGET.pgc new file mode 100644 index 0000000..2738647 --- /dev/null +++ b/app/src/vl/svc/VL_THRGET.pgc @@ -0,0 +1,26 @@ +/* + * VL_THRGET.pgc - vl 모듈 서비스 VL_THRGET (one service = one file). + * 기능: 임계값 조회 (규칙 임계값+심각도+상태 직접 SELECT, RC 1=규칙 없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_THRGET.h" + +void VL_THRGET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + char h_rule[32], h_sev[8], h_st[16]; + long h_thr; + EXEC SQL END DECLARE SECTION; + gets_(b, T_STR3, h_rule, sizeof(h_rule)); + if (h_rule[0] == 0) { userlog("VL_THRGET: T_STR3(rule_code) 누락"); FAIL(b); } + EXEC SQL SELECT threshold, severity, status INTO :h_thr, :h_sev, :h_st + FROM vl_rule WHERE rule_code = :h_rule; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); } /* 1=규칙 없음 */ + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG1, h_thr); + Bchg(b, T_STR4, 0, h_sev, 0L); + Bchg(b, T_STR5, 0, h_st, 0L); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/vl/svc/VL_THRRESET.h b/app/src/vl/svc/VL_THRRESET.h new file mode 100644 index 0000000..990bddf --- /dev/null +++ b/app/src/vl/svc/VL_THRRESET.h @@ -0,0 +1,12 @@ +/* + * VL_THRRESET.h - vl 서비스 VL_THRRESET 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR3,T_RC + */ +#ifndef VL_THRRESET_H +#define VL_THRRESET_H + +#include "vl_svc.h" + +void VL_THRRESET(TPSVCINFO *p); + +#endif /* VL_THRRESET_H */ diff --git a/app/src/vl/svc/VL_THRRESET.pgc b/app/src/vl/svc/VL_THRRESET.pgc new file mode 100644 index 0000000..c4aa85b --- /dev/null +++ b/app/src/vl/svc/VL_THRRESET.pgc @@ -0,0 +1,23 @@ +/* + * VL_THRRESET.pgc - vl 모듈 서비스 VL_THRRESET (one service = one file). + * 기능: 임계값 초기화 (threshold 0 으로 리셋, RC 1=대상 규칙 없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_THRRESET.h" + +void VL_THRRESET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_rule[32]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_STR3, h_rule, sizeof(h_rule)); + if (h_rule[0] == 0) { userlog("VL_THRRESET: T_STR3(rule_code) 누락"); FAIL(b); } + EXEC SQL UPDATE vl_rule SET threshold = 0, updated_at = now() + WHERE rule_code = :h_rule; + if (sqlca.sqlcode < 0) FAIL(b); + if ((long)sqlca.sqlerrd[2] == 0) rc = 1; /* 대상 규칙 없음 */ + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_THRSCALE.h b/app/src/vl/svc/VL_THRSCALE.h new file mode 100644 index 0000000..1de5fe2 --- /dev/null +++ b/app/src/vl/svc/VL_THRSCALE.h @@ -0,0 +1,12 @@ +/* + * VL_THRSCALE.h - vl 서비스 VL_THRSCALE 카피북 (copybook / record header). + * 사용 UBF 필드: T_STR3,T_ARG1,T_ARG2,T_ARG3,T_RC + */ +#ifndef VL_THRSCALE_H +#define VL_THRSCALE_H + +#include "vl_svc.h" + +void VL_THRSCALE(TPSVCINFO *p); + +#endif /* VL_THRSCALE_H */ diff --git a/app/src/vl/svc/VL_THRSCALE.pgc b/app/src/vl/svc/VL_THRSCALE.pgc new file mode 100644 index 0000000..bbfd851 --- /dev/null +++ b/app/src/vl/svc/VL_THRSCALE.pgc @@ -0,0 +1,31 @@ +/* + * VL_THRSCALE.pgc - vl 모듈 서비스 VL_THRSCALE (one service = one file). + * 기능: 임계값 배율 조정 (threshold *= T_ARG1%, RC 2=배율오류 3=규칙없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_THRSCALE.h" + +void VL_THRSCALE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long factor; + EXEC SQL BEGIN DECLARE SECTION; + char h_rule[32]; + long h_old, h_new; + EXEC SQL END DECLARE SECTION; + gets_(b, T_STR3, h_rule, sizeof(h_rule)); + if (h_rule[0] == 0) { userlog("VL_THRSCALE: T_STR3(rule_code) 누락"); FAIL(b); } + factor = getl(b, T_ARG1); + if (factor <= 0) { setl(b, T_RC, 2); OK(b); } /* 2=배율 오류 */ + EXEC SQL SELECT threshold INTO :h_old FROM vl_rule WHERE rule_code = :h_rule; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 3); OK(b); } /* 3=규칙 없음 */ + if (sqlca.sqlcode < 0) FAIL(b); + h_new = h_old * factor / 100; + EXEC SQL UPDATE vl_rule SET threshold = :h_new, updated_at = now() + WHERE rule_code = :h_rule; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_ARG2, h_old); + setl(b, T_ARG3, h_new); + setl(b, T_RC, 0); + OK(b); +} diff --git a/app/src/vl/svc/VL_TXNREV.h b/app/src/vl/svc/VL_TXNREV.h new file mode 100644 index 0000000..3c59fc4 --- /dev/null +++ b/app/src/vl/svc/VL_TXNREV.h @@ -0,0 +1,12 @@ +/* + * VL_TXNREV.h - vl 서비스 VL_TXNREV 카피북 (copybook / record header). + * 사용 UBF 필드: T_PURCHASE_ID,T_AMOUNT,T_NET,T_RC + */ +#ifndef VL_TXNREV_H +#define VL_TXNREV_H + +#include "vl_svc.h" + +void VL_TXNREV(TPSVCINFO *p); + +#endif /* VL_TXNREV_H */ diff --git a/app/src/vl/svc/VL_TXNREV.pgc b/app/src/vl/svc/VL_TXNREV.pgc new file mode 100644 index 0000000..7980b74 --- /dev/null +++ b/app/src/vl/svc/VL_TXNREV.pgc @@ -0,0 +1,27 @@ +/* + * VL_TXNREV.pgc - vl 모듈 서비스 VL_TXNREV (one service = one file). + * 기능: 역전거래 검출 건별 (RC 비트 1=음수금액 2=음수순액 4=순액>금액, 8=매입없음). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_TXNREV.h" + +void VL_TXNREV(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_amt, h_net; + EXEC SQL END DECLARE SECTION; + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL SELECT amount, net INTO :h_amt, :h_net FROM purchase + WHERE purchase_id = :h_pid; + if (sqlca.sqlcode == 100) { setl(b, T_RC, 8); OK(b); } /* 8=매입 없음 */ + if (sqlca.sqlcode < 0) FAIL(b); + if (h_amt < 0) rc |= 1; /* 음수 매입금액 */ + if (h_net < 0) rc |= 2; /* 음수 정산순액 */ + if (h_net > h_amt) rc |= 4; /* 순액이 원금 초과(역전) */ + setl(b, T_AMOUNT, h_amt); + setl(b, T_NET, h_net); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_VELOCITY.h b/app/src/vl/svc/VL_VELOCITY.h new file mode 100644 index 0000000..1970cb9 --- /dev/null +++ b/app/src/vl/svc/VL_VELOCITY.h @@ -0,0 +1,12 @@ +/* + * VL_VELOCITY.h - vl 서비스 VL_VELOCITY 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_ARG1,T_COUNT,T_RC + */ +#ifndef VL_VELOCITY_H +#define VL_VELOCITY_H + +#include "vl_svc.h" + +void VL_VELOCITY(TPSVCINFO *p); + +#endif /* VL_VELOCITY_H */ diff --git a/app/src/vl/svc/VL_VELOCITY.pgc b/app/src/vl/svc/VL_VELOCITY.pgc new file mode 100644 index 0000000..083f603 --- /dev/null +++ b/app/src/vl/svc/VL_VELOCITY.pgc @@ -0,0 +1,29 @@ +/* + * VL_VELOCITY.pgc - vl 모듈 서비스 VL_VELOCITY (one service = one file). + * 기능: 거래 속도 이상 탐지 (당일 매입 건수>T_ARG1 기본100 가맹점 수, RC 1=존재). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_VELOCITY.h" + +void VL_VELOCITY(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_thr, h_cnt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + h_thr = getl(b, T_ARG1); + if (h_thr <= 0) h_thr = 100; + EXEC SQL SELECT count(*) INTO :h_cnt FROM ( + SELECT merchant_id FROM purchase + WHERE biz_date = :h_bizdate + GROUP BY merchant_id HAVING count(*) > :h_thr) t; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 과다 거래 가맹점 존재 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/svc/VL_ZERONET.h b/app/src/vl/svc/VL_ZERONET.h new file mode 100644 index 0000000..2d1696e --- /dev/null +++ b/app/src/vl/svc/VL_ZERONET.h @@ -0,0 +1,12 @@ +/* + * VL_ZERONET.h - vl 서비스 VL_ZERONET 카피북 (copybook / record header). + * 사용 UBF 필드: T_BIZDATE,T_COUNT,T_RC + */ +#ifndef VL_ZERONET_H +#define VL_ZERONET_H + +#include "vl_svc.h" + +void VL_ZERONET(TPSVCINFO *p); + +#endif /* VL_ZERONET_H */ diff --git a/app/src/vl/svc/VL_ZERONET.pgc b/app/src/vl/svc/VL_ZERONET.pgc new file mode 100644 index 0000000..2392eb9 --- /dev/null +++ b/app/src/vl/svc/VL_ZERONET.pgc @@ -0,0 +1,25 @@ +/* + * VL_ZERONET.pgc - vl 모듈 서비스 VL_ZERONET (one service = one file). + * 기능: 정산순액 소실 검출 (금액>0인데 순액=0 매입 수, RC 1=존재). + * caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); vl_svr가 tpsvrinit에서 tpadvertise 한다. + */ +#include "VL_ZERONET.h" + +void VL_ZERONET(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long rc = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_cnt; + EXEC SQL END DECLARE SECTION; + gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate)); + if (h_bizdate[0] == 0) strcpy(h_bizdate, "2026-07-19"); + EXEC SQL SELECT count(*) INTO :h_cnt FROM purchase + WHERE biz_date = :h_bizdate AND net = 0 AND amount > 0; + if (sqlca.sqlcode < 0) FAIL(b); + if (h_cnt > 0) rc = 1; /* 순액 소실 매입 존재 */ + setl(b, T_COUNT, h_cnt); + setl(b, T_RC, rc); + OK(b); +} diff --git a/app/src/vl/vl_svr.pgc b/app/src/vl/vl_svr.pgc index d36fc38..7693816 100644 --- a/app/src/vl/vl_svr.pgc +++ b/app/src/vl/vl_svr.pgc @@ -2,7 +2,7 @@ * vl_svr.pgc - vl (정합성검증/validation) MODULE SERVER (thin dispatcher). * * The legacy "one module server, many services" pattern: this binary owns NO - * business logic. Each of vl's ~75 online 검증 services now lives in its own file + * business logic. Each of vl's 133 online 검증 services now lives in its own file * (app/src/vl/svc/.pgc) with its own copybook header * (app/src/vl/svc/.h); each SELECTs the target rows, applies a 검증 * branch, and returns an 에러코드 in T_RC. Objects are archived into libvlsvc.a @@ -93,6 +93,66 @@ #include "VL_INSTCHK.h" #include "VL_FXCHK.h" +/* --- vl 정합성검증 확장 서비스 (+58) --- */ +#include "VL_AXPURAPPR.h" +#include "VL_AXPURSETT.h" +#include "VL_AXSETTPAY.h" +#include "VL_AXLEDGDC.h" +#include "VL_DUPPURCH.h" +#include "VL_DUPAPPR.h" +#include "VL_DUPLEDGR.h" +#include "VL_DUPSETTMR.h" +#include "VL_APPRNOPUR.h" +#include "VL_PURNOAPPR.h" +#include "VL_LONGOPEN.h" +#include "VL_TXNREV.h" +#include "VL_SETTLREV.h" +#include "VL_APPRLATE.h" +#include "VL_NEGFEE.h" +#include "VL_FEEHIGH.h" +#include "VL_OVERLIMIT.h" +#include "VL_LIMITPCT.h" +#include "VL_DATEFUT.h" +#include "VL_DATEGAP.h" +#include "VL_ZERONET.h" +#include "VL_RIAPPR.h" +#include "VL_RIPAY.h" +#include "VL_RITAXINV.h" +#include "VL_RICANCEL.h" +#include "VL_RIINSTALL.h" +#include "VL_RIFX.h" +#include "VL_SUMTXNCNT.h" +#include "VL_SUMGROSS.h" +#include "VL_SUMNETCHK.h" +#include "VL_SUMFEECHK.h" +#include "VL_RULEVER.h" +#include "VL_RULERENAME.h" +#include "VL_RULESEV.h" +#include "VL_RULEON.h" +#include "VL_RULECOUNT.h" +#include "VL_RULEFIND.h" +#include "VL_SUSPTRIAGE.h" +#include "VL_SUSPESCAL.h" +#include "VL_SUSPRECHK.h" +#include "VL_SUSPASSIGN.h" +#include "VL_SUSPNOTE.h" +#include "VL_SUSPBULK.h" +#include "VL_SUSPAGEAVG.h" +#include "VL_THRGET.h" +#include "VL_THRSCALE.h" +#include "VL_THRRESET.h" +#include "VL_THRCOMPARE.h" +#include "VL_SPIKEAMT.h" +#include "VL_OUTLIER.h" +#include "VL_VELOCITY.h" +#include "VL_ROUNDAMT.h" +#include "VL_NIGHTTXN.h" +#include "VL_CATSKEW.h" +#include "VL_ISSUERMIX.h" +#include "VL_CHANSPLIT.h" +#include "VL_STATMIX.h" +#include "VL_CANCELRATE.h" + /* ----- advertise table: {service name, function} (fn is extern from its file) */ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"VL_SALES", VL_SALES}, @@ -170,6 +230,64 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = { {"VL_CANCELMIS", VL_CANCELMIS}, {"VL_INSTCHK", VL_INSTCHK}, {"VL_FXCHK", VL_FXCHK}, + {"VL_AXPURAPPR", VL_AXPURAPPR}, + {"VL_AXPURSETT", VL_AXPURSETT}, + {"VL_AXSETTPAY", VL_AXSETTPAY}, + {"VL_AXLEDGDC", VL_AXLEDGDC}, + {"VL_DUPPURCH", VL_DUPPURCH}, + {"VL_DUPAPPR", VL_DUPAPPR}, + {"VL_DUPLEDGR", VL_DUPLEDGR}, + {"VL_DUPSETTMR", VL_DUPSETTMR}, + {"VL_APPRNOPUR", VL_APPRNOPUR}, + {"VL_PURNOAPPR", VL_PURNOAPPR}, + {"VL_LONGOPEN", VL_LONGOPEN}, + {"VL_TXNREV", VL_TXNREV}, + {"VL_SETTLREV", VL_SETTLREV}, + {"VL_APPRLATE", VL_APPRLATE}, + {"VL_NEGFEE", VL_NEGFEE}, + {"VL_FEEHIGH", VL_FEEHIGH}, + {"VL_OVERLIMIT", VL_OVERLIMIT}, + {"VL_LIMITPCT", VL_LIMITPCT}, + {"VL_DATEFUT", VL_DATEFUT}, + {"VL_DATEGAP", VL_DATEGAP}, + {"VL_ZERONET", VL_ZERONET}, + {"VL_RIAPPR", VL_RIAPPR}, + {"VL_RIPAY", VL_RIPAY}, + {"VL_RITAXINV", VL_RITAXINV}, + {"VL_RICANCEL", VL_RICANCEL}, + {"VL_RIINSTALL", VL_RIINSTALL}, + {"VL_RIFX", VL_RIFX}, + {"VL_SUMTXNCNT", VL_SUMTXNCNT}, + {"VL_SUMGROSS", VL_SUMGROSS}, + {"VL_SUMNETCHK", VL_SUMNETCHK}, + {"VL_SUMFEECHK", VL_SUMFEECHK}, + {"VL_RULEVER", VL_RULEVER}, + {"VL_RULERENAME", VL_RULERENAME}, + {"VL_RULESEV", VL_RULESEV}, + {"VL_RULEON", VL_RULEON}, + {"VL_RULECOUNT", VL_RULECOUNT}, + {"VL_RULEFIND", VL_RULEFIND}, + {"VL_SUSPTRIAGE", VL_SUSPTRIAGE}, + {"VL_SUSPESCAL", VL_SUSPESCAL}, + {"VL_SUSPRECHK", VL_SUSPRECHK}, + {"VL_SUSPASSIGN", VL_SUSPASSIGN}, + {"VL_SUSPNOTE", VL_SUSPNOTE}, + {"VL_SUSPBULK", VL_SUSPBULK}, + {"VL_SUSPAGEAVG", VL_SUSPAGEAVG}, + {"VL_THRGET", VL_THRGET}, + {"VL_THRSCALE", VL_THRSCALE}, + {"VL_THRRESET", VL_THRRESET}, + {"VL_THRCOMPARE", VL_THRCOMPARE}, + {"VL_SPIKEAMT", VL_SPIKEAMT}, + {"VL_OUTLIER", VL_OUTLIER}, + {"VL_VELOCITY", VL_VELOCITY}, + {"VL_ROUNDAMT", VL_ROUNDAMT}, + {"VL_NIGHTTXN", VL_NIGHTTXN}, + {"VL_CATSKEW", VL_CATSKEW}, + {"VL_ISSUERMIX", VL_ISSUERMIX}, + {"VL_CHANSPLIT", VL_CHANSPLIT}, + {"VL_STATMIX", VL_STATMIX}, + {"VL_CANCELRATE", VL_CANCELRATE}, {NULL, NULL} }; diff --git a/app/ui/index.html b/app/ui/index.html index 6de5a3f..b2f374c 100644 --- a/app/ui/index.html +++ b/app/ui/index.html @@ -142,6 +142,22 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ALERTAMT", + "desc": "매입 금액초과 알림: 가맹점의 영업일 매입 중 임계금액(T_AMOUNT)을 초과한", + "in": [ + "T_AMOUNT", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMT1", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -158,6 +174,50 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ALERTDUP", + "desc": "매입 중복 알림: 가맹점의 영업일 매입 중 동일 금액이 2건 이상 반복된", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ALERTQRY", + "desc": "매입 알림 유형별 조회: 미매입 원장을 알림 유형(속도/한도/중복) 접두어로", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_COUNT" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ALERTVEL", + "desc": "매입 속도 알림: 채널의 최근 5분간 유입 건수를 재어 분당 허용치를 넘으면", + "in": [ + "T_ARG1", + "T_CHANNEL" + ], + "out": [ + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -210,6 +270,80 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_BRANDQRY", + "desc": "매입 브랜드별 조회: 지정 발급사(T_ISSUER)의 영업일 매입 건수와 총액,", + "in": [ + "T_ISSUER", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_FEE", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_BRANDRANK", + "desc": "매입 브랜드별 순위: 발급사 매출 상위 순으로 커서 조회하며 누적 점유율을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_ARG1", + "T_ARG2" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_BRANDSUM", + "desc": "매입 브랜드별 집계: 발급사(카드 브랜드) 기준으로 매출 합계를 그룹 집계해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_ARG1" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_BULKCHK", + "desc": "대량매입 배치검증: 영업일 접수(A) 매입을 커서로 순회하며 금액 양수 및", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_BULKRPT", + "desc": "대량매입 리포트: 영업일 매입을 상태(접수/대사/정산/취소)별로 한 번에 분해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_GROSS", + "T_NET" + ] + }, { "mod": "ac", "modnm": "매입", @@ -241,6 +375,20 @@ const SVC_SCREENS = [ "T_CANCEL_ID" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CB_ARB", + "desc": "지불거절 중재: 라운드2까지 미해결 건을 카드사 중재로 넘긴다. 해당 CB", + "in": [ + "T_CANCEL_ID", + "T_REASON" + ], + "out": [ + "T_RC", + "T_PURCHASE_ID" + ] + }, { "mod": "ac", "modnm": "매입", @@ -255,6 +403,19 @@ const SVC_SCREENS = [ "T_PURCHASE_ID" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CB_FINAL", + "desc": "지불거절 최종확정: 중재 결과로 매입사 패소가 확정되면 원매입을 취소(C)로", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -285,6 +446,35 @@ const SVC_SCREENS = [ "T_AMOUNT" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CB_REVERSE", + "desc": "지불거절 역전: 방어 승소로 CB 를 무효화한다. 최근 CB/CB2 행을 조회해", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_RC", + "T_CANCEL_ID" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CB_RND2", + "desc": "지불거절 2차(라운드2): 1차 방어 이후 재청구를 접수한다. 원매입에 이미", + "in": [ + "T_PURCHASE_ID", + "T_AMOUNT", + "T_REASON", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_CANCEL_ID" + ] + }, { "mod": "ac", "modnm": "매입", @@ -301,6 +491,32 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CCYQRY", + "desc": "매입 통화별 조회: 지정 통화(T_CCY)의 원통화 합계와 원화 환산 합계를", + "in": [ + "T_CCY" + ], + "out": [ + "T_FX_AMT", + "T_GROSS", + "T_DELTA" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CCYRANK", + "desc": "매입 통화별 순위: 해외매입 통화 그룹을 원화 합계 내림차순으로 커서 조회해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1" + ] + }, { "mod": "ac", "modnm": "매입", @@ -317,6 +533,34 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CHANDETAIL", + "desc": "매입 채널별 상세 조회: 영업일 채널 그룹의 건수/총액/평균을 커서로 훑어", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_COUNT", + "T_ARG1", + "T_AMT2" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CHANRANK", + "desc": "매입 채널별 순위: 영업일 채널을 건수 내림차순으로 정렬해 커서 조회하고", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, { "mod": "ac", "modnm": "매입", @@ -373,6 +617,19 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CORRAPPR", + "desc": "매입 정정 승인: 접수된 조정(ac_fee_adj) 행의 사유 앞에 OK 승인 표식을", + "in": [ + "T_ADJ" + ], + "out": [ + "T_RC", + "T_ADJ" + ] + }, { "mod": "ac", "modnm": "매입", @@ -401,6 +658,66 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CORRRSN", + "desc": "매입 정정 사유별 처리: 수수료 조정 원장(ac_fee_adj)에 사유 코드가 붙은", + "in": [ + "T_PURCHASE_ID", + "T_DELTA", + "T_REASON", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CORRSTAT", + "desc": "매입 정정 사유별 통계: 조정 원장을 사유 접두어별로 그룹 집계해 사유코드·", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_ARG1" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CTRYQRY", + "desc": "매입 국가별 조회: 통화권역 대표 통화(T_CCY)로 해외매입(ac_fx)을 필터해", + "in": [ + "T_CCY", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_ARG1" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CTRYRANK", + "desc": "매입 국가별 순위: 통화코드를 권역(미주/일본/유럽/기타)으로 분류하는 C", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -462,6 +779,21 @@ const SVC_SCREENS = [ "T_AMOUNT" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_DOWSTAT", + "desc": "매입 요일별 통계: 지정 기간(T_STR1~T_STR2)의 매입을 요일(0~6)별로 그룹", + "in": [ + "T_STR1", + "T_STR2" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -520,6 +852,21 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_EDIQRY", + "desc": "EDI 조회: 지정 가맹점(T_MERCHANT)의 영업일 EDI 접수 문서 건수와 금액", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -534,6 +881,153 @@ const SVC_SCREENS = [ "T_AMOUNT" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_EDIRSNDCH", + "desc": "EDI 재전송 채널별: 영업일 미수신 EDI 문서를 가맹점별로 커서 조회해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_EDISTAT", + "desc": "EDI 통계: 영업일 EDI 문서를 금액대(소액/중액/고액)로 나눠 대역별 건수를", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_GROSS" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ERRCLASS", + "desc": "매입 오류유형별 분류: 미매입 원장(ac_unmatch)의 사유 접두어를 읽어", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_COUNT" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ERRQRY", + "desc": "매입 오류유형별 조회: 사유 접두어(T_REASON)로 시작하는 미매입 건을", + "in": [ + "T_REASON" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ERRRETRY", + "desc": "매입 오류 재처리: 보류(H) 상태로 묶여 있던 오류 매입을 접수(A)로 되돌려", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ERRSTAT", + "desc": "매입 오류유형별 통계: 미매입 원장을 사유 접두어 기준으로 한 번의 집계에서", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_COUNT" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ESCROW", + "desc": "매입 에스크로 예치: 분쟁 소지 매입 금액을 정산 전 유보하기 위해 원장에", + "in": [ + "T_PURCHASE_ID", + "T_AMOUNT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_SETTLE_ID" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ESCROWREL", + "desc": "매입 에스크로 해제: 예치(ESCROW)와 해제(ESCROW_REL) 원장 잔액을 계산해", + "in": [ + "T_PURCHASE_ID", + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_EVENTBUY", + "desc": "이벤트 매입: 프로모션 행사 매입을 EVENT 채널로 접수한다. 활성 가맹점만", + "in": [ + "T_AMOUNT", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_PURCHASE_ID", + "T_FEE" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_EXCHANGE", + "desc": "매입 교환 처리: 반품 후 동일가 재구매를 한 번에 처리한다. 원매입에 대해", + "in": [ + "T_PURCHASE_ID", + "T_AMOUNT", + "T_REASON", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_CANCEL_ID" + ] + }, { "mod": "ac", "modnm": "매입", @@ -648,6 +1142,67 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_GRADESTAT", + "desc": "매입 가맹점등급별 통계: 가맹점 MDR(bps)을 프리미엄/일반/기본 등급으로 나눠", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_GROSS" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_GUARANTEE", + "desc": "매입 보증 등록: 대형 매입에 대한 이행보증금을 원장에 GUARANTEE 항목으로", + "in": [ + "T_PURCHASE_ID", + "T_AMOUNT", + "T_ARG1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_AMT1", + "T_SETTLE_ID" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_GUARQRY", + "desc": "매입 보증 조회: 한 매입에 적립된 보증금(GUARANTEE) 총액과 건수를 원장에서", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_AMT1", + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_HALFSTAT", + "desc": "매입 반기별 통계: 지정 연도(T_ARG1) 매입을 상반기(1~6월)/하반기(7~12월)로", + "in": [ + "T_ARG1" + ], + "out": [ + "T_ARG2", + "T_ARG3", + "T_COUNT", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -674,6 +1229,22 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_HOURSTAT", + "desc": "매입 시간대별 통계: 접수시각을 새벽/오전/오후/야간 네 구간으로 나눠 구간별", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -689,6 +1260,64 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_INSTCXL", + "desc": "할부 취소: 한 매입의 할부 스케줄 전 회차를 원장에서 삭제해 할부를 해지한다.", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_INSTPLAN", + "desc": "할부 계획 조회: 한 매입의 약정 개월수와 회차 합계, 회차당 금액을 단건", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_INSTALL_N", + "T_GROSS", + "T_AMT1", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_INSTPREPAY", + "desc": "할부 조기상환: 지정 회차(T_ARG1) 이후 남은 할부금 합계를 계산해 원장에", + "in": [ + "T_PURCHASE_ID", + "T_ARG1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_GROSS", + "T_COUNT" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_INSTQRY", + "desc": "할부 회차조회: 한 매입의 할부 스케줄(ac_install)을 회차 순으로 커서 조회해", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_INSTALL_N", + "T_GROSS", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -730,6 +1359,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_LIMITEXC", + "desc": "매입 한도 예외처리: 가맹점 일한도와 당일 누적 매입을 비교해 한도 초과 시", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_DELTA" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_LIMITOVR", + "desc": "매입 한도 초과 승인: 한도 초과로 보류(H)된 매입을 관리자가 예외 승인해", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -872,6 +1528,51 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_PROMOQRY", + "desc": "프로모션 조회: 영업일 프로모션(category=PROMO) 매입의 건수와 매출, 감면", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_FEE", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_PROMOSTAT", + "desc": "프로모션 통계: 영업일 업종/분류별 매출을 그룹 집계해 분류코드·매출 쌍을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_AMT2", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_QTRSTAT", + "desc": "매입 분기별 통계: 지정 연도(T_ARG1)의 매입 매출을 1~4분기로 나눠 분기별", + "in": [ + "T_ARG1" + ], + "out": [ + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_ARG5", + "T_GROSS" + ] + }, { "mod": "ac", "modnm": "매입", @@ -888,6 +1589,47 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_REBUYAPPR", + "desc": "재매입 승인: 취소(C)된 원매입에 한해 승인 원장(approval)에 재승인 행을", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_RC", + "T_ID1" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_REBUYHIST", + "desc": "재매입 이력: 한 매입의 승인 원장을 승인시각 순으로 커서 조회해 승인 ID를", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_REBUYREJ", + "desc": "재매입 거절: 재매입 요청을 반려하고 미매입 원장에 REBUY_REJ 사유로", + "in": [ + "T_PURCHASE_ID", + "T_REASON", + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -1011,6 +1753,35 @@ const SVC_SCREENS = [ "T_AMOUNT" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_RETURNQRY", + "desc": "매입 반품 조회: 가맹점의 영업일 반품/교환(RETURN·EXCHANGE) 취소 이력을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_RETURNSTAT", + "desc": "매입 반품 통계: 영업일 취소 원장을 반품/교환/환불 유형별로 한 번에 분해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_GROSS" + ] + }, { "mod": "ac", "modnm": "매입", @@ -1026,6 +1797,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_SETTLEEXP", + "desc": "매입 정산 예정 조회: 가맹점의 대사완료(M) 매입 건수와 순정산 예정 총액을", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -1041,6 +1827,22 @@ const SVC_SCREENS = [ "T_SETTLE_ID" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_SETTLEPRE", + "desc": "매입-정산 예정대사: 가맹점의 영업일 대사완료(M) 매입 순정산 예정액과 이미", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_NET", + "T_AMT1", + "T_DELTA", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -1113,6 +1915,36 @@ const SVC_SCREENS = [ "T_TAX" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_TAXMODIFY", + "desc": "세금계산서 수정발행: 기 발행된 계산서(ac_tax_invoice)의 공급가·부가세를", + "in": [ + "T_ID1", + "T_GROSS" + ], + "out": [ + "T_RC", + "T_GROSS", + "T_TAX" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_TAXQRY", + "desc": "세금계산서 조회: 한 매입에 발행된 계산서의 공급가·부가세 누계와 발행", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_GROSS", + "T_TAX", + "T_COUNT", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -1128,6 +1960,20 @@ const SVC_SCREENS = [ "T_TAX" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_TAXREVERSE", + "desc": "세금계산서 역발행: 매출 취소분에 대한 (-)공급가 계산서를 새로 발행한다.", + "in": [ + "T_ID1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID2" + ] + }, { "mod": "ac", "modnm": "매입", @@ -1390,6 +2236,23 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "AUTH_PARTAPPR", + "desc": "부분 승인: 가용한도(일한도-사용액)가 요청액보다 작으면 가용한도만큼만", + "in": [ + "T_KEY1", + "T_MERCHANT", + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_AMT1", + "T_RC", + "T_ID2" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1435,6 +2298,32 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "AUTH_REPROC_REQ", + "desc": "승인 재처리 요청: 거절(D)/보류(H) 상태의 승인을 재처리 대상으로 다시", + "in": [ + "T_ID2" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "AUTH_REPROC_STAT", + "desc": "재처리 현황: 영업일 승인 원장에서 보류(H) 대기 건수와, 갱신시각이 생성시각", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1564,6 +2453,18 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "BLACKLIST_COUNT", + "desc": "블랙리스트 통계: 전체 등재 수, 도난(REPORTED_STOLEN/STOLEN) 등재 수,", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1590,6 +2491,87 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "BLACKLIST_PROMO", + "desc": "블랙 승격 후보 조회: 영업일 부정 적발 점수 합이 1500 이상이면서 아직", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "BRAND_AUTH_STAT", + "desc": "브랜드별 승인 통계: 영업일 승인 원장을 카드 선두자리(브랜드)로 그룹핑해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "BRAND_POLICY", + "desc": "브랜드별 승인 정책: 카드 선두자리로 브랜드(VISA/MC/AMEX/LOCAL)를 판별하고", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CANCEL_AMT_INQ", + "desc": "취소가능액 조회: 원승인 금액에서 이미 취소된 누계를 빼 취소가능 잔액을", + "in": [ + "T_ID2" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_AMT3", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CANCEL_FULL", + "desc": "전액 취소: 승인(A)/매입확정(M) 상태의 원승인을 전액 취소 처리한다. 취소", + "in": [ + "T_ID2", + "T_REASON" + ], + "out": [ + "T_RC", + "T_ID1", + "T_AMT1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CANCEL_HISTORY", + "desc": "승인건 취소 이력: 특정 원승인에 걸린 취소들을 시간순 커서로 순회하며", + "in": [ + "T_ID2" + ], + "out": [ + "T_COUNT", + "T_AMT1" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1619,6 +2601,35 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CANCEL_SUMMARY", + "desc": "일별 취소 집계: 영업일 취소 원장을 전체/전액(FULL)/부분(PARTIAL)로 나눠", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMT1", + "T_ARG1", + "T_ARG2" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CARD_ACTIVATE", + "desc": "카드 신규 활성화: 원장이 없으면 요청 한도(기본 100만원)로 생성하고, 이미", + "in": [ + "T_KEY1", + "T_AMT1" + ], + "out": [ + "T_AMT1", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1638,6 +2649,45 @@ const SVC_SCREENS = [ "T_ARG5" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CARD_EXPIRE_CHK", + "desc": "카드 휴면(만료 근사) 점검: 마지막 승인 이후 경과일을 EPOCH 로 환산해", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CARD_LOST_REG", + "desc": "분실 등록: 카드 원장 상태를 LOST 로 바꾸고 블랙리스트에 LOST 사유로", + "in": [ + "T_KEY1", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CARD_REACTIVATE", + "desc": "카드 재활성화: 정지/동결/분실 상태의 원장을 ACTIVE 로 되돌리고, 분실·토큰", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1664,6 +2714,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CARD_STATE_HIST", + "desc": "카드 승인상태 이력 분포: 카드의 전체 승인 건을 상태(status)별로 집계하는", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1680,6 +2743,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CARD_STOLEN_REG", + "desc": "도난 등록: 원장 상태를 STOLEN 으로 전환하고 블랙리스트를 STOLEN 사유로", + "in": [ + "T_KEY1", + "T_REASON", + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1706,6 +2784,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CHANNEL_POLICY", + "desc": "채널별 승인 정책: 매입 원장에서 당일 해당 채널(POS/EDC/EDI/FOREIGN)의", + "in": [ + "T_CHANNEL" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CHANNEL_STAT", + "desc": "채널별 매입 통계: 영업일 매입을 채널별로 그룹핑하는 커서를 순회하며", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1733,6 +2838,18 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "FRAUD_CASE_CLOSE", + "desc": "부정 케이스 종결: 미종결 부정로그의 사유 뒤에 '/CLOSED' 를 붙여 종결 표시", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1747,6 +2864,18 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "FRAUD_CASE_LIST", + "desc": "부정 케이스 상위목록: 영업일 부정로그를 점수 내림차순 상위 5건 커서로", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1812,6 +2941,66 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "FRAUD_RULE_INTL", + "desc": "고액(해외성) 이상거래 룰: 최근 1일 100만원 초과 승인이 3건 이상이면", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC", + "T_ID1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "FRAUD_RULE_MCC", + "desc": "관찰가맹 이상거래 룰: 카드의 당일 승인 중 상태가 WATCH 인 가맹점 건수를", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC", + "T_ID1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "FRAUD_RULE_NIGHT", + "desc": "심야 이상거래 룰: 최근 3일 심야시간대(23~06시) 승인 시도가 6건 이상이면", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC", + "T_ID1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "FRAUD_SCORE_SUM", + "desc": "카드 위험도 종합: 카드의 부정 적발 건수/점수합/최고점수를 집계해 최고점수", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1828,6 +3017,35 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "KEY_ROTATE_LOG", + "desc": "암호키 회전 로그: 요청 키버전(T_ARG1)의 차기 버전을 산출하고 키식별자", + "in": [ + "T_ARG1", + "T_KEY1" + ], + "out": [ + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_ADJUST", + "desc": "한도 증감 조정: 요청 증감액(T_DELTA)을 일한도에 가감하되 음수 한도가 되지", + "in": [ + "T_KEY1", + "T_DELTA" + ], + "out": [ + "T_RC", + "T_AMT1" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1843,6 +3061,35 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_CUMUL_INQ", + "desc": "전(全)기간 누적 사용 조회: 카드의 전체 승인 이력에서 누적 건수/누적 합계/", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_AMT1", + "T_AMT2" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_DAILY_INQ", + "desc": "일 한도 상세 조회: 원장에서 일한도/사용액/임시한도/상태를 한 번에 읽어", + "in": [ + "T_KEY1" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_AMT3", + "T_AMT4" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1854,6 +3101,31 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_EXHAUST_LST", + "desc": "한도 소진 카드 목록: 요청 기준율(bps, 기본 9000=90%) 이상으로 한도를 소진한", + "in": [ + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_FREEZE", + "desc": "한도 동결: ACTIVE 상태의 카드 원장만 FROZEN 으로 전환한다. 이미 다른", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1883,6 +3155,63 @@ const SVC_SCREENS = [ "T_AMT3" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_MONTH_CHK", + "desc": "월 한도 점검: 이번 달 승인(A/M/S) 누적액에 요청금액을 더해 월 한도", + "in": [ + "T_KEY1", + "T_AMOUNT" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_MONTH_INQ", + "desc": "월 누적 사용 조회: 이번 달 승인건 기준으로 건수/합계/평균단가를 한 번의", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_AMT1", + "T_AMT2" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_OVER_ALERT", + "desc": "한도 소진 임박 알림: 사용액/일한도를 bps 로 환산해 90% 이상이면 경보", + "in": [ + "T_KEY1" + ], + "out": [ + "T_ARG1", + "T_AMT1", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_PERTX_CHK", + "desc": "건별(1회) 한도 점검: 일한도의 20% 를 건별 상한으로 삼아 요청 단건 금액이", + "in": [ + "T_KEY1", + "T_AMOUNT" + ], + "out": [ + "T_AMT1", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1957,6 +3286,30 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_TIER_INQ", + "desc": "한도 등급 조회: 일한도 구간으로 BASIC/SILVER/GOLD/PLATINUM 등급을 매겨", + "in": [ + "T_KEY1" + ], + "out": [ + "T_AMT1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_UNFREEZE", + "desc": "한도 동결 해제: FROZEN 상태의 카드 원장만 ACTIVE 로 되돌린다. 대상이", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1971,6 +3324,32 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "MCC_AUTH_STAT", + "desc": "업종별 매입 금액 통계: 영업일 매입을 업종(category)별 합계로 그룹핑하는", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_GROSS" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "MCC_POLICY", + "desc": "업종(MCC)별 승인 정책: 매입 원장의 당일 업종 실적(건수/합계)을 읽어", + "in": [ + "T_CATEGORY" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2104,6 +3483,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "PREAUTH_PARTUSE", + "desc": "사전승인 부분 사용: 예비승인(P) 잔액에서 사용액을 차감(부분 소진)한다.", + "in": [ + "T_ID2", + "T_AMOUNT" + ], + "out": [ + "T_RC", + "T_AMT1" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2150,6 +3543,36 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "STANDIN_LIMIT", + "desc": "대행 한도 점검: 발급사 미응답 시 대행 승인은 일한도의 절반을 상한으로 두어,", + "in": [ + "T_KEY1", + "T_AMOUNT" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "STANDIN_RECON", + "desc": "대행 정산 대사: 영업일 대행 승인 중 매입확정(captured_at NOT NULL) 건과", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2164,6 +3587,47 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "STANDIN_STAT", + "desc": "대행(stand-in) 승인 통계: 영업일 auth_type='STANDIN' 승인을 대상으로", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_ARG1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "STAT_AMT_BAND", + "desc": "금액대별 승인 통계: 영업일 승인을 1만/5만/10만/50만/그이상 구간(CASE)으로", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "STAT_APPROVE_TR", + "desc": "카드 승인 추세: 최근 7일과 그 직전 7일의 승인 건수를 각각 필터 집계로 뽑아", + "in": [ + "T_KEY1" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2182,6 +3646,35 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "STAT_DAILY_SUM", + "desc": "일별 승인 종합: 영업일 승인 원장의 접수/승인/거절/취소 건수와 승인 합계를", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_GROSS" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "STAT_DECLINE", + "desc": "거절사유별 통계: 영업일 거절(D) 건을 사유별로 그룹핑하는 커서를 순회하며", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2209,6 +3702,42 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "TOKEN_INQ", + "desc": "토큰 상태 조회: 원장 상태와 블랙리스트 등재 여부를 합쳐 토큰의 종합 상태", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "TOKEN_ISSUE", + "desc": "결제 토큰 발급: PAN 의 Luhn 검증 후 앞6·뒤4 자리만 남기고 가운데를 마스킹한", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "TOKEN_REVOKE", + "desc": "토큰 폐기: 카드(토큰 원본 PAN)를 TOKEN_REVOKED 사유로 블랙리스트에 등재해", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2221,6 +3750,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "VELOCITY_AMT", + "desc": "금액 velocity 룰: 직전 1시간 승인 합계에 요청금액을 더해 200만원을 넘으면", + "in": [ + "T_KEY1", + "T_AMOUNT" + ], + "out": [ + "T_GROSS", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "VELOCITY_CHAN", + "desc": "승인유형 velocity 룰: 직전 1시간 카드 승인을 승인유형(auth_type)별로", + "in": [ + "T_KEY1" + ], + "out": [ + "T_ARG1", + "T_COUNT" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2236,6 +3792,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "VELOCITY_DAY", + "desc": "일 velocity 룰: 당일 카드 승인 건수와 합계를 세어 20건 초과 또는 합계", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2250,6 +3820,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "VELOCITY_MCC", + "desc": "가맹점 분산 velocity 룰: 직전 1시간 동안 서로 다른 가맹점 수(카드가 돌린", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2264,6 +3847,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "VELOCITY_RESET", + "desc": "velocity 적발 리셋: 특정 카드의 당일 velocity 계열 부정 적발(reason 접두", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2279,6 +3876,44 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "WHITELIST_ADD", + "desc": "화이트리스트 등록: 카드의 블랙리스트 등재를 삭제하고 원장 상태가 ACTIVE 가", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "WHITELIST_CHK", + "desc": "화이트 여부 판정: 블랙리스트 미등재이면서 원장이 존재하는 카드를 화이트", + "in": [ + "T_KEY1" + ], + "out": [ + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "WHITELIST_DEL", + "desc": "화이트리스트 해제: 카드를 다시 블랙리스트에 (사유와 함께) 등재/갱신해", + "in": [ + "T_KEY1", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2322,6 +3957,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_APPRDELEG", + "desc": "마감 승인 위임: 위임 대상자(T_STR1)를 memo(APPR:DELEG:..)에 기록하고 상태 유지.", + "in": [ + "T_ID1", + "T_STR1" + ], + "out": [ + "T_ID1", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_APPRFINAL", + "desc": "최종 마감 승인: 2차 승인(APPR:L2) 마감을 최종 승인(status=A)으로 확정.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1", + "T_ARG1" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2337,6 +3999,32 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_APPRL1", + "desc": "1차 마감 승인: 개시(O)/완료(C) 마감을 1차 승인(memo=APPR:L1) 단계로 전이.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1", + "T_ARG1" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_APPRL2", + "desc": "2차 마감 승인: 1차 승인(APPR:L1) 마감을 2차 승인(APPR:L2)으로 전이.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1", + "T_ARG1" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2373,6 +4061,31 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_APPRSTAT", + "desc": "마감 승인 현황: 승인 단계별(L1/L2/최종A) 마감 건수를 집계한다.", + "in": [], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_COUNT" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_APPRWD", + "desc": "마감 승인 회수: 승인(A) 마감을 완료(C) 상태로 되돌리고 승인 이력 초기화.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2434,6 +4147,76 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CHKALL", + "desc": "마감 전 통합 체크: 미마감(M)/미매칭/보류(H)/void(V) 합산 점수 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CHKLIMIT", + "desc": "마감 전 한도 체크: 가맹점 일한도(daily_limit) 초과 매입 존재 여부 판정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CHKPENDING", + "desc": "마감 전 보류 체크: 당일 보류(H) 매입 존재 여부 판정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CHKUNMATCH", + "desc": "마감 전 미매칭 체크: 당일 미매칭(ac_unmatch) 존재 여부 판정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CHKVOID", + "desc": "마감 전 void 체크: 당일 void(V) 매입 잔존 여부 판정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2480,6 +4263,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CNTVERIFY", + "desc": "건수 정합검증: 마감이력 txn_count 와 해당 영업일 매입 실건수를 대사한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ARG1", + "T_COUNT", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2518,6 +4316,61 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CORPVERIFY", + "desc": "법인별 마감 검증: 마감이력 헤더 건수/금액과 스냅샷 집계 정합 점검.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_AMT1", + "T_AMT2", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CXLDUP", + "desc": "중복마감 취소: 동일 기간 중복 마감이력 중 최신을 남기고 취소(status=X) 처리.", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CXLERROR", + "desc": "오류정정 취소: 오류정정 사유로 마감을 취소(status=X)한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CXLLATE", + "desc": "지연자료 취소: 지연자료 반영 사유로 마감을 취소(status=X)한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2530,6 +4383,33 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CXLREQ", + "desc": "마감 취소 요청: 취소 사유(T_REASON)를 memo(CXLREQ:..)에 기록해 요청 표기(상태 유지).", + "in": [ + "T_ID1", + "T_REASON" + ], + "out": [ + "T_ID1", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CXLSTAT", + "desc": "마감 취소 현황: 취소 사유별(오류정정/지연자료/중복마감) 취소 건수를 집계한다.", + "in": [], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_COUNT" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2542,6 +4422,23 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_DAYVERIFY", + "desc": "일 마감 검증: 당일 마감이력 집계와 매입 실집계 대사.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2594,6 +4491,19 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_DUELIST", + "desc": "마감 예정 목록: 기준일 이전 14일 내 영업일 중 DAILY 마감이 없는 미마감 대상일을 나열.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2620,6 +4530,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_FEECROSS", + "desc": "수수료 크로스검증: 마감 스냅샷 fee 합계와 해당 영업일 매입 fee 합계를 대사한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2664,6 +4589,22 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_FXCLOSE", + "desc": "해외(FOREIGN) 채널 마감: 해외 매입 집계 + ac_fx 원화환산 합계 후 FX 마감.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_COUNT", + "T_GROSS", + "T_AMT1", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2692,6 +4633,19 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_HOLICHK", + "desc": "휴일 마감 판정: 기준일의 요일(dow)로 주말 여부를 보고 마감정책(정상/이월)을 결정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2705,6 +4659,37 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_HYCLOSE", + "desc": "반기 마감: 상/하반기 6개월 매입 집계 후 HALFYR 마감이력 생성.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_ARG1", + "T_COUNT", + "T_GROSS" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_HYVERIFY", + "desc": "반기 마감 검증: 반기 마감이력과 반기 매입 실집계 대사.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2718,6 +4703,22 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_ISSCLOSE", + "desc": "발급사별 마감: 발급사(issuer) 단위 매입 집계 후 ISS 마감.", + "in": [ + "T_BIZDATE", + "T_ISSUER" + ], + "out": [ + "T_ID1", + "T_COUNT", + "T_GROSS", + "T_FEE" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2755,6 +4756,20 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_MAINCLOSE", + "desc": "본마감: 가마감(O) 이력을 당일 전체 매입으로 재집계해 확정(C)으로 승격.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1", + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2786,6 +4801,20 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_MERXCHECK", + "desc": "가맹점별 크로스검증: 마감 스냅샷 gross 와 매입 amount 를 가맹점 단위로 대사해", + "in": [ + "T_ID1" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2812,6 +4841,38 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_MONVERIFY", + "desc": "월 마감 검증: 월 마감이력과 월 매입 실집계 대사.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_COUNT", + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_NETCROSS", + "desc": "정산액 크로스검증: 마감 스냅샷 net 합계와 해당 영업일 매입 net 합계를 대사한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2826,6 +4887,18 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_NEXTBIZ", + "desc": "익영업일 산출: 기준일 포함 이후 주말을 건너뛴 첫 영업일과 건너뛴 일수를 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2853,6 +4926,22 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_POSCLOSE", + "desc": "POS 채널 마감: POS 채널 당일 매입 집계 후 POS 마감이력.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_COUNT", + "T_GROSS", + "T_FEE", + "T_NET" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2893,6 +4982,21 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_PROVCMP", + "desc": "가마감/본마감 비교: 동일 영업일의 가마감(TENT) 잠정 집계와 확정(DAILY C) 차이 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2905,6 +5009,21 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_QTRVERIFY", + "desc": "분기 마감 검증: 분기 마감이력과 분기 매입 실집계 대사.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2931,6 +5050,33 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RECLOSECHK", + "desc": "재마감 가능여부: 승인(A)/잠금(L) 상태면 재마감 불가로 판정한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RECLOSEREG", + "desc": "재마감 등록: 기존 마감을 재마감(R) 표기하고 동일 기간에 신규 마감이력 개시.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1", + "T_ID2", + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2994,6 +5140,17 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RPTAPPR", + "desc": "승인 마감 리포트: 승인(A) 상태 마감이력을 최신순으로 나열한다.", + "in": [], + "out": [ + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3020,6 +5177,42 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RPTCXL", + "desc": "취소 마감 리포트: 취소(X) 상태 마감이력을 취소 사유(memo)와 함께 나열한다.", + "in": [], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RPTDELAY", + "desc": "지연 마감 리포트: 기준일 이전인데 아직 대사완료(M) 상태로 남은 미마감 매입을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG2" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RPTDIV", + "desc": "부문별 마감 리포트: 영업일 매입을 업종(category) 부문으로 집계해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3033,6 +5226,29 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RPTPERIOD", + "desc": "기간별 마감 리포트: close_type(T_KEY1) 의 최근 마감이력을 기간별로 나열한다.", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_GROSS" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RPTSTATUS", + "desc": "상태별 마감 리포트: 마감이력을 상태(O/C/A/L/X/R)별 건수/총액으로 집계한다.", + "in": [], + "out": [ + "T_COUNT" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3046,6 +5262,16 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_SCHEDNEXT", + "desc": "다음 마감 일정: 기준일 다음날부터 주말(토/일)을 건너뛴 첫 영업일을 마감 예정일로 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [] + }, { "mod": "cl", "modnm": "마감", @@ -3057,6 +5283,52 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_SEGCLOSE", + "desc": "부문별 마감: 업종(category) 부문 단위 매입 집계 후 SEG 마감.", + "in": [ + "T_BIZDATE", + "T_CATEGORY" + ], + "out": [ + "T_ID1", + "T_COUNT", + "T_GROSS" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_SEGVERIFY", + "desc": "부문별 마감 검증: 부문 마감이력 총액과 해당 부문 매입 집계 대사.", + "in": [ + "T_ID1", + "T_CATEGORY" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_SEMIMONTH", + "desc": "반월 마감: 전반(1~15)/후반(16~말) 구간 집계 후 SEMIMON 마감.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_ARG1", + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3081,6 +5353,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_SLABREACH", + "desc": "마감 SLA 위반: 기준일 이전인데 아직 개시(O)에 머무는 미완결 마감 건을 위반으로 판정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3096,6 +5381,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_SLASET", + "desc": "마감 SLA 등록: close_type(T_KEY1)별 마감 목표시간(T_ARG1, 시간)을 seq_reg 에 upsert.", + "in": [ + "T_KEY1", + "T_ARG1" + ], + "out": [ + "T_ARG1" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3112,6 +5410,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_SNAPMER", + "desc": "스냅샷 가맹점 비교: 두 마감(T_ID1,T_ID2) 스냅샷을 가맹점별 gross 로 FULL JOIN 해", + "in": [ + "T_ID1", + "T_ID2" + ], + "out": [ + "T_COUNT", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3165,6 +5478,41 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_STCHGAUDIT", + "desc": "마감 상태변경 감사: 특정 기간(period_key) 마감이력의 상태 분포를 상태별로 집계해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_ARG5", + "T_ARG6", + "T_COUNT" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_STCROSS", + "desc": "상태 크로스검증: 당일 매입 상태 분포(A/M/S/C)를 집계해 미마감(A+M) 잔량으로", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_COUNT", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3179,6 +5527,35 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_TENDAY", + "desc": "순(10일) 마감: 상/중/하순 구간 매입 집계 후 TENDAY 마감이력 생성.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_ARG1", + "T_COUNT", + "T_GROSS" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_TENTCLOSE", + "desc": "가마감(잠정): 당일 접수/대사(A,M) 매입 잠정 집계로 가마감(status=O) 이력 생성.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3195,6 +5572,33 @@ const SVC_SCREENS = [ "T_AMT3" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_TRIGRPT", + "desc": "마감 후 리포트 트리거: 마감완료(C)이나 리포트 미생성(memo!=RPT:*)인 마감을 대상으로", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_TRIGSETTLE", + "desc": "마감 후 정산 트리거: 마감 완료 당일 정산완료(S) 대상 건수/정산액을 산출하고", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3248,6 +5652,35 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_WEEKLY", + "desc": "주 마감: 주간(월~일) 매입을 집계해 WEEKLY 마감이력 생성.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_COUNT", + "T_GROSS" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_WEEKVERIFY", + "desc": "주 마감 검증: 주간 마감이력과 주간 매입 실집계 대사.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3275,6 +5708,23 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_YEARVERIFY", + "desc": "연 마감 검증: 연간 마감이력과 연간 매입 실집계 대사.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_COUNT", + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3288,6 +5738,21 @@ const SVC_SCREENS = [ "T_STR3" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_ADLER32", + "desc": "Adler-32 체크섬(순수 계산): 입력(T_STR1)에 대해 두 누산기 a(초기 1)와", + "in": [ + "T_STR1" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_STR4" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3359,6 +5824,22 @@ const SVC_SCREENS = [ "T_ID2" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_AUDIT_ERRPCT", + "desc": "감사로그 오류 비율: dbio(cmdb_log_err_ratio)로 기준일 오류(E) 로그 비율(bps)", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_ARG2", + "T_ARG3", + "T_STATUS", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3375,6 +5856,65 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_AUDIT_TAIL", + "desc": "감사로그 최근 N건: 기준일(T_BIZDATE)에서 최근 로그 N개(T_ARG1, 기본 5,", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ID1", + "T_STATUS" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_AUDIT_TOP", + "desc": "감사로그 최다 서비스: 기준일(T_BIZDATE) 공통로그를 서비스명으로 그룹핑해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_SVCNAME", + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_BATDEP_CHK", + "desc": "배치 선행 완료 점검: 잡(T_KEY1)의 선행 잡(DEP:)을 찾아, 기준일", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_STR4", + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_BATDEP_SET", + "desc": "배치 의존성 등록: 잡(T_KEY1)의 선행 잡(T_STR1)을 DEP: 파라미터에", + "in": [ + "T_KEY1", + "T_STR1", + "T_BIZDATE" + ], + "out": [ + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3392,6 +5932,21 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_BATLAST", + "desc": "배치 최근 실행 조회: 잡(T_KEY1)의 완료 마커(svc=BATRUN, \" ...\")를", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_ID1", + "T_MSG" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3408,6 +5963,20 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_BATSCHD_DEL", + "desc": "배치 스케줄 삭제: 잡(T_KEY1)의 스케줄 파라미터(SCHD:)와 딸린 의존성", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3422,6 +5991,20 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_BATSCHD_LIST", + "desc": "배치 스케줄 목록 집계: 등록된 스케줄(SCHD:%) 중 주기별(DAILY/WEEKLY/MONTHLY)", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3447,6 +6030,21 @@ const SVC_SCREENS = [ "T_BIZDATE" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_BIZDAY_ADD", + "desc": "N영업일 가산: 기준일(T_BIZDATE)에서 N(T_ARG1)영업일 뒤 날짜를 구한다.", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_ARG2" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3544,6 +6142,21 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODEGRP_DIFF", + "desc": "코드그룹 차집합 비교: 기준 그룹(T_KEY1)에는 있으나 대상 그룹(T_KEY2)에는", + "in": [ + "T_KEY1", + "T_KEY2" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3574,6 +6187,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODEGRP_SPAN", + "desc": "코드그룹 정렬범위 조회: 그룹(T_KEY1) 내 sort_ord 의 최대-최소 폭을 dbio", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC", + "T_ARG1", + "T_COUNT", + "T_ARG2" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3591,6 +6219,21 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODEGRP_VER", + "desc": "코드그룹 버전 관리: 코드그룹의 형상 버전을 VER: 파라미터로 유지한다.", + "in": [ + "T_KEY1", + "T_ARG1", + "T_BIZDATE" + ], + "out": [ + "T_ARG2", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3604,6 +6247,77 @@ const SVC_SCREENS = [ "T_STR3" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODE_EXPORT", + "desc": "코드그룹 직렬화: 그룹(T_KEY1)의 사용중 코드를 정렬순서대로 커서 순회하며", + "in": [ + "T_KEY1" + ], + "out": [ + "T_STR4", + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODE_MOVE", + "desc": "코드 그룹 이동: 특정 코드(T_KEY2)를 원 그룹(T_KEY1)에서 대상 그룹(T_STR1)으로", + "in": [ + "T_KEY1", + "T_KEY2", + "T_STR1", + "T_BIZDATE" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODE_REORDER", + "desc": "코드 정렬순서 재부여: 대상 그룹(T_KEY1)의 사용중 코드를 코드값 오름차순으로", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODE_SEARCH", + "desc": "코드명 검색: 입력어(T_STR1)를 코드명 부분일치 패턴(%검색어%)으로 만들어", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_STR4" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODE_TOGGLE", + "desc": "코드 사용여부 토글: 대상 코드(그룹 T_KEY1 / 코드 T_KEY2)의 use_yn 를 현재", + "in": [ + "T_KEY1", + "T_KEY2" + ], + "out": [ + "T_RC", + "T_STATUS" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3632,6 +6346,22 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_DAYOFYEAR", + "desc": "연중 경과일/잔여일(순수 계산): 입력일(T_STR1)이 그 해의 몇 번째 날인지", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3777,6 +6507,53 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_FX_INVERT", + "desc": "역환율 산출: 통화(T_CCY)의 고시환율(rate_bps = KRW*100/unit)을 조회해", + "in": [ + "T_CCY", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ARG1", + "T_ARG2" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_FX_MARGIN", + "desc": "매매기준율 스프레드(순수 계산): 고시환율(T_ARG1, bps)에 스프레드율", + "in": [ + "T_ARG1", + "T_ARG2" + ], + "out": [ + "T_RC", + "T_ARG3", + "T_ARG4", + "T_DELTA" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_FX_MEAN", + "desc": "기준일 평균환율: dbio(cmdb_fx_avg)로 기준일 전체 통화의 평균환율을 구하고,", + "in": [ + "T_BIZDATE", + "T_CCY" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3791,6 +6568,57 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_FX_TICKS", + "desc": "환율 고시 현황: 기준일(T_BIZDATE)에 고시된 통화 수와 CCY 코드그룹의 등록", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2", + "T_ARG3" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_FX_TREND", + "desc": "환율 변동률: 통화(T_CCY)의 이전일(T_STR1)과 기준일(T_BIZDATE) 고시환율을", + "in": [ + "T_CCY", + "T_STR1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ARG1", + "T_ARG2", + "T_DELTA", + "T_ARG3", + "T_STATUS" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_FX_TRIANGLE", + "desc": "삼각환율 정합성: 원화 기준 두 통화(T_CCY=base, T_STR1=quote)의 고시환율로", + "in": [ + "T_CCY", + "T_STR1", + "T_ARG1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ARG2", + "T_ARG3" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3807,6 +6635,64 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HASH_DJB2", + "desc": "djb2 해시(순수 계산): 입력 문자열(T_STR1)에 대해 hash = hash*33 + c 점화식", + "in": [ + "T_STR1" + ], + "out": [ + "T_ARG1", + "T_STR4", + "T_COUNT" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HASH_FNV", + "desc": "FNV-1a 해시(순수 계산): 입력(T_STR1)을 오프셋 베이시스(2166136261)에서", + "in": [ + "T_STR1" + ], + "out": [ + "T_ARG1", + "T_STR4", + "T_ARG2" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HASH_SUM", + "desc": "가중 체크섬(순수 계산): 입력(T_STR1) 각 문자의 코드값에 위치 가중(1부터", + "in": [ + "T_STR1" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_STR4" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HEALTH_ALL", + "desc": "종합 헬스 스코어: 기준일 로그/환율/채번/파라미터 존재를 각 25점으로 가중해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_STATUS", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3821,6 +6707,22 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HEALTH_FX", + "desc": "시스템 헬스(환율 신선도): 기준일(T_BIZDATE)에 고시된 통화가 하나라도 있는지,", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_STATUS", + "T_COUNT", + "T_ARG2", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3836,6 +6738,20 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HEALTH_PARAM", + "desc": "필수 파라미터 점검: 운영에 반드시 필요한 키 목록(BIZ_DATE/VAT_RATE/WHT_BPS", + "in": [], + "out": [ + "T_ARG1", + "T_ARG2", + "T_STR4", + "T_STATUS", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3891,6 +6807,22 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HOLIDAY_NEXT", + "desc": "다음 휴일 조회: 기준일(T_BIZDATE) 이후 국가(T_STR1, 기본 KR)의 가장 가까운", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_MSG", + "T_ARG1" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3905,6 +6837,22 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HOLIDAY_YEAR", + "desc": "연도별 휴일 집계: 연도(T_ARG1)와 국가(T_STR1, 기본 KR)의 등록 휴일 수와", + "in": [ + "T_ARG1", + "T_STR1" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_ARG2", + "T_ARG3" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3919,6 +6867,21 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_ISO_WEEK", + "desc": "ISO-8601 주차 산출(순수 계산): 입력일(T_STR1)의 통일된 일련일수를 구해", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_ARG1", + "T_ARG2", + "T_ARG3" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3957,6 +6920,20 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MASK_ACCT", + "desc": "계좌번호 마스킹(순수 계산): 하이픈 구분자는 위치를 보존하면서, 숫자 기준", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_ARG1" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3971,6 +6948,20 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MASK_EMAIL", + "desc": "이메일 마스킹(순수 계산): 로컬파트(@ 앞)의 첫 글자만 남기고 나머지를 '*' 로", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_ARG1" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3999,6 +6990,36 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MASK_RRN", + "desc": "주민등록번호 마스킹(순수 계산): \"YYMMDD-SXXXXXX\" 13자리 형식을 검증하고,", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_ARG1" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MKT_STATUS", + "desc": "시장 개장 여부: 대상일(T_BIZDATE)이 국가(T_STR1, 기본 KR) 기준 개장일인지", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_STATUS", + "T_STR4", + "T_ARG1", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4012,6 +7033,21 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MSG_DEL", + "desc": "다국어 메시지 폐기: 언어(T_STR1)와 메시지 ID(T_KEY1)로 MSG_ 그룹의", + "in": [ + "T_STR1", + "T_KEY1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_COUNT" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4026,6 +7062,22 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MSG_FORMAT", + "desc": "템플릿 치환(순수 계산): 본문(T_STR1)의 자리표시자 {0}{1}{2} 를 인자", + "in": [ + "T_STR1", + "T_STR2", + "T_STR3", + "T_STR4" + ], + "out": [ + "T_MSG", + "T_COUNT" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4041,6 +7093,35 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MSG_LANGS", + "desc": "메시지 다국어 커버리지: 특정 메시지 ID(T_KEY1)가 어떤 언어 그룹(MSG_%)에", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_STR4" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MSG_RENDER", + "desc": "다국어 메시지 렌더: DB 에서 언어별 템플릿(MSG_/ID)을 조회한 뒤 본문의", + "in": [ + "T_STR1", + "T_KEY1", + "T_STR3", + "T_STR4" + ], + "out": [ + "T_RC", + "T_MSG" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4096,6 +7177,38 @@ const SVC_SCREENS = [ "T_MSG" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_NOTI_QUOTA", + "desc": "알림 발송 한도 점검: 채널(T_CHANNEL)의 기준일 발송 건수를 세고, 파라미터", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_ARG2", + "T_COUNT", + "T_ARG3", + "T_RC" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_NOTI_RETRY", + "desc": "알림 재발송: 원 발송 로그 ID(T_ID1)의 내용을 조회해 동일 본문을 재큐잉", + "in": [ + "T_ID1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID2", + "T_MSG" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4112,6 +7225,40 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_NOTI_STAT", + "desc": "알림 채널별 통계: 기준일(T_BIZDATE) NOTI 로그를 본문 접두([SMS>/[EMAIL>/", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_COUNT" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_NOTI_TMPL", + "desc": "템플릿 알림 발송: 언어(T_STR2)/템플릿ID(T_KEY1)로 DB 템플릿을 조회해 {0} 을", + "in": [ + "T_STR2", + "T_KEY1", + "T_STR3", + "T_CHANNEL", + "T_STR1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1", + "T_MSG" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4139,6 +7286,81 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_PARAM_CANCEL", + "desc": "파라미터 변경요청 철회: 대기 중인 요청 키(REQ:)를 승인 전에 삭제한다.", + "in": [ + "T_STR1", + "T_STR3", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_STR4" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_PARAM_DIFF", + "desc": "파라미터 요청-현재 비교: 대기 요청 값(REQ:)과 현재 본 값을 읽어와", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_STR5", + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_PARAM_HIST", + "desc": "파라미터 변경 이력 조회: 승인 워크플로가 남긴 공통로그(svc=PARAMWF)에서", + "in": [ + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_ID1", + "T_MSG" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_PARAM_LIST", + "desc": "파라미터 접두 목록: 접두어(T_STR1, 예 \"SCHD:\")로 시작하는 파라미터 키의", + "in": [ + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_PARAM_NUM", + "desc": "숫자 파라미터 조회+범위검증: 키(T_STR1)의 값을 정수로 파싱하고, 호출자가", + "in": [ + "T_STR1", + "T_ARG1", + "T_ARG2" + ], + "out": [ + "T_RC", + "T_ARG3", + "T_ARG4" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4231,6 +7453,20 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_QTR_END", + "desc": "분기말일 산출(순수 계산): 입력일(T_STR1)이 속한 분기(1~4)를 판정하고 그", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_ARG1" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4243,6 +7479,22 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_SEQ_ALLOC", + "desc": "채번 범위 예약: 카운터(T_KEY1)의 seq_val 을 한 번의 UPDATE 로 N(T_ARG1)만큼", + "in": [ + "T_KEY1", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ID1", + "T_ID2", + "T_COUNT" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4257,6 +7509,22 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_SEQ_MULTI", + "desc": "연속 채번(블록): 카운터(T_KEY1)에서 N개(T_ARG1)의 일련번호를 연속 채번해", + "in": [ + "T_KEY1", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ID1", + "T_ID2", + "T_COUNT" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4286,6 +7554,21 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_SEQ_STAMP", + "desc": "일자형 채번: 유형(T_KEY1)과 영업일(T_BIZDATE)을 결합한 카운터명", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ARG1", + "T_STR4" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4300,6 +7583,20 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_SEQ_VERIFY", + "desc": "채번 체크디짓 검증(순수 계산): 발번 문자열(T_STR1)의 마지막 자리를 체크디짓", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_ARG1", + "T_ARG2" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4312,6 +7609,39 @@ const SVC_SCREENS = [ "T_STATUS" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_STR_PAD", + "desc": "문자열 패딩(순수 계산): 입력(T_STR1)을 목표 폭(T_ARG1)까지 채운다. 방향", + "in": [ + "T_STR1", + "T_STR2", + "T_KEY1", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2", + "T_STR4", + "T_ARG1" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_STR_TRIM", + "desc": "문자열 정규화(순수 계산): 입력(T_STR1)의 양끝 공백/탭을 제거하고, 내부의", + "in": [ + "T_STR1" + ], + "out": [ + "T_STR4", + "T_ARG1", + "T_ARG2", + "T_ARG3" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4326,6 +7656,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ACCRUEDPAY", + "desc": "미지급비용 계상: 미지급수수료비용(D)/미지급금(C)을 균형 계상.", + "in": [ + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ACCRUEDRECV", + "desc": "미수수익 계상: 현재 RECV 잔액을 참조해 미수수익(D)/이자수익(C)을 계상.", + "in": [ + "T_BIZDATE", + "T_COUNT" + ], + "out": [ + "T_NET", + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4339,6 +7696,21 @@ const SVC_SCREENS = [ "T_ID2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ACCTGROUP", + "desc": "계정그룹별 집계: 계정과목 접두어(prefix)로 묶어 그룹별 차대 합계를 순회.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMT2", + "T_AMT3", + "T_DELTA" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4374,6 +7746,22 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ACCTPIVOT", + "desc": "계정x일자 피벗 잔액: 기간 내 일자별 순액을 누적해 누적잔액과 최고점을 산출.", + "in": [ + "T_KEY1", + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_NET", + "T_AMT2" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4404,6 +7792,20 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ACCTTOPN", + "desc": "상위 N 계정 잔액: 계정별 순액 절대값 내림차순으로 상위 N개를 훑어 1위를 반환.", + "in": [ + "T_COUNT", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4437,6 +7839,36 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ALLOWANCE", + "desc": "대손충당금 설정: RECV 잔액의 일정률을 대손상각비(D)/대손충당금(C)으로 설정.", + "in": [ + "T_BIZDATE", + "T_COUNT" + ], + "out": [ + "T_NET", + "T_ID1", + "T_AMT2" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ALLOWRECALC", + "desc": "충당금 재산정: 필요 충당금(RECV x 율)과 현재 충당금 잔액 차이를 조정 전기.", + "in": [ + "T_BIZDATE", + "T_COUNT" + ], + "out": [ + "T_DELTA", + "T_ID1", + "T_AMT2" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4448,6 +7880,32 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_APPRDELEG", + "desc": "승인 위임: 작성(D) 전표의 승인 권한을 대리인에게 위임(메모 누적).", + "in": [ + "T_ID1", + "T_STR1" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_APPRHIST", + "desc": "승인 이력 조회: 당일 전기(P) 완료된 전표 수와 차변 총액을 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMT2" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4475,6 +7933,20 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_AUDITSCAN", + "desc": "원장 감사 스캔: 취소(X) 전표인데 라인이 여전히 posted 인 불일치와 고아 라인을 탐지.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_COUNT" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4505,6 +7977,21 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_BALCERT", + "desc": "잔액증명서 발급: 계정 차변/대변을 재계산해 계정잔액에 upsert 하고 잔액을 증명.", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4534,6 +8021,19 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_BATCHAPPROVE", + "desc": "일괄 승인: 당일 작성(D) 전표를 순회하며 차대 균형이 맞는 것만 전기(P) 승인.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4551,6 +8051,22 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_BLACKADJUST", + "desc": "흑색 정정분개: 과소계상분을 양수(흑색) 분개 쌍으로 추가 보정.", + "in": [ + "T_KEY1", + "T_BIZDATE", + "T_ID1", + "T_DELTA" + ], + "out": [ + "T_ID2", + "T_DELTA" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4567,6 +8083,20 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_BSSUMMARY", + "desc": "재무상태 요약: 자산(RECV/CASH/PREPAID) - 부채(MERCH_PAY/UNEARNED_REV) = 자본.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4595,6 +8125,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_CLOSELIST", + "desc": "결산 체크리스트: 미결전표/시산표 불균형/무분개 전표를 집계해 마감 준비도를 판정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4619,6 +8164,34 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_CORPLEDGER", + "desc": "법인(가맹점)별 원장 집계: 가맹점 원장 라인의 차변/대변 잔액을 산출.", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_NET" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_CORPSUM", + "desc": "법인별 순액 집계: 가맹점별 (차변-대변) 순액의 절대값 합과 법인 수를 순회.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4630,6 +8203,22 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_CORRPAIR", + "desc": "정정 짝분개: 잘못 분류된 계정을 올바른 계정으로 재분류(D 신계정/C 구계정).", + "in": [ + "T_KEY1", + "T_KEY2", + "T_BIZDATE", + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4683,6 +8272,22 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_DEPTLEDGER", + "desc": "부문(전표유형)별 원장 조회: 유형별 차변/대변 합계와 분개 건수를 집계.", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_DELTA", + "T_COUNT" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4697,6 +8302,36 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_DEPTRANGE", + "desc": "부문 기간 금액범위: 전표유형의 기간 내 분개 최소/최대 금액과 건수를 조회.", + "in": [ + "T_KEY1", + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_COUNT", + "T_DELTA" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_DEPTSUM", + "desc": "부문별 차변 합계 최대 유형 탐색: 전표유형별 차변합을 순회해 최대 부문을 선별.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4744,6 +8379,33 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_EQUITYMOVE", + "desc": "자본 변동: 기간 내 이익잉여금(RETAINED) 대변순액 변동을 집계.", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_NET", + "T_COUNT" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_EXPENSEROLL", + "desc": "비용 롤업: 마감일까지 비용계정(_EXP)별 차변순액을 순회해 총비용을 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4802,6 +8464,33 @@ const SVC_SCREENS = [ "T_AMT3" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_HALFCLOSE", + "desc": "반기 결산 분개: 반기 FEE_INC 순액을 이익잉여금(RETAINED)으로 균형 대체.", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_NET", + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_INCOMEROLL", + "desc": "수익 롤업: 마감일까지 수익계정(_INC)별 대변순액을 순회해 총수익을 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4824,6 +8513,20 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_LEDGSETTLE", + "desc": "원장-정산 대사: base 원장(ledger) SETTLE 합계와 원장라인 지급 합계를 대조.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_DELTA" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4878,6 +8581,21 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_MERCHCERT", + "desc": "가맹점 잔액증명: 가맹점잔액 기록치와 라인 재계산치를 대조해 증명(OK/NG).", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_DELTA" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4894,6 +8612,20 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_MONCLOSEJE", + "desc": "월차 결산 분개: 당월 RECV 순증가를 미수수익(RECV_ACCRUED)으로 계상.", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_NET", + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4936,6 +8668,21 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PARTREVERSE", + "desc": "부분 역분개: 원전표에서 특정 계정 라인만 차대를 뒤집어 신규 전표로 복사.", + "in": [ + "T_ID1", + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_ID2", + "T_COUNT" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4951,6 +8698,20 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PAYMATCH", + "desc": "지급 매칭: 원장 지급(MERCH_PAY 대변) 건수와 정산 건수를 대조해 매칭율 판정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4981,6 +8742,31 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PENDCLEAR", + "desc": "미결전표 정리: 기준일 이전 작성(D) 전표를 취소(X)하고 라인을 마감 처리.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PENDESCAL", + "desc": "미결 에스컬레이션: 기준일보다 오래 방치된 작성(D) 전표 건수를 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMT2" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4993,6 +8779,32 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PENDROUTE", + "desc": "미결전표 배정: 특정 작성(D) 전표의 메모에 담당자를 기록해 배정.", + "in": [ + "T_ID1", + "T_STR1" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PENDSTAT", + "desc": "미결 상태별 집계: 당일 전표를 상태(D/P/M/S/X)별로 순회 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5020,6 +8832,20 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PLSUMMARY", + "desc": "손익 요약: 당일 수익(_INC)과 비용(_EXP)의 순액을 계산해 순손익을 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5037,6 +8863,34 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PREPAIDAMORT", + "desc": "선급 상각: 선급비용 총액을 개월(N)로 나눠 1개월분을 비용(D)/선급(C) 상각.", + "in": [ + "T_BIZDATE", + "T_AMOUNT", + "T_COUNT" + ], + "out": [ + "T_ID1", + "T_NET" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PREPAIDPOST", + "desc": "선급비용 계상: 선급비용(D)/현금(C)으로 지출 시점 자산 계상.", + "in": [ + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5051,6 +8905,20 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_QTRCLOSEJE", + "desc": "분기 결산 분개: 당분기 대손비용 발생분을 미지급(ACCRUED_PAY)으로 계상.", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_NET", + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5062,6 +8930,20 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_RECOVERY", + "desc": "상각채권 회수: 제각 채권 회수를 현금(D)/대손충당금환입(C)으로 인식.", + "in": [ + "T_BIZDATE", + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5076,6 +8958,36 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_REDADJUST", + "desc": "적색 정정분개: 과대계상된 차변을 음수(적색) 분개 쌍으로 취소 감액.", + "in": [ + "T_KEY1", + "T_BIZDATE", + "T_ID1", + "T_DELTA" + ], + "out": [ + "T_ID2", + "T_DELTA" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_REDBLACKNET", + "desc": "적흑 상계: 원전표에 연결된 적색/흑색 정정 금액을 합산해 순정정액을 반환.", + "in": [ + "T_ID1" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5113,6 +9025,48 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_REPOST", + "desc": "재전기: 취소(X)된 전표의 라인을 방향 그대로 신규 전기(P) 전표로 복제.", + "in": [ + "T_ID1", + "T_BIZDATE" + ], + "out": [ + "T_ID2", + "T_COUNT" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_REVACCRUE", + "desc": "수익 발생 계상: 발생주의 수익을 미수수익(D)/수수료수익(C)으로 균형 계상.", + "in": [ + "T_BIZDATE", + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_REVDEFER", + "desc": "수익 이연: 미실현 수익을 수수료수익(D)/선수수익(C)으로 이연 대체.", + "in": [ + "T_BIZDATE", + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5127,6 +9081,20 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_REVRELEASE", + "desc": "이연수익 환입: 선수수익(D)을 수수료수익(C)으로 기간 경과분 환입 인식.", + "in": [ + "T_BIZDATE", + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5139,6 +9107,47 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ROLLTB", + "desc": "누적 시산표: 마감일까지 계정별 누계 차대를 순회해 시산표 upsert 및 총차액 검증.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_DELTA" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_SEQAUDIT", + "desc": "전표 채번 감사: 당일 전표번호 min/max/건수로 채번 누락(gap)을 검출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_DELTA" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_SETTLEPAYGAP", + "desc": "정산-지급 차이: 정산 net 합계와 원장 지급(MERCH_PAY 대변) 합계의 갭을 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_DELTA" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5169,6 +9178,22 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_SPANTB", + "desc": "기간 시산표: 계정의 기간 차변/대변 합계와 차액을 산출해 시산표에 upsert.", + "in": [ + "T_KEY1", + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_DELTA" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5281,6 +9306,22 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_THREEWAY", + "desc": "원장-정산-매입 3단 대사: 원장 지급대변, 정산 net, 매입 net 을 대조해 차이 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_AMT4", + "T_DELTA", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5312,6 +9353,47 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_UNAPPROVE", + "desc": "승인 취소: 마감(S)되지 않은 전기(P) 전표를 작성(D) 상태로 되돌림.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_UNEARNPOST", + "desc": "선수수익 계상: 현금(D)/선수수익(C)으로 선수금 수령 시점 부채 계상.", + "in": [ + "T_BIZDATE", + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_UNEARNRECOG", + "desc": "선수수익 인식: 선수수익 총액을 기간(N)으로 나눠 1기분을 수익 인식.", + "in": [ + "T_BIZDATE", + "T_AMOUNT", + "T_COUNT" + ], + "out": [ + "T_ID1", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5367,6 +9449,74 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_WEEKCLOSE", + "desc": "주차 결산: 지정 주(기간) 내 작성(D) 전표를 일괄 전기(P)로 승격.", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_WRITEOFF", + "desc": "대손 상각: 회수불능 채권을 대손충당금(D)/미수금(C)으로 제각.", + "in": [ + "T_BIZDATE", + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_XFERPOST", + "desc": "대체 전기: 두 계정 간 대체(D 대상/C 원천) 전기 후 양 계정 잔액을 재계산.", + "in": [ + "T_KEY1", + "T_KEY2", + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_YEARCARRY", + "desc": "연말 이월: 마감일까지 계정별 누계잔액을 익년 개시일 계정잔액으로 이월(upsert).", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_YEARCLOSE", + "desc": "연말 결산 대체: 손익계정(_INC/_EXP) 누계 잔액을 계정별로 RETAINED 로 대체.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5424,6 +9574,39 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_AUTHADV", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3", + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_ID1", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_AUTHADVRSP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ID1", + "T_ID2" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5459,6 +9642,23 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_BALINQ", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3", + "T_BIZDATE" + ], + "out": [ + "T_STR2", + "T_STR4", + "T_ID1", + "T_ARG1" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5472,6 +9672,35 @@ const SVC_SCREENS = [ "T_STR6" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_BMPMERGE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR6", + "T_STR7" + ], + "out": [ + "T_RC", + "T_STR6", + "T_ARG3" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_BMPPARSE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR6" + ], + "out": [ + "T_RC", + "T_STR3", + "T_COUNT" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5507,6 +9736,23 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_CARDVER", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR5", + "T_BIZDATE" + ], + "out": [ + "T_STR2", + "T_STR6", + "T_COUNT", + "T_ID1" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5534,6 +9780,21 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_CHANFAILOVER", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR1" + ], + "out": [ + "T_RC", + "T_STATUS", + "T_STR5" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5551,6 +9812,33 @@ const SVC_SCREENS = [ "T_ARG8" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_CHANLIST", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [], + "out": [ + "T_STR3", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_CHANMAP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_CHANNEL", + "T_STR5" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5565,6 +9853,33 @@ const SVC_SCREENS = [ "T_STR5" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_CHANPING", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL" + ], + "out": [ + "T_RC", + "T_STATUS" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_CHANRESET", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL" + ], + "out": [ + "T_RC", + "T_STR5", + "T_STATUS" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5622,6 +9937,23 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_DECODE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC", + "T_STR1", + "T_STR4", + "T_AMOUNT", + "T_ARG1", + "T_CHANNEL" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5667,6 +9999,21 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_DUPPURGE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_ID1" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5681,6 +10028,53 @@ const SVC_SCREENS = [ "T_STATUS" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_ENCODE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR1", + "T_STR4", + "T_CHANNEL", + "T_AMOUNT", + "T_ARG1" + ], + "out": [ + "T_STR3", + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_ERRCLASS", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR2" + ], + "out": [ + "T_RC", + "T_STR5", + "T_ARG3" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_ERRESCAL", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE", + "T_ARG4" + ], + "out": [ + "T_COUNT", + "T_ARG4", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5727,6 +10121,21 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_ERRREQUEUE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE", + "T_ID1" + ], + "out": [ + "T_RC", + "T_ID2", + "T_ARG1" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5740,6 +10149,125 @@ const SVC_SCREENS = [ "T_ID2" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_FEP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3" + ], + "out": [ + "T_STR3", + "T_COUNT", + "T_ARG2", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_FIELDGET", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3", + "T_ARG1", + "T_ARG2" + ], + "out": [ + "T_RC", + "T_STR5", + "T_COUNT" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_FIELDSET", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3", + "T_STR5", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_STR3", + "T_COUNT", + "T_ARG2" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_FINADV", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3", + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_STR4", + "T_ID1", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_FINADVRSP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_STR5", + "T_ID1" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_FINREQ", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3", + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_STR2", + "T_ID1", + "T_ARG1", + "T_INSTALL_N", + "T_AMT1" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_FINRSP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_AMOUNT", + "T_ARG2", + "T_ARG1" + ], + "out": [ + "T_STR2", + "T_ID1", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5784,6 +10312,56 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_HOSTADPT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR1", + "T_STR3" + ], + "out": [ + "T_STR3", + "T_STR5", + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_INFLIGHT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_ARG4" + ], + "out": [ + "T_COUNT", + "T_ARG4", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_INQRSP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_AMT1", + "T_ARG1" + ], + "out": [ + "T_STR3", + "T_ID1", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5802,6 +10380,36 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_ISOADPT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC", + "T_STR1", + "T_STR5", + "T_CHANNEL" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_KEYCHECK", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_KEY2" + ], + "out": [ + "T_RC", + "T_KEY1", + "T_ARG3" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5815,6 +10423,35 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_KEYEXPIRE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_ARG3", + "T_ARG4" + ], + "out": [ + "T_ARG1", + "T_ARG6", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_KEYLOAD", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL" + ], + "out": [ + "T_KEY1", + "T_ARG3", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5844,6 +10481,21 @@ const SVC_SCREENS = [ "T_ARG4" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_LOADBAL", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_CHANNEL", + "T_COUNT", + "T_ARG3" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5884,6 +10536,20 @@ const SVC_SCREENS = [ "T_ARG3" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_MACSESSION", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3" + ], + "out": [ + "T_RC", + "T_KEY1" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5926,6 +10592,40 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_NMADVICE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3", + "T_BIZDATE" + ], + "out": [ + "T_KEY1", + "T_STR4", + "T_ID1", + "T_ARG3", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_NMRSP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3", + "T_BIZDATE" + ], + "out": [ + "T_STR4", + "T_ID1", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5969,6 +10669,40 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_PINBLOCK", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_KEY1", + "T_STR5" + ], + "out": [ + "T_RC", + "T_KEY2", + "T_COUNT" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_PREAUTH", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3", + "T_BIZDATE", + "T_AMOUNT", + "T_ARG4" + ], + "out": [ + "T_AMT1", + "T_ARG4", + "T_ID1", + "T_ARG1" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6003,6 +10737,38 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_RATELIMIT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_ARG4" + ], + "out": [ + "T_COUNT", + "T_ARG4", + "T_ARG8", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_READYZ", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG8", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6036,6 +10802,22 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_RELAYADPT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC", + "T_CHANNEL", + "T_STR3", + "T_STR5", + "T_COUNT" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6052,6 +10834,20 @@ const SVC_SCREENS = [ "T_ID2" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_RESNDONE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_ID2", + "T_ARG4" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6081,6 +10877,22 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_REVADVRSP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ID1", + "T_ARG2" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6098,6 +10910,23 @@ const SVC_SCREENS = [ "T_ID2" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_REVRSP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_AMOUNT", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_STR2", + "T_ID1" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6129,6 +10958,36 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_RTCOUNT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_RTDISABLE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_ID1", + "T_CHANNEL", + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6143,6 +11002,35 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_RTENABLE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_RTFIND", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_RC", + "T_STR3", + "T_ARG4" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6208,6 +11096,51 @@ const SVC_SCREENS = [ "T_ARG6" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_SEQFILL", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_SEQNEXT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2", + "T_ARG6" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_SEQRESET", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2", + "T_ARG3" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6278,6 +11211,21 @@ const SVC_SCREENS = [ "T_STATUS" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_STALEMARK", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE", + "T_ARG4" + ], + "out": [ + "T_COUNT", + "T_ARG5", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6288,6 +11236,38 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_STATAMT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE", + "T_AMT1" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG8", + "T_AMT2" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_STATCHAN", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_GROSS", + "T_ARG8" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6305,6 +11285,22 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_STATERR", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE", + "T_ARG4" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG8", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6337,6 +11333,21 @@ const SVC_SCREENS = [ "T_ARG8" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_STATPEAK", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_COUNT", + "T_ARG3", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6369,6 +11380,23 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_STATSUM", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_AMT1", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6500,6 +11528,75 @@ const SVC_SCREENS = [ "T_CHANNEL" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_VAN3ADPT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC", + "T_STR3", + "T_STR1", + "T_AMOUNT", + "T_CHANNEL" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_VAN4ADPT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC", + "T_STR3", + "T_COUNT", + "T_CHANNEL" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_ACCT_INQ", + "desc": "기능: 정산계좌 원문 조회 (등록 여부 판정 포함).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_ACCT_MASK", + "desc": "기능: 정산계좌 마스킹 조회 (앞3 뒤2만 노출, 중간은 * 처리하여 반환 - 개인정보 보호).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_ACCT_REG", + "desc": "기능: 정산계좌 등록/변경 (숫자 10~16자리 형식 검증 통과분만 마스터에 반영, 검증 이력 기록).", + "in": [ + "T_MERCHANT", + "T_STR1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6538,6 +11635,45 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_AGENT_INQ", + "desc": "기능: 모집인 단건 조회 (AGENT 코드그룹에서 명칭/상태 반환).", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_AGENT_REG", + "desc": "기능: 모집인 등록 (AGENT 코드그룹 upsert, 소속 제휴사 코드를 T_STR2로 검증).", + "in": [ + "T_KEY1", + "T_STR1", + "T_STR2" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_AGENT_STATUS", + "desc": "기능: 모집인 상태 변경 (ACTIVE/SUSPENDED/RETIRED 허용값 검증 후 갱신).", + "in": [ + "T_KEY1", + "T_STATUS" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6551,6 +11687,58 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_APPLY_ASSIGN", + "desc": "기능: 심사 담당자 배정 (담당자 코드를 REVIEWER 매핑 이력에 기록).", + "in": [ + "T_MERCHANT", + "T_STR1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_APPLY_CNT", + "desc": "기능: 심사 상태별 신청 건수 집계 (대시보드용, 특정 상태 또는 전 워크플로 상태).", + "in": [ + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_APPLY_DOCCHK", + "desc": "기능: 심사 서류 접수 확인 (필수 마스터 필드 충족도 점검, 심사 서브단계).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_APPLY_HOLD", + "desc": "기능: 심사 진행 중 신청 건 보류 (APPLIED->HOLD, 사유 이력 기록).", + "in": [ + "T_MERCHANT", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6579,6 +11767,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_APPLY_RESUME", + "desc": "기능: 보류된 심사 재개 (HOLD->APPLIED, 심사 대기열 복귀).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6592,6 +11793,19 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_APPLY_SCORE", + "desc": "기능: 심사 점수 산정 (MCC 리스크 + 서류 완비도 가중 합산, 등급 예비판정).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6606,6 +11820,31 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_APPLY_WITHDRAW", + "desc": "기능: 신청 철회 (신청/보류 단계 건을 WITHDRAWN 처리, 활성 가맹점은 철회 불가).", + "in": [ + "T_MERCHANT", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_AUDIT_DIFF", + "desc": "기능: 마스터 최근 변경 전후 비교 (가장 최신 이력 1건의 액션/이전값/이후값 반환).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6631,6 +11870,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_AUDIT_STAT", + "desc": "기능: 전 마스터 감사 통계 (특정 액션의 전체 발생 건수 + 관련 가맹점 수 집계).", + "in": [ + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6694,6 +11947,96 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_BIZ_CLOSE", + "desc": "기능: 폐업 확정 처리 (상태 CLOSED, 미결 계약 잔여 여부를 함께 점검하여 경고 코드 반환).", + "in": [ + "T_MERCHANT", + "T_REASON" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_BIZ_REOPEN", + "desc": "기능: 재개업 처리 (SUSPENDED/DORMANT 가맹점을 ACTIVE로 복귀, 폐업/삭제 건은 불가).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_BIZ_SUSPEND", + "desc": "기능: 휴업 신고 처리 (ACTIVE 가맹점을 SUSPENDED로, 소속 활성 단말도 함께 정지).", + "in": [ + "T_MERCHANT", + "T_REASON" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_BRANCH_INQ", + "desc": "기능: 영업점 조회 (\"명칭|상위코드\" 저장값을 명칭만 분리하여 반환).", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_BRANCH_LIST", + "desc": "기능: 영업점 계층 요약 (전체 수 + 최상위(상위=-) 영업점 수 집계).", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_BRANCH_REG", + "desc": "기능: 영업점 마스터 등록 (BRANCH 코드그룹 upsert, 상위 영업점 코드를 code_name에 저장).", + "in": [ + "T_KEY1", + "T_STR1", + "T_KEY2" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CHK_ACCT", + "desc": "기능: 정합성 점검 - 정산계좌 미등록 활성 가맹점 적발 (정산 지급 리스크 사전 탐지).", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6707,6 +12050,18 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CHK_GRADE", + "desc": "기능: 정합성 점검 - 마스터 등급과 누적 매출 기준 산정 등급의 불일치 적발.", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6719,6 +12074,30 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CHK_MCC", + "desc": "기능: 정합성 점검 - MCC 마스터에 없는 업종코드를 참조하는 가맹점 적발 (외래키 무결성).", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CHK_ORPHAN", + "desc": "기능: 정합성 점검 - 대표가 비활성/부재인 대표-하위 관계 적발 (MERCH_REL 고아 링크).", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6817,6 +12196,19 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CONTRACT_AMEND", + "desc": "기능: 유효 계약의 조건 변경 (만기일 연장/단축, 변경 전후를 이력에 함께 기록).", + "in": [ + "T_MERCHANT", + "T_ARG1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6830,6 +12222,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CONTRACT_EXPCHK", + "desc": "기능: 만기 임박 점검 (최신 계약 만기일까지 남은 일수 산출, 갱신 필요 판정).", + "in": [ + "T_MERCHANT", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CONTRACT_HIST", + "desc": "기능: 가맹점 계약 변경 이력 요약 (체결/변경 횟수 + 최신 만기일 반환).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6857,6 +12276,18 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CONTRACT_REACT", + "desc": "기능: 정지된 계약 재개 (미해제 CONTRACT_SUSP가 있을 때만 CONTRACT_REACT 기록).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6870,6 +12301,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CONTRACT_SUSPEND", + "desc": "기능: 계약 일시정지 (활성 가맹점을 유지하되 CONTRACT_SUSP 이력 마킹, 중복 정지 차단).", + "in": [ + "T_MERCHANT", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6961,6 +12405,19 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_GRADE_HIST", + "desc": "기능: 등급 변경 이력 요약 (등급 관련 이력 건수 + 마스터 현행 등급 반환).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7035,6 +12492,46 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_MCC_ASSIGN", + "desc": "기능: 가맹점 업종(MCC) 재배정 (마스터에 등록된 MCC로만 변경, 변경 이력 기록).", + "in": [ + "T_MERCHANT", + "T_STR1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_MCC_CNT", + "desc": "기능: 특정 MCC를 사용하는 가맹점 수 + 해당 MCC 리스크 등급 동시 반환.", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_MCC_DEL", + "desc": "기능: MCC 업종코드 삭제 (참조하는 가맹점이 없을 때만 물리 삭제, 안전 삭제).", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7076,6 +12573,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_MCC_RISKSET", + "desc": "기능: MCC 리스크 등급 설정 (1~5 범위 검증 후 mm_mcc.risk_level 갱신).", + "in": [ + "T_STR1", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7139,6 +12650,20 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_MERCH_DORMANT", + "desc": "기능: 장기 무거래 가맹점 휴면 전환 (ACTIVE->DORMANT, 최근 집계 매출 0 확인).", + "in": [ + "T_MERCHANT", + "T_REASON" + ], + "out": [ + "T_RC", + "T_GROSS" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7248,6 +12773,18 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_PARTNER_LIST", + "desc": "기능: 활성 제휴사 총수 조회 (PARTNER 코드그룹 중 ACTIVE 상태 집계).", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7275,6 +12812,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_PARTNER_UPD", + "desc": "기능: 제휴사 마스터 명칭/표시순서 변경 (PARTNER 코드그룹, 기존 건만 갱신).", + "in": [ + "T_KEY1", + "T_STR1", + "T_ARG1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7288,6 +12839,45 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_RATE_CANCEL", + "desc": "기능: 대기중 요율 변경 요청 철회 (미처리 RATE_REQ가 있을 때만 RATE_REJ로 자체 종결).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_RATE_HIST", + "desc": "기능: 요율 승인 워크플로 이력 집계 (요청/승인/반려 건수를 각각 반환).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2", + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_RATE_PENDING", + "desc": "기능: 전 가맹점 미처리 요율 요청 수 집계 (승인/반려로 종결되지 않은 RATE_REQ 카운트).", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7317,6 +12907,32 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_RATE_STATUS", + "desc": "기능: 최신 요율 변경 요청의 진행 상태 판정 (요청 이후 승인/반려 이력 존재 여부).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_REL_CHILDREN", + "desc": "기능: 대표 가맹점에 연결된 하위 가맹점 수 집계 (MERCH_REL code_name=대표 기준).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7343,6 +12959,31 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_REL_MOVE", + "desc": "기능: 하위 가맹점을 다른 대표로 이관 (기존 연결의 대표를 신규 활성 대표로 교체).", + "in": [ + "T_KEY1", + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_REL_PARENT", + "desc": "기능: 하위 가맹점의 대표 가맹점 조회 (MERCH_REL 코드그룹에서 code=하위 -> code_name=대표).", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7355,6 +12996,59 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_REL_VERIFY", + "desc": "기능: 대표-하위 관계 정합성 검증 (대표가 활성 마스터에 존재하는지, 순환/자기참조 여부).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_RISK_INQ", + "desc": "기능: 현행 리스크 등급 조회 (최신 RISK_SET 수동값 우선, 없으면 최신 RISK 산정 밴드).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_RISK_SCORE", + "desc": "기능: 가맹점 리스크 점수 산정 (MCC 위험도*20 + 저등급 가산 + 비활성 상태 가산).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_RISK_SET", + "desc": "기능: 가맹점 리스크 등급 수동 설정 (1~5, 심사역 재량 오버라이드를 RISK_SET 이력에 기록).", + "in": [ + "T_MERCHANT", + "T_ARG1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7388,6 +13082,18 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_ACTIVATE", + "desc": "기능: 단말 개통 (정지/비활성 단말을 ACTIVE로 전환).", + "in": [ + "T_STR5" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7403,6 +13109,72 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_DEACT", + "desc": "기능: 단말 개통 해지 (ACTIVE 단말을 INACTIVE로, 사유 기록).", + "in": [ + "T_STR5" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_HIST", + "desc": "기능: 단말 상태 확인 조회 (현재 상태/소속/모델 반환, 존재 여부 판정).", + "in": [ + "T_STR5" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_INSTALL", + "desc": "기능: 등록된 단말의 설치 확정 (상태 REGISTERED->ACTIVE, 가맹점 활성 여부 확인).", + "in": [ + "T_STR5", + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_LIST", + "desc": "기능: 가맹점별 단말 현황 요약 (전체/활성/폐기 건수 집계).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2", + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_MODEL_UPD", + "desc": "기능: 단말 모델(기종) 정보 변경. 출력: T_STR6에 이전 모델을 실어 반환.", + "in": [ + "T_STR5", + "T_STR6" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7416,6 +13188,31 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_REASSIGN", + "desc": "기능: 단말 소속 가맹점 재배정 (기존 소속에서 신규 활성 가맹점으로 이관).", + "in": [ + "T_STR5", + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_RETIRE", + "desc": "기능: 단말 폐기 처리 (상태 RETIRED로 확정, 활성 단말은 폐기 전 해지 필요).", + "in": [ + "T_STR5" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7441,6 +13238,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_SWAP", + "desc": "기능: 단말 교체 (구단말 폐기 + 신단말이 구단말의 가맹점/모델을 승계 등록).", + "in": [ + "T_STR5", + "T_STR6" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7479,6 +13289,19 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_ACCTFREEZE", + "desc": "지급계좌 동결: 활성(ACTIVE) 계좌만 FROZEN 으로 전환한다. 먼저 해당 계좌의", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -7494,6 +13317,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_ACCTREOPEN", + "desc": "동결 해제: 동결(FROZEN) 계좌를 ACTIVE 로 복구하고, 동결 사유로 보류(H)됐던", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_ACCTVERIFY", + "desc": "지급계좌 검증: 계좌의 상태/예금주/잔액을 조회하고, 요청에 예금주가 주어지면", + "in": [ + "T_KEY1", + "T_STR1" + ], + "out": [ + "T_RC", + "T_AMT1" + ] + }, { "mod": "py", "modnm": "지급", @@ -7543,6 +13393,19 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_ADVGRANT", + "desc": "선지급 승인: 선지급(ADVANCE) 채널의 요청(R) 건만 승인(A)으로 전이한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_AMOUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -7575,6 +13438,31 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_APPRCANCEL", + "desc": "지급 승인 취소: 아직 파일에 편성되지 않은 승인(A) 건을 요청(R)으로 되돌린다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_APPRHIST", + "desc": "지급 승인/원장 이력: 지정 지급건의 py_ledger 기표를 시간순 커서로 훑어", + "in": [ + "T_ID1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7603,6 +13491,18 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_APPRPEND", + "desc": "승인 대기 집계: 아직 승인 전인 요청(R) 지급건을 가맹점별로 묶어 대기 건수/", + "in": [], + "out": [ + "T_COUNT", + "T_ARG2", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7633,6 +13533,22 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_BANKVERIFY", + "desc": "지급파일 검증: 파일 헤더(record_count/total_amount)와 실제 편성 지급건의", + "in": [ + "T_ID2" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_COUNT", + "T_DELTA", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7682,6 +13598,36 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_CARRYOVER", + "desc": "지급 이월(단건): 미지급(R/A) 건의 지급기준일(biz_date)을 지정 차기일자로", + "in": [ + "T_ID1", + "T_KEY2", + "T_REASON" + ], + "out": [ + "T_RC", + "T_ADJ" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_CHARGEBACK", + "desc": "지급 되물림: 분쟁 등으로 지급완료(P) 건을 되물릴 때, 원건은 보존한 채", + "in": [ + "T_ID1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID2", + "T_NET" + ] + }, { "mod": "py", "modnm": "지급", @@ -7694,6 +13640,35 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_CLOSEDSTOP", + "desc": "폐업 가맹점 지급정지: 가맹점 상태가 CLOSED 인 가맹점의 미지급(R/A) 건을", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_COMBINE", + "desc": "지급 합산: 같은 계좌의 당일 요청(R) 건들을 하나로 합산 지급한다. 합계", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID2", + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "py", "modnm": "지급", @@ -7718,6 +13693,19 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_DORMLIST", + "desc": "휴면 계좌 목록: 지정 기준일 이후 지급 이력이 전혀 없는 활성(ACTIVE) 계좌를", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7732,6 +13720,19 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FAILCLEAR", + "desc": "실패 종결: 재처리 불가한 반려(J) 단건을 취소(C)로 종결하고 종결 사유를", + "in": [ + "T_ID1", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7748,6 +13749,19 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FAILRSN", + "desc": "실패 재시도횟수별 분포: 당일 반려(J) 건을 retry_count 별 커서로 건수/net", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7777,6 +13791,24 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FEECAP", + "desc": "수수료 상한 적용: 미지급 건 수수료가 상한(T_AMOUNT)을 넘으면 상한까지", + "in": [ + "T_ID1", + "T_AMOUNT", + "T_BIZDATE", + "T_REASON" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_FEE", + "T_NET" + ] + }, { "mod": "py", "modnm": "지급", @@ -7792,6 +13824,21 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FEEFIXED", + "desc": "정액 수수료 적용: 미지급(R/A) 건에 요청 정액 수수료(T_AMOUNT)를 부과하고", + "in": [ + "T_ID1", + "T_AMOUNT" + ], + "out": [ + "T_RC", + "T_FEE", + "T_NET" + ] + }, { "mod": "py", "modnm": "지급", @@ -7822,6 +13869,35 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FEEREFUND", + "desc": "수수료 환급: 지급완료(P) 건의 수수료를 지급계좌에 되돌려 입금하고,", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_FEE" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FEESUM", + "desc": "수수료 집계: 당일 지급건의 수수료 합/건수와, py_adjust 에 쌓인 조정 합을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_FEE", + "T_ADJ", + "T_NET" + ] + }, { "mod": "py", "modnm": "지급", @@ -7838,6 +13914,32 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FILECANCEL", + "desc": "지급파일 취소: 확정(C) 전 파일만 취소 가능. 편성(F) 건을 승인(A)+file_id=0", + "in": [ + "T_ID2" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FILECONF", + "desc": "지급파일 확정: 생성(G) 파일만 확정(C)+confirmed_at 로 전이하고, 그 파일에", + "in": [ + "T_ID2" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -7898,6 +14000,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_GARNISH", + "desc": "가압류 등록: 활성 계좌를 GARNISH 로 표시하고, 그 계좌 미지급(R/A) 잔액을", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "py", "modnm": "지급", @@ -7937,6 +14054,44 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_HOLDBYRSN", + "desc": "보류 사유별 집계: 당일 보류(H) 건을 reject_reason 별 커서로 건수/net 합", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_HOLDCLEAR", + "desc": "보류 일괄 해제: 지정 계좌의 보류(H) 지급건을 모두 승인(A)으로 되돌리고", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_HOLDEXT", + "desc": "보류 연장: 보류(H) 건의 반려사유 뒤에 '(연장)' 표식을 덧붙여 갱신한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7951,6 +14106,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_HOLDNOTE", + "desc": "보류 사유 갱신: 보류(H) 단건의 반려사유를 요청 사유로 교체한다. dbio 로", + "in": [ + "T_ID1", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -8037,6 +14205,47 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_LIMITLIST", + "desc": "지급 한도 사용현황 목록: 가맹점별 일한도와 당일 확정성(A/F/P) 사용액을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_LIMITSET", + "desc": "지급 일한도 설정: 가맹점의 일 지급한도(merchant.daily_limit)를 새 값으로", + "in": [ + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_RC", + "T_AMT1" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_LIMITWARN", + "desc": "지급 한도 근접 경보: 당일 사용액이 일한도의 지정 비율(T_ARG1 %, 기본 80)", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -8096,6 +14305,20 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_NOTIFYALL", + "desc": "전 채널 일괄 통지(시뮬): 지급완료(P) 단건을 SMS/EMAIL/PUSH 세 채널로 각각", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_NET" + ] + }, { "mod": "py", "modnm": "지급", @@ -8111,6 +14334,32 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_NOTIFYFAIL", + "desc": "지급실패 통지(시뮬): 반려(J) 건의 가맹점/반려사유를 읽어 실패 안내 본문을", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_NOTIFYKAKAO", + "desc": "알림톡 일괄 통지(시뮬): 당일 지급완료(P) 건수/금액을 한 번에 집계해 알림톡", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "py", "modnm": "지급", @@ -8125,6 +14374,32 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_NOTIFYPUSH", + "desc": "지급완료 푸시 통지(시뮬): 지급완료(P)건을 py_account 조인으로 예금주/은행을", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_NET" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_NOTIFYRESEND", + "desc": "통지 재발송: 지급완료(P) 건의 retry_count 를 재발송 횟수로 삼아 3회까지만", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -8138,6 +14413,62 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_OFFSETAPPLY", + "desc": "상계 적용: 미지급(R/A) 건의 net 에서 상계액을 차감(net 미만으로 캡)하고,", + "in": [ + "T_ID1", + "T_AMOUNT", + "T_BIZDATE", + "T_REASON" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_NET" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_PENDLIST", + "desc": "미지급 목록: 당일 미완결(요청R/승인A/보류H) 지급건을 커서로 훑어 지급ID/", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_REACTIVATE", + "desc": "휴면 계좌 재활성: 휴면(DORMANT) 처리된 지급계좌를 다시 활성(ACTIVE)으로", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_RECLAIM", + "desc": "과지급 환수: 지급완료(P) 건에서 요청 환수액(T_AMOUNT, net 한도)만큼을", + "in": [ + "T_ID1", + "T_AMOUNT" + ], + "out": [ + "T_RC", + "T_AMT1" + ] + }, { "mod": "py", "modnm": "지급", @@ -8202,6 +14533,22 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_REPAYACCT", + "desc": "계좌변경 재지급: 반려(J)된 지급건의 금액/수수료를 승계해 새 계좌(T_KEY2)로", + "in": [ + "T_ID1", + "T_KEY2", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID2", + "T_NET" + ] + }, { "mod": "py", "modnm": "지급", @@ -8227,6 +14574,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_REQUEUE", + "desc": "실패 일괄 재처리 적재: 당일 반려(J) 건 중 재시도 여력(retry_count<3)이", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -8239,6 +14599,32 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_RESVLIST", + "desc": "예약 지급 목록: 지정 실행일(T_BIZDATE)에 걸린 예약(S) 을 py_schedule 과", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_RESVMODIFY", + "desc": "예약 변경: 아직 실행되지 않은 예약(S) 의 실행일(run_date)을 새 일자로", + "in": [ + "T_ID2", + "T_BIZDATE" + ], + "out": [ + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -8251,6 +14637,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_RETRYONE", + "desc": "단건 재시도: 반려(J) 지급건 하나를 요청(R)으로 되돌리며 retry_count 를", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -8263,6 +14662,20 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_ROLLFWD", + "desc": "미지급 일괄 이월: 지정 기준일 이전의 미지급(R) 건 전부를 차기 영업일", + "in": [ + "T_BIZDATE", + "T_KEY2" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -8303,6 +14716,22 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_SEIZEAMT", + "desc": "압류 금액 상계: 압류 계좌의 보류(H) 지급건 net 합을 압류가능액으로 산정하고,", + "in": [ + "T_KEY1", + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_RC", + "T_AMT1", + "T_AMT2" + ] + }, { "mod": "py", "modnm": "지급", @@ -8317,6 +14746,17 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_SEIZELIST", + "desc": "압류 계좌 목록: 압류(SEIZED) 상태 계좌를 커서로 훑어 계좌번호/예금주와 그", + "in": [], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -8346,6 +14786,22 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_SPLITEVEN", + "desc": "균등 분할: 요청(R) 지급건을 N 건으로 균등 분할하되 나머지는 첫 건에 몰아", + "in": [ + "T_ID1", + "T_COUNT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_AMOUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -8363,6 +14819,19 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_STATAMT", + "desc": "금액대별 지급 통계: 당일 지급건을 net 기준 소액/중액/고액 구간으로 CASE", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -8423,6 +14892,33 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_STATFEE", + "desc": "채널별 수수료 통계: 당일 지급건을 channel 별로 수수료합/net합 집계하되", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_STATMONTH", + "desc": "월별 지급 통계: 지정 기간(T_BIZDATE~T_KEY2)의 지급건을 'YYYY-MM' 단위로", + "in": [ + "T_BIZDATE", + "T_KEY2" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -8439,6 +14935,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_STATSTATUS", + "desc": "상태별 지급 통계: 당일 지급건을 status 별 커서로 건수/net합/수수료합까지", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -8481,6 +14990,75 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_URGENTQ", + "desc": "긴급 지급 큐: 당일 요청(R) 건 중 금액이 임계치(T_AMOUNT) 이상인 건을 금액", + "in": [ + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_VOIDONE", + "desc": "지급 무효(단건): 아직 지급 전인 요청/승인(R/A) 건만 무효 취소(C)로 전이하고", + "in": [ + "T_ID1", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_XFERCONF", + "desc": "이체 확정: 지급완료(P) 건에 대해 이체 실행 결과를 py_ledger 에 XFER 기표로", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_NET" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_XFERDIFF", + "desc": "이체 차액 목록: 당일 지급건의 net 과 py_ledger XFER 기표 합이 어긋나는", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_XFERFILE", + "desc": "이체유형별 지급파일 생성: 지정 채널(STD/SPLIT/BULK/ADVANCE)의 승인(A) 건만", + "in": [ + "T_CHANNEL", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_ID2", + "T_GROSS" + ] + }, { "mod": "py", "modnm": "지급", @@ -8522,6 +15100,32 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_XFERTYPE", + "desc": "이체유형별 지급 집계: 당일 지급건을 channel 별 커서로 건수/net 합 집계해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_XFERUNMATCH", + "desc": "미대사 목록: 지급완료(P) 이면서 이체 기표(py_ledger XFER)가 전혀 없는", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8551,6 +15155,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_ACQMATCH", + "desc": "매입-입금 축 대사: 당일 매입 순액 합계와 원장 RECON 입금 합계를 비교해 차액을 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8610,6 +15228,32 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_APPRGAP", + "desc": "승인 누락 감지: 당일 매입 중 승인 원장이 없는 건을 예외로 등록하고 누락 건수를 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_APPRMATCH", + "desc": "승인-매입 축 대사: 당일 매입 중 승인건과 매칭된 건수와 승인 누락 건수를 분리 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8625,6 +15269,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_AUDITADD", + "desc": "대사 감사추적 기록: 지정 매입의 대사 처리 이력을 AUDIT 유형 로그로 감사 추적에 적재.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8665,6 +15322,49 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_BACKDATE", + "desc": "소급 대사: 지정 매입을 과거 영업일자로 소급하여 대사 로그를 MATCHED 로 기록.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID", + "T_STR1" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_BRANDDIFF", + "desc": "브랜드별 차이 분석: 업종(category) 단위 매입 총액과 수수료 합계 비율을 점검.", + "in": [ + "T_BIZDATE", + "T_CATEGORY" + ], + "out": [ + "T_GROSS", + "T_FEE", + "T_RC" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_BRANDRECON", + "desc": "브랜드별 대사: 업종/브랜드(category) 단위로 당일 매입 총액과 건수를 집계.", + "in": [ + "T_BIZDATE", + "T_CATEGORY" + ], + "out": [ + "T_GROSS", + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8763,6 +15463,45 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_CANCMATCH", + "desc": "취소분 대사: 당일 취소(ac_cancel) 금액 합계와 취소 건수를 유형 무관하게 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMOUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_CARRYFWD", + "desc": "익일 이월 처리: 당일 미해결 예외를 지정 익일자로 복제 등록하여 이월시킨다.", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_CARRYIN", + "desc": "전일 이월 대사: 전 영업일의 미해결 예외 건수를 조회해 당일 이월 대상으로 반환.", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8776,6 +15515,33 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_CHANDIFF", + "desc": "채널별 차이 분석: 지정 채널의 매입 순액과 원장 SETTLE 합계 차이를 rc_diff 에 기록.", + "in": [ + "T_BIZDATE", + "T_CHANNEL" + ], + "out": [ + "T_DELTA" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_CHANRECON", + "desc": "채널별 대사: 지정 채널의 당일 매입 건수와 대사완료(M/S) 건수를 대조.", + "in": [ + "T_BIZDATE", + "T_CHANNEL" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8828,6 +15594,18 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_DEPGAP", + "desc": "입금 누락 감지: 매입은 있으나 RECON 원장이 없는 미입금 건수를 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8843,6 +15621,20 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_DEPPAY", + "desc": "입금-지급 축 대사: 원장 XFER 이체 합계와 정산 순액 합계의 지급 정합성을 대조.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_GROSS", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8858,6 +15650,19 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_DIFFRANK", + "desc": "차이 순위 분석: 당일 절대 차이금액이 가장 큰 매입 1건과 그 차액을 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_PURCHASE_ID", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8897,6 +15702,19 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_EDCRECON", + "desc": "EDC 채널 대사: EDC 단말 매입의 순액 합계와 미대사 건수를 함께 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_NET", + "T_ARG1" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8939,6 +15757,32 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_EXCNOTIFY", + "desc": "대사 예외 알림: 당일 미해결 예외 건수를 집계해 임계 초과 시 rc_alert 경보를 발생.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_FEEBRK", + "desc": "수수료 내역 대사: 당일 매입 수수료 원계와 수수료조정 누계를 비교해 순수수료 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_FEE", + "T_DELTA", + "T_ARG1" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8967,6 +15811,86 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_FIXDUP", + "desc": "중복 자동보정: 동일 매입에 SETTLE 원장이 2건 이상 중복된 매입 건수를 탐지.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_FIXFEE", + "desc": "수수료 자동보정: 매입 수수료가 MDR 이론치와 다르면 차이를 조정 델타로 반환.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_FEE", + "T_ARG1", + "T_DELTA" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_FIXFX", + "desc": "외화 자동보정: ac_fx 의 원화환산액과 원통화*환율 이론치의 차이를 산출.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_FIXROUND", + "desc": "반올림 자동보정: 절대차 1 이하 단수 차이를 수수료조정(ac_fee_adj)으로 상계 기록.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_FIXSIGN", + "desc": "부호 자동보정: 부호 반전 rc_diff 라인의 차액 부호를 정정하여 재기록.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_FXDIFF", + "desc": "외화 환산 차이: 당일 ac_fx 원화환산 합계와 원통화*환율 이론합계의 차이를 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9034,6 +15958,32 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_HOLDCLR", + "desc": "대사 보류 해제: 보류(H) 매입을 접수(A)로 되돌리고 관련 예외를 해결 처리.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_HOLDLIST", + "desc": "보류 목록 현황: 당일 보류(H) 상태 매입의 건수와 순액 합계를 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9049,6 +15999,20 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_HOLDSET", + "desc": "대사 보류 설정: 지정 매입 상태를 보류(H)로 변경하고 보류 예외를 사유와 함께 등록.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID", + "T_REASON" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9065,6 +16029,65 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_ISSDIFF", + "desc": "발급사별 차이 분석: 발급사 단위 매입 순액과 정산 순액 차이를 계산해 반환.", + "in": [ + "T_BIZDATE", + "T_ISSUER" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_ISSRECON", + "desc": "발급사별 대사: 지정 발급사의 당일 매입 순액과 정산 순액을 발급사 단위로 대조.", + "in": [ + "T_BIZDATE", + "T_ISSUER" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_KINDSPLIT", + "desc": "차이 유형 분류: 당일 rc_diff 를 금액(AMT)/건수(CNT) 유형으로 나누어 각각 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_LEDGERBAL", + "desc": "원장 축 잔액 대사: 당일 RECON/SETTLE/XFER 기표 합계의 순잔액이 0 인지 확인.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9106,6 +16129,58 @@ const SVC_SCREENS = [ "T_PURCHASE_ID" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_MANCANCEL", + "desc": "수작업 신청 취소: 아직 확정되지 않은 수작업 HOLD 신청 로그를 삭제.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_MANLIST", + "desc": "수작업 신청 현황: 당일 수작업 대사 로그를 상태별(HOLD/확정)로 나누어 건수 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_MANNG", + "desc": "수작업 대사 반려: 해당 매입의 수작업 HOLD 로그를 ROLLBACK 으로 반려 처리.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_MANOK", + "desc": "수작업 대사 확정: 해당 매입의 수작업 HOLD 로그를 CONFIRMED 로 승인 처리.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9136,6 +16211,19 @@ const SVC_SCREENS = [ "T_PURCHASE_ID" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_MANREQ", + "desc": "수작업 대사 신청: 지정 매입에 대해 수작업 대사 요청을 HOLD 상태 로그로 등록.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9150,6 +16238,32 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_MGRPDIFF", + "desc": "가맹점그룹별 차이 분석: 가맹점 접두 그룹의 매입 순액과 정산 순액 차액을 집계.", + "in": [ + "T_BIZDATE", + "T_KEY1" + ], + "out": [ + "T_DELTA" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_MGRPRECON", + "desc": "가맹점그룹별 대사: 가맹점 ID 접두 그룹(prefix) 단위로 매입 순액을 묶어 대사.", + "in": [ + "T_BIZDATE", + "T_KEY1" + ], + "out": [ + "T_NET" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9176,6 +16290,19 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_OVERAMT", + "desc": "과다 차이 분석: 당일 rc_diff 중 실제가 기대를 초과(diff_amt<0)한 과다 건수/합계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9191,6 +16318,18 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_PARTDEP", + "desc": "부분 입금 대사: 매입 순액보다 RECON 입금 누계가 작은 부분입금 건수를 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9205,6 +16344,32 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_PAYGAP", + "desc": "지급 누락 감지: 대사완료(M) 매입 중 정산 레코드가 없는 미지급 건수를 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_PAYLEDGER", + "desc": "지급-원장 축 대사: 정산 순액과 원장 SETTLE 기표 합계가 일치하는지 검증하고 차액 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_ARG1", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9219,6 +16384,19 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_POSRECON", + "desc": "POS 채널 대사: POS 채널 매입의 대사완료율을 건수 기준으로 산출(백분율).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9246,6 +16424,59 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_RATECHAN", + "desc": "채널별 대사율: 지정 채널의 당일 매입 대비 대사완료 비율을 백분율로 산출.", + "in": [ + "T_BIZDATE", + "T_CHANNEL" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_RATEDAY", + "desc": "일별 대사율: 당일 매입 대비 대사완료 건수 비율을 백분율로 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_RATEMERCH", + "desc": "가맹점별 대사율: 지정 가맹점의 당일 매입 대비 대사완료 비율을 백분율로 산출.", + "in": [ + "T_BIZDATE", + "T_MERCHANT" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_RATEMON", + "desc": "월별 대사율: 기준일이 속한 달 전체 매입 대비 대사완료 비율을 백분율로 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9260,6 +16491,84 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_RATEWEEK", + "desc": "주별 대사율: 기준일 포함 최근 7영업일 매입 대비 대사완료 비율을 백분율로 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_RECORR", + "desc": "정정후 재대사: 수수료조정이 반영된 매입을 대상으로 순액 재검증 후 로그를 남긴다.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_NET" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_REDIFF", + "desc": "차이발생 재대사: 지정 매입의 기대 순액과 SETTLE 실적 차이를 재산출해 rc_diff 갱신.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_DELTA" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_REFULL", + "desc": "전체 재대사: 당일 전체 매입 대비 대사완료(M/S) 비율을 재계산해 진척률로 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_REFUNDREC", + "desc": "환불분 대사: 취소 유형이 REFUND 인 환불 금액과 건수를 별도로 대사 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMOUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_RELATE", + "desc": "지연도착 재대사: 원장 기표가 매입일보다 늦게 도착한 지연 건을 재대사 대상으로 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9356,6 +16665,30 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_ROUNDDIFF", + "desc": "반올림 차이 분석: 절대차가 1 이하인 단수 차이 건을 반올림 오차로 분류해 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_SIGNDIFF", + "desc": "부호 오류 차이 분석: 기대와 실제의 부호가 서로 반대인 rc_diff 라인을 검출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9448,6 +16781,18 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_TIMEDIFF", + "desc": "기표시점 차이 분석: 매입 영업일과 원장 기표 영업일이 다른 이월/선기표 건수를 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9477,6 +16822,33 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_TRAILQRY", + "desc": "대사 감사추적 조회: 지정 매입의 전체 대사 로그 이력 건수와 최종 로그 ID 를 반환.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT", + "T_ID1" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_UNDERAMT", + "desc": "과소 차이 분석: 당일 rc_diff 중 실제가 기대에 미달(diff_amt>0)한 과소 건수/합계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9570,6 +16942,21 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_ADJREVERSE", + "desc": "조정 취소(역분개): 기존 정산 조정(T_ADJ) 행을 조회해 동일 금액의 반대 부호 조정을", + "in": [ + "T_ADJ", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9601,6 +16988,20 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_APPEALSTAT", + "desc": "이의신청 통계: 정산 조정 중 이의신청 관련(reason LIKE 'DISPUTE%') 건수와 청구", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_DELTA", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9613,6 +17014,35 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_APPROVEALL", + "desc": "정산 일괄 승인: 당일 지급 예정(SCHEDULED) 전 건을 한 번에 지급 완료(PAID)로", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_BADDEBT", + "desc": "대손 상각: 특정 가맹점의 장기 보류(HELD) 지급 건을 커서로 순회해 총 미수액을", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_COUNT", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9626,6 +17056,36 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_BANDSIM", + "desc": "구간별 요율 시뮬레이션: 입력 금액에 적용 가능한(band_min <= amount) 모든 구간 중", + "in": [ + "T_AMOUNT", + "T_STR1" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_FEE" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_BIWEEKLY", + "desc": "격주 정산: 가맹점 당일 정산 net 합계를 집계하고 영업일 기준 10일 뒤(약 2주)", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1", + "T_NET" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9699,6 +17159,58 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_CASHBACK", + "desc": "캐시백 정산: 거래 금액에 캐시백율(T_ARG1 bps)을 적용한 캐시백을 가맹점 지급", + "in": [ + "T_AMOUNT", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1", + "T_AMOUNT" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_CHAINSET", + "desc": "체인점 통합 정산: 업종/분류(T_CATEGORY)로 묶인 체인 매입의 정산 net 을 조인 집계해", + "in": [ + "T_MERCHANT", + "T_CATEGORY", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1", + "T_COUNT", + "T_NET" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_CHARGEBACK", + "desc": "차지백 정산 처리: 차지백 금액(T_AMOUNT)을 음(-)의 정산 조정으로 회수하고 해당", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_AMOUNT", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9714,6 +17226,20 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_COLLATERAL", + "desc": "담보/보증금 잔액: 정산 상세의 DEPOSIT 라인을 합산해 가맹점 누적 보증금(담보)", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_AMOUNT", + "T_COUNT", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9724,6 +17250,24 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_COUPONADJ", + "desc": "쿠폰 조정 정산: 발급 쿠폰 금액(T_ARG1)을 가맹점 부담분으로 양(+)의 정산 조정에", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_ARG1", + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9768,6 +17312,20 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_CYCLED3", + "desc": "D+3 정산 주기: 영업일(T_BIZDATE)로부터 영업일 기준 3일 뒤 지급일을 산출해", + "in": [ + "T_NET", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9785,6 +17343,35 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_CYCLEQTR", + "desc": "분기 정산 주기: 기준일(T_BIZDATE) 이하의 미지급 정산 net 을 누적 합계로 집계해", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1", + "T_COUNT", + "T_NET" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_CYCLESET", + "desc": "정산 주기 설정 조회: 가맹점 한도(daily_limit)를 기준으로 적용 정산 주기 코드", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_AMOUNT" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9831,6 +17418,21 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_DEBTNET", + "desc": "채권 상계: 가맹점의 지급 예정(SCHEDULED) 채무와 보류(HELD) 미수 채권을 각각", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_NET", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9865,6 +17467,25 @@ const SVC_SCREENS = [ "T_ADJ" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_DEPOSITSET", + "desc": "보증금 설정/차감: 가맹점 보증금 예치액(T_ARG1)을 정산 상세에 DEPOSIT 라인으로", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_NET", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_AMOUNT", + "T_NET" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9893,6 +17514,22 @@ const SVC_SCREENS = [ "T_ADJ" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_DISPUTERSV", + "desc": "이의신청 결과 반영: 판정(T_STATUS: A=인용/R=기각)에 따라 인용 시 청구액을 양(+)의", + "in": [ + "T_PURCHASE_ID", + "T_DELTA", + "T_MERCHANT", + "T_STATUS", + "T_BIZDATE" + ], + "out": [ + "T_ADJ" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9928,6 +17565,24 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_EVENTFEE", + "desc": "이벤트 수수료 감면: 이벤트 기간 거래에 대해 정상 요율의 절반을 적용한 감면", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_AMOUNT", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_FEE", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9960,6 +17615,40 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_FLATFEE", + "desc": "정액 수수료 적용: 거래 건마다 고정 금액 수수료를 정산 상세(st_settle_dtl)에", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_FEE", + "T_ID1" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_FLATVSPCT", + "desc": "정액 대 정률 비교: 정액(T_ARG1)과 정률(금액 x T_ARG2 bps)을 각각 계산해 더 저렴한", + "in": [ + "T_AMOUNT", + "T_ARG1", + "T_ARG2" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_FEE", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9990,6 +17679,69 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_GRADESTAT", + "desc": "가맹점 등급별 통계: 가맹점 요율(mdr_bps) 그룹별 가맹점 수를 커서로 집계해 등급", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2", + "T_RC" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_GROWTHINC", + "desc": "성장 인센티브: 당일 매출과 비교일(T_STR1) 매출을 비교해 증가분(성장분)에 대해서만", + "in": [ + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_RC", + "T_DELTA", + "T_ADJ", + "T_AMT1" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_GRPALLOC", + "desc": "그룹 정산 배분: 그룹 통합 지급총액(T_AMOUNT)을 구성원의 net 비중에 따라 안분해", + "in": [ + "T_AMOUNT", + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_AMOUNT" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_GRPROLLUP", + "desc": "가맹점 그룹 집계 rollup: prefix(T_KEY1) 로 묶인 그룹 구성원의 당일 매출/수수료/net", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_FEE", + "T_NET" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10031,6 +17783,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_INSTANTPAY", + "desc": "즉시 정산 지급: 정산 net 을 당일 지급으로 즉시 확정(status=PAID)해 지급", + "in": [ + "T_NET", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10087,6 +17854,37 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_LOCKBATCH", + "desc": "정산 배치 잠금: 당일 확정된 정산(status=SETTLED)을 일괄 LOCKED 로 전환해 이후", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_LOYALTY", + "desc": "로열티 인센티브: 가맹점의 전체 기간 누적 매출(st_merch_settle 전 일자 합계)에", + "in": [ + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_GROSS", + "T_COUNT", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10134,6 +17932,22 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_MKTGSHARE", + "desc": "마케팅 분담금 정산: 가맹점 일별 집계(st_merch_settle)의 총매출에 분담율", + "in": [ + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_GROSS", + "T_FEE" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10147,6 +17961,20 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_MONTHSTAT", + "desc": "월별 정산 통계: 대상 월(T_STR1, YYYY-MM)에 속하는 정산의 건수/net 합계/최대 net 을", + "in": [ + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_AMT1" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10174,6 +18002,18 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_NEXTPAYDT", + "desc": "다음 지급 영업일 계산: 입력 영업일의 다음 영업일(주말 스킵)을 공통 유틸", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10206,6 +18046,40 @@ const SVC_SCREENS = [ "T_SETTLE_ID" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_PARTNERSET", + "desc": "제휴사 정산 배분: 정산 net 금액에서 제휴 지분율(T_ARG1 bps)만큼을 제휴사", + "in": [ + "T_NET", + "T_ARG1", + "T_STR1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1", + "T_AMOUNT" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_PARTPAY", + "desc": "부분 지급 처리: 정산 net 중 일부(T_ARG1)만 당일 지급(PAID)으로 확정하고", + "in": [ + "T_NET", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_AMOUNT", + "T_NET" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10244,6 +18118,58 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_PCTFEE", + "desc": "정률 수수료 계산: 거래 금액에 입력 요율(bps, T_ARG1)을 곱해(acq_fee) 수수료를", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_AMOUNT", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_FEE", + "T_RC", + "T_ID1" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_PRECONFIRM", + "desc": "정산 확정 전 검증: 당일 지급 중 보류(HELD) 건이 남아있는지 확인해 확정 가능", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_RC" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_PROMOAPPLY", + "desc": "프로모션 할인 적용: 산정된 수수료(T_FEE)에 프로모션 할인율(T_ARG1 bps)을 적용해", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_FEE", + "T_ARG1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_DELTA", + "T_NET" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10282,6 +18208,76 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_RATECARD", + "desc": "요율 카드 조회: 특정 수수료 유형의 등록 구간 개수와 최소/최대 요율을 집계해", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_AMT1", + "T_AMT2" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_RATECMP", + "desc": "요율 유형 비교: 두 수수료 유형(T_STR1/T_STR2)의 입력 금액 적용 요율을", + "in": [ + "T_AMOUNT", + "T_STR1", + "T_STR2" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA", + "T_FEE" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_RATEFLOOR", + "desc": "요율 하한 적용: 정률 수수료(금액 x bps)가 하한액(T_ARG2)에 미달하면 하한으로", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_AMOUNT", + "T_ARG1", + "T_ARG2", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_FEE", + "T_RC" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_RATEMAX", + "desc": "요율 상한 적용: 정률 수수료(금액 x bps)를 계산하되 상한액(T_ARG2)을 초과하면", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_AMOUNT", + "T_ARG1", + "T_ARG2", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_FEE", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10297,6 +18293,24 @@ const SVC_SCREENS = [ "T_FEE" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_REBATE", + "desc": "리베이트 정산: 부과 수수료(T_FEE)의 일정 비율(T_ARG1 bps)을 가맹점에 돌려주는", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_FEE", + "T_ARG1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10309,6 +18323,20 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_RECVAGE", + "desc": "미수금 연령 분석: 기준일(T_BIZDATE) 이전 지급일의 보류(HELD) 지급 건수와 금액을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10324,6 +18352,57 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_RECVSTAT", + "desc": "미수금 통계: 지급(st_payment) 상태별 건수와 금액을 커서로 순회 집계해 보류/예정/", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_AMT1" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_REFERRAL", + "desc": "추천 인센티브: 추천으로 유치된 가맹점 수(prefix T_KEY1 매칭 건수)에 건당 보상액", + "in": [ + "T_ARG1", + "T_MERCHANT", + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1", + "T_COUNT", + "T_AMOUNT" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_REFUNDCALC", + "desc": "환불 재계산: 환불액(T_ARG1)에 비례한 수수료 환급분을 계산해 정산 상세에", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_ARG1", + "T_ARG2", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_FEE", + "T_ID1" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10340,6 +18419,24 @@ const SVC_SCREENS = [ "T_FEE" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_RELAYSPLIT", + "desc": "중계수수료 분배: 중계 수수료(T_FEE)를 지분율(T_ARG1 bps)에 따라 중계사 몫과", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_FEE", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10364,6 +18461,25 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_REPARTIAL", + "desc": "부분 재정산: 부분취소액(T_DELTA)만큼 줄어든 잔여 거래금액으로 수수료를 재계산해", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_AMOUNT", + "T_DELTA", + "T_ARG1", + "T_BIZDATE" + ], + "out": [ + "T_ADJ", + "T_FEE", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10425,6 +18541,20 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_SLABFEE", + "desc": "슬라브(누진 구간) 요율 계산: 요율표의 각 구간에 대해 금액이 걸치는 부분에만", + "in": [ + "T_AMOUNT", + "T_STR1" + ], + "out": [ + "T_FEE", + "T_COUNT" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10496,6 +18626,21 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_SUPPLYCALC", + "desc": "공급가액 역산: 부가세 포함 총액(T_AMOUNT)에서 공급가액과 부가세를 분리", + "in": [ + "T_AMOUNT" + ], + "out": [ + "T_RC", + "T_GROSS", + "T_NET", + "T_TAX" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10513,6 +18658,36 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_TAXSUMMARY", + "desc": "세금 요약: 입력 수수료(T_FEE)를 공급가액으로 부가세(acq_vat)와 원천세", + "in": [ + "T_FEE", + "T_ARG1" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_TAX", + "T_NET" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_TIERFEE", + "desc": "구간별 정률 적용: 입력 금액에 해당하는 최상위 구간(band_min <= amount)의 요율을", + "in": [ + "T_AMOUNT", + "T_STR1" + ], + "out": [ + "T_RC", + "T_FEE" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10541,6 +18716,19 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_UNLOCK", + "desc": "정산 잠금 해제: 잠금된 정산(status=LOCKED)을 다시 SETTLED 로 되돌려 조정/재계산을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10556,6 +18744,20 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_VANDETAIL", + "desc": "VAN 수수료 세분 명세: 당일 정산 상세 중 VAN 라인의 건수/합계/평균을 집계해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_FEE", + "T_AMT1" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10586,6 +18788,41 @@ const SVC_SCREENS = [ "T_TAX" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_VATADJUST", + "desc": "부가세 조정: 조정된 공급가액(T_ARG1)에 대한 재산정 부가세(acq_vat)를 정산 상세에", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_ARG1", + "T_TAX", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_TAX", + "T_DELTA", + "T_ID1" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_VATDETAIL", + "desc": "부가세 명세: 가맹점 당일 수수료 상세(MDR/VAN/RELAY 합계)를 공급가액으로 보고", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_TAX", + "T_COUNT" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10602,6 +18839,39 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_VOIDRECALC", + "desc": "void(전체 취소) 재계산: 매입 정산 net 전액을 역분개(음수 조정)하고 해당", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_NET", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_ADJ", + "T_DELTA" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_VOLREBATE", + "desc": "거래량 리베이트: 가맹점 일별 집계의 거래 건수 구간에 따라 리베이트율을 차등", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_COUNT", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10618,6 +18888,22 @@ const SVC_SCREENS = [ "T_TAX" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_WHTDETAIL", + "desc": "원천세 명세: 가맹점 당일 수수료 합계에 원천징수율(T_ARG1 bps)을 적용해", + "in": [ + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_TAX", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10634,6 +18920,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_WORKFLOW", + "desc": "정산 워크플로 상태 전이: 요청 전이(from=T_STR1 -> to=T_STR2)가 허용 전이표", + "in": [ + "T_STR1", + "T_STR2", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10732,6 +19033,32 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_APPRLATE", + "desc": "기능: 지연 승인 검출 건별 (승인시각-매입생성 경과초>T_ARG1 기본86400, RC 1=지연).", + "in": [ + "T_PURCHASE_ID", + "T_ARG1" + ], + "out": [ + "T_ARG2", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_APPRNOPUR", + "desc": "기능: 미매입 승인 검출 (매입 원거래 없는 고아 승인 전수, RC 1=존재; 최소 승인ID 반환).", + "in": [], + "out": [ + "T_COUNT", + "T_ID1", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10745,6 +19072,67 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_AXLEDGDC", + "desc": "기능: 정합성 축 검증 원장 대차 (RECON 차변 vs SETTLE 대변, RC 비트 1=대차불일치 2=지급대응 없음).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_AMT3", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_AXPURAPPR", + "desc": "기능: 정합성 축 검증 매입-승인 (일자별 총매입금액 vs 승인가결 매입금액 합, RC 1=불일치).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_AXPURSETT", + "desc": "기능: 정합성 축 검증 매입-정산 (정산대상 매입 순액 vs settlement 순액 합, RC 1=불일치).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_COUNT", + "T_DELTA", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_AXSETTPAY", + "desc": "기능: 정합성 축 검증 정산-지급 (settlement 순액 합 vs 원장 지급 XFER 합, RC 1=불일치).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10802,6 +19190,22 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_CANCELRATE", + "desc": "기능: 취소율 검증 (취소 건수/매입 건수 비율>=T_ARG1% 기본10, RC 1=취소율 과다).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10814,6 +19218,38 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_CATSKEW", + "desc": "기능: 업종 편중 탐지 (최다 업종 비중>=T_ARG1% 기본80, RC 1=편중).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_CHANSPLIT", + "desc": "기능: 채널 분포 검증 (FOREIGN 채널 비중>=T_ARG1% 기본30, RC 1=편중).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10863,6 +19299,30 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_DATEFUT", + "desc": "기능: 미래일자 매입 검출 (영업일이 오늘 이후인 매입 수, RC 1=존재).", + "in": [], + "out": [ + "T_COUNT", + "T_ID1", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_DATEGAP", + "desc": "기능: 매입-정산 일자 역전 검출 (정산일<매입일 건수, RC 1=존재; 최소 정산ID 반환).", + "in": [], + "out": [ + "T_COUNT", + "T_SETTLE_ID", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10907,6 +19367,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_DUPAPPR", + "desc": "기능: 중복 승인 검출 건별 (동일 매입 승인가결 2건 이상, RC 1=중복; 검증로그 기록).", + "in": [ + "T_PURCHASE_ID", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10920,6 +19394,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_DUPLEDGR", + "desc": "기능: 중복 원장 엔트리 검출 (동일 매입+엔트리유형 최대 그룹 건수>1, RC 1=중복).", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10949,6 +19436,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_DUPPURCH", + "desc": "기능: 중복 매입 검출 건별 (동일 가맹점+금액+영업일의 타 매입 존재, RC 1=중복).", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10965,6 +19465,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_DUPSETTMR", + "desc": "기능: 가맹점 중복 정산 검출 (동일 가맹점+영업일 settlement 2건 이상, RC 1=중복).", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11007,6 +19521,21 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_FEEHIGH", + "desc": "기능: 수수료 과다 검출 건별 (fee 실효요율 bps>T_ARG1 기본500, RC 1=과다 8=매입없음).", + "in": [ + "T_PURCHASE_ID", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_FEE", + "T_ARG2" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11069,6 +19598,21 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_ISSUERMIX", + "desc": "기능: 발급사 분포 이상 (최다 발급사 비중>=T_ARG1% 기본80, RC 1=편중 8=무거래).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2", + "T_ARG3" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11158,6 +19702,37 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_LIMITPCT", + "desc": "기능: 한도 사용률 검증 (가맹점 일 매입합/한도 %가 T_ARG1 기본90 이상, RC 1=근접 8=가맹점없음).", + "in": [ + "T_MERCHANT", + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2", + "T_AMT1" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_LONGOPEN", + "desc": "기능: 장기 미결 의심거래 검출 (OPEN 상태로 N일 초과 경과, 기준 T_ARG1 기본7, RC 1=존재).", + "in": [ + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ID1", + "T_ARG2", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11196,6 +19771,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_NEGFEE", + "desc": "기능: 음수 수수료 매입 검출 (일자별 fee<0 매입 수, RC 1=존재; 최소 매입ID 반환).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ID1", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11211,6 +19800,63 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_NIGHTTXN", + "desc": "기능: 심야 거래 패턴 탐지 (0-6시 거래 비중>=T_ARG1% 기본20, RC 1=편중).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_OUTLIER", + "desc": "기능: 이상치 매입 탐지 (금액>당일 평균*배수 T_ARG1 기본4 건수, RC 1=존재).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_OVERLIMIT", + "desc": "기능: 가맹점 한도위반 검출 (일 매입합>일한도 가맹점 수, RC 1=존재).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_PURNOAPPR", + "desc": "기능: 무승인 매입 검출 (일자별 승인 전무 매입 수, 취소분 제외, RC 1=존재).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ID1", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11265,6 +19911,54 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RIAPPR", + "desc": "기능: 참조무결성 승인 (취소/무효 매입에 승인가결 존재 건수, RC 1=위반).", + "in": [], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RICANCEL", + "desc": "기능: 참조무결성 취소 (일자별 매입 원거래 없는 취소 수, RC 1=위반; 최소 취소ID 반환).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_CANCEL_ID", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RIFX", + "desc": "기능: 참조무결성 해외 (매입 원거래 없는 해외 내역 수+환산합, RC 1=위반).", + "in": [], + "out": [ + "T_COUNT", + "T_AMT1", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RIINSTALL", + "desc": "기능: 참조무결성 할부 (매입 원거래 없는 할부 내역 수, NOT IN, RC 1=위반).", + "in": [], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11295,6 +19989,18 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RIPAY", + "desc": "기능: 참조무결성 지급 (지급 XFER 원장의 매입 원거래 부재 건수, RC 1=위반).", + "in": [], + "out": [ + "T_COUNT", + "T_ID1", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11324,6 +20030,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RITAXINV", + "desc": "기능: 참조무결성 세금계산서 (매입 원거래 없는 세금계산서 수, LEFT JOIN, RC 1=위반).", + "in": [], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_ROUNDAMT", + "desc": "기능: 라운드금액 패턴 탐지 (10만원 배수 매입 비중>=T_ARG1% 기본50, RC 1=편중).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11337,6 +20070,33 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RULECOUNT", + "desc": "기능: 검증규칙 심각도 분포 (INFO/WARN/ERROR/CRIT 건수, RC 1=CRIT 규칙 존재).", + "in": [], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RULEFIND", + "desc": "기능: 검증규칙 단건 조회 (dbio vldb_get_rule 로 임계값/심각도 반환, RC 1=미존재).", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11380,6 +20140,18 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RULEON", + "desc": "기능: 검증규칙 활성화 (status ACTIVE 전환, RC 1=이미 활성 2=상태변경 실패 3=규칙없음).", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11395,6 +20167,32 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RULERENAME", + "desc": "기능: 검증규칙 명칭 변경 (rule_name UPDATE, 빈 값이면 기본명, RC 1=대상 규칙 없음).", + "in": [ + "T_STR3", + "T_STR1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RULESEV", + "desc": "기능: 검증규칙 심각도 변경 (INFO/WARN/ERROR/CRIT 유효성 검사 후 UPDATE, RC 1=규칙없음 2=심각도오류).", + "in": [ + "T_STR3", + "T_STR4" + ], + "out": [ + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11408,6 +20206,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RULEVER", + "desc": "기능: 검증규칙 최신성 검사 (T_ARG1 기본30일 이내 갱신 여부, RC 1=노후 2=규칙없음).", + "in": [ + "T_STR3", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11434,6 +20246,52 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SETTLREV", + "desc": "기능: 정산 역전 검출 (일자별 순액 음수 settlement 건수, RC 1=존재; 최악치 반환).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMT1", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SPIKEAMT", + "desc": "기능: 금액 급증 탐지 (당일 최대 매입>평균*배수 T_ARG1 기본5, RC 1=급증).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_STATMIX", + "desc": "기능: 상태 분포 검증 (취소 C 상태 비중>=T_ARG1% 기본15, RC 1=취소 과다).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11475,6 +20333,35 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUMFEECHK", + "desc": "기능: 통계-원천 수수료 대사 (일자별 가맹점 수수료 집계 vs 원천 합 불일치 수, RC 1=존재).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUMGROSS", + "desc": "기능: 통계-원천 총액 대사 (merchant_summary 총액 vs 원천 매입금액 합, RC 1=불일치 8=통계없음).", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_GROSS", + "T_AMT1", + "T_DELTA" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11488,6 +20375,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUMNETCHK", + "desc": "기능: 통계-원천 순액 대사 (merchant_summary 순액 vs 원천 매입순액 합, RC 1=불일치 8=통계없음).", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_NET", + "T_AMT1" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11505,6 +20407,22 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUMTXNCNT", + "desc": "기능: 통계-원천 건수 대사 (merchant_summary 건수 vs 원천 매입 건수, RC 1=불일치 8=통계없음).", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_ARG1", + "T_DELTA" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11534,6 +20452,59 @@ const SVC_SCREENS = [ "T_PURCHASE_ID" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUSPAGEAVG", + "desc": "기능: 의심거래 평균 경과일 (OPEN 평균 경과일>T_ARG1 기본5, RC 1=평균 경과 초과).", + "in": [ + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUSPASSIGN", + "desc": "기능: 의심거래 담당 배정 (미종결 건 detail 에 담당자 기록, RC 1=대상없음/종결됨).", + "in": [ + "T_ID1", + "T_STR1" + ], + "out": [ + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUSPBULK", + "desc": "기능: 의심거래 일괄 확인 (일자별 OPEN 건 CONFIRMED 일괄 전환, RC 1=처리 대상 없음).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUSPESCAL", + "desc": "기능: 의심거래 상향 (WARN→ERROR→CRIT 단계 승격, RC 1=상향불가 3=미존재).", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11547,6 +20518,33 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUSPNOTE", + "desc": "기능: 의심거래 코멘트 추가 (상태 무관 detail 에 메모 append, RC 1=대상 미존재).", + "in": [ + "T_ID1", + "T_MSG" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUSPRECHK", + "desc": "기능: 의심거래 재검증 (CONFIRMED→OPEN 재개, 재검증자 기록, RC 1=CONFIRMED 아님/미존재).", + "in": [ + "T_ID1", + "T_STR1" + ], + "out": [ + "T_ARG1", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11576,6 +20574,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUSPTRIAGE", + "desc": "기능: 의심거래 분류 (OPEN 건 심각도 재지정, RC 1=대상없음/OPEN아님 2=심각도오류).", + "in": [ + "T_ID1", + "T_STR4" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11603,6 +20615,20 @@ const SVC_SCREENS = [ "T_ARG3" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_THRCOMPARE", + "desc": "기능: 임계값 비교 (실측 T_ARG1 vs 규칙 임계값, RC 1=초과 8=규칙없음).", + "in": [ + "T_STR3", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11617,6 +20643,46 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_THRGET", + "desc": "기능: 임계값 조회 (규칙 임계값+심각도+상태 직접 SELECT, RC 1=규칙 없음).", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_THRRESET", + "desc": "기능: 임계값 초기화 (threshold 0 으로 리셋, RC 1=대상 규칙 없음).", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_THRSCALE", + "desc": "기능: 임계값 배율 조정 (threshold *= T_ARG1%, RC 2=배율오류 3=규칙없음).", + "in": [ + "T_STR3", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2", + "T_ARG3" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11676,6 +20742,20 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_TXNREV", + "desc": "기능: 역전거래 검출 건별 (RC 비트 1=음수금액 2=음수순액 4=순액>금액, 8=매입없음).", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_RC", + "T_AMOUNT", + "T_NET" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11702,6 +20782,33 @@ const SVC_SCREENS = [ "T_ARG2", "T_RC" ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_VELOCITY", + "desc": "기능: 거래 속도 이상 탐지 (당일 매입 건수>T_ARG1 기본100 가맹점 수, RC 1=존재).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_ZERONET", + "desc": "기능: 정산순액 소실 검출 (금액>0인데 순액=0 매입 수, RC 1=존재).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] } ]; diff --git a/app/ui/screens.js b/app/ui/screens.js index 8a9b108..2ba769b 100644 --- a/app/ui/screens.js +++ b/app/ui/screens.js @@ -16,6 +16,22 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ALERTAMT", + "desc": "매입 금액초과 알림: 가맹점의 영업일 매입 중 임계금액(T_AMOUNT)을 초과한", + "in": [ + "T_AMOUNT", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMT1", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -32,6 +48,50 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ALERTDUP", + "desc": "매입 중복 알림: 가맹점의 영업일 매입 중 동일 금액이 2건 이상 반복된", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ALERTQRY", + "desc": "매입 알림 유형별 조회: 미매입 원장을 알림 유형(속도/한도/중복) 접두어로", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_COUNT" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ALERTVEL", + "desc": "매입 속도 알림: 채널의 최근 5분간 유입 건수를 재어 분당 허용치를 넘으면", + "in": [ + "T_ARG1", + "T_CHANNEL" + ], + "out": [ + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -84,6 +144,80 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_BRANDQRY", + "desc": "매입 브랜드별 조회: 지정 발급사(T_ISSUER)의 영업일 매입 건수와 총액,", + "in": [ + "T_ISSUER", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_FEE", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_BRANDRANK", + "desc": "매입 브랜드별 순위: 발급사 매출 상위 순으로 커서 조회하며 누적 점유율을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_ARG1", + "T_ARG2" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_BRANDSUM", + "desc": "매입 브랜드별 집계: 발급사(카드 브랜드) 기준으로 매출 합계를 그룹 집계해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_ARG1" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_BULKCHK", + "desc": "대량매입 배치검증: 영업일 접수(A) 매입을 커서로 순회하며 금액 양수 및", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_BULKRPT", + "desc": "대량매입 리포트: 영업일 매입을 상태(접수/대사/정산/취소)별로 한 번에 분해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_GROSS", + "T_NET" + ] + }, { "mod": "ac", "modnm": "매입", @@ -115,6 +249,20 @@ const SVC_SCREENS = [ "T_CANCEL_ID" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CB_ARB", + "desc": "지불거절 중재: 라운드2까지 미해결 건을 카드사 중재로 넘긴다. 해당 CB", + "in": [ + "T_CANCEL_ID", + "T_REASON" + ], + "out": [ + "T_RC", + "T_PURCHASE_ID" + ] + }, { "mod": "ac", "modnm": "매입", @@ -129,6 +277,19 @@ const SVC_SCREENS = [ "T_PURCHASE_ID" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CB_FINAL", + "desc": "지불거절 최종확정: 중재 결과로 매입사 패소가 확정되면 원매입을 취소(C)로", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -159,6 +320,35 @@ const SVC_SCREENS = [ "T_AMOUNT" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CB_REVERSE", + "desc": "지불거절 역전: 방어 승소로 CB 를 무효화한다. 최근 CB/CB2 행을 조회해", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_RC", + "T_CANCEL_ID" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CB_RND2", + "desc": "지불거절 2차(라운드2): 1차 방어 이후 재청구를 접수한다. 원매입에 이미", + "in": [ + "T_PURCHASE_ID", + "T_AMOUNT", + "T_REASON", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_CANCEL_ID" + ] + }, { "mod": "ac", "modnm": "매입", @@ -175,6 +365,32 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CCYQRY", + "desc": "매입 통화별 조회: 지정 통화(T_CCY)의 원통화 합계와 원화 환산 합계를", + "in": [ + "T_CCY" + ], + "out": [ + "T_FX_AMT", + "T_GROSS", + "T_DELTA" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CCYRANK", + "desc": "매입 통화별 순위: 해외매입 통화 그룹을 원화 합계 내림차순으로 커서 조회해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1" + ] + }, { "mod": "ac", "modnm": "매입", @@ -191,6 +407,34 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CHANDETAIL", + "desc": "매입 채널별 상세 조회: 영업일 채널 그룹의 건수/총액/평균을 커서로 훑어", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_COUNT", + "T_ARG1", + "T_AMT2" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CHANRANK", + "desc": "매입 채널별 순위: 영업일 채널을 건수 내림차순으로 정렬해 커서 조회하고", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, { "mod": "ac", "modnm": "매입", @@ -247,6 +491,19 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CORRAPPR", + "desc": "매입 정정 승인: 접수된 조정(ac_fee_adj) 행의 사유 앞에 OK 승인 표식을", + "in": [ + "T_ADJ" + ], + "out": [ + "T_RC", + "T_ADJ" + ] + }, { "mod": "ac", "modnm": "매입", @@ -275,6 +532,66 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CORRRSN", + "desc": "매입 정정 사유별 처리: 수수료 조정 원장(ac_fee_adj)에 사유 코드가 붙은", + "in": [ + "T_PURCHASE_ID", + "T_DELTA", + "T_REASON", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CORRSTAT", + "desc": "매입 정정 사유별 통계: 조정 원장을 사유 접두어별로 그룹 집계해 사유코드·", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_ARG1" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CTRYQRY", + "desc": "매입 국가별 조회: 통화권역 대표 통화(T_CCY)로 해외매입(ac_fx)을 필터해", + "in": [ + "T_CCY", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_ARG1" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_CTRYRANK", + "desc": "매입 국가별 순위: 통화코드를 권역(미주/일본/유럽/기타)으로 분류하는 C", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -336,6 +653,21 @@ const SVC_SCREENS = [ "T_AMOUNT" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_DOWSTAT", + "desc": "매입 요일별 통계: 지정 기간(T_STR1~T_STR2)의 매입을 요일(0~6)별로 그룹", + "in": [ + "T_STR1", + "T_STR2" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -394,6 +726,21 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_EDIQRY", + "desc": "EDI 조회: 지정 가맹점(T_MERCHANT)의 영업일 EDI 접수 문서 건수와 금액", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -408,6 +755,153 @@ const SVC_SCREENS = [ "T_AMOUNT" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_EDIRSNDCH", + "desc": "EDI 재전송 채널별: 영업일 미수신 EDI 문서를 가맹점별로 커서 조회해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_EDISTAT", + "desc": "EDI 통계: 영업일 EDI 문서를 금액대(소액/중액/고액)로 나눠 대역별 건수를", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_GROSS" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ERRCLASS", + "desc": "매입 오류유형별 분류: 미매입 원장(ac_unmatch)의 사유 접두어를 읽어", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_COUNT" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ERRQRY", + "desc": "매입 오류유형별 조회: 사유 접두어(T_REASON)로 시작하는 미매입 건을", + "in": [ + "T_REASON" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ERRRETRY", + "desc": "매입 오류 재처리: 보류(H) 상태로 묶여 있던 오류 매입을 접수(A)로 되돌려", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ERRSTAT", + "desc": "매입 오류유형별 통계: 미매입 원장을 사유 접두어 기준으로 한 번의 집계에서", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_COUNT" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ESCROW", + "desc": "매입 에스크로 예치: 분쟁 소지 매입 금액을 정산 전 유보하기 위해 원장에", + "in": [ + "T_PURCHASE_ID", + "T_AMOUNT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_SETTLE_ID" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_ESCROWREL", + "desc": "매입 에스크로 해제: 예치(ESCROW)와 해제(ESCROW_REL) 원장 잔액을 계산해", + "in": [ + "T_PURCHASE_ID", + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_EVENTBUY", + "desc": "이벤트 매입: 프로모션 행사 매입을 EVENT 채널로 접수한다. 활성 가맹점만", + "in": [ + "T_AMOUNT", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_PURCHASE_ID", + "T_FEE" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_EXCHANGE", + "desc": "매입 교환 처리: 반품 후 동일가 재구매를 한 번에 처리한다. 원매입에 대해", + "in": [ + "T_PURCHASE_ID", + "T_AMOUNT", + "T_REASON", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_CANCEL_ID" + ] + }, { "mod": "ac", "modnm": "매입", @@ -522,6 +1016,67 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_GRADESTAT", + "desc": "매입 가맹점등급별 통계: 가맹점 MDR(bps)을 프리미엄/일반/기본 등급으로 나눠", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_GROSS" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_GUARANTEE", + "desc": "매입 보증 등록: 대형 매입에 대한 이행보증금을 원장에 GUARANTEE 항목으로", + "in": [ + "T_PURCHASE_ID", + "T_AMOUNT", + "T_ARG1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_AMT1", + "T_SETTLE_ID" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_GUARQRY", + "desc": "매입 보증 조회: 한 매입에 적립된 보증금(GUARANTEE) 총액과 건수를 원장에서", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_AMT1", + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_HALFSTAT", + "desc": "매입 반기별 통계: 지정 연도(T_ARG1) 매입을 상반기(1~6월)/하반기(7~12월)로", + "in": [ + "T_ARG1" + ], + "out": [ + "T_ARG2", + "T_ARG3", + "T_COUNT", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -548,6 +1103,22 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_HOURSTAT", + "desc": "매입 시간대별 통계: 접수시각을 새벽/오전/오후/야간 네 구간으로 나눠 구간별", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -563,6 +1134,64 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_INSTCXL", + "desc": "할부 취소: 한 매입의 할부 스케줄 전 회차를 원장에서 삭제해 할부를 해지한다.", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_INSTPLAN", + "desc": "할부 계획 조회: 한 매입의 약정 개월수와 회차 합계, 회차당 금액을 단건", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_INSTALL_N", + "T_GROSS", + "T_AMT1", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_INSTPREPAY", + "desc": "할부 조기상환: 지정 회차(T_ARG1) 이후 남은 할부금 합계를 계산해 원장에", + "in": [ + "T_PURCHASE_ID", + "T_ARG1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_GROSS", + "T_COUNT" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_INSTQRY", + "desc": "할부 회차조회: 한 매입의 할부 스케줄(ac_install)을 회차 순으로 커서 조회해", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_INSTALL_N", + "T_GROSS", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -604,6 +1233,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_LIMITEXC", + "desc": "매입 한도 예외처리: 가맹점 일한도와 당일 누적 매입을 비교해 한도 초과 시", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_DELTA" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_LIMITOVR", + "desc": "매입 한도 초과 승인: 한도 초과로 보류(H)된 매입을 관리자가 예외 승인해", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -746,6 +1402,51 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_PROMOQRY", + "desc": "프로모션 조회: 영업일 프로모션(category=PROMO) 매입의 건수와 매출, 감면", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_FEE", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_PROMOSTAT", + "desc": "프로모션 통계: 영업일 업종/분류별 매출을 그룹 집계해 분류코드·매출 쌍을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_AMT2", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_QTRSTAT", + "desc": "매입 분기별 통계: 지정 연도(T_ARG1)의 매입 매출을 1~4분기로 나눠 분기별", + "in": [ + "T_ARG1" + ], + "out": [ + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_ARG5", + "T_GROSS" + ] + }, { "mod": "ac", "modnm": "매입", @@ -762,6 +1463,47 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_REBUYAPPR", + "desc": "재매입 승인: 취소(C)된 원매입에 한해 승인 원장(approval)에 재승인 행을", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_RC", + "T_ID1" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_REBUYHIST", + "desc": "재매입 이력: 한 매입의 승인 원장을 승인시각 순으로 커서 조회해 승인 ID를", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_REBUYREJ", + "desc": "재매입 거절: 재매입 요청을 반려하고 미매입 원장에 REBUY_REJ 사유로", + "in": [ + "T_PURCHASE_ID", + "T_REASON", + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -885,6 +1627,35 @@ const SVC_SCREENS = [ "T_AMOUNT" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_RETURNQRY", + "desc": "매입 반품 조회: 가맹점의 영업일 반품/교환(RETURN·EXCHANGE) 취소 이력을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_RETURNSTAT", + "desc": "매입 반품 통계: 영업일 취소 원장을 반품/교환/환불 유형별로 한 번에 분해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_GROSS" + ] + }, { "mod": "ac", "modnm": "매입", @@ -900,6 +1671,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_SETTLEEXP", + "desc": "매입 정산 예정 조회: 가맹점의 대사완료(M) 매입 건수와 순정산 예정 총액을", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -915,6 +1701,22 @@ const SVC_SCREENS = [ "T_SETTLE_ID" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_SETTLEPRE", + "desc": "매입-정산 예정대사: 가맹점의 영업일 대사완료(M) 매입 순정산 예정액과 이미", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_NET", + "T_AMT1", + "T_DELTA", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -987,6 +1789,36 @@ const SVC_SCREENS = [ "T_TAX" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_TAXMODIFY", + "desc": "세금계산서 수정발행: 기 발행된 계산서(ac_tax_invoice)의 공급가·부가세를", + "in": [ + "T_ID1", + "T_GROSS" + ], + "out": [ + "T_RC", + "T_GROSS", + "T_TAX" + ] + }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_TAXQRY", + "desc": "세금계산서 조회: 한 매입에 발행된 계산서의 공급가·부가세 누계와 발행", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_GROSS", + "T_TAX", + "T_COUNT", + "T_RC" + ] + }, { "mod": "ac", "modnm": "매입", @@ -1002,6 +1834,20 @@ const SVC_SCREENS = [ "T_TAX" ] }, + { + "mod": "ac", + "modnm": "매입", + "svc": "ACQ_TAXREVERSE", + "desc": "세금계산서 역발행: 매출 취소분에 대한 (-)공급가 계산서를 새로 발행한다.", + "in": [ + "T_ID1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID2" + ] + }, { "mod": "ac", "modnm": "매입", @@ -1264,6 +2110,23 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "AUTH_PARTAPPR", + "desc": "부분 승인: 가용한도(일한도-사용액)가 요청액보다 작으면 가용한도만큼만", + "in": [ + "T_KEY1", + "T_MERCHANT", + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_AMT1", + "T_RC", + "T_ID2" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1309,6 +2172,32 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "AUTH_REPROC_REQ", + "desc": "승인 재처리 요청: 거절(D)/보류(H) 상태의 승인을 재처리 대상으로 다시", + "in": [ + "T_ID2" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "AUTH_REPROC_STAT", + "desc": "재처리 현황: 영업일 승인 원장에서 보류(H) 대기 건수와, 갱신시각이 생성시각", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1438,6 +2327,18 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "BLACKLIST_COUNT", + "desc": "블랙리스트 통계: 전체 등재 수, 도난(REPORTED_STOLEN/STOLEN) 등재 수,", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1464,6 +2365,87 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "BLACKLIST_PROMO", + "desc": "블랙 승격 후보 조회: 영업일 부정 적발 점수 합이 1500 이상이면서 아직", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "BRAND_AUTH_STAT", + "desc": "브랜드별 승인 통계: 영업일 승인 원장을 카드 선두자리(브랜드)로 그룹핑해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "BRAND_POLICY", + "desc": "브랜드별 승인 정책: 카드 선두자리로 브랜드(VISA/MC/AMEX/LOCAL)를 판별하고", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CANCEL_AMT_INQ", + "desc": "취소가능액 조회: 원승인 금액에서 이미 취소된 누계를 빼 취소가능 잔액을", + "in": [ + "T_ID2" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_AMT3", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CANCEL_FULL", + "desc": "전액 취소: 승인(A)/매입확정(M) 상태의 원승인을 전액 취소 처리한다. 취소", + "in": [ + "T_ID2", + "T_REASON" + ], + "out": [ + "T_RC", + "T_ID1", + "T_AMT1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CANCEL_HISTORY", + "desc": "승인건 취소 이력: 특정 원승인에 걸린 취소들을 시간순 커서로 순회하며", + "in": [ + "T_ID2" + ], + "out": [ + "T_COUNT", + "T_AMT1" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1493,6 +2475,35 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CANCEL_SUMMARY", + "desc": "일별 취소 집계: 영업일 취소 원장을 전체/전액(FULL)/부분(PARTIAL)로 나눠", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMT1", + "T_ARG1", + "T_ARG2" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CARD_ACTIVATE", + "desc": "카드 신규 활성화: 원장이 없으면 요청 한도(기본 100만원)로 생성하고, 이미", + "in": [ + "T_KEY1", + "T_AMT1" + ], + "out": [ + "T_AMT1", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1512,6 +2523,45 @@ const SVC_SCREENS = [ "T_ARG5" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CARD_EXPIRE_CHK", + "desc": "카드 휴면(만료 근사) 점검: 마지막 승인 이후 경과일을 EPOCH 로 환산해", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CARD_LOST_REG", + "desc": "분실 등록: 카드 원장 상태를 LOST 로 바꾸고 블랙리스트에 LOST 사유로", + "in": [ + "T_KEY1", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CARD_REACTIVATE", + "desc": "카드 재활성화: 정지/동결/분실 상태의 원장을 ACTIVE 로 되돌리고, 분실·토큰", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1538,6 +2588,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CARD_STATE_HIST", + "desc": "카드 승인상태 이력 분포: 카드의 전체 승인 건을 상태(status)별로 집계하는", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1554,6 +2617,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CARD_STOLEN_REG", + "desc": "도난 등록: 원장 상태를 STOLEN 으로 전환하고 블랙리스트를 STOLEN 사유로", + "in": [ + "T_KEY1", + "T_REASON", + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1580,6 +2658,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CHANNEL_POLICY", + "desc": "채널별 승인 정책: 매입 원장에서 당일 해당 채널(POS/EDC/EDI/FOREIGN)의", + "in": [ + "T_CHANNEL" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "CHANNEL_STAT", + "desc": "채널별 매입 통계: 영업일 매입을 채널별로 그룹핑하는 커서를 순회하며", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1607,6 +2712,18 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "FRAUD_CASE_CLOSE", + "desc": "부정 케이스 종결: 미종결 부정로그의 사유 뒤에 '/CLOSED' 를 붙여 종결 표시", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1621,6 +2738,18 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "FRAUD_CASE_LIST", + "desc": "부정 케이스 상위목록: 영업일 부정로그를 점수 내림차순 상위 5건 커서로", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1686,6 +2815,66 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "FRAUD_RULE_INTL", + "desc": "고액(해외성) 이상거래 룰: 최근 1일 100만원 초과 승인이 3건 이상이면", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC", + "T_ID1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "FRAUD_RULE_MCC", + "desc": "관찰가맹 이상거래 룰: 카드의 당일 승인 중 상태가 WATCH 인 가맹점 건수를", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC", + "T_ID1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "FRAUD_RULE_NIGHT", + "desc": "심야 이상거래 룰: 최근 3일 심야시간대(23~06시) 승인 시도가 6건 이상이면", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC", + "T_ID1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "FRAUD_SCORE_SUM", + "desc": "카드 위험도 종합: 카드의 부정 적발 건수/점수합/최고점수를 집계해 최고점수", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1702,6 +2891,35 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "KEY_ROTATE_LOG", + "desc": "암호키 회전 로그: 요청 키버전(T_ARG1)의 차기 버전을 산출하고 키식별자", + "in": [ + "T_ARG1", + "T_KEY1" + ], + "out": [ + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_ADJUST", + "desc": "한도 증감 조정: 요청 증감액(T_DELTA)을 일한도에 가감하되 음수 한도가 되지", + "in": [ + "T_KEY1", + "T_DELTA" + ], + "out": [ + "T_RC", + "T_AMT1" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1717,6 +2935,35 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_CUMUL_INQ", + "desc": "전(全)기간 누적 사용 조회: 카드의 전체 승인 이력에서 누적 건수/누적 합계/", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_AMT1", + "T_AMT2" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_DAILY_INQ", + "desc": "일 한도 상세 조회: 원장에서 일한도/사용액/임시한도/상태를 한 번에 읽어", + "in": [ + "T_KEY1" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_AMT3", + "T_AMT4" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1728,6 +2975,31 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_EXHAUST_LST", + "desc": "한도 소진 카드 목록: 요청 기준율(bps, 기본 9000=90%) 이상으로 한도를 소진한", + "in": [ + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_FREEZE", + "desc": "한도 동결: ACTIVE 상태의 카드 원장만 FROZEN 으로 전환한다. 이미 다른", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1757,6 +3029,63 @@ const SVC_SCREENS = [ "T_AMT3" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_MONTH_CHK", + "desc": "월 한도 점검: 이번 달 승인(A/M/S) 누적액에 요청금액을 더해 월 한도", + "in": [ + "T_KEY1", + "T_AMOUNT" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_MONTH_INQ", + "desc": "월 누적 사용 조회: 이번 달 승인건 기준으로 건수/합계/평균단가를 한 번의", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_AMT1", + "T_AMT2" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_OVER_ALERT", + "desc": "한도 소진 임박 알림: 사용액/일한도를 bps 로 환산해 90% 이상이면 경보", + "in": [ + "T_KEY1" + ], + "out": [ + "T_ARG1", + "T_AMT1", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_PERTX_CHK", + "desc": "건별(1회) 한도 점검: 일한도의 20% 를 건별 상한으로 삼아 요청 단건 금액이", + "in": [ + "T_KEY1", + "T_AMOUNT" + ], + "out": [ + "T_AMT1", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1831,6 +3160,30 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_TIER_INQ", + "desc": "한도 등급 조회: 일한도 구간으로 BASIC/SILVER/GOLD/PLATINUM 등급을 매겨", + "in": [ + "T_KEY1" + ], + "out": [ + "T_AMT1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "LIMIT_UNFREEZE", + "desc": "한도 동결 해제: FROZEN 상태의 카드 원장만 ACTIVE 로 되돌린다. 대상이", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1845,6 +3198,32 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "MCC_AUTH_STAT", + "desc": "업종별 매입 금액 통계: 영업일 매입을 업종(category)별 합계로 그룹핑하는", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_GROSS" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "MCC_POLICY", + "desc": "업종(MCC)별 승인 정책: 매입 원장의 당일 업종 실적(건수/합계)을 읽어", + "in": [ + "T_CATEGORY" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -1978,6 +3357,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "PREAUTH_PARTUSE", + "desc": "사전승인 부분 사용: 예비승인(P) 잔액에서 사용액을 차감(부분 소진)한다.", + "in": [ + "T_ID2", + "T_AMOUNT" + ], + "out": [ + "T_RC", + "T_AMT1" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2024,6 +3417,36 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "STANDIN_LIMIT", + "desc": "대행 한도 점검: 발급사 미응답 시 대행 승인은 일한도의 절반을 상한으로 두어,", + "in": [ + "T_KEY1", + "T_AMOUNT" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "STANDIN_RECON", + "desc": "대행 정산 대사: 영업일 대행 승인 중 매입확정(captured_at NOT NULL) 건과", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2038,6 +3461,47 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "STANDIN_STAT", + "desc": "대행(stand-in) 승인 통계: 영업일 auth_type='STANDIN' 승인을 대상으로", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_ARG1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "STAT_AMT_BAND", + "desc": "금액대별 승인 통계: 영업일 승인을 1만/5만/10만/50만/그이상 구간(CASE)으로", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "STAT_APPROVE_TR", + "desc": "카드 승인 추세: 최근 7일과 그 직전 7일의 승인 건수를 각각 필터 집계로 뽑아", + "in": [ + "T_KEY1" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2056,6 +3520,35 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "STAT_DAILY_SUM", + "desc": "일별 승인 종합: 영업일 승인 원장의 접수/승인/거절/취소 건수와 승인 합계를", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_GROSS" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "STAT_DECLINE", + "desc": "거절사유별 통계: 영업일 거절(D) 건을 사유별로 그룹핑하는 커서를 순회하며", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2083,6 +3576,42 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "TOKEN_INQ", + "desc": "토큰 상태 조회: 원장 상태와 블랙리스트 등재 여부를 합쳐 토큰의 종합 상태", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "TOKEN_ISSUE", + "desc": "결제 토큰 발급: PAN 의 Luhn 검증 후 앞6·뒤4 자리만 남기고 가운데를 마스킹한", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "TOKEN_REVOKE", + "desc": "토큰 폐기: 카드(토큰 원본 PAN)를 TOKEN_REVOKED 사유로 블랙리스트에 등재해", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2095,6 +3624,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "VELOCITY_AMT", + "desc": "금액 velocity 룰: 직전 1시간 승인 합계에 요청금액을 더해 200만원을 넘으면", + "in": [ + "T_KEY1", + "T_AMOUNT" + ], + "out": [ + "T_GROSS", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "VELOCITY_CHAN", + "desc": "승인유형 velocity 룰: 직전 1시간 카드 승인을 승인유형(auth_type)별로", + "in": [ + "T_KEY1" + ], + "out": [ + "T_ARG1", + "T_COUNT" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2110,6 +3666,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "VELOCITY_DAY", + "desc": "일 velocity 룰: 당일 카드 승인 건수와 합계를 세어 20건 초과 또는 합계", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2124,6 +3694,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "VELOCITY_MCC", + "desc": "가맹점 분산 velocity 룰: 직전 1시간 동안 서로 다른 가맹점 수(카드가 돌린", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2138,6 +3721,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "VELOCITY_RESET", + "desc": "velocity 적발 리셋: 특정 카드의 당일 velocity 계열 부정 적발(reason 접두", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "au", "modnm": "승인/한도", @@ -2153,6 +3750,44 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "WHITELIST_ADD", + "desc": "화이트리스트 등록: 카드의 블랙리스트 등재를 삭제하고 원장 상태가 ACTIVE 가", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "WHITELIST_CHK", + "desc": "화이트 여부 판정: 블랙리스트 미등재이면서 원장이 존재하는 카드를 화이트", + "in": [ + "T_KEY1" + ], + "out": [ + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "au", + "modnm": "승인/한도", + "svc": "WHITELIST_DEL", + "desc": "화이트리스트 해제: 카드를 다시 블랙리스트에 (사유와 함께) 등재/갱신해", + "in": [ + "T_KEY1", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2196,6 +3831,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_APPRDELEG", + "desc": "마감 승인 위임: 위임 대상자(T_STR1)를 memo(APPR:DELEG:..)에 기록하고 상태 유지.", + "in": [ + "T_ID1", + "T_STR1" + ], + "out": [ + "T_ID1", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_APPRFINAL", + "desc": "최종 마감 승인: 2차 승인(APPR:L2) 마감을 최종 승인(status=A)으로 확정.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1", + "T_ARG1" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2211,6 +3873,32 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_APPRL1", + "desc": "1차 마감 승인: 개시(O)/완료(C) 마감을 1차 승인(memo=APPR:L1) 단계로 전이.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1", + "T_ARG1" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_APPRL2", + "desc": "2차 마감 승인: 1차 승인(APPR:L1) 마감을 2차 승인(APPR:L2)으로 전이.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1", + "T_ARG1" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2247,6 +3935,31 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_APPRSTAT", + "desc": "마감 승인 현황: 승인 단계별(L1/L2/최종A) 마감 건수를 집계한다.", + "in": [], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_COUNT" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_APPRWD", + "desc": "마감 승인 회수: 승인(A) 마감을 완료(C) 상태로 되돌리고 승인 이력 초기화.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2308,6 +4021,76 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CHKALL", + "desc": "마감 전 통합 체크: 미마감(M)/미매칭/보류(H)/void(V) 합산 점수 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CHKLIMIT", + "desc": "마감 전 한도 체크: 가맹점 일한도(daily_limit) 초과 매입 존재 여부 판정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CHKPENDING", + "desc": "마감 전 보류 체크: 당일 보류(H) 매입 존재 여부 판정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CHKUNMATCH", + "desc": "마감 전 미매칭 체크: 당일 미매칭(ac_unmatch) 존재 여부 판정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CHKVOID", + "desc": "마감 전 void 체크: 당일 void(V) 매입 잔존 여부 판정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2354,6 +4137,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CNTVERIFY", + "desc": "건수 정합검증: 마감이력 txn_count 와 해당 영업일 매입 실건수를 대사한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ARG1", + "T_COUNT", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2392,6 +4190,61 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CORPVERIFY", + "desc": "법인별 마감 검증: 마감이력 헤더 건수/금액과 스냅샷 집계 정합 점검.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_AMT1", + "T_AMT2", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CXLDUP", + "desc": "중복마감 취소: 동일 기간 중복 마감이력 중 최신을 남기고 취소(status=X) 처리.", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CXLERROR", + "desc": "오류정정 취소: 오류정정 사유로 마감을 취소(status=X)한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CXLLATE", + "desc": "지연자료 취소: 지연자료 반영 사유로 마감을 취소(status=X)한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2404,6 +4257,33 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CXLREQ", + "desc": "마감 취소 요청: 취소 사유(T_REASON)를 memo(CXLREQ:..)에 기록해 요청 표기(상태 유지).", + "in": [ + "T_ID1", + "T_REASON" + ], + "out": [ + "T_ID1", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_CXLSTAT", + "desc": "마감 취소 현황: 취소 사유별(오류정정/지연자료/중복마감) 취소 건수를 집계한다.", + "in": [], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_COUNT" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2416,6 +4296,23 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_DAYVERIFY", + "desc": "일 마감 검증: 당일 마감이력 집계와 매입 실집계 대사.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2468,6 +4365,19 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_DUELIST", + "desc": "마감 예정 목록: 기준일 이전 14일 내 영업일 중 DAILY 마감이 없는 미마감 대상일을 나열.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2494,6 +4404,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_FEECROSS", + "desc": "수수료 크로스검증: 마감 스냅샷 fee 합계와 해당 영업일 매입 fee 합계를 대사한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2538,6 +4463,22 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_FXCLOSE", + "desc": "해외(FOREIGN) 채널 마감: 해외 매입 집계 + ac_fx 원화환산 합계 후 FX 마감.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_COUNT", + "T_GROSS", + "T_AMT1", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2566,6 +4507,19 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_HOLICHK", + "desc": "휴일 마감 판정: 기준일의 요일(dow)로 주말 여부를 보고 마감정책(정상/이월)을 결정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2579,6 +4533,37 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_HYCLOSE", + "desc": "반기 마감: 상/하반기 6개월 매입 집계 후 HALFYR 마감이력 생성.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_ARG1", + "T_COUNT", + "T_GROSS" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_HYVERIFY", + "desc": "반기 마감 검증: 반기 마감이력과 반기 매입 실집계 대사.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2592,6 +4577,22 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_ISSCLOSE", + "desc": "발급사별 마감: 발급사(issuer) 단위 매입 집계 후 ISS 마감.", + "in": [ + "T_BIZDATE", + "T_ISSUER" + ], + "out": [ + "T_ID1", + "T_COUNT", + "T_GROSS", + "T_FEE" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2629,6 +4630,20 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_MAINCLOSE", + "desc": "본마감: 가마감(O) 이력을 당일 전체 매입으로 재집계해 확정(C)으로 승격.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1", + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2660,6 +4675,20 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_MERXCHECK", + "desc": "가맹점별 크로스검증: 마감 스냅샷 gross 와 매입 amount 를 가맹점 단위로 대사해", + "in": [ + "T_ID1" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2686,6 +4715,38 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_MONVERIFY", + "desc": "월 마감 검증: 월 마감이력과 월 매입 실집계 대사.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_COUNT", + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_NETCROSS", + "desc": "정산액 크로스검증: 마감 스냅샷 net 합계와 해당 영업일 매입 net 합계를 대사한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2700,6 +4761,18 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_NEXTBIZ", + "desc": "익영업일 산출: 기준일 포함 이후 주말을 건너뛴 첫 영업일과 건너뛴 일수를 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2727,6 +4800,22 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_POSCLOSE", + "desc": "POS 채널 마감: POS 채널 당일 매입 집계 후 POS 마감이력.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_COUNT", + "T_GROSS", + "T_FEE", + "T_NET" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2767,6 +4856,21 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_PROVCMP", + "desc": "가마감/본마감 비교: 동일 영업일의 가마감(TENT) 잠정 집계와 확정(DAILY C) 차이 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2779,6 +4883,21 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_QTRVERIFY", + "desc": "분기 마감 검증: 분기 마감이력과 분기 매입 실집계 대사.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2805,6 +4924,33 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RECLOSECHK", + "desc": "재마감 가능여부: 승인(A)/잠금(L) 상태면 재마감 불가로 판정한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RECLOSEREG", + "desc": "재마감 등록: 기존 마감을 재마감(R) 표기하고 동일 기간에 신규 마감이력 개시.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1", + "T_ID2", + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2868,6 +5014,17 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RPTAPPR", + "desc": "승인 마감 리포트: 승인(A) 상태 마감이력을 최신순으로 나열한다.", + "in": [], + "out": [ + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2894,6 +5051,42 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RPTCXL", + "desc": "취소 마감 리포트: 취소(X) 상태 마감이력을 취소 사유(memo)와 함께 나열한다.", + "in": [], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RPTDELAY", + "desc": "지연 마감 리포트: 기준일 이전인데 아직 대사완료(M) 상태로 남은 미마감 매입을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG2" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RPTDIV", + "desc": "부문별 마감 리포트: 영업일 매입을 업종(category) 부문으로 집계해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2907,6 +5100,29 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RPTPERIOD", + "desc": "기간별 마감 리포트: close_type(T_KEY1) 의 최근 마감이력을 기간별로 나열한다.", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_GROSS" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_RPTSTATUS", + "desc": "상태별 마감 리포트: 마감이력을 상태(O/C/A/L/X/R)별 건수/총액으로 집계한다.", + "in": [], + "out": [ + "T_COUNT" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2920,6 +5136,16 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_SCHEDNEXT", + "desc": "다음 마감 일정: 기준일 다음날부터 주말(토/일)을 건너뛴 첫 영업일을 마감 예정일로 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [] + }, { "mod": "cl", "modnm": "마감", @@ -2931,6 +5157,52 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_SEGCLOSE", + "desc": "부문별 마감: 업종(category) 부문 단위 매입 집계 후 SEG 마감.", + "in": [ + "T_BIZDATE", + "T_CATEGORY" + ], + "out": [ + "T_ID1", + "T_COUNT", + "T_GROSS" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_SEGVERIFY", + "desc": "부문별 마감 검증: 부문 마감이력 총액과 해당 부문 매입 집계 대사.", + "in": [ + "T_ID1", + "T_CATEGORY" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_SEMIMONTH", + "desc": "반월 마감: 전반(1~15)/후반(16~말) 구간 집계 후 SEMIMON 마감.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_ARG1", + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2955,6 +5227,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_SLABREACH", + "desc": "마감 SLA 위반: 기준일 이전인데 아직 개시(O)에 머무는 미완결 마감 건을 위반으로 판정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2970,6 +5255,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_SLASET", + "desc": "마감 SLA 등록: close_type(T_KEY1)별 마감 목표시간(T_ARG1, 시간)을 seq_reg 에 upsert.", + "in": [ + "T_KEY1", + "T_ARG1" + ], + "out": [ + "T_ARG1" + ] + }, { "mod": "cl", "modnm": "마감", @@ -2986,6 +5284,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_SNAPMER", + "desc": "스냅샷 가맹점 비교: 두 마감(T_ID1,T_ID2) 스냅샷을 가맹점별 gross 로 FULL JOIN 해", + "in": [ + "T_ID1", + "T_ID2" + ], + "out": [ + "T_COUNT", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3039,6 +5352,41 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_STCHGAUDIT", + "desc": "마감 상태변경 감사: 특정 기간(period_key) 마감이력의 상태 분포를 상태별로 집계해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_ARG5", + "T_ARG6", + "T_COUNT" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_STCROSS", + "desc": "상태 크로스검증: 당일 매입 상태 분포(A/M/S/C)를 집계해 미마감(A+M) 잔량으로", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_COUNT", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3053,6 +5401,35 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_TENDAY", + "desc": "순(10일) 마감: 상/중/하순 구간 매입 집계 후 TENDAY 마감이력 생성.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_ARG1", + "T_COUNT", + "T_GROSS" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_TENTCLOSE", + "desc": "가마감(잠정): 당일 접수/대사(A,M) 매입 잠정 집계로 가마감(status=O) 이력 생성.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3069,6 +5446,33 @@ const SVC_SCREENS = [ "T_AMT3" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_TRIGRPT", + "desc": "마감 후 리포트 트리거: 마감완료(C)이나 리포트 미생성(memo!=RPT:*)인 마감을 대상으로", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_TRIGSETTLE", + "desc": "마감 후 정산 트리거: 마감 완료 당일 정산완료(S) 대상 건수/정산액을 산출하고", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3122,6 +5526,35 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_WEEKLY", + "desc": "주 마감: 주간(월~일) 매입을 집계해 WEEKLY 마감이력 생성.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_COUNT", + "T_GROSS" + ] + }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_WEEKVERIFY", + "desc": "주 마감 검증: 주간 마감이력과 주간 매입 실집계 대사.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cl", "modnm": "마감", @@ -3149,6 +5582,23 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cl", + "modnm": "마감", + "svc": "CL_YEARVERIFY", + "desc": "연 마감 검증: 연간 마감이력과 연간 매입 실집계 대사.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_COUNT", + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3162,6 +5612,21 @@ const SVC_SCREENS = [ "T_STR3" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_ADLER32", + "desc": "Adler-32 체크섬(순수 계산): 입력(T_STR1)에 대해 두 누산기 a(초기 1)와", + "in": [ + "T_STR1" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_STR4" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3233,6 +5698,22 @@ const SVC_SCREENS = [ "T_ID2" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_AUDIT_ERRPCT", + "desc": "감사로그 오류 비율: dbio(cmdb_log_err_ratio)로 기준일 오류(E) 로그 비율(bps)", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_ARG2", + "T_ARG3", + "T_STATUS", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3249,6 +5730,65 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_AUDIT_TAIL", + "desc": "감사로그 최근 N건: 기준일(T_BIZDATE)에서 최근 로그 N개(T_ARG1, 기본 5,", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ID1", + "T_STATUS" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_AUDIT_TOP", + "desc": "감사로그 최다 서비스: 기준일(T_BIZDATE) 공통로그를 서비스명으로 그룹핑해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_SVCNAME", + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_BATDEP_CHK", + "desc": "배치 선행 완료 점검: 잡(T_KEY1)의 선행 잡(DEP:)을 찾아, 기준일", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_STR4", + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_BATDEP_SET", + "desc": "배치 의존성 등록: 잡(T_KEY1)의 선행 잡(T_STR1)을 DEP: 파라미터에", + "in": [ + "T_KEY1", + "T_STR1", + "T_BIZDATE" + ], + "out": [ + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3266,6 +5806,21 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_BATLAST", + "desc": "배치 최근 실행 조회: 잡(T_KEY1)의 완료 마커(svc=BATRUN, \" ...\")를", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_ID1", + "T_MSG" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3282,6 +5837,20 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_BATSCHD_DEL", + "desc": "배치 스케줄 삭제: 잡(T_KEY1)의 스케줄 파라미터(SCHD:)와 딸린 의존성", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3296,6 +5865,20 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_BATSCHD_LIST", + "desc": "배치 스케줄 목록 집계: 등록된 스케줄(SCHD:%) 중 주기별(DAILY/WEEKLY/MONTHLY)", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3321,6 +5904,21 @@ const SVC_SCREENS = [ "T_BIZDATE" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_BIZDAY_ADD", + "desc": "N영업일 가산: 기준일(T_BIZDATE)에서 N(T_ARG1)영업일 뒤 날짜를 구한다.", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_ARG2" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3418,6 +6016,21 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODEGRP_DIFF", + "desc": "코드그룹 차집합 비교: 기준 그룹(T_KEY1)에는 있으나 대상 그룹(T_KEY2)에는", + "in": [ + "T_KEY1", + "T_KEY2" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3448,6 +6061,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODEGRP_SPAN", + "desc": "코드그룹 정렬범위 조회: 그룹(T_KEY1) 내 sort_ord 의 최대-최소 폭을 dbio", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC", + "T_ARG1", + "T_COUNT", + "T_ARG2" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3465,6 +6093,21 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODEGRP_VER", + "desc": "코드그룹 버전 관리: 코드그룹의 형상 버전을 VER: 파라미터로 유지한다.", + "in": [ + "T_KEY1", + "T_ARG1", + "T_BIZDATE" + ], + "out": [ + "T_ARG2", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3478,6 +6121,77 @@ const SVC_SCREENS = [ "T_STR3" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODE_EXPORT", + "desc": "코드그룹 직렬화: 그룹(T_KEY1)의 사용중 코드를 정렬순서대로 커서 순회하며", + "in": [ + "T_KEY1" + ], + "out": [ + "T_STR4", + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODE_MOVE", + "desc": "코드 그룹 이동: 특정 코드(T_KEY2)를 원 그룹(T_KEY1)에서 대상 그룹(T_STR1)으로", + "in": [ + "T_KEY1", + "T_KEY2", + "T_STR1", + "T_BIZDATE" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODE_REORDER", + "desc": "코드 정렬순서 재부여: 대상 그룹(T_KEY1)의 사용중 코드를 코드값 오름차순으로", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODE_SEARCH", + "desc": "코드명 검색: 입력어(T_STR1)를 코드명 부분일치 패턴(%검색어%)으로 만들어", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_STR4" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_CODE_TOGGLE", + "desc": "코드 사용여부 토글: 대상 코드(그룹 T_KEY1 / 코드 T_KEY2)의 use_yn 를 현재", + "in": [ + "T_KEY1", + "T_KEY2" + ], + "out": [ + "T_RC", + "T_STATUS" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3506,6 +6220,22 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_DAYOFYEAR", + "desc": "연중 경과일/잔여일(순수 계산): 입력일(T_STR1)이 그 해의 몇 번째 날인지", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3651,6 +6381,53 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_FX_INVERT", + "desc": "역환율 산출: 통화(T_CCY)의 고시환율(rate_bps = KRW*100/unit)을 조회해", + "in": [ + "T_CCY", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ARG1", + "T_ARG2" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_FX_MARGIN", + "desc": "매매기준율 스프레드(순수 계산): 고시환율(T_ARG1, bps)에 스프레드율", + "in": [ + "T_ARG1", + "T_ARG2" + ], + "out": [ + "T_RC", + "T_ARG3", + "T_ARG4", + "T_DELTA" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_FX_MEAN", + "desc": "기준일 평균환율: dbio(cmdb_fx_avg)로 기준일 전체 통화의 평균환율을 구하고,", + "in": [ + "T_BIZDATE", + "T_CCY" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3665,6 +6442,57 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_FX_TICKS", + "desc": "환율 고시 현황: 기준일(T_BIZDATE)에 고시된 통화 수와 CCY 코드그룹의 등록", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2", + "T_ARG3" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_FX_TREND", + "desc": "환율 변동률: 통화(T_CCY)의 이전일(T_STR1)과 기준일(T_BIZDATE) 고시환율을", + "in": [ + "T_CCY", + "T_STR1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ARG1", + "T_ARG2", + "T_DELTA", + "T_ARG3", + "T_STATUS" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_FX_TRIANGLE", + "desc": "삼각환율 정합성: 원화 기준 두 통화(T_CCY=base, T_STR1=quote)의 고시환율로", + "in": [ + "T_CCY", + "T_STR1", + "T_ARG1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ARG2", + "T_ARG3" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3681,6 +6509,64 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HASH_DJB2", + "desc": "djb2 해시(순수 계산): 입력 문자열(T_STR1)에 대해 hash = hash*33 + c 점화식", + "in": [ + "T_STR1" + ], + "out": [ + "T_ARG1", + "T_STR4", + "T_COUNT" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HASH_FNV", + "desc": "FNV-1a 해시(순수 계산): 입력(T_STR1)을 오프셋 베이시스(2166136261)에서", + "in": [ + "T_STR1" + ], + "out": [ + "T_ARG1", + "T_STR4", + "T_ARG2" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HASH_SUM", + "desc": "가중 체크섬(순수 계산): 입력(T_STR1) 각 문자의 코드값에 위치 가중(1부터", + "in": [ + "T_STR1" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_STR4" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HEALTH_ALL", + "desc": "종합 헬스 스코어: 기준일 로그/환율/채번/파라미터 존재를 각 25점으로 가중해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_STATUS", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3695,6 +6581,22 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HEALTH_FX", + "desc": "시스템 헬스(환율 신선도): 기준일(T_BIZDATE)에 고시된 통화가 하나라도 있는지,", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_STATUS", + "T_COUNT", + "T_ARG2", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3710,6 +6612,20 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HEALTH_PARAM", + "desc": "필수 파라미터 점검: 운영에 반드시 필요한 키 목록(BIZ_DATE/VAT_RATE/WHT_BPS", + "in": [], + "out": [ + "T_ARG1", + "T_ARG2", + "T_STR4", + "T_STATUS", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3765,6 +6681,22 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HOLIDAY_NEXT", + "desc": "다음 휴일 조회: 기준일(T_BIZDATE) 이후 국가(T_STR1, 기본 KR)의 가장 가까운", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_MSG", + "T_ARG1" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3779,6 +6711,22 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_HOLIDAY_YEAR", + "desc": "연도별 휴일 집계: 연도(T_ARG1)와 국가(T_STR1, 기본 KR)의 등록 휴일 수와", + "in": [ + "T_ARG1", + "T_STR1" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_ARG2", + "T_ARG3" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3793,6 +6741,21 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_ISO_WEEK", + "desc": "ISO-8601 주차 산출(순수 계산): 입력일(T_STR1)의 통일된 일련일수를 구해", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_ARG1", + "T_ARG2", + "T_ARG3" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3831,6 +6794,20 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MASK_ACCT", + "desc": "계좌번호 마스킹(순수 계산): 하이픈 구분자는 위치를 보존하면서, 숫자 기준", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_ARG1" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3845,6 +6822,20 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MASK_EMAIL", + "desc": "이메일 마스킹(순수 계산): 로컬파트(@ 앞)의 첫 글자만 남기고 나머지를 '*' 로", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_ARG1" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3873,6 +6864,36 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MASK_RRN", + "desc": "주민등록번호 마스킹(순수 계산): \"YYMMDD-SXXXXXX\" 13자리 형식을 검증하고,", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_ARG1" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MKT_STATUS", + "desc": "시장 개장 여부: 대상일(T_BIZDATE)이 국가(T_STR1, 기본 KR) 기준 개장일인지", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_STATUS", + "T_STR4", + "T_ARG1", + "T_RC" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3886,6 +6907,21 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MSG_DEL", + "desc": "다국어 메시지 폐기: 언어(T_STR1)와 메시지 ID(T_KEY1)로 MSG_ 그룹의", + "in": [ + "T_STR1", + "T_KEY1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_COUNT" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3900,6 +6936,22 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MSG_FORMAT", + "desc": "템플릿 치환(순수 계산): 본문(T_STR1)의 자리표시자 {0}{1}{2} 를 인자", + "in": [ + "T_STR1", + "T_STR2", + "T_STR3", + "T_STR4" + ], + "out": [ + "T_MSG", + "T_COUNT" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3915,6 +6967,35 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MSG_LANGS", + "desc": "메시지 다국어 커버리지: 특정 메시지 ID(T_KEY1)가 어떤 언어 그룹(MSG_%)에", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_STR4" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_MSG_RENDER", + "desc": "다국어 메시지 렌더: DB 에서 언어별 템플릿(MSG_/ID)을 조회한 뒤 본문의", + "in": [ + "T_STR1", + "T_KEY1", + "T_STR3", + "T_STR4" + ], + "out": [ + "T_RC", + "T_MSG" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3970,6 +7051,38 @@ const SVC_SCREENS = [ "T_MSG" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_NOTI_QUOTA", + "desc": "알림 발송 한도 점검: 채널(T_CHANNEL)의 기준일 발송 건수를 세고, 파라미터", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_ARG2", + "T_COUNT", + "T_ARG3", + "T_RC" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_NOTI_RETRY", + "desc": "알림 재발송: 원 발송 로그 ID(T_ID1)의 내용을 조회해 동일 본문을 재큐잉", + "in": [ + "T_ID1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID2", + "T_MSG" + ] + }, { "mod": "cm", "modnm": "공통", @@ -3986,6 +7099,40 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_NOTI_STAT", + "desc": "알림 채널별 통계: 기준일(T_BIZDATE) NOTI 로그를 본문 접두([SMS>/[EMAIL>/", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_COUNT" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_NOTI_TMPL", + "desc": "템플릿 알림 발송: 언어(T_STR2)/템플릿ID(T_KEY1)로 DB 템플릿을 조회해 {0} 을", + "in": [ + "T_STR2", + "T_KEY1", + "T_STR3", + "T_CHANNEL", + "T_STR1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1", + "T_MSG" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4013,6 +7160,81 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_PARAM_CANCEL", + "desc": "파라미터 변경요청 철회: 대기 중인 요청 키(REQ:)를 승인 전에 삭제한다.", + "in": [ + "T_STR1", + "T_STR3", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_STR4" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_PARAM_DIFF", + "desc": "파라미터 요청-현재 비교: 대기 요청 값(REQ:)과 현재 본 값을 읽어와", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_STR5", + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_PARAM_HIST", + "desc": "파라미터 변경 이력 조회: 승인 워크플로가 남긴 공통로그(svc=PARAMWF)에서", + "in": [ + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_ID1", + "T_MSG" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_PARAM_LIST", + "desc": "파라미터 접두 목록: 접두어(T_STR1, 예 \"SCHD:\")로 시작하는 파라미터 키의", + "in": [ + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_PARAM_NUM", + "desc": "숫자 파라미터 조회+범위검증: 키(T_STR1)의 값을 정수로 파싱하고, 호출자가", + "in": [ + "T_STR1", + "T_ARG1", + "T_ARG2" + ], + "out": [ + "T_RC", + "T_ARG3", + "T_ARG4" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4105,6 +7327,20 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_QTR_END", + "desc": "분기말일 산출(순수 계산): 입력일(T_STR1)이 속한 분기(1~4)를 판정하고 그", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_STR4", + "T_ARG1" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4117,6 +7353,22 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_SEQ_ALLOC", + "desc": "채번 범위 예약: 카운터(T_KEY1)의 seq_val 을 한 번의 UPDATE 로 N(T_ARG1)만큼", + "in": [ + "T_KEY1", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ID1", + "T_ID2", + "T_COUNT" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4131,6 +7383,22 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_SEQ_MULTI", + "desc": "연속 채번(블록): 카운터(T_KEY1)에서 N개(T_ARG1)의 일련번호를 연속 채번해", + "in": [ + "T_KEY1", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ID1", + "T_ID2", + "T_COUNT" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4160,6 +7428,21 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_SEQ_STAMP", + "desc": "일자형 채번: 유형(T_KEY1)과 영업일(T_BIZDATE)을 결합한 카운터명", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ARG1", + "T_STR4" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4174,6 +7457,20 @@ const SVC_SCREENS = [ "T_STR4" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_SEQ_VERIFY", + "desc": "채번 체크디짓 검증(순수 계산): 발번 문자열(T_STR1)의 마지막 자리를 체크디짓", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_ARG1", + "T_ARG2" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4186,6 +7483,39 @@ const SVC_SCREENS = [ "T_STATUS" ] }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_STR_PAD", + "desc": "문자열 패딩(순수 계산): 입력(T_STR1)을 목표 폭(T_ARG1)까지 채운다. 방향", + "in": [ + "T_STR1", + "T_STR2", + "T_KEY1", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2", + "T_STR4", + "T_ARG1" + ] + }, + { + "mod": "cm", + "modnm": "공통", + "svc": "CM_STR_TRIM", + "desc": "문자열 정규화(순수 계산): 입력(T_STR1)의 양끝 공백/탭을 제거하고, 내부의", + "in": [ + "T_STR1" + ], + "out": [ + "T_STR4", + "T_ARG1", + "T_ARG2", + "T_ARG3" + ] + }, { "mod": "cm", "modnm": "공통", @@ -4200,6 +7530,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ACCRUEDPAY", + "desc": "미지급비용 계상: 미지급수수료비용(D)/미지급금(C)을 균형 계상.", + "in": [ + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ACCRUEDRECV", + "desc": "미수수익 계상: 현재 RECV 잔액을 참조해 미수수익(D)/이자수익(C)을 계상.", + "in": [ + "T_BIZDATE", + "T_COUNT" + ], + "out": [ + "T_NET", + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4213,6 +7570,21 @@ const SVC_SCREENS = [ "T_ID2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ACCTGROUP", + "desc": "계정그룹별 집계: 계정과목 접두어(prefix)로 묶어 그룹별 차대 합계를 순회.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMT2", + "T_AMT3", + "T_DELTA" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4248,6 +7620,22 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ACCTPIVOT", + "desc": "계정x일자 피벗 잔액: 기간 내 일자별 순액을 누적해 누적잔액과 최고점을 산출.", + "in": [ + "T_KEY1", + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_NET", + "T_AMT2" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4278,6 +7666,20 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ACCTTOPN", + "desc": "상위 N 계정 잔액: 계정별 순액 절대값 내림차순으로 상위 N개를 훑어 1위를 반환.", + "in": [ + "T_COUNT", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4311,6 +7713,36 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ALLOWANCE", + "desc": "대손충당금 설정: RECV 잔액의 일정률을 대손상각비(D)/대손충당금(C)으로 설정.", + "in": [ + "T_BIZDATE", + "T_COUNT" + ], + "out": [ + "T_NET", + "T_ID1", + "T_AMT2" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ALLOWRECALC", + "desc": "충당금 재산정: 필요 충당금(RECV x 율)과 현재 충당금 잔액 차이를 조정 전기.", + "in": [ + "T_BIZDATE", + "T_COUNT" + ], + "out": [ + "T_DELTA", + "T_ID1", + "T_AMT2" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4322,6 +7754,32 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_APPRDELEG", + "desc": "승인 위임: 작성(D) 전표의 승인 권한을 대리인에게 위임(메모 누적).", + "in": [ + "T_ID1", + "T_STR1" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_APPRHIST", + "desc": "승인 이력 조회: 당일 전기(P) 완료된 전표 수와 차변 총액을 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMT2" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4349,6 +7807,20 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_AUDITSCAN", + "desc": "원장 감사 스캔: 취소(X) 전표인데 라인이 여전히 posted 인 불일치와 고아 라인을 탐지.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_COUNT" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4379,6 +7851,21 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_BALCERT", + "desc": "잔액증명서 발급: 계정 차변/대변을 재계산해 계정잔액에 upsert 하고 잔액을 증명.", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4408,6 +7895,19 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_BATCHAPPROVE", + "desc": "일괄 승인: 당일 작성(D) 전표를 순회하며 차대 균형이 맞는 것만 전기(P) 승인.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4425,6 +7925,22 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_BLACKADJUST", + "desc": "흑색 정정분개: 과소계상분을 양수(흑색) 분개 쌍으로 추가 보정.", + "in": [ + "T_KEY1", + "T_BIZDATE", + "T_ID1", + "T_DELTA" + ], + "out": [ + "T_ID2", + "T_DELTA" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4441,6 +7957,20 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_BSSUMMARY", + "desc": "재무상태 요약: 자산(RECV/CASH/PREPAID) - 부채(MERCH_PAY/UNEARNED_REV) = 자본.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4469,6 +7999,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_CLOSELIST", + "desc": "결산 체크리스트: 미결전표/시산표 불균형/무분개 전표를 집계해 마감 준비도를 판정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4493,6 +8038,34 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_CORPLEDGER", + "desc": "법인(가맹점)별 원장 집계: 가맹점 원장 라인의 차변/대변 잔액을 산출.", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_NET" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_CORPSUM", + "desc": "법인별 순액 집계: 가맹점별 (차변-대변) 순액의 절대값 합과 법인 수를 순회.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4504,6 +8077,22 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_CORRPAIR", + "desc": "정정 짝분개: 잘못 분류된 계정을 올바른 계정으로 재분류(D 신계정/C 구계정).", + "in": [ + "T_KEY1", + "T_KEY2", + "T_BIZDATE", + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4557,6 +8146,22 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_DEPTLEDGER", + "desc": "부문(전표유형)별 원장 조회: 유형별 차변/대변 합계와 분개 건수를 집계.", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_DELTA", + "T_COUNT" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4571,6 +8176,36 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_DEPTRANGE", + "desc": "부문 기간 금액범위: 전표유형의 기간 내 분개 최소/최대 금액과 건수를 조회.", + "in": [ + "T_KEY1", + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_COUNT", + "T_DELTA" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_DEPTSUM", + "desc": "부문별 차변 합계 최대 유형 탐색: 전표유형별 차변합을 순회해 최대 부문을 선별.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4618,6 +8253,33 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_EQUITYMOVE", + "desc": "자본 변동: 기간 내 이익잉여금(RETAINED) 대변순액 변동을 집계.", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_NET", + "T_COUNT" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_EXPENSEROLL", + "desc": "비용 롤업: 마감일까지 비용계정(_EXP)별 차변순액을 순회해 총비용을 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4676,6 +8338,33 @@ const SVC_SCREENS = [ "T_AMT3" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_HALFCLOSE", + "desc": "반기 결산 분개: 반기 FEE_INC 순액을 이익잉여금(RETAINED)으로 균형 대체.", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_NET", + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_INCOMEROLL", + "desc": "수익 롤업: 마감일까지 수익계정(_INC)별 대변순액을 순회해 총수익을 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4698,6 +8387,20 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_LEDGSETTLE", + "desc": "원장-정산 대사: base 원장(ledger) SETTLE 합계와 원장라인 지급 합계를 대조.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_DELTA" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4752,6 +8455,21 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_MERCHCERT", + "desc": "가맹점 잔액증명: 가맹점잔액 기록치와 라인 재계산치를 대조해 증명(OK/NG).", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_DELTA" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4768,6 +8486,20 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_MONCLOSEJE", + "desc": "월차 결산 분개: 당월 RECV 순증가를 미수수익(RECV_ACCRUED)으로 계상.", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_NET", + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4810,6 +8542,21 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PARTREVERSE", + "desc": "부분 역분개: 원전표에서 특정 계정 라인만 차대를 뒤집어 신규 전표로 복사.", + "in": [ + "T_ID1", + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_ID2", + "T_COUNT" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4825,6 +8572,20 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PAYMATCH", + "desc": "지급 매칭: 원장 지급(MERCH_PAY 대변) 건수와 정산 건수를 대조해 매칭율 판정.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4855,6 +8616,31 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PENDCLEAR", + "desc": "미결전표 정리: 기준일 이전 작성(D) 전표를 취소(X)하고 라인을 마감 처리.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PENDESCAL", + "desc": "미결 에스컬레이션: 기준일보다 오래 방치된 작성(D) 전표 건수를 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMT2" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4867,6 +8653,32 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PENDROUTE", + "desc": "미결전표 배정: 특정 작성(D) 전표의 메모에 담당자를 기록해 배정.", + "in": [ + "T_ID1", + "T_STR1" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PENDSTAT", + "desc": "미결 상태별 집계: 당일 전표를 상태(D/P/M/S/X)별로 순회 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4894,6 +8706,20 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PLSUMMARY", + "desc": "손익 요약: 당일 수익(_INC)과 비용(_EXP)의 순액을 계산해 순손익을 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4911,6 +8737,34 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PREPAIDAMORT", + "desc": "선급 상각: 선급비용 총액을 개월(N)로 나눠 1개월분을 비용(D)/선급(C) 상각.", + "in": [ + "T_BIZDATE", + "T_AMOUNT", + "T_COUNT" + ], + "out": [ + "T_ID1", + "T_NET" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_PREPAIDPOST", + "desc": "선급비용 계상: 선급비용(D)/현금(C)으로 지출 시점 자산 계상.", + "in": [ + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4925,6 +8779,20 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_QTRCLOSEJE", + "desc": "분기 결산 분개: 당분기 대손비용 발생분을 미지급(ACCRUED_PAY)으로 계상.", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_NET", + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4936,6 +8804,20 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_RECOVERY", + "desc": "상각채권 회수: 제각 채권 회수를 현금(D)/대손충당금환입(C)으로 인식.", + "in": [ + "T_BIZDATE", + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4950,6 +8832,36 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_REDADJUST", + "desc": "적색 정정분개: 과대계상된 차변을 음수(적색) 분개 쌍으로 취소 감액.", + "in": [ + "T_KEY1", + "T_BIZDATE", + "T_ID1", + "T_DELTA" + ], + "out": [ + "T_ID2", + "T_DELTA" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_REDBLACKNET", + "desc": "적흑 상계: 원전표에 연결된 적색/흑색 정정 금액을 합산해 순정정액을 반환.", + "in": [ + "T_ID1" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -4987,6 +8899,48 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_REPOST", + "desc": "재전기: 취소(X)된 전표의 라인을 방향 그대로 신규 전기(P) 전표로 복제.", + "in": [ + "T_ID1", + "T_BIZDATE" + ], + "out": [ + "T_ID2", + "T_COUNT" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_REVACCRUE", + "desc": "수익 발생 계상: 발생주의 수익을 미수수익(D)/수수료수익(C)으로 균형 계상.", + "in": [ + "T_BIZDATE", + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_REVDEFER", + "desc": "수익 이연: 미실현 수익을 수수료수익(D)/선수수익(C)으로 이연 대체.", + "in": [ + "T_BIZDATE", + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5001,6 +8955,20 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_REVRELEASE", + "desc": "이연수익 환입: 선수수익(D)을 수수료수익(C)으로 기간 경과분 환입 인식.", + "in": [ + "T_BIZDATE", + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5013,6 +8981,47 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_ROLLTB", + "desc": "누적 시산표: 마감일까지 계정별 누계 차대를 순회해 시산표 upsert 및 총차액 검증.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_DELTA" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_SEQAUDIT", + "desc": "전표 채번 감사: 당일 전표번호 min/max/건수로 채번 누락(gap)을 검출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_DELTA" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_SETTLEPAYGAP", + "desc": "정산-지급 차이: 정산 net 합계와 원장 지급(MERCH_PAY 대변) 합계의 갭을 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_DELTA" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5043,6 +9052,22 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_SPANTB", + "desc": "기간 시산표: 계정의 기간 차변/대변 합계와 차액을 산출해 시산표에 upsert.", + "in": [ + "T_KEY1", + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_DELTA" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5155,6 +9180,22 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_THREEWAY", + "desc": "원장-정산-매입 3단 대사: 원장 지급대변, 정산 net, 매입 net 을 대조해 차이 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT2", + "T_AMT3", + "T_AMT4", + "T_DELTA", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5186,6 +9227,47 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_UNAPPROVE", + "desc": "승인 취소: 마감(S)되지 않은 전기(P) 전표를 작성(D) 상태로 되돌림.", + "in": [ + "T_ID1" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_UNEARNPOST", + "desc": "선수수익 계상: 현금(D)/선수수익(C)으로 선수금 수령 시점 부채 계상.", + "in": [ + "T_BIZDATE", + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_UNEARNRECOG", + "desc": "선수수익 인식: 선수수익 총액을 기간(N)으로 나눠 1기분을 수익 인식.", + "in": [ + "T_BIZDATE", + "T_AMOUNT", + "T_COUNT" + ], + "out": [ + "T_ID1", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5241,6 +9323,74 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_WEEKCLOSE", + "desc": "주차 결산: 지정 주(기간) 내 작성(D) 전표를 일괄 전기(P)로 승격.", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_WRITEOFF", + "desc": "대손 상각: 회수불능 채권을 대손충당금(D)/미수금(C)으로 제각.", + "in": [ + "T_BIZDATE", + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_XFERPOST", + "desc": "대체 전기: 두 계정 간 대체(D 대상/C 원천) 전기 후 양 계정 잔액을 재계산.", + "in": [ + "T_KEY1", + "T_KEY2", + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_YEARCARRY", + "desc": "연말 이월: 마감일까지 계정별 누계잔액을 익년 개시일 계정잔액으로 이월(upsert).", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "lg", + "modnm": "원장", + "svc": "LG_YEARCLOSE", + "desc": "연말 결산 대체: 손익계정(_INC/_EXP) 누계 잔액을 계정별로 RETAINED 로 대체.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET" + ] + }, { "mod": "lg", "modnm": "원장", @@ -5298,6 +9448,39 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_AUTHADV", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3", + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_ID1", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_AUTHADVRSP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ID1", + "T_ID2" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5333,6 +9516,23 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_BALINQ", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3", + "T_BIZDATE" + ], + "out": [ + "T_STR2", + "T_STR4", + "T_ID1", + "T_ARG1" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5346,6 +9546,35 @@ const SVC_SCREENS = [ "T_STR6" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_BMPMERGE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR6", + "T_STR7" + ], + "out": [ + "T_RC", + "T_STR6", + "T_ARG3" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_BMPPARSE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR6" + ], + "out": [ + "T_RC", + "T_STR3", + "T_COUNT" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5381,6 +9610,23 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_CARDVER", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR5", + "T_BIZDATE" + ], + "out": [ + "T_STR2", + "T_STR6", + "T_COUNT", + "T_ID1" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5408,6 +9654,21 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_CHANFAILOVER", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR1" + ], + "out": [ + "T_RC", + "T_STATUS", + "T_STR5" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5425,6 +9686,33 @@ const SVC_SCREENS = [ "T_ARG8" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_CHANLIST", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [], + "out": [ + "T_STR3", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_CHANMAP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_CHANNEL", + "T_STR5" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5439,6 +9727,33 @@ const SVC_SCREENS = [ "T_STR5" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_CHANPING", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL" + ], + "out": [ + "T_RC", + "T_STATUS" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_CHANRESET", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL" + ], + "out": [ + "T_RC", + "T_STR5", + "T_STATUS" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5496,6 +9811,23 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_DECODE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC", + "T_STR1", + "T_STR4", + "T_AMOUNT", + "T_ARG1", + "T_CHANNEL" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5541,6 +9873,21 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_DUPPURGE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_ID1" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5555,6 +9902,53 @@ const SVC_SCREENS = [ "T_STATUS" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_ENCODE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR1", + "T_STR4", + "T_CHANNEL", + "T_AMOUNT", + "T_ARG1" + ], + "out": [ + "T_STR3", + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_ERRCLASS", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR2" + ], + "out": [ + "T_RC", + "T_STR5", + "T_ARG3" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_ERRESCAL", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE", + "T_ARG4" + ], + "out": [ + "T_COUNT", + "T_ARG4", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5601,6 +9995,21 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_ERRREQUEUE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE", + "T_ID1" + ], + "out": [ + "T_RC", + "T_ID2", + "T_ARG1" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5614,6 +10023,125 @@ const SVC_SCREENS = [ "T_ID2" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_FEP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3" + ], + "out": [ + "T_STR3", + "T_COUNT", + "T_ARG2", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_FIELDGET", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3", + "T_ARG1", + "T_ARG2" + ], + "out": [ + "T_RC", + "T_STR5", + "T_COUNT" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_FIELDSET", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3", + "T_STR5", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_STR3", + "T_COUNT", + "T_ARG2" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_FINADV", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3", + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_STR4", + "T_ID1", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_FINADVRSP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_STR5", + "T_ID1" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_FINREQ", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3", + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_STR2", + "T_ID1", + "T_ARG1", + "T_INSTALL_N", + "T_AMT1" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_FINRSP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_AMOUNT", + "T_ARG2", + "T_ARG1" + ], + "out": [ + "T_STR2", + "T_ID1", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5658,6 +10186,56 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_HOSTADPT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR1", + "T_STR3" + ], + "out": [ + "T_STR3", + "T_STR5", + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_INFLIGHT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_ARG4" + ], + "out": [ + "T_COUNT", + "T_ARG4", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_INQRSP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_AMT1", + "T_ARG1" + ], + "out": [ + "T_STR3", + "T_ID1", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5676,6 +10254,36 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_ISOADPT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC", + "T_STR1", + "T_STR5", + "T_CHANNEL" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_KEYCHECK", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_KEY2" + ], + "out": [ + "T_RC", + "T_KEY1", + "T_ARG3" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5689,6 +10297,35 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_KEYEXPIRE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_ARG3", + "T_ARG4" + ], + "out": [ + "T_ARG1", + "T_ARG6", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_KEYLOAD", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL" + ], + "out": [ + "T_KEY1", + "T_ARG3", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5718,6 +10355,21 @@ const SVC_SCREENS = [ "T_ARG4" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_LOADBAL", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_CHANNEL", + "T_COUNT", + "T_ARG3" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5758,6 +10410,20 @@ const SVC_SCREENS = [ "T_ARG3" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_MACSESSION", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3" + ], + "out": [ + "T_RC", + "T_KEY1" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5800,6 +10466,40 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_NMADVICE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3", + "T_BIZDATE" + ], + "out": [ + "T_KEY1", + "T_STR4", + "T_ID1", + "T_ARG3", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_NMRSP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3", + "T_BIZDATE" + ], + "out": [ + "T_STR4", + "T_ID1", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5843,6 +10543,40 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_PINBLOCK", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_KEY1", + "T_STR5" + ], + "out": [ + "T_RC", + "T_KEY2", + "T_COUNT" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_PREAUTH", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR3", + "T_BIZDATE", + "T_AMOUNT", + "T_ARG4" + ], + "out": [ + "T_AMT1", + "T_ARG4", + "T_ID1", + "T_ARG1" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5877,6 +10611,38 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_RATELIMIT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_ARG4" + ], + "out": [ + "T_COUNT", + "T_ARG4", + "T_ARG8", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_READYZ", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG8", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5910,6 +10676,22 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_RELAYADPT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC", + "T_CHANNEL", + "T_STR3", + "T_STR5", + "T_COUNT" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5926,6 +10708,20 @@ const SVC_SCREENS = [ "T_ID2" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_RESNDONE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_ID2", + "T_ARG4" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5955,6 +10751,22 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_REVADVRSP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ID1", + "T_ARG2" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -5972,6 +10784,23 @@ const SVC_SCREENS = [ "T_ID2" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_REVRSP", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_AMOUNT", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_STR2", + "T_ID1" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6003,6 +10832,36 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_RTCOUNT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_RTDISABLE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_ID1", + "T_CHANNEL", + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6017,6 +10876,35 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_RTENABLE", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_RTFIND", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_RC", + "T_STR3", + "T_ARG4" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6082,6 +10970,51 @@ const SVC_SCREENS = [ "T_ARG6" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_SEQFILL", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_SEQNEXT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2", + "T_ARG6" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_SEQRESET", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2", + "T_ARG3" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6152,6 +11085,21 @@ const SVC_SCREENS = [ "T_STATUS" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_STALEMARK", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE", + "T_ARG4" + ], + "out": [ + "T_COUNT", + "T_ARG5", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6162,6 +11110,38 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_STATAMT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE", + "T_AMT1" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG8", + "T_AMT2" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_STATCHAN", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_CHANNEL", + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_GROSS", + "T_ARG8" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6179,6 +11159,22 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_STATERR", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE", + "T_ARG4" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG8", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6211,6 +11207,21 @@ const SVC_SCREENS = [ "T_ARG8" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_STATPEAK", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_COUNT", + "T_ARG3", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6243,6 +11254,23 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_STATSUM", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_AMT1", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "mg", "modnm": "전문게이트웨이", @@ -6374,6 +11402,75 @@ const SVC_SCREENS = [ "T_CHANNEL" ] }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_VAN3ADPT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC", + "T_STR3", + "T_STR1", + "T_AMOUNT", + "T_CHANNEL" + ] + }, + { + "mod": "mg", + "modnm": "전문게이트웨이", + "svc": "MG_VAN4ADPT", + "desc": "신규 스케일아웃 서비스; 캘러의 XA 브랜치에서 실행. mg_svr 가 광고.", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC", + "T_STR3", + "T_COUNT", + "T_CHANNEL" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_ACCT_INQ", + "desc": "기능: 정산계좌 원문 조회 (등록 여부 판정 포함).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_ACCT_MASK", + "desc": "기능: 정산계좌 마스킹 조회 (앞3 뒤2만 노출, 중간은 * 처리하여 반환 - 개인정보 보호).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_ACCT_REG", + "desc": "기능: 정산계좌 등록/변경 (숫자 10~16자리 형식 검증 통과분만 마스터에 반영, 검증 이력 기록).", + "in": [ + "T_MERCHANT", + "T_STR1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6412,6 +11509,45 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_AGENT_INQ", + "desc": "기능: 모집인 단건 조회 (AGENT 코드그룹에서 명칭/상태 반환).", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_AGENT_REG", + "desc": "기능: 모집인 등록 (AGENT 코드그룹 upsert, 소속 제휴사 코드를 T_STR2로 검증).", + "in": [ + "T_KEY1", + "T_STR1", + "T_STR2" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_AGENT_STATUS", + "desc": "기능: 모집인 상태 변경 (ACTIVE/SUSPENDED/RETIRED 허용값 검증 후 갱신).", + "in": [ + "T_KEY1", + "T_STATUS" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6425,6 +11561,58 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_APPLY_ASSIGN", + "desc": "기능: 심사 담당자 배정 (담당자 코드를 REVIEWER 매핑 이력에 기록).", + "in": [ + "T_MERCHANT", + "T_STR1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_APPLY_CNT", + "desc": "기능: 심사 상태별 신청 건수 집계 (대시보드용, 특정 상태 또는 전 워크플로 상태).", + "in": [ + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_APPLY_DOCCHK", + "desc": "기능: 심사 서류 접수 확인 (필수 마스터 필드 충족도 점검, 심사 서브단계).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_APPLY_HOLD", + "desc": "기능: 심사 진행 중 신청 건 보류 (APPLIED->HOLD, 사유 이력 기록).", + "in": [ + "T_MERCHANT", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6453,6 +11641,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_APPLY_RESUME", + "desc": "기능: 보류된 심사 재개 (HOLD->APPLIED, 심사 대기열 복귀).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6466,6 +11667,19 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_APPLY_SCORE", + "desc": "기능: 심사 점수 산정 (MCC 리스크 + 서류 완비도 가중 합산, 등급 예비판정).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6480,6 +11694,31 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_APPLY_WITHDRAW", + "desc": "기능: 신청 철회 (신청/보류 단계 건을 WITHDRAWN 처리, 활성 가맹점은 철회 불가).", + "in": [ + "T_MERCHANT", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_AUDIT_DIFF", + "desc": "기능: 마스터 최근 변경 전후 비교 (가장 최신 이력 1건의 액션/이전값/이후값 반환).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6505,6 +11744,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_AUDIT_STAT", + "desc": "기능: 전 마스터 감사 통계 (특정 액션의 전체 발생 건수 + 관련 가맹점 수 집계).", + "in": [ + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6568,6 +11821,96 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_BIZ_CLOSE", + "desc": "기능: 폐업 확정 처리 (상태 CLOSED, 미결 계약 잔여 여부를 함께 점검하여 경고 코드 반환).", + "in": [ + "T_MERCHANT", + "T_REASON" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_BIZ_REOPEN", + "desc": "기능: 재개업 처리 (SUSPENDED/DORMANT 가맹점을 ACTIVE로 복귀, 폐업/삭제 건은 불가).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_BIZ_SUSPEND", + "desc": "기능: 휴업 신고 처리 (ACTIVE 가맹점을 SUSPENDED로, 소속 활성 단말도 함께 정지).", + "in": [ + "T_MERCHANT", + "T_REASON" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_BRANCH_INQ", + "desc": "기능: 영업점 조회 (\"명칭|상위코드\" 저장값을 명칭만 분리하여 반환).", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_BRANCH_LIST", + "desc": "기능: 영업점 계층 요약 (전체 수 + 최상위(상위=-) 영업점 수 집계).", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_BRANCH_REG", + "desc": "기능: 영업점 마스터 등록 (BRANCH 코드그룹 upsert, 상위 영업점 코드를 code_name에 저장).", + "in": [ + "T_KEY1", + "T_STR1", + "T_KEY2" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CHK_ACCT", + "desc": "기능: 정합성 점검 - 정산계좌 미등록 활성 가맹점 적발 (정산 지급 리스크 사전 탐지).", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6581,6 +11924,18 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CHK_GRADE", + "desc": "기능: 정합성 점검 - 마스터 등급과 누적 매출 기준 산정 등급의 불일치 적발.", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6593,6 +11948,30 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CHK_MCC", + "desc": "기능: 정합성 점검 - MCC 마스터에 없는 업종코드를 참조하는 가맹점 적발 (외래키 무결성).", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CHK_ORPHAN", + "desc": "기능: 정합성 점검 - 대표가 비활성/부재인 대표-하위 관계 적발 (MERCH_REL 고아 링크).", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6691,6 +12070,19 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CONTRACT_AMEND", + "desc": "기능: 유효 계약의 조건 변경 (만기일 연장/단축, 변경 전후를 이력에 함께 기록).", + "in": [ + "T_MERCHANT", + "T_ARG1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6704,6 +12096,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CONTRACT_EXPCHK", + "desc": "기능: 만기 임박 점검 (최신 계약 만기일까지 남은 일수 산출, 갱신 필요 판정).", + "in": [ + "T_MERCHANT", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CONTRACT_HIST", + "desc": "기능: 가맹점 계약 변경 이력 요약 (체결/변경 횟수 + 최신 만기일 반환).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6731,6 +12150,18 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CONTRACT_REACT", + "desc": "기능: 정지된 계약 재개 (미해제 CONTRACT_SUSP가 있을 때만 CONTRACT_REACT 기록).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6744,6 +12175,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_CONTRACT_SUSPEND", + "desc": "기능: 계약 일시정지 (활성 가맹점을 유지하되 CONTRACT_SUSP 이력 마킹, 중복 정지 차단).", + "in": [ + "T_MERCHANT", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6835,6 +12279,19 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_GRADE_HIST", + "desc": "기능: 등급 변경 이력 요약 (등급 관련 이력 건수 + 마스터 현행 등급 반환).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6909,6 +12366,46 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_MCC_ASSIGN", + "desc": "기능: 가맹점 업종(MCC) 재배정 (마스터에 등록된 MCC로만 변경, 변경 이력 기록).", + "in": [ + "T_MERCHANT", + "T_STR1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_MCC_CNT", + "desc": "기능: 특정 MCC를 사용하는 가맹점 수 + 해당 MCC 리스크 등급 동시 반환.", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_MCC_DEL", + "desc": "기능: MCC 업종코드 삭제 (참조하는 가맹점이 없을 때만 물리 삭제, 안전 삭제).", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -6950,6 +12447,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_MCC_RISKSET", + "desc": "기능: MCC 리스크 등급 설정 (1~5 범위 검증 후 mm_mcc.risk_level 갱신).", + "in": [ + "T_STR1", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7013,6 +12524,20 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_MERCH_DORMANT", + "desc": "기능: 장기 무거래 가맹점 휴면 전환 (ACTIVE->DORMANT, 최근 집계 매출 0 확인).", + "in": [ + "T_MERCHANT", + "T_REASON" + ], + "out": [ + "T_RC", + "T_GROSS" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7122,6 +12647,18 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_PARTNER_LIST", + "desc": "기능: 활성 제휴사 총수 조회 (PARTNER 코드그룹 중 ACTIVE 상태 집계).", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7149,6 +12686,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_PARTNER_UPD", + "desc": "기능: 제휴사 마스터 명칭/표시순서 변경 (PARTNER 코드그룹, 기존 건만 갱신).", + "in": [ + "T_KEY1", + "T_STR1", + "T_ARG1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7162,6 +12713,45 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_RATE_CANCEL", + "desc": "기능: 대기중 요율 변경 요청 철회 (미처리 RATE_REQ가 있을 때만 RATE_REJ로 자체 종결).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_RATE_HIST", + "desc": "기능: 요율 승인 워크플로 이력 집계 (요청/승인/반려 건수를 각각 반환).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2", + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_RATE_PENDING", + "desc": "기능: 전 가맹점 미처리 요율 요청 수 집계 (승인/반려로 종결되지 않은 RATE_REQ 카운트).", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7191,6 +12781,32 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_RATE_STATUS", + "desc": "기능: 최신 요율 변경 요청의 진행 상태 판정 (요청 이후 승인/반려 이력 존재 여부).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_REL_CHILDREN", + "desc": "기능: 대표 가맹점에 연결된 하위 가맹점 수 집계 (MERCH_REL code_name=대표 기준).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7217,6 +12833,31 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_REL_MOVE", + "desc": "기능: 하위 가맹점을 다른 대표로 이관 (기존 연결의 대표를 신규 활성 대표로 교체).", + "in": [ + "T_KEY1", + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_REL_PARENT", + "desc": "기능: 하위 가맹점의 대표 가맹점 조회 (MERCH_REL 코드그룹에서 code=하위 -> code_name=대표).", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7229,6 +12870,59 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_REL_VERIFY", + "desc": "기능: 대표-하위 관계 정합성 검증 (대표가 활성 마스터에 존재하는지, 순환/자기참조 여부).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_RISK_INQ", + "desc": "기능: 현행 리스크 등급 조회 (최신 RISK_SET 수동값 우선, 없으면 최신 RISK 산정 밴드).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_RISK_SCORE", + "desc": "기능: 가맹점 리스크 점수 산정 (MCC 위험도*20 + 저등급 가산 + 비활성 상태 가산).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_RISK_SET", + "desc": "기능: 가맹점 리스크 등급 수동 설정 (1~5, 심사역 재량 오버라이드를 RISK_SET 이력에 기록).", + "in": [ + "T_MERCHANT", + "T_ARG1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7262,6 +12956,18 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_ACTIVATE", + "desc": "기능: 단말 개통 (정지/비활성 단말을 ACTIVE로 전환).", + "in": [ + "T_STR5" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7277,6 +12983,72 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_DEACT", + "desc": "기능: 단말 개통 해지 (ACTIVE 단말을 INACTIVE로, 사유 기록).", + "in": [ + "T_STR5" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_HIST", + "desc": "기능: 단말 상태 확인 조회 (현재 상태/소속/모델 반환, 존재 여부 판정).", + "in": [ + "T_STR5" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_INSTALL", + "desc": "기능: 등록된 단말의 설치 확정 (상태 REGISTERED->ACTIVE, 가맹점 활성 여부 확인).", + "in": [ + "T_STR5", + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_LIST", + "desc": "기능: 가맹점별 단말 현황 요약 (전체/활성/폐기 건수 집계).", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2", + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_MODEL_UPD", + "desc": "기능: 단말 모델(기종) 정보 변경. 출력: T_STR6에 이전 모델을 실어 반환.", + "in": [ + "T_STR5", + "T_STR6" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7290,6 +13062,31 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_REASSIGN", + "desc": "기능: 단말 소속 가맹점 재배정 (기존 소속에서 신규 활성 가맹점으로 이관).", + "in": [ + "T_STR5", + "T_MERCHANT" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_RETIRE", + "desc": "기능: 단말 폐기 처리 (상태 RETIRED로 확정, 활성 단말은 폐기 전 해지 필요).", + "in": [ + "T_STR5" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7315,6 +13112,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "mm", + "modnm": "마스터", + "svc": "MM_TERM_SWAP", + "desc": "기능: 단말 교체 (구단말 폐기 + 신단말이 구단말의 가맹점/모델을 승계 등록).", + "in": [ + "T_STR5", + "T_STR6" + ], + "out": [ + "T_RC" + ] + }, { "mod": "mm", "modnm": "마스터", @@ -7353,6 +13163,19 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_ACCTFREEZE", + "desc": "지급계좌 동결: 활성(ACTIVE) 계좌만 FROZEN 으로 전환한다. 먼저 해당 계좌의", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -7368,6 +13191,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_ACCTREOPEN", + "desc": "동결 해제: 동결(FROZEN) 계좌를 ACTIVE 로 복구하고, 동결 사유로 보류(H)됐던", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_ACCTVERIFY", + "desc": "지급계좌 검증: 계좌의 상태/예금주/잔액을 조회하고, 요청에 예금주가 주어지면", + "in": [ + "T_KEY1", + "T_STR1" + ], + "out": [ + "T_RC", + "T_AMT1" + ] + }, { "mod": "py", "modnm": "지급", @@ -7417,6 +13267,19 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_ADVGRANT", + "desc": "선지급 승인: 선지급(ADVANCE) 채널의 요청(R) 건만 승인(A)으로 전이한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_AMOUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -7449,6 +13312,31 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_APPRCANCEL", + "desc": "지급 승인 취소: 아직 파일에 편성되지 않은 승인(A) 건을 요청(R)으로 되돌린다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_APPRHIST", + "desc": "지급 승인/원장 이력: 지정 지급건의 py_ledger 기표를 시간순 커서로 훑어", + "in": [ + "T_ID1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7477,6 +13365,18 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_APPRPEND", + "desc": "승인 대기 집계: 아직 승인 전인 요청(R) 지급건을 가맹점별로 묶어 대기 건수/", + "in": [], + "out": [ + "T_COUNT", + "T_ARG2", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7507,6 +13407,22 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_BANKVERIFY", + "desc": "지급파일 검증: 파일 헤더(record_count/total_amount)와 실제 편성 지급건의", + "in": [ + "T_ID2" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_COUNT", + "T_DELTA", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7556,6 +13472,36 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_CARRYOVER", + "desc": "지급 이월(단건): 미지급(R/A) 건의 지급기준일(biz_date)을 지정 차기일자로", + "in": [ + "T_ID1", + "T_KEY2", + "T_REASON" + ], + "out": [ + "T_RC", + "T_ADJ" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_CHARGEBACK", + "desc": "지급 되물림: 분쟁 등으로 지급완료(P) 건을 되물릴 때, 원건은 보존한 채", + "in": [ + "T_ID1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID2", + "T_NET" + ] + }, { "mod": "py", "modnm": "지급", @@ -7568,6 +13514,35 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_CLOSEDSTOP", + "desc": "폐업 가맹점 지급정지: 가맹점 상태가 CLOSED 인 가맹점의 미지급(R/A) 건을", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_COMBINE", + "desc": "지급 합산: 같은 계좌의 당일 요청(R) 건들을 하나로 합산 지급한다. 합계", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID2", + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "py", "modnm": "지급", @@ -7592,6 +13567,19 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_DORMLIST", + "desc": "휴면 계좌 목록: 지정 기준일 이후 지급 이력이 전혀 없는 활성(ACTIVE) 계좌를", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7606,6 +13594,19 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FAILCLEAR", + "desc": "실패 종결: 재처리 불가한 반려(J) 단건을 취소(C)로 종결하고 종결 사유를", + "in": [ + "T_ID1", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7622,6 +13623,19 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FAILRSN", + "desc": "실패 재시도횟수별 분포: 당일 반려(J) 건을 retry_count 별 커서로 건수/net", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7651,6 +13665,24 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FEECAP", + "desc": "수수료 상한 적용: 미지급 건 수수료가 상한(T_AMOUNT)을 넘으면 상한까지", + "in": [ + "T_ID1", + "T_AMOUNT", + "T_BIZDATE", + "T_REASON" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_FEE", + "T_NET" + ] + }, { "mod": "py", "modnm": "지급", @@ -7666,6 +13698,21 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FEEFIXED", + "desc": "정액 수수료 적용: 미지급(R/A) 건에 요청 정액 수수료(T_AMOUNT)를 부과하고", + "in": [ + "T_ID1", + "T_AMOUNT" + ], + "out": [ + "T_RC", + "T_FEE", + "T_NET" + ] + }, { "mod": "py", "modnm": "지급", @@ -7696,6 +13743,35 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FEEREFUND", + "desc": "수수료 환급: 지급완료(P) 건의 수수료를 지급계좌에 되돌려 입금하고,", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_FEE" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FEESUM", + "desc": "수수료 집계: 당일 지급건의 수수료 합/건수와, py_adjust 에 쌓인 조정 합을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_FEE", + "T_ADJ", + "T_NET" + ] + }, { "mod": "py", "modnm": "지급", @@ -7712,6 +13788,32 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FILECANCEL", + "desc": "지급파일 취소: 확정(C) 전 파일만 취소 가능. 편성(F) 건을 승인(A)+file_id=0", + "in": [ + "T_ID2" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_FILECONF", + "desc": "지급파일 확정: 생성(G) 파일만 확정(C)+confirmed_at 로 전이하고, 그 파일에", + "in": [ + "T_ID2" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -7772,6 +13874,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_GARNISH", + "desc": "가압류 등록: 활성 계좌를 GARNISH 로 표시하고, 그 계좌 미지급(R/A) 잔액을", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "py", "modnm": "지급", @@ -7811,6 +13928,44 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_HOLDBYRSN", + "desc": "보류 사유별 집계: 당일 보류(H) 건을 reject_reason 별 커서로 건수/net 합", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_HOLDCLEAR", + "desc": "보류 일괄 해제: 지정 계좌의 보류(H) 지급건을 모두 승인(A)으로 되돌리고", + "in": [ + "T_KEY1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_HOLDEXT", + "desc": "보류 연장: 보류(H) 건의 반려사유 뒤에 '(연장)' 표식을 덧붙여 갱신한다.", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7825,6 +13980,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_HOLDNOTE", + "desc": "보류 사유 갱신: 보류(H) 단건의 반려사유를 요청 사유로 교체한다. dbio 로", + "in": [ + "T_ID1", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7911,6 +14079,47 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_LIMITLIST", + "desc": "지급 한도 사용현황 목록: 가맹점별 일한도와 당일 확정성(A/F/P) 사용액을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_LIMITSET", + "desc": "지급 일한도 설정: 가맹점의 일 지급한도(merchant.daily_limit)를 새 값으로", + "in": [ + "T_MERCHANT", + "T_AMOUNT" + ], + "out": [ + "T_RC", + "T_AMT1" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_LIMITWARN", + "desc": "지급 한도 근접 경보: 당일 사용액이 일한도의 지정 비율(T_ARG1 %, 기본 80)", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -7970,6 +14179,20 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_NOTIFYALL", + "desc": "전 채널 일괄 통지(시뮬): 지급완료(P) 단건을 SMS/EMAIL/PUSH 세 채널로 각각", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_NET" + ] + }, { "mod": "py", "modnm": "지급", @@ -7985,6 +14208,32 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_NOTIFYFAIL", + "desc": "지급실패 통지(시뮬): 반려(J) 건의 가맹점/반려사유를 읽어 실패 안내 본문을", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_NOTIFYKAKAO", + "desc": "알림톡 일괄 통지(시뮬): 당일 지급완료(P) 건수/금액을 한 번에 집계해 알림톡", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_GROSS" + ] + }, { "mod": "py", "modnm": "지급", @@ -7999,6 +14248,32 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_NOTIFYPUSH", + "desc": "지급완료 푸시 통지(시뮬): 지급완료(P)건을 py_account 조인으로 예금주/은행을", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_NET" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_NOTIFYRESEND", + "desc": "통지 재발송: 지급완료(P) 건의 retry_count 를 재발송 횟수로 삼아 3회까지만", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -8012,6 +14287,62 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_OFFSETAPPLY", + "desc": "상계 적용: 미지급(R/A) 건의 net 에서 상계액을 차감(net 미만으로 캡)하고,", + "in": [ + "T_ID1", + "T_AMOUNT", + "T_BIZDATE", + "T_REASON" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_NET" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_PENDLIST", + "desc": "미지급 목록: 당일 미완결(요청R/승인A/보류H) 지급건을 커서로 훑어 지급ID/", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_REACTIVATE", + "desc": "휴면 계좌 재활성: 휴면(DORMANT) 처리된 지급계좌를 다시 활성(ACTIVE)으로", + "in": [ + "T_KEY1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_RECLAIM", + "desc": "과지급 환수: 지급완료(P) 건에서 요청 환수액(T_AMOUNT, net 한도)만큼을", + "in": [ + "T_ID1", + "T_AMOUNT" + ], + "out": [ + "T_RC", + "T_AMT1" + ] + }, { "mod": "py", "modnm": "지급", @@ -8076,6 +14407,22 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_REPAYACCT", + "desc": "계좌변경 재지급: 반려(J)된 지급건의 금액/수수료를 승계해 새 계좌(T_KEY2)로", + "in": [ + "T_ID1", + "T_KEY2", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID2", + "T_NET" + ] + }, { "mod": "py", "modnm": "지급", @@ -8101,6 +14448,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_REQUEUE", + "desc": "실패 일괄 재처리 적재: 당일 반려(J) 건 중 재시도 여력(retry_count<3)이", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -8113,6 +14473,32 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_RESVLIST", + "desc": "예약 지급 목록: 지정 실행일(T_BIZDATE)에 걸린 예약(S) 을 py_schedule 과", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_RESVMODIFY", + "desc": "예약 변경: 아직 실행되지 않은 예약(S) 의 실행일(run_date)을 새 일자로", + "in": [ + "T_ID2", + "T_BIZDATE" + ], + "out": [ + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -8125,6 +14511,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_RETRYONE", + "desc": "단건 재시도: 반려(J) 지급건 하나를 요청(R)으로 되돌리며 retry_count 를", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -8137,6 +14536,20 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_ROLLFWD", + "desc": "미지급 일괄 이월: 지정 기준일 이전의 미지급(R) 건 전부를 차기 영업일", + "in": [ + "T_BIZDATE", + "T_KEY2" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -8177,6 +14590,22 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_SEIZEAMT", + "desc": "압류 금액 상계: 압류 계좌의 보류(H) 지급건 net 합을 압류가능액으로 산정하고,", + "in": [ + "T_KEY1", + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_RC", + "T_AMT1", + "T_AMT2" + ] + }, { "mod": "py", "modnm": "지급", @@ -8191,6 +14620,17 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_SEIZELIST", + "desc": "압류 계좌 목록: 압류(SEIZED) 상태 계좌를 커서로 훑어 계좌번호/예금주와 그", + "in": [], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -8220,6 +14660,22 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_SPLITEVEN", + "desc": "균등 분할: 요청(R) 지급건을 N 건으로 균등 분할하되 나머지는 첫 건에 몰아", + "in": [ + "T_ID1", + "T_COUNT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_AMOUNT" + ] + }, { "mod": "py", "modnm": "지급", @@ -8237,6 +14693,19 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_STATAMT", + "desc": "금액대별 지급 통계: 당일 지급건을 net 기준 소액/중액/고액 구간으로 CASE", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -8297,6 +14766,33 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_STATFEE", + "desc": "채널별 수수료 통계: 당일 지급건을 channel 별로 수수료합/net합 집계하되", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_STATMONTH", + "desc": "월별 지급 통계: 지정 기간(T_BIZDATE~T_KEY2)의 지급건을 'YYYY-MM' 단위로", + "in": [ + "T_BIZDATE", + "T_KEY2" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -8313,6 +14809,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_STATSTATUS", + "desc": "상태별 지급 통계: 당일 지급건을 status 별 커서로 건수/net합/수수료합까지", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "py", "modnm": "지급", @@ -8355,6 +14864,75 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_URGENTQ", + "desc": "긴급 지급 큐: 당일 요청(R) 건 중 금액이 임계치(T_AMOUNT) 이상인 건을 금액", + "in": [ + "T_BIZDATE", + "T_AMOUNT" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_VOIDONE", + "desc": "지급 무효(단건): 아직 지급 전인 요청/승인(R/A) 건만 무효 취소(C)로 전이하고", + "in": [ + "T_ID1", + "T_REASON" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_XFERCONF", + "desc": "이체 확정: 지급완료(P) 건에 대해 이체 실행 결과를 py_ledger 에 XFER 기표로", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC", + "T_NET" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_XFERDIFF", + "desc": "이체 차액 목록: 당일 지급건의 net 과 py_ledger XFER 기표 합이 어긋나는", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_XFERFILE", + "desc": "이체유형별 지급파일 생성: 지정 채널(STD/SPLIT/BULK/ADVANCE)의 승인(A) 건만", + "in": [ + "T_CHANNEL", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_ID2", + "T_GROSS" + ] + }, { "mod": "py", "modnm": "지급", @@ -8396,6 +14974,32 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_XFERTYPE", + "desc": "이체유형별 지급 집계: 당일 지급건을 channel 별 커서로 건수/net 합 집계해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "py", + "modnm": "지급", + "svc": "PAY_XFERUNMATCH", + "desc": "미대사 목록: 지급완료(P) 이면서 이체 기표(py_ledger XFER)가 전혀 없는", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8425,6 +15029,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_ACQMATCH", + "desc": "매입-입금 축 대사: 당일 매입 순액 합계와 원장 RECON 입금 합계를 비교해 차액을 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8484,6 +15102,32 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_APPRGAP", + "desc": "승인 누락 감지: 당일 매입 중 승인 원장이 없는 건을 예외로 등록하고 누락 건수를 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_APPRMATCH", + "desc": "승인-매입 축 대사: 당일 매입 중 승인건과 매칭된 건수와 승인 누락 건수를 분리 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8499,6 +15143,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_AUDITADD", + "desc": "대사 감사추적 기록: 지정 매입의 대사 처리 이력을 AUDIT 유형 로그로 감사 추적에 적재.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8539,6 +15196,49 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_BACKDATE", + "desc": "소급 대사: 지정 매입을 과거 영업일자로 소급하여 대사 로그를 MATCHED 로 기록.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID", + "T_STR1" + ], + "out": [ + "T_ID1" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_BRANDDIFF", + "desc": "브랜드별 차이 분석: 업종(category) 단위 매입 총액과 수수료 합계 비율을 점검.", + "in": [ + "T_BIZDATE", + "T_CATEGORY" + ], + "out": [ + "T_GROSS", + "T_FEE", + "T_RC" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_BRANDRECON", + "desc": "브랜드별 대사: 업종/브랜드(category) 단위로 당일 매입 총액과 건수를 집계.", + "in": [ + "T_BIZDATE", + "T_CATEGORY" + ], + "out": [ + "T_GROSS", + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8637,6 +15337,45 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_CANCMATCH", + "desc": "취소분 대사: 당일 취소(ac_cancel) 금액 합계와 취소 건수를 유형 무관하게 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMOUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_CARRYFWD", + "desc": "익일 이월 처리: 당일 미해결 예외를 지정 익일자로 복제 등록하여 이월시킨다.", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_CARRYIN", + "desc": "전일 이월 대사: 전 영업일의 미해결 예외 건수를 조회해 당일 이월 대상으로 반환.", + "in": [ + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8650,6 +15389,33 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_CHANDIFF", + "desc": "채널별 차이 분석: 지정 채널의 매입 순액과 원장 SETTLE 합계 차이를 rc_diff 에 기록.", + "in": [ + "T_BIZDATE", + "T_CHANNEL" + ], + "out": [ + "T_DELTA" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_CHANRECON", + "desc": "채널별 대사: 지정 채널의 당일 매입 건수와 대사완료(M/S) 건수를 대조.", + "in": [ + "T_BIZDATE", + "T_CHANNEL" + ], + "out": [ + "T_COUNT", + "T_ARG1" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8702,6 +15468,18 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_DEPGAP", + "desc": "입금 누락 감지: 매입은 있으나 RECON 원장이 없는 미입금 건수를 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8717,6 +15495,20 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_DEPPAY", + "desc": "입금-지급 축 대사: 원장 XFER 이체 합계와 정산 순액 합계의 지급 정합성을 대조.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_GROSS", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8732,6 +15524,19 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_DIFFRANK", + "desc": "차이 순위 분석: 당일 절대 차이금액이 가장 큰 매입 1건과 그 차액을 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_PURCHASE_ID", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8771,6 +15576,19 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_EDCRECON", + "desc": "EDC 채널 대사: EDC 단말 매입의 순액 합계와 미대사 건수를 함께 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_NET", + "T_ARG1" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8813,6 +15631,32 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_EXCNOTIFY", + "desc": "대사 예외 알림: 당일 미해결 예외 건수를 집계해 임계 초과 시 rc_alert 경보를 발생.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_FEEBRK", + "desc": "수수료 내역 대사: 당일 매입 수수료 원계와 수수료조정 누계를 비교해 순수수료 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_FEE", + "T_DELTA", + "T_ARG1" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8841,6 +15685,86 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_FIXDUP", + "desc": "중복 자동보정: 동일 매입에 SETTLE 원장이 2건 이상 중복된 매입 건수를 탐지.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_FIXFEE", + "desc": "수수료 자동보정: 매입 수수료가 MDR 이론치와 다르면 차이를 조정 델타로 반환.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_FEE", + "T_ARG1", + "T_DELTA" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_FIXFX", + "desc": "외화 자동보정: ac_fx 의 원화환산액과 원통화*환율 이론치의 차이를 산출.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_FIXROUND", + "desc": "반올림 자동보정: 절대차 1 이하 단수 차이를 수수료조정(ac_fee_adj)으로 상계 기록.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_FIXSIGN", + "desc": "부호 자동보정: 부호 반전 rc_diff 라인의 차액 부호를 정정하여 재기록.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_FXDIFF", + "desc": "외화 환산 차이: 당일 ac_fx 원화환산 합계와 원통화*환율 이론합계의 차이를 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8908,6 +15832,32 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_HOLDCLR", + "desc": "대사 보류 해제: 보류(H) 매입을 접수(A)로 되돌리고 관련 예외를 해결 처리.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_HOLDLIST", + "desc": "보류 목록 현황: 당일 보류(H) 상태 매입의 건수와 순액 합계를 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_NET" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8923,6 +15873,20 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_HOLDSET", + "desc": "대사 보류 설정: 지정 매입 상태를 보류(H)로 변경하고 보류 예외를 사유와 함께 등록.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID", + "T_REASON" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8939,6 +15903,65 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_ISSDIFF", + "desc": "발급사별 차이 분석: 발급사 단위 매입 순액과 정산 순액 차이를 계산해 반환.", + "in": [ + "T_BIZDATE", + "T_ISSUER" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_ISSRECON", + "desc": "발급사별 대사: 지정 발급사의 당일 매입 순액과 정산 순액을 발급사 단위로 대조.", + "in": [ + "T_BIZDATE", + "T_ISSUER" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_KINDSPLIT", + "desc": "차이 유형 분류: 당일 rc_diff 를 금액(AMT)/건수(CNT) 유형으로 나누어 각각 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_LEDGERBAL", + "desc": "원장 축 잔액 대사: 당일 RECON/SETTLE/XFER 기표 합계의 순잔액이 0 인지 확인.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -8980,6 +16003,58 @@ const SVC_SCREENS = [ "T_PURCHASE_ID" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_MANCANCEL", + "desc": "수작업 신청 취소: 아직 확정되지 않은 수작업 HOLD 신청 로그를 삭제.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_MANLIST", + "desc": "수작업 신청 현황: 당일 수작업 대사 로그를 상태별(HOLD/확정)로 나누어 건수 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_ARG1", + "T_ARG2" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_MANNG", + "desc": "수작업 대사 반려: 해당 매입의 수작업 HOLD 로그를 ROLLBACK 으로 반려 처리.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_MANOK", + "desc": "수작업 대사 확정: 해당 매입의 수작업 HOLD 로그를 CONFIRMED 로 승인 처리.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9010,6 +16085,19 @@ const SVC_SCREENS = [ "T_PURCHASE_ID" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_MANREQ", + "desc": "수작업 대사 신청: 지정 매입에 대해 수작업 대사 요청을 HOLD 상태 로그로 등록.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9024,6 +16112,32 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_MGRPDIFF", + "desc": "가맹점그룹별 차이 분석: 가맹점 접두 그룹의 매입 순액과 정산 순액 차액을 집계.", + "in": [ + "T_BIZDATE", + "T_KEY1" + ], + "out": [ + "T_DELTA" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_MGRPRECON", + "desc": "가맹점그룹별 대사: 가맹점 ID 접두 그룹(prefix) 단위로 매입 순액을 묶어 대사.", + "in": [ + "T_BIZDATE", + "T_KEY1" + ], + "out": [ + "T_NET" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9050,6 +16164,19 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_OVERAMT", + "desc": "과다 차이 분석: 당일 rc_diff 중 실제가 기대를 초과(diff_amt<0)한 과다 건수/합계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9065,6 +16192,18 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_PARTDEP", + "desc": "부분 입금 대사: 매입 순액보다 RECON 입금 누계가 작은 부분입금 건수를 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9079,6 +16218,32 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_PAYGAP", + "desc": "지급 누락 감지: 대사완료(M) 매입 중 정산 레코드가 없는 미지급 건수를 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_PAYLEDGER", + "desc": "지급-원장 축 대사: 정산 순액과 원장 SETTLE 기표 합계가 일치하는지 검증하고 차액 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_ARG1", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9093,6 +16258,19 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_POSRECON", + "desc": "POS 채널 대사: POS 채널 매입의 대사완료율을 건수 기준으로 산출(백분율).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9120,6 +16298,59 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_RATECHAN", + "desc": "채널별 대사율: 지정 채널의 당일 매입 대비 대사완료 비율을 백분율로 산출.", + "in": [ + "T_BIZDATE", + "T_CHANNEL" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_RATEDAY", + "desc": "일별 대사율: 당일 매입 대비 대사완료 건수 비율을 백분율로 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_RATEMERCH", + "desc": "가맹점별 대사율: 지정 가맹점의 당일 매입 대비 대사완료 비율을 백분율로 산출.", + "in": [ + "T_BIZDATE", + "T_MERCHANT" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_RATEMON", + "desc": "월별 대사율: 기준일이 속한 달 전체 매입 대비 대사완료 비율을 백분율로 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9134,6 +16365,84 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_RATEWEEK", + "desc": "주별 대사율: 기준일 포함 최근 7영업일 매입 대비 대사완료 비율을 백분율로 산출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_RECORR", + "desc": "정정후 재대사: 수수료조정이 반영된 매입을 대상으로 순액 재검증 후 로그를 남긴다.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_NET" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_REDIFF", + "desc": "차이발생 재대사: 지정 매입의 기대 순액과 SETTLE 실적 차이를 재산출해 rc_diff 갱신.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_DELTA" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_REFULL", + "desc": "전체 재대사: 당일 전체 매입 대비 대사완료(M/S) 비율을 재계산해 진척률로 반환.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_REFUNDREC", + "desc": "환불분 대사: 취소 유형이 REFUND 인 환불 금액과 건수를 별도로 대사 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMOUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_RELATE", + "desc": "지연도착 재대사: 원장 기표가 매입일보다 늦게 도착한 지연 건을 재대사 대상으로 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9230,6 +16539,30 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_ROUNDDIFF", + "desc": "반올림 차이 분석: 절대차가 1 이하인 단수 차이 건을 반올림 오차로 분류해 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_SIGNDIFF", + "desc": "부호 오류 차이 분석: 기대와 실제의 부호가 서로 반대인 rc_diff 라인을 검출.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9322,6 +16655,18 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_TIMEDIFF", + "desc": "기표시점 차이 분석: 매입 영업일과 원장 기표 영업일이 다른 이월/선기표 건수를 집계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9351,6 +16696,33 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_TRAILQRY", + "desc": "대사 감사추적 조회: 지정 매입의 전체 대사 로그 이력 건수와 최종 로그 ID 를 반환.", + "in": [ + "T_BIZDATE", + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT", + "T_ID1" + ] + }, + { + "mod": "rc", + "modnm": "대사", + "svc": "RC_UNDERAMT", + "desc": "과소 차이 분석: 당일 rc_diff 중 실제가 기대에 미달(diff_amt>0)한 과소 건수/합계.", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_DELTA" + ] + }, { "mod": "rc", "modnm": "대사", @@ -9444,6 +16816,21 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_ADJREVERSE", + "desc": "조정 취소(역분개): 기존 정산 조정(T_ADJ) 행을 조회해 동일 금액의 반대 부호 조정을", + "in": [ + "T_ADJ", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9475,6 +16862,20 @@ const SVC_SCREENS = [ "T_ID1" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_APPEALSTAT", + "desc": "이의신청 통계: 정산 조정 중 이의신청 관련(reason LIKE 'DISPUTE%') 건수와 청구", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_DELTA", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9487,6 +16888,35 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_APPROVEALL", + "desc": "정산 일괄 승인: 당일 지급 예정(SCHEDULED) 전 건을 한 번에 지급 완료(PAID)로", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_BADDEBT", + "desc": "대손 상각: 특정 가맹점의 장기 보류(HELD) 지급 건을 커서로 순회해 총 미수액을", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_COUNT", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9500,6 +16930,36 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_BANDSIM", + "desc": "구간별 요율 시뮬레이션: 입력 금액에 적용 가능한(band_min <= amount) 모든 구간 중", + "in": [ + "T_AMOUNT", + "T_STR1" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_FEE" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_BIWEEKLY", + "desc": "격주 정산: 가맹점 당일 정산 net 합계를 집계하고 영업일 기준 10일 뒤(약 2주)", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1", + "T_NET" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9573,6 +17033,58 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_CASHBACK", + "desc": "캐시백 정산: 거래 금액에 캐시백율(T_ARG1 bps)을 적용한 캐시백을 가맹점 지급", + "in": [ + "T_AMOUNT", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1", + "T_AMOUNT" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_CHAINSET", + "desc": "체인점 통합 정산: 업종/분류(T_CATEGORY)로 묶인 체인 매입의 정산 net 을 조인 집계해", + "in": [ + "T_MERCHANT", + "T_CATEGORY", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1", + "T_COUNT", + "T_NET" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_CHARGEBACK", + "desc": "차지백 정산 처리: 차지백 금액(T_AMOUNT)을 음(-)의 정산 조정으로 회수하고 해당", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_AMOUNT", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9588,6 +17100,20 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_COLLATERAL", + "desc": "담보/보증금 잔액: 정산 상세의 DEPOSIT 라인을 합산해 가맹점 누적 보증금(담보)", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_AMOUNT", + "T_COUNT", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9598,6 +17124,24 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_COUPONADJ", + "desc": "쿠폰 조정 정산: 발급 쿠폰 금액(T_ARG1)을 가맹점 부담분으로 양(+)의 정산 조정에", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_ARG1", + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9642,6 +17186,20 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_CYCLED3", + "desc": "D+3 정산 주기: 영업일(T_BIZDATE)로부터 영업일 기준 3일 뒤 지급일을 산출해", + "in": [ + "T_NET", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_ID1" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9659,6 +17217,35 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_CYCLEQTR", + "desc": "분기 정산 주기: 기준일(T_BIZDATE) 이하의 미지급 정산 net 을 누적 합계로 집계해", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1", + "T_COUNT", + "T_NET" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_CYCLESET", + "desc": "정산 주기 설정 조회: 가맹점 한도(daily_limit)를 기준으로 적용 정산 주기 코드", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_RC", + "T_AMOUNT" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9705,6 +17292,21 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_DEBTNET", + "desc": "채권 상계: 가맹점의 지급 예정(SCHEDULED) 채무와 보류(HELD) 미수 채권을 각각", + "in": [ + "T_MERCHANT" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_NET", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9739,6 +17341,25 @@ const SVC_SCREENS = [ "T_ADJ" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_DEPOSITSET", + "desc": "보증금 설정/차감: 가맹점 보증금 예치액(T_ARG1)을 정산 상세에 DEPOSIT 라인으로", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_NET", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_AMOUNT", + "T_NET" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9767,6 +17388,22 @@ const SVC_SCREENS = [ "T_ADJ" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_DISPUTERSV", + "desc": "이의신청 결과 반영: 판정(T_STATUS: A=인용/R=기각)에 따라 인용 시 청구액을 양(+)의", + "in": [ + "T_PURCHASE_ID", + "T_DELTA", + "T_MERCHANT", + "T_STATUS", + "T_BIZDATE" + ], + "out": [ + "T_ADJ" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9802,6 +17439,24 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_EVENTFEE", + "desc": "이벤트 수수료 감면: 이벤트 기간 거래에 대해 정상 요율의 절반을 적용한 감면", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_AMOUNT", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_FEE", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9834,6 +17489,40 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_FLATFEE", + "desc": "정액 수수료 적용: 거래 건마다 고정 금액 수수료를 정산 상세(st_settle_dtl)에", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_FEE", + "T_ID1" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_FLATVSPCT", + "desc": "정액 대 정률 비교: 정액(T_ARG1)과 정률(금액 x T_ARG2 bps)을 각각 계산해 더 저렴한", + "in": [ + "T_AMOUNT", + "T_ARG1", + "T_ARG2" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_FEE", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9864,6 +17553,69 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_GRADESTAT", + "desc": "가맹점 등급별 통계: 가맹점 요율(mdr_bps) 그룹별 가맹점 수를 커서로 집계해 등급", + "in": [], + "out": [ + "T_COUNT", + "T_ARG1", + "T_ARG2", + "T_RC" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_GROWTHINC", + "desc": "성장 인센티브: 당일 매출과 비교일(T_STR1) 매출을 비교해 증가분(성장분)에 대해서만", + "in": [ + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE", + "T_STR1" + ], + "out": [ + "T_RC", + "T_DELTA", + "T_ADJ", + "T_AMT1" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_GRPALLOC", + "desc": "그룹 정산 배분: 그룹 통합 지급총액(T_AMOUNT)을 구성원의 net 비중에 따라 안분해", + "in": [ + "T_AMOUNT", + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_AMOUNT" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_GRPROLLUP", + "desc": "가맹점 그룹 집계 rollup: prefix(T_KEY1) 로 묶인 그룹 구성원의 당일 매출/수수료/net", + "in": [ + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_FEE", + "T_NET" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9905,6 +17657,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_INSTANTPAY", + "desc": "즉시 정산 지급: 정산 net 을 당일 지급으로 즉시 확정(status=PAID)해 지급", + "in": [ + "T_NET", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -9961,6 +17728,37 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_LOCKBATCH", + "desc": "정산 배치 잠금: 당일 확정된 정산(status=SETTLED)을 일괄 LOCKED 로 전환해 이후", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_LOYALTY", + "desc": "로열티 인센티브: 가맹점의 전체 기간 누적 매출(st_merch_settle 전 일자 합계)에", + "in": [ + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_GROSS", + "T_COUNT", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10008,6 +17806,22 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_MKTGSHARE", + "desc": "마케팅 분담금 정산: 가맹점 일별 집계(st_merch_settle)의 총매출에 분담율", + "in": [ + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_GROSS", + "T_FEE" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10021,6 +17835,20 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_MONTHSTAT", + "desc": "월별 정산 통계: 대상 월(T_STR1, YYYY-MM)에 속하는 정산의 건수/net 합계/최대 net 을", + "in": [ + "T_STR1" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_AMT1" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10048,6 +17876,18 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_NEXTPAYDT", + "desc": "다음 지급 영업일 계산: 입력 영업일의 다음 영업일(주말 스킵)을 공통 유틸", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10080,6 +17920,40 @@ const SVC_SCREENS = [ "T_SETTLE_ID" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_PARTNERSET", + "desc": "제휴사 정산 배분: 정산 net 금액에서 제휴 지분율(T_ARG1 bps)만큼을 제휴사", + "in": [ + "T_NET", + "T_ARG1", + "T_STR1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1", + "T_AMOUNT" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_PARTPAY", + "desc": "부분 지급 처리: 정산 net 중 일부(T_ARG1)만 당일 지급(PAID)으로 확정하고", + "in": [ + "T_NET", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_ID1", + "T_AMOUNT", + "T_NET" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10118,6 +17992,58 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_PCTFEE", + "desc": "정률 수수료 계산: 거래 금액에 입력 요율(bps, T_ARG1)을 곱해(acq_fee) 수수료를", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_AMOUNT", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_FEE", + "T_RC", + "T_ID1" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_PRECONFIRM", + "desc": "정산 확정 전 검증: 당일 지급 중 보류(HELD) 건이 남아있는지 확인해 확정 가능", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_RC" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_PROMOAPPLY", + "desc": "프로모션 할인 적용: 산정된 수수료(T_FEE)에 프로모션 할인율(T_ARG1 bps)을 적용해", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_FEE", + "T_ARG1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_DELTA", + "T_NET" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10156,6 +18082,76 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_RATECARD", + "desc": "요율 카드 조회: 특정 수수료 유형의 등록 구간 개수와 최소/최대 요율을 집계해", + "in": [ + "T_STR1" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_AMT1", + "T_AMT2" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_RATECMP", + "desc": "요율 유형 비교: 두 수수료 유형(T_STR1/T_STR2)의 입력 금액 적용 요율을", + "in": [ + "T_AMOUNT", + "T_STR1", + "T_STR2" + ], + "out": [ + "T_ARG1", + "T_ARG2", + "T_DELTA", + "T_FEE" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_RATEFLOOR", + "desc": "요율 하한 적용: 정률 수수료(금액 x bps)가 하한액(T_ARG2)에 미달하면 하한으로", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_AMOUNT", + "T_ARG1", + "T_ARG2", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_FEE", + "T_RC" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_RATEMAX", + "desc": "요율 상한 적용: 정률 수수료(금액 x bps)를 계산하되 상한액(T_ARG2)을 초과하면", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_AMOUNT", + "T_ARG1", + "T_ARG2", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_FEE", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10171,6 +18167,24 @@ const SVC_SCREENS = [ "T_FEE" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_REBATE", + "desc": "리베이트 정산: 부과 수수료(T_FEE)의 일정 비율(T_ARG1 bps)을 가맹점에 돌려주는", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_FEE", + "T_ARG1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10183,6 +18197,20 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_RECVAGE", + "desc": "미수금 연령 분석: 기준일(T_BIZDATE) 이전 지급일의 보류(HELD) 지급 건수와 금액을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10198,6 +18226,57 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_RECVSTAT", + "desc": "미수금 통계: 지급(st_payment) 상태별 건수와 금액을 커서로 순회 집계해 보류/예정/", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_GROSS", + "T_AMT1" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_REFERRAL", + "desc": "추천 인센티브: 추천으로 유치된 가맹점 수(prefix T_KEY1 매칭 건수)에 건당 보상액", + "in": [ + "T_ARG1", + "T_MERCHANT", + "T_KEY1", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ID1", + "T_COUNT", + "T_AMOUNT" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_REFUNDCALC", + "desc": "환불 재계산: 환불액(T_ARG1)에 비례한 수수료 환급분을 계산해 정산 상세에", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_ARG1", + "T_ARG2", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_FEE", + "T_ID1" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10214,6 +18293,24 @@ const SVC_SCREENS = [ "T_FEE" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_RELAYSPLIT", + "desc": "중계수수료 분배: 중계 수수료(T_FEE)를 지분율(T_ARG1 bps)에 따라 중계사 몫과", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_FEE", + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10238,6 +18335,25 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_REPARTIAL", + "desc": "부분 재정산: 부분취소액(T_DELTA)만큼 줄어든 잔여 거래금액으로 수수료를 재계산해", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_AMOUNT", + "T_DELTA", + "T_ARG1", + "T_BIZDATE" + ], + "out": [ + "T_ADJ", + "T_FEE", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10299,6 +18415,20 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_SLABFEE", + "desc": "슬라브(누진 구간) 요율 계산: 요율표의 각 구간에 대해 금액이 걸치는 부분에만", + "in": [ + "T_AMOUNT", + "T_STR1" + ], + "out": [ + "T_FEE", + "T_COUNT" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10370,6 +18500,21 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_SUPPLYCALC", + "desc": "공급가액 역산: 부가세 포함 총액(T_AMOUNT)에서 공급가액과 부가세를 분리", + "in": [ + "T_AMOUNT" + ], + "out": [ + "T_RC", + "T_GROSS", + "T_NET", + "T_TAX" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10387,6 +18532,36 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_TAXSUMMARY", + "desc": "세금 요약: 입력 수수료(T_FEE)를 공급가액으로 부가세(acq_vat)와 원천세", + "in": [ + "T_FEE", + "T_ARG1" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_TAX", + "T_NET" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_TIERFEE", + "desc": "구간별 정률 적용: 입력 금액에 해당하는 최상위 구간(band_min <= amount)의 요율을", + "in": [ + "T_AMOUNT", + "T_STR1" + ], + "out": [ + "T_RC", + "T_FEE" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10415,6 +18590,19 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_UNLOCK", + "desc": "정산 잠금 해제: 잠금된 정산(status=LOCKED)을 다시 SETTLED 로 되돌려 조정/재계산을", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10430,6 +18618,20 @@ const SVC_SCREENS = [ "T_GROSS" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_VANDETAIL", + "desc": "VAN 수수료 세분 명세: 당일 정산 상세 중 VAN 라인의 건수/합계/평균을 집계해", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_FEE", + "T_AMT1" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10460,6 +18662,41 @@ const SVC_SCREENS = [ "T_TAX" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_VATADJUST", + "desc": "부가세 조정: 조정된 공급가액(T_ARG1)에 대한 재산정 부가세(acq_vat)를 정산 상세에", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_ARG1", + "T_TAX", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_TAX", + "T_DELTA", + "T_ID1" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_VATDETAIL", + "desc": "부가세 명세: 가맹점 당일 수수료 상세(MDR/VAN/RELAY 합계)를 공급가액으로 보고", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_TAX", + "T_COUNT" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10476,6 +18713,39 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_VOIDRECALC", + "desc": "void(전체 취소) 재계산: 매입 정산 net 전액을 역분개(음수 조정)하고 해당", + "in": [ + "T_SETTLE_ID", + "T_PURCHASE_ID", + "T_NET", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_ADJ", + "T_DELTA" + ] + }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_VOLREBATE", + "desc": "거래량 리베이트: 가맹점 일별 집계의 거래 건수 구간에 따라 리베이트율을 차등", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_ADJ", + "T_COUNT", + "T_DELTA" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10492,6 +18762,22 @@ const SVC_SCREENS = [ "T_TAX" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_WHTDETAIL", + "desc": "원천세 명세: 가맹점 당일 수수료 합계에 원천징수율(T_ARG1 bps)을 적용해", + "in": [ + "T_ARG1", + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_GROSS", + "T_TAX", + "T_RC" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10508,6 +18794,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "st", + "modnm": "정산/수수료", + "svc": "ST_WORKFLOW", + "desc": "정산 워크플로 상태 전이: 요청 전이(from=T_STR1 -> to=T_STR2)가 허용 전이표", + "in": [ + "T_STR1", + "T_STR2", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT" + ] + }, { "mod": "st", "modnm": "정산/수수료", @@ -10606,6 +18907,32 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_APPRLATE", + "desc": "기능: 지연 승인 검출 건별 (승인시각-매입생성 경과초>T_ARG1 기본86400, RC 1=지연).", + "in": [ + "T_PURCHASE_ID", + "T_ARG1" + ], + "out": [ + "T_ARG2", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_APPRNOPUR", + "desc": "기능: 미매입 승인 검출 (매입 원거래 없는 고아 승인 전수, RC 1=존재; 최소 승인ID 반환).", + "in": [], + "out": [ + "T_COUNT", + "T_ID1", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10619,6 +18946,67 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_AXLEDGDC", + "desc": "기능: 정합성 축 검증 원장 대차 (RECON 차변 vs SETTLE 대변, RC 비트 1=대차불일치 2=지급대응 없음).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_AMT3", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_AXPURAPPR", + "desc": "기능: 정합성 축 검증 매입-승인 (일자별 총매입금액 vs 승인가결 매입금액 합, RC 1=불일치).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_AXPURSETT", + "desc": "기능: 정합성 축 검증 매입-정산 (정산대상 매입 순액 vs settlement 순액 합, RC 1=불일치).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_COUNT", + "T_DELTA", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_AXSETTPAY", + "desc": "기능: 정합성 축 검증 정산-지급 (settlement 순액 합 vs 원장 지급 XFER 합, RC 1=불일치).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_DELTA", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10676,6 +19064,22 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_CANCELRATE", + "desc": "기능: 취소율 검증 (취소 건수/매입 건수 비율>=T_ARG1% 기본10, RC 1=취소율 과다).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10688,6 +19092,38 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_CATSKEW", + "desc": "기능: 업종 편중 탐지 (최다 업종 비중>=T_ARG1% 기본80, RC 1=편중).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_CHANSPLIT", + "desc": "기능: 채널 분포 검증 (FOREIGN 채널 비중>=T_ARG1% 기본30, RC 1=편중).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10737,6 +19173,30 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_DATEFUT", + "desc": "기능: 미래일자 매입 검출 (영업일이 오늘 이후인 매입 수, RC 1=존재).", + "in": [], + "out": [ + "T_COUNT", + "T_ID1", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_DATEGAP", + "desc": "기능: 매입-정산 일자 역전 검출 (정산일<매입일 건수, RC 1=존재; 최소 정산ID 반환).", + "in": [], + "out": [ + "T_COUNT", + "T_SETTLE_ID", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10781,6 +19241,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_DUPAPPR", + "desc": "기능: 중복 승인 검출 건별 (동일 매입 승인가결 2건 이상, RC 1=중복; 검증로그 기록).", + "in": [ + "T_PURCHASE_ID", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10794,6 +19268,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_DUPLEDGR", + "desc": "기능: 중복 원장 엔트리 검출 (동일 매입+엔트리유형 최대 그룹 건수>1, RC 1=중복).", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10823,6 +19310,19 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_DUPPURCH", + "desc": "기능: 중복 매입 검출 건별 (동일 가맹점+금액+영업일의 타 매입 존재, RC 1=중복).", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10839,6 +19339,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_DUPSETTMR", + "desc": "기능: 가맹점 중복 정산 검출 (동일 가맹점+영업일 settlement 2건 이상, RC 1=중복).", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10881,6 +19395,21 @@ const SVC_SCREENS = [ "T_COUNT" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_FEEHIGH", + "desc": "기능: 수수료 과다 검출 건별 (fee 실효요율 bps>T_ARG1 기본500, RC 1=과다 8=매입없음).", + "in": [ + "T_PURCHASE_ID", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_FEE", + "T_ARG2" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -10943,6 +19472,21 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_ISSUERMIX", + "desc": "기능: 발급사 분포 이상 (최다 발급사 비중>=T_ARG1% 기본80, RC 1=편중 8=무거래).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2", + "T_ARG3" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11032,6 +19576,37 @@ const SVC_SCREENS = [ "T_ARG2" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_LIMITPCT", + "desc": "기능: 한도 사용률 검증 (가맹점 일 매입합/한도 %가 T_ARG1 기본90 이상, RC 1=근접 8=가맹점없음).", + "in": [ + "T_MERCHANT", + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2", + "T_AMT1" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_LONGOPEN", + "desc": "기능: 장기 미결 의심거래 검출 (OPEN 상태로 N일 초과 경과, 기준 T_ARG1 기본7, RC 1=존재).", + "in": [ + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ID1", + "T_ARG2", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11070,6 +19645,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_NEGFEE", + "desc": "기능: 음수 수수료 매입 검출 (일자별 fee<0 매입 수, RC 1=존재; 최소 매입ID 반환).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ID1", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11085,6 +19674,63 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_NIGHTTXN", + "desc": "기능: 심야 거래 패턴 탐지 (0-6시 거래 비중>=T_ARG1% 기본20, RC 1=편중).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_OUTLIER", + "desc": "기능: 이상치 매입 탐지 (금액>당일 평균*배수 T_ARG1 기본4 건수, RC 1=존재).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_OVERLIMIT", + "desc": "기능: 가맹점 한도위반 검출 (일 매입합>일한도 가맹점 수, RC 1=존재).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_PURNOAPPR", + "desc": "기능: 무승인 매입 검출 (일자별 승인 전무 매입 수, 취소분 제외, RC 1=존재).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_ID1", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11139,6 +19785,54 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RIAPPR", + "desc": "기능: 참조무결성 승인 (취소/무효 매입에 승인가결 존재 건수, RC 1=위반).", + "in": [], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RICANCEL", + "desc": "기능: 참조무결성 취소 (일자별 매입 원거래 없는 취소 수, RC 1=위반; 최소 취소ID 반환).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_CANCEL_ID", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RIFX", + "desc": "기능: 참조무결성 해외 (매입 원거래 없는 해외 내역 수+환산합, RC 1=위반).", + "in": [], + "out": [ + "T_COUNT", + "T_AMT1", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RIINSTALL", + "desc": "기능: 참조무결성 할부 (매입 원거래 없는 할부 내역 수, NOT IN, RC 1=위반).", + "in": [], + "out": [ + "T_COUNT", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11169,6 +19863,18 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RIPAY", + "desc": "기능: 참조무결성 지급 (지급 XFER 원장의 매입 원거래 부재 건수, RC 1=위반).", + "in": [], + "out": [ + "T_COUNT", + "T_ID1", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11198,6 +19904,33 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RITAXINV", + "desc": "기능: 참조무결성 세금계산서 (매입 원거래 없는 세금계산서 수, LEFT JOIN, RC 1=위반).", + "in": [], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_ROUNDAMT", + "desc": "기능: 라운드금액 패턴 탐지 (10만원 배수 매입 비중>=T_ARG1% 기본50, RC 1=편중).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11211,6 +19944,33 @@ const SVC_SCREENS = [ ], "out": [] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RULECOUNT", + "desc": "기능: 검증규칙 심각도 분포 (INFO/WARN/ERROR/CRIT 건수, RC 1=CRIT 규칙 존재).", + "in": [], + "out": [ + "T_ARG1", + "T_ARG2", + "T_ARG3", + "T_ARG4", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RULEFIND", + "desc": "기능: 검증규칙 단건 조회 (dbio vldb_get_rule 로 임계값/심각도 반환, RC 1=미존재).", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11254,6 +20014,18 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RULEON", + "desc": "기능: 검증규칙 활성화 (status ACTIVE 전환, RC 1=이미 활성 2=상태변경 실패 3=규칙없음).", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11269,6 +20041,32 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RULERENAME", + "desc": "기능: 검증규칙 명칭 변경 (rule_name UPDATE, 빈 값이면 기본명, RC 1=대상 규칙 없음).", + "in": [ + "T_STR3", + "T_STR1" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RULESEV", + "desc": "기능: 검증규칙 심각도 변경 (INFO/WARN/ERROR/CRIT 유효성 검사 후 UPDATE, RC 1=규칙없음 2=심각도오류).", + "in": [ + "T_STR3", + "T_STR4" + ], + "out": [ + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11282,6 +20080,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_RULEVER", + "desc": "기능: 검증규칙 최신성 검사 (T_ARG1 기본30일 이내 갱신 여부, RC 1=노후 2=규칙없음).", + "in": [ + "T_STR3", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11308,6 +20120,52 @@ const SVC_SCREENS = [ "T_NET" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SETTLREV", + "desc": "기능: 정산 역전 검출 (일자별 순액 음수 settlement 건수, RC 1=존재; 최악치 반환).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_AMT1", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SPIKEAMT", + "desc": "기능: 금액 급증 탐지 (당일 최대 매입>평균*배수 T_ARG1 기본5, RC 1=급증).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_AMT1", + "T_AMT2", + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_STATMIX", + "desc": "기능: 상태 분포 검증 (취소 C 상태 비중>=T_ARG1% 기본15, RC 1=취소 과다).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_ARG3", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11349,6 +20207,35 @@ const SVC_SCREENS = [ "T_DELTA" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUMFEECHK", + "desc": "기능: 통계-원천 수수료 대사 (일자별 가맹점 수수료 집계 vs 원천 합 불일치 수, RC 1=존재).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUMGROSS", + "desc": "기능: 통계-원천 총액 대사 (merchant_summary 총액 vs 원천 매입금액 합, RC 1=불일치 8=통계없음).", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_GROSS", + "T_AMT1", + "T_DELTA" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11362,6 +20249,21 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUMNETCHK", + "desc": "기능: 통계-원천 순액 대사 (merchant_summary 순액 vs 원천 매입순액 합, RC 1=불일치 8=통계없음).", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_NET", + "T_AMT1" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11379,6 +20281,22 @@ const SVC_SCREENS = [ "T_AMT1" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUMTXNCNT", + "desc": "기능: 통계-원천 건수 대사 (merchant_summary 건수 vs 원천 매입 건수, RC 1=불일치 8=통계없음).", + "in": [ + "T_MERCHANT", + "T_BIZDATE" + ], + "out": [ + "T_RC", + "T_COUNT", + "T_ARG1", + "T_DELTA" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11408,6 +20326,59 @@ const SVC_SCREENS = [ "T_PURCHASE_ID" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUSPAGEAVG", + "desc": "기능: 의심거래 평균 경과일 (OPEN 평균 경과일>T_ARG1 기본5, RC 1=평균 경과 초과).", + "in": [ + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_ARG2", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUSPASSIGN", + "desc": "기능: 의심거래 담당 배정 (미종결 건 detail 에 담당자 기록, RC 1=대상없음/종결됨).", + "in": [ + "T_ID1", + "T_STR1" + ], + "out": [ + "T_ARG1", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUSPBULK", + "desc": "기능: 의심거래 일괄 확인 (일자별 OPEN 건 CONFIRMED 일괄 전환, RC 1=처리 대상 없음).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUSPESCAL", + "desc": "기능: 의심거래 상향 (WARN→ERROR→CRIT 단계 승격, RC 1=상향불가 3=미존재).", + "in": [ + "T_ID1" + ], + "out": [ + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11421,6 +20392,33 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUSPNOTE", + "desc": "기능: 의심거래 코멘트 추가 (상태 무관 detail 에 메모 append, RC 1=대상 미존재).", + "in": [ + "T_ID1", + "T_MSG" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUSPRECHK", + "desc": "기능: 의심거래 재검증 (CONFIRMED→OPEN 재개, 재검증자 기록, RC 1=CONFIRMED 아님/미존재).", + "in": [ + "T_ID1", + "T_STR1" + ], + "out": [ + "T_ARG1", + "T_RC" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11450,6 +20448,20 @@ const SVC_SCREENS = [ "T_RC" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_SUSPTRIAGE", + "desc": "기능: 의심거래 분류 (OPEN 건 심각도 재지정, RC 1=대상없음/OPEN아님 2=심각도오류).", + "in": [ + "T_ID1", + "T_STR4" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11477,6 +20489,20 @@ const SVC_SCREENS = [ "T_ARG3" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_THRCOMPARE", + "desc": "기능: 임계값 비교 (실측 T_ARG1 vs 규칙 임계값, RC 1=초과 8=규칙없음).", + "in": [ + "T_STR3", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11491,6 +20517,46 @@ const SVC_SCREENS = [ "T_ARG1" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_THRGET", + "desc": "기능: 임계값 조회 (규칙 임계값+심각도+상태 직접 SELECT, RC 1=규칙 없음).", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC", + "T_ARG1" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_THRRESET", + "desc": "기능: 임계값 초기화 (threshold 0 으로 리셋, RC 1=대상 규칙 없음).", + "in": [ + "T_STR3" + ], + "out": [ + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_THRSCALE", + "desc": "기능: 임계값 배율 조정 (threshold *= T_ARG1%, RC 2=배율오류 3=규칙없음).", + "in": [ + "T_STR3", + "T_ARG1" + ], + "out": [ + "T_RC", + "T_ARG2", + "T_ARG3" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11550,6 +20616,20 @@ const SVC_SCREENS = [ "T_AMT2" ] }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_TXNREV", + "desc": "기능: 역전거래 검출 건별 (RC 비트 1=음수금액 2=음수순액 4=순액>금액, 8=매입없음).", + "in": [ + "T_PURCHASE_ID" + ], + "out": [ + "T_RC", + "T_AMOUNT", + "T_NET" + ] + }, { "mod": "vl", "modnm": "정합성검증", @@ -11576,5 +20656,32 @@ const SVC_SCREENS = [ "T_ARG2", "T_RC" ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_VELOCITY", + "desc": "기능: 거래 속도 이상 탐지 (당일 매입 건수>T_ARG1 기본100 가맹점 수, RC 1=존재).", + "in": [ + "T_BIZDATE", + "T_ARG1" + ], + "out": [ + "T_COUNT", + "T_RC" + ] + }, + { + "mod": "vl", + "modnm": "정합성검증", + "svc": "VL_ZERONET", + "desc": "기능: 정산순액 소실 검출 (금액>0인데 순액=0 매입 수, RC 1=존재).", + "in": [ + "T_BIZDATE" + ], + "out": [ + "T_COUNT", + "T_RC" + ] } ];