diff --git a/app/build.sh b/app/build.sh index 76d6497..50a1096 100644 --- a/app/build.sh +++ b/app/build.sh @@ -29,12 +29,20 @@ PGLIB="$(pg_config --libdir)" mkdir -p "$BIN" "$LIB" "$OBJ" # Includes for every compile (CFLAGS is honored by buildserver/buildclient and -# by our direct gcc calls). Add every module's dbio dir so headers resolve. +# by our direct gcc calls). Add every module's dbio AND svc dir so headers resolve +# (svc/*.pgc copybooks include the module's dbio + common headers). CFLAGS="-I$UBF -I$EXINC -I$PGINC -I$SRC/common" for d in "$SRC"/*/dbio; do [ -d "$d" ] && CFLAGS="$CFLAGS -I$d"; done +for d in "$SRC"/*/svc; do [ -d "$d" ] && CFLAGS="$CFLAGS -I$d"; done export CFLAGS PGLINK="-L$PGLIB -lecpg -lpq" +# ecpg needs the same header search path as gcc (its preprocessor opens the +# #include'd copybooks to find EXEC SQL host-var decls). Mirror CFLAGS' -I set. +ECPG_INC="-I$UBF -I$SRC/common" +for d in "$SRC"/*/dbio; do [ -d "$d" ] && ECPG_INC="$ECPG_INC -I$d"; done +for d in "$SRC"/*/svc; do [ -d "$d" ] && ECPG_INC="$ECPG_INC -I$d"; done + log() { echo "[build] $*"; } # --------------------------------------------------------------------------- @@ -59,7 +67,7 @@ ecpg_gen() { local pgc="$1" base out base="$(basename "${pgc%.pgc}")" out="$OBJ/${base}.c" - ecpg -I"$UBF" -I"$(dirname "$pgc")" -o "$out" "$pgc" + ecpg $ECPG_INC -I"$(dirname "$pgc")" -o "$out" "$pgc" echo "$out" } @@ -79,9 +87,11 @@ NSVR=0; NDBIO=0; NBATCH=0 # --------------------------------------------------------------------------- # 2..4 per module: dbio lib, server, batches # --------------------------------------------------------------------------- +NSVC=0 for m in "${MODS[@]}"; do MDIR="$SRC/$m" DBIO_LIB="" + SVC_LIB="" # -- dbio static lib (optional) -- if compgen -G "$MDIR/dbio/*.pgc" >/dev/null; then @@ -98,11 +108,29 @@ for m in "${MODS[@]}"; do NDBIO=$((NDBIO+1)) fi - # -- module server _svr -- + # -- per-service static lib (NEW legacy layout, optional) -- + # A module may keep its services INLINE in _svr.pgc (old style) OR split + # them into svc/*.pgc, one service per file (new style). Both are supported; + # when svc/*.pgc exist they are archived into libsvc.a and linked in. + if compgen -G "$MDIR/svc/*.pgc" >/dev/null; then + log "lib${m}svc.a" + SVC_OBJS=() + for pgc in "$MDIR"/svc/*.pgc; do + c="$(ecpg_gen "$pgc")" + o="${c%.c}.o" + gcc -c $CFLAGS "$c" -o "$o" + SVC_OBJS+=("$o") + done + ar rcs "$LIB/lib${m}svc.a" "${SVC_OBJS[@]}" + SVC_LIB="$LIB/lib${m}svc.a" + NSVC=$((NSVC+1)) + fi + + # -- module server _svr (links svc lib + dbio lib + common) -- log "${m}_svr" SVR_C="$(ecpg_gen "$MDIR/${m}_svr.pgc")" buildserver -o "$BIN/${m}_svr" -f "$SVR_C" \ - -a "$DBIO_LIB $LIB/libacqcommon.a $PGLINK" + -a "$SVC_LIB $DBIO_LIB $LIB/libacqcommon.a $PGLINK" NSVR=$((NSVR+1)) # -- batches (optional): each *.pgc -> its own client executable -- @@ -184,5 +212,5 @@ $(gen_servers) EOF -log "DONE: modules=${#MODS[@]} servers=$NSVR dbio_libs=$NDBIO batches=$NBATCH clients=$NCLI" +log "DONE: modules=${#MODS[@]} servers=$NSVR svc_libs=$NSVC dbio_libs=$NDBIO batches=$NBATCH clients=$NCLI" echo "[build] server binaries: ${MODS[*]/%/_svr}" diff --git a/app/src/ac/ac_svr.pgc b/app/src/ac/ac_svr.pgc index 02d4e33..1cb1f98 100644 --- a/app/src/ac/ac_svr.pgc +++ b/app/src/ac/ac_svr.pgc @@ -1,70 +1,73 @@ /* - * ac_svr.pgc - ac (매입) MODULE SERVER. + * ac_svr.pgc - ac (매입) MODULE SERVER (thin dispatcher). * - * ONE binary that tpadvertise()s ALL ~30 online ac services (the real Tuxedo - * "one module server, many services" pattern). Every service is a distinct 매입 - * operation with genuine EXEC SQL (directly, or via the linked libacdbio.a dbio - * layer) and common math from libacqcommon.a. + * The legacy "one module server, many services" pattern: this binary owns NO + * business logic. Each of ac's ~30 online services now lives in its own file + * (app/src/ac/svc/.pgc) with its own copybook header + * (app/src/ac/svc/.h); those objects are archived into libacsvc.a and + * linked into this server. Here we only: + * - include every service copybook (for the extern prototypes), + * - tpopen() the ECPG XA RM, + * - tpadvertise() all services from a {name, fn} table, + * - tpclose() at shutdown. * * XA: the connection is opened once by tpopen() (ECPG XA switch libndrxxaecpg.so); - * there is NO EXEC SQL CONNECT. Each tpcall runs on this process's XA branch, so - * a service sees its OWN uncommitted writes but NOT a sibling branch's (see the - * ACQUIRE->RECONCILE->SETTLE chain, which crosses ac_svr/rc_svr/st_svr). + * there is NO EXEC SQL CONNECT. Each tpcall runs on this process's XA branch (see + * the ACQUIRE->RECONCILE->SETTLE chain, which crosses ac_svr/rc_svr/st_svr). */ -#include -#include -#include #include #include #include -#include -#include "acq.fd.h" -#include "ac_dbio.h" -#include "acq_common.h" -/* ----- small UBF helpers ------------------------------------------------- */ -static long getl(UBFH *b, BFLDID f) { long v = 0; Bget(b, f, 0, (char *)&v, 0L); return v; } -static void gets_(UBFH *b, BFLDID f, char *out, int cap) -{ BFLDLEN l = cap; out[0] = 0; Bget(b, f, 0, out, &l); } -static void setl(UBFH *b, BFLDID f, long v) { Bchg(b, f, 0, (char *)&v, 0L); } +/* every ac service's copybook header (declares its record struct + prototype) */ +#include "ACQUIRE.h" +#include "ACQ_DDC.h" +#include "ACQ_EDI.h" +#include "ACQ_EDC.h" +#include "ACQ_CANCEL.h" +#include "ACQ_CORRECT.h" +#include "ACQ_PARTIAL.h" +#include "ACQ_INSTALL.h" +#include "ACQ_FOREIGN.h" +#include "ACQ_UNMATCH.h" +#include "ACQ_REPROC.h" +#include "ACQ_DUPCHK.h" +#include "ACQ_LIMITCHK.h" +#include "ACQ_FEEADJ.h" +#include "ACQ_STATUS.h" +#include "ACQ_MERCHSUM.h" +#include "ACQ_ISSUERCLS.h" +#include "ACQ_WHT.h" +#include "ACQ_TAXINV.h" +#include "ACQ_APPRLINK.h" +#include "ACQ_DATECHG.h" +#include "ACQ_AMTFIX.h" +#include "ACQ_RECLASS.h" +#include "ACQ_XFERLINK.h" +#include "ACQ_APPRMAP.h" +#include "ACQ_SETTLELINK.h" +#include "ACQ_HOLD.h" +#include "ACQ_RELEASE.h" +#include "ACQ_VOID.h" +#include "ACQ_REFUND.h" -#define FAIL(b) do { tpreturn(TPFAIL, 0, (char *)(b), 0L, 0L); return; } while (0) -#define OK(b) do { tpreturn(TPSUCCESS, 0, (char *)(b), 0L, 0L); return; } while (0) - -/* ----- service prototypes ------------------------------------------------ */ -void ACQUIRE(TPSVCINFO *p); void ACQ_DDC(TPSVCINFO *p); -void ACQ_EDI(TPSVCINFO *p); void ACQ_EDC(TPSVCINFO *p); -void ACQ_CANCEL(TPSVCINFO *p); void ACQ_CORRECT(TPSVCINFO *p); -void ACQ_PARTIAL(TPSVCINFO *p); void ACQ_INSTALL(TPSVCINFO *p); -void ACQ_FOREIGN(TPSVCINFO *p); void ACQ_UNMATCH(TPSVCINFO *p); -void ACQ_REPROC(TPSVCINFO *p); void ACQ_DUPCHK(TPSVCINFO *p); -void ACQ_LIMITCHK(TPSVCINFO *p); void ACQ_FEEADJ(TPSVCINFO *p); -void ACQ_STATUS(TPSVCINFO *p); void ACQ_MERCHSUM(TPSVCINFO *p); -void ACQ_ISSUERCLS(TPSVCINFO *p);void ACQ_WHT(TPSVCINFO *p); -void ACQ_TAXINV(TPSVCINFO *p); void ACQ_APPRLINK(TPSVCINFO *p); -void ACQ_DATECHG(TPSVCINFO *p); void ACQ_AMTFIX(TPSVCINFO *p); -void ACQ_RECLASS(TPSVCINFO *p); void ACQ_XFERLINK(TPSVCINFO *p); -void ACQ_APPRMAP(TPSVCINFO *p); void ACQ_SETTLELINK(TPSVCINFO *p); -void ACQ_HOLD(TPSVCINFO *p); void ACQ_RELEASE(TPSVCINFO *p); -void ACQ_VOID(TPSVCINFO *p); void ACQ_REFUND(TPSVCINFO *p); - -/* ----- advertise table (this is what "scales to 30" cleanly) ------------- */ +/* ----- 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}, - {"ACQ_EDI", ACQ_EDI}, {"ACQ_EDC", ACQ_EDC}, - {"ACQ_CANCEL", ACQ_CANCEL}, {"ACQ_CORRECT", ACQ_CORRECT}, - {"ACQ_PARTIAL", ACQ_PARTIAL}, {"ACQ_INSTALL", ACQ_INSTALL}, - {"ACQ_FOREIGN", ACQ_FOREIGN}, {"ACQ_UNMATCH", ACQ_UNMATCH}, - {"ACQ_REPROC", ACQ_REPROC}, {"ACQ_DUPCHK", ACQ_DUPCHK}, - {"ACQ_LIMITCHK", ACQ_LIMITCHK},{"ACQ_FEEADJ", ACQ_FEEADJ}, - {"ACQ_STATUS", ACQ_STATUS}, {"ACQ_MERCHSUM", ACQ_MERCHSUM}, + {"ACQUIRE", ACQUIRE}, {"ACQ_DDC", ACQ_DDC}, + {"ACQ_EDI", ACQ_EDI}, {"ACQ_EDC", ACQ_EDC}, + {"ACQ_CANCEL", ACQ_CANCEL}, {"ACQ_CORRECT", ACQ_CORRECT}, + {"ACQ_PARTIAL", ACQ_PARTIAL}, {"ACQ_INSTALL", ACQ_INSTALL}, + {"ACQ_FOREIGN", ACQ_FOREIGN}, {"ACQ_UNMATCH", ACQ_UNMATCH}, + {"ACQ_REPROC", ACQ_REPROC}, {"ACQ_DUPCHK", ACQ_DUPCHK}, + {"ACQ_LIMITCHK", ACQ_LIMITCHK}, {"ACQ_FEEADJ", ACQ_FEEADJ}, + {"ACQ_STATUS", ACQ_STATUS}, {"ACQ_MERCHSUM", ACQ_MERCHSUM}, {"ACQ_ISSUERCLS", ACQ_ISSUERCLS},{"ACQ_WHT", ACQ_WHT}, - {"ACQ_TAXINV", ACQ_TAXINV}, {"ACQ_APPRLINK", ACQ_APPRLINK}, - {"ACQ_DATECHG", ACQ_DATECHG}, {"ACQ_AMTFIX", ACQ_AMTFIX}, - {"ACQ_RECLASS", ACQ_RECLASS}, {"ACQ_XFERLINK", ACQ_XFERLINK}, - {"ACQ_APPRMAP", ACQ_APPRMAP}, {"ACQ_SETTLELINK", ACQ_SETTLELINK}, - {"ACQ_HOLD", ACQ_HOLD}, {"ACQ_RELEASE", ACQ_RELEASE}, - {"ACQ_VOID", ACQ_VOID}, {"ACQ_REFUND", ACQ_REFUND}, + {"ACQ_TAXINV", ACQ_TAXINV}, {"ACQ_APPRLINK", ACQ_APPRLINK}, + {"ACQ_DATECHG", ACQ_DATECHG}, {"ACQ_AMTFIX", ACQ_AMTFIX}, + {"ACQ_RECLASS", ACQ_RECLASS}, {"ACQ_XFERLINK", ACQ_XFERLINK}, + {"ACQ_APPRMAP", ACQ_APPRMAP}, {"ACQ_SETTLELINK", ACQ_SETTLELINK}, + {"ACQ_HOLD", ACQ_HOLD}, {"ACQ_RELEASE", ACQ_RELEASE}, + {"ACQ_VOID", ACQ_VOID}, {"ACQ_REFUND", ACQ_REFUND}, {NULL, NULL} }; @@ -91,597 +94,4 @@ void tpsvrdone(void) userlog("ac_svr: tpsvrdone"); } -/* ========================================================================= */ -/* 1. ACQUIRE - 매입 접수 + 승인, then the XA chain RECONCILE->SETTLE. */ -/* ========================================================================= */ -void ACQUIRE(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char merch[64], bizdate[16]; - long amount, bps = 25, fee, net, pid, rlen = 0; - - gets_(b, T_MERCHANT, merch, sizeof(merch)); - if (merch[0] == 0) { userlog("ACQUIRE: missing T_MERCHANT"); FAIL(b); } - amount = getl(b, T_AMOUNT); - gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); - - if (acdb_merchant_mdr(merch, &bps) < 0) bps = 25; /* keep 25bps default */ - fee = acq_fee(amount, bps); - net = amount - fee; - - pid = acdb_next_purchase_id(); - if (pid < 0) FAIL(b); - if (acdb_insert_purchase(pid, merch, amount, fee, net, "A", "POS", bizdate) < 0) FAIL(b); - if (acdb_insert_approval(pid, 1) < 0) FAIL(b); - - setl(b, T_PURCHASE_ID, pid); - setl(b, T_FEE, fee); - setl(b, T_NET, net); - Bchg(b, T_STATUS, 0, "A", 0L); - - userlog("ACQUIRE pid=%ld merch=%s amount=%ld fee=%ld net=%ld -> RECONCILE", - pid, merch, amount, fee, net); - - /* continue the SAME global tx into rc_svr (distinct branch) */ - if (tpcall("RECONCILE", (char *)b, 0L, (char **)&b, &rlen, 0L) < 0) { - userlog("ACQUIRE: tpcall(RECONCILE) FAIL: %s", tpstrerror(tperrno)); - FAIL(b); - } - /* our branch resumes: settle-complete the purchase we own */ - if (acdb_update_status(pid, "S") < 0) FAIL(b); - Bchg(b, T_STATUS, 0, "S", 0L); - OK(b); -} - -/* 2. ACQ_DDC - Dynamic Currency Conversion: record fx leg for a purchase. */ -void ACQ_DDC(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char bizdate[16], ccy[8]; - EXEC SQL BEGIN DECLARE SECTION; - long h_id, h_pid, h_fx, h_krw, h_rate; - char h_ccy[8], h_bizdate[16]; - EXEC SQL END DECLARE SECTION; - - h_pid = getl(b, T_PURCHASE_ID); - h_fx = getl(b, T_FX_AMT); - gets_(b, T_CCY, ccy, sizeof(ccy)); if (ccy[0] == 0) strcpy(ccy, "USD"); - gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); - h_rate = 1350; /* 13.50 KRW per unit (bps of 100) */ - h_krw = h_fx * h_rate / 100; - strncpy(h_ccy, ccy, sizeof(h_ccy)-1); h_ccy[sizeof(h_ccy)-1] = 0; - strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; - - EXEC SQL SELECT nextval('ac_fx_seq') INTO :h_id; - if (sqlca.sqlcode < 0) FAIL(b); - EXEC SQL INSERT INTO ac_fx (fx_id, purchase_id, ccy, fx_amount, krw_amount, rate_bps, biz_date) - VALUES (:h_id, :h_pid, :h_ccy, :h_fx, :h_krw, :h_rate, :h_bizdate); - if (sqlca.sqlcode < 0) { userlog("ACQ_DDC INSERT FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } - setl(b, T_AMOUNT, h_krw); - OK(b); -} - -/* 3. ACQ_EDI - EDI 청구 문서 접수. */ -void ACQ_EDI(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char merch[64], docno[32], bizdate[16]; - EXEC SQL BEGIN DECLARE SECTION; - long h_id, h_amount; - char h_merch[64], h_doc[32], h_bizdate[16]; - EXEC SQL END DECLARE SECTION; - - gets_(b, T_MERCHANT, merch, sizeof(merch)); - gets_(b, T_DOCNO, docno, sizeof(docno)); if (docno[0] == 0) strcpy(docno, "EDI-AUTO"); - gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); - h_amount = getl(b, T_AMOUNT); - strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; - strncpy(h_doc, docno, sizeof(h_doc)-1); h_doc[sizeof(h_doc)-1] = 0; - strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; - - EXEC SQL SELECT nextval('ac_edi_seq') INTO :h_id; - if (sqlca.sqlcode < 0) FAIL(b); - EXEC SQL INSERT INTO ac_edi (edi_id, merchant_id, doc_no, amount, biz_date) - VALUES (:h_id, :h_merch, :h_doc, :h_amount, :h_bizdate); - if (sqlca.sqlcode < 0) { userlog("ACQ_EDI INSERT FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } - OK(b); -} - -/* 4. ACQ_EDC - EDC 단말 매입 접수 (채널=EDC, 체인 없음). */ -void ACQ_EDC(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char merch[64], bizdate[16]; - long amount, bps = 25, fee, net, pid; - - gets_(b, T_MERCHANT, merch, sizeof(merch)); - amount = getl(b, T_AMOUNT); - gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); - if (acdb_merchant_mdr(merch, &bps) < 0) bps = 25; - fee = acq_fee(amount, bps); net = amount - fee; - pid = acdb_next_purchase_id(); - if (pid < 0) FAIL(b); - if (acdb_insert_purchase(pid, merch, amount, fee, net, "A", "EDC", bizdate) < 0) FAIL(b); - if (acdb_insert_approval(pid, 1) < 0) FAIL(b); - setl(b, T_PURCHASE_ID, pid); setl(b, T_FEE, fee); setl(b, T_NET, net); - OK(b); -} - -/* 5. ACQ_CANCEL - 전체 취소. */ -void ACQ_CANCEL(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char bizdate[16], reason[128]; - EXEC SQL BEGIN DECLARE SECTION; - long h_id, h_pid, h_amt; - char h_bizdate[16], h_reason[128]; - EXEC SQL END DECLARE SECTION; - - h_pid = getl(b, T_PURCHASE_ID); - h_amt = getl(b, T_AMOUNT); - gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); - gets_(b, T_REASON, reason, sizeof(reason)); if (reason[0] == 0) strcpy(reason, "CUSTOMER"); - strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; - strncpy(h_reason, reason, sizeof(h_reason)-1); h_reason[sizeof(h_reason)-1] = 0; - - 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, 'FULL', :h_amt, :h_reason, :h_bizdate); - if (sqlca.sqlcode < 0) FAIL(b); - if (acdb_update_status(h_pid, "C") < 0) FAIL(b); - setl(b, T_CANCEL_ID, h_id); - Bchg(b, T_STATUS, 0, "C", 0L); - OK(b); -} - -/* 6. ACQ_CORRECT - 금액 정정 + 수수료 재계산. */ -void ACQ_CORRECT(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char merch[64]; - long pid, amount, bps = 25, fee, net; - pid = getl(b, T_PURCHASE_ID); amount = getl(b, T_AMOUNT); - gets_(b, T_MERCHANT, merch, sizeof(merch)); - if (merch[0] && acdb_merchant_mdr(merch, &bps) < 0) bps = 25; - fee = acq_fee(amount, bps); net = amount - fee; - if (acdb_update_amount(pid, amount, fee, net) < 0) FAIL(b); - setl(b, T_FEE, fee); setl(b, T_NET, net); - OK(b); -} - -/* 7. ACQ_PARTIAL - 부분 취소. */ -void ACQ_PARTIAL(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char bizdate[16]; - EXEC SQL BEGIN DECLARE SECTION; - long h_id, h_pid, h_amt; - 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, bizdate, sizeof(bizdate)); - strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; - 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, 'PARTIAL', :h_amt, 'PARTIAL', :h_bizdate); - if (sqlca.sqlcode < 0) FAIL(b); - setl(b, T_CANCEL_ID, h_id); - OK(b); -} - -/* 8. ACQ_INSTALL - 할부 개월 분할 등록. */ -void ACQ_INSTALL(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char bizdate[16]; - long months, amount, i; - EXEC SQL BEGIN DECLARE SECTION; - long h_id, h_pid, h_seq, h_months, h_mamt; - char h_bizdate[16]; - EXEC SQL END DECLARE SECTION; - - h_pid = getl(b, T_PURCHASE_ID); - months = getl(b, T_INSTALL_N); if (months < 1) months = 3; - amount = getl(b, T_AMOUNT); - gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); - strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; - h_months = months; h_mamt = amount / months; - for (i = 1; i <= months; i++) { - h_seq = i; - EXEC SQL SELECT nextval('ac_install_seq') INTO :h_id; - if (sqlca.sqlcode < 0) FAIL(b); - EXEC SQL INSERT INTO ac_install (install_id, purchase_id, seq_no, months, month_amount, biz_date) - VALUES (:h_id, :h_pid, :h_seq, :h_months, :h_mamt, :h_bizdate); - if (sqlca.sqlcode < 0) FAIL(b); - } - setl(b, T_COUNT, months); - OK(b); -} - -/* 9. ACQ_FOREIGN - 해외 매입 (채널=FOREIGN) + fx leg. */ -void ACQ_FOREIGN(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char merch[64], bizdate[16], ccy[8]; - long amount, bps = 30, fee, net, pid; - EXEC SQL BEGIN DECLARE SECTION; - long h_id, h_pid, h_fx, h_krw, h_rate; - char h_ccy[8], h_bizdate[16]; - EXEC SQL END DECLARE SECTION; - - gets_(b, T_MERCHANT, merch, sizeof(merch)); - amount = getl(b, T_AMOUNT); - gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); - gets_(b, T_CCY, ccy, sizeof(ccy)); if (ccy[0] == 0) strcpy(ccy, "USD"); - if (acdb_merchant_mdr(merch, &bps) < 0) bps = 30; - fee = acq_fee(amount, bps); net = amount - fee; - pid = acdb_next_purchase_id(); - if (pid < 0) FAIL(b); - if (acdb_insert_purchase(pid, merch, amount, fee, net, "A", "FOREIGN", bizdate) < 0) FAIL(b); - - h_pid = pid; h_fx = amount; h_rate = 1350; h_krw = amount; - strncpy(h_ccy, ccy, sizeof(h_ccy)-1); h_ccy[sizeof(h_ccy)-1] = 0; - strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; - EXEC SQL SELECT nextval('ac_fx_seq') INTO :h_id; - if (sqlca.sqlcode < 0) FAIL(b); - EXEC SQL INSERT INTO ac_fx (fx_id, purchase_id, ccy, fx_amount, krw_amount, rate_bps, biz_date) - VALUES (:h_id, :h_pid, :h_ccy, :h_fx, :h_krw, :h_rate, :h_bizdate); - if (sqlca.sqlcode < 0) FAIL(b); - setl(b, T_PURCHASE_ID, pid); setl(b, T_FEE, fee); - OK(b); -} - -/* 10. ACQ_UNMATCH - 미매입(원장 없는 매입) 추출. */ -void ACQ_UNMATCH(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 INSERT INTO ac_unmatch (unmatch_id, purchase_id, reason, biz_date) - SELECT nextval('ac_unmatch_seq'), p.purchase_id, 'NO_LEDGER', p.biz_date - 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 NOT EXISTS (SELECT 1 FROM ac_unmatch u WHERE u.purchase_id = p.purchase_id); - if (sqlca.sqlcode < 0) { userlog("ACQ_UNMATCH FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } - h_cnt = sqlca.sqlerrd[2]; - setl(b, T_COUNT, h_cnt); - OK(b); -} - -/* 11. ACQ_REPROC - 재처리(상태 A로 되돌림). */ -void ACQ_REPROC(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - long pid = getl(b, T_PURCHASE_ID); - if (acdb_update_status(pid, "A") < 0) FAIL(b); - Bchg(b, T_STATUS, 0, "A", 0L); - OK(b); -} - -/* 12. ACQ_DUPCHK - 중복 매입 건수 조회. */ -void ACQ_DUPCHK(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char merch[64], bizdate[16]; - EXEC SQL BEGIN DECLARE SECTION; - long h_cnt, h_amt; - 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)); - h_amt = getl(b, T_AMOUNT); - 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(*) INTO :h_cnt FROM purchase - WHERE merchant_id = :h_merch AND amount = :h_amt AND biz_date = :h_bizdate; - if (sqlca.sqlcode < 0) FAIL(b); - setl(b, T_COUNT, h_cnt); - OK(b); -} - -/* 13. ACQ_LIMITCHK - 가맹점 일한도 점검. */ -void ACQ_LIMITCHK(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char merch[64], bizdate[16]; - EXEC SQL BEGIN DECLARE SECTION; - long h_sum, h_lim; - 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) INTO :h_sum FROM purchase - WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; - if (sqlca.sqlcode < 0) FAIL(b); - EXEC SQL SELECT daily_limit INTO :h_lim FROM merchant WHERE merchant_id = :h_merch; - if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); - setl(b, T_GROSS, h_sum); - setl(b, T_RC, h_sum > h_lim ? 1 : 0); - OK(b); -} - -/* 14. ACQ_FEEADJ - 수수료 조정 (dbio) + purchase 반영. */ -void ACQ_FEEADJ(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char bizdate[16], reason[128]; - long pid, delta, adj; - EXEC SQL BEGIN DECLARE SECTION; - long e_pid, e_delta; - EXEC SQL END DECLARE SECTION; - - pid = getl(b, T_PURCHASE_ID); - delta = getl(b, T_DELTA); - gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); - gets_(b, T_REASON, reason, sizeof(reason)); if (reason[0] == 0) strcpy(reason, "ADJ"); - adj = acdb_insert_fee_adj(pid, delta, reason, bizdate); - if (adj < 0) FAIL(b); - - e_pid = pid; e_delta = delta; - EXEC SQL UPDATE purchase SET fee = fee + :e_delta, net = net - :e_delta, updated_at = now() - WHERE purchase_id = :e_pid; - if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); - setl(b, T_ADJ, adj); - OK(b); -} - -/* 15. ACQ_STATUS - 매입 상태 조회 (dbio). */ -void ACQ_STATUS(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - long pid = getl(b, T_PURCHASE_ID); - char st[4]; - if (acdb_get_status(pid, st) < 0) FAIL(b); - Bchg(b, T_STATUS, 0, st, 0L); - OK(b); -} - -/* 16. ACQ_MERCHSUM - 단일 가맹점 당일 집계 upsert (dbio). */ -void ACQ_MERCHSUM(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char merch[64], bizdate[16]; - EXEC SQL BEGIN DECLARE SECTION; - long h_cnt, h_gross, h_fee, h_net; - char h_merch[64], h_bizdate[16]; - EXEC SQL END DECLARE SECTION; - gets_(b, T_MERCHANT, merch, sizeof(merch)); - gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); - strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; - strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; - EXEC SQL SELECT count(*), coalesce(sum(amount),0), coalesce(sum(fee),0), coalesce(sum(net),0) - INTO :h_cnt, :h_gross, :h_fee, :h_net FROM purchase - WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; - if (sqlca.sqlcode < 0) FAIL(b); - if (acdb_upsert_merch_sum(merch, bizdate, h_gross, h_fee, h_net, h_cnt) < 0) FAIL(b); - setl(b, T_COUNT, h_cnt); setl(b, T_GROSS, h_gross); - OK(b); -} - -/* 17. ACQ_ISSUERCLS - 발급사 분류 갱신. */ -void ACQ_ISSUERCLS(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char issuer[32]; - EXEC SQL BEGIN DECLARE SECTION; - long h_pid; - char h_issuer[32]; - EXEC SQL END DECLARE SECTION; - h_pid = getl(b, T_PURCHASE_ID); - gets_(b, T_ISSUER, issuer, sizeof(issuer)); if (issuer[0] == 0) strcpy(issuer, "BC"); - strncpy(h_issuer, issuer, sizeof(h_issuer)-1); h_issuer[sizeof(h_issuer)-1] = 0; - EXEC SQL UPDATE purchase SET issuer = :h_issuer, updated_at = now() WHERE purchase_id = :h_pid; - if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); - OK(b); -} - -/* 18. ACQ_WHT - 원천징수 (수수료의 22%). */ -void ACQ_WHT(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char bizdate[16]; - long pid, tax, wht_id; - EXEC SQL BEGIN DECLARE SECTION; - long e_pid, e_fee; - EXEC SQL END DECLARE SECTION; - pid = getl(b, T_PURCHASE_ID); - gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); - e_pid = pid; - EXEC SQL SELECT fee INTO :e_fee FROM purchase WHERE purchase_id = :e_pid; - if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); - tax = acq_wht_amt(e_fee, 2200); /* 22% */ - wht_id = acdb_insert_wht(pid, tax, bizdate); - if (wht_id < 0) FAIL(b); - setl(b, T_TAX, tax); - OK(b); -} - -/* 19. ACQ_TAXINV - 세금계산서 발행 (부가세 10%). */ -void ACQ_TAXINV(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char merch[64], bizdate[16]; - long pid, supply, vat, inv; - pid = getl(b, T_PURCHASE_ID); - supply = getl(b, T_AMOUNT); - gets_(b, T_MERCHANT, merch, sizeof(merch)); - gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); - vat = acq_vat(supply); - inv = acdb_insert_tax_invoice(pid, merch, supply, vat, bizdate); - if (inv < 0) FAIL(b); - setl(b, T_TAX, vat); - OK(b); -} - -/* 20. ACQ_APPRLINK - 승인 연계 기록. */ -void ACQ_APPRLINK(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - long pid = getl(b, T_PURCHASE_ID); - if (acdb_insert_approval(pid, 1) < 0) FAIL(b); - OK(b); -} - -/* 21. ACQ_DATECHG - 영업일 변경. */ -void ACQ_DATECHG(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char bizdate[16]; - EXEC SQL BEGIN DECLARE SECTION; - long h_pid; - char h_bizdate[16]; - EXEC SQL END DECLARE SECTION; - h_pid = getl(b, T_PURCHASE_ID); - 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 purchase SET biz_date = :h_bizdate, updated_at = now() WHERE purchase_id = :h_pid; - if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); - OK(b); -} - -/* 22. ACQ_AMTFIX - 금액 확정 정정 (절대값). */ -void ACQ_AMTFIX(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char merch[64]; - long pid, amount, bps = 25, fee, net; - pid = getl(b, T_PURCHASE_ID); amount = getl(b, T_AMOUNT); - gets_(b, T_MERCHANT, merch, sizeof(merch)); - if (merch[0] && acdb_merchant_mdr(merch, &bps) < 0) bps = 25; - fee = acq_fee(amount, bps); net = amount - fee; - if (acdb_update_amount(pid, amount, fee, net) < 0) FAIL(b); - setl(b, T_FEE, fee); setl(b, T_NET, net); - OK(b); -} - -/* 23. ACQ_RECLASS - 업종/분류 재지정. */ -void ACQ_RECLASS(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char cat[32]; - EXEC SQL BEGIN DECLARE SECTION; - long h_pid; - char h_cat[32]; - EXEC SQL END DECLARE SECTION; - h_pid = getl(b, T_PURCHASE_ID); - 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 UPDATE purchase SET category = :h_cat, updated_at = now() WHERE purchase_id = :h_pid; - if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); - OK(b); -} - -/* 24. ACQ_XFERLINK - 이체 연계 원장 기록. */ -void ACQ_XFERLINK(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char bizdate[16]; - long pid = getl(b, T_PURCHASE_ID), amt = getl(b, T_AMOUNT); - gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); - if (acdb_insert_ledger(pid, "XFER", amt, bizdate) < 0) FAIL(b); - OK(b); -} - -/* 25. ACQ_APPRMAP - 승인 매핑 건수 조회. */ -void ACQ_APPRMAP(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - EXEC SQL BEGIN DECLARE SECTION; - long h_pid, h_cnt; - EXEC SQL END DECLARE SECTION; - h_pid = getl(b, T_PURCHASE_ID); - EXEC SQL SELECT count(*) INTO :h_cnt FROM approval WHERE purchase_id = :h_pid; - if (sqlca.sqlcode < 0) FAIL(b); - setl(b, T_COUNT, h_cnt); - OK(b); -} - -/* 26. ACQ_SETTLELINK - 정산 연계 원장 기록. */ -void ACQ_SETTLELINK(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char bizdate[16]; - long pid = getl(b, T_PURCHASE_ID), net = getl(b, T_NET); - gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); - if (acdb_insert_ledger(pid, "SETTLELINK", net, bizdate) < 0) FAIL(b); - OK(b); -} - -/* 27. ACQ_HOLD - 매입 보류. */ -void ACQ_HOLD(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - long pid = getl(b, T_PURCHASE_ID); - if (acdb_update_status(pid, "H") < 0) FAIL(b); - Bchg(b, T_STATUS, 0, "H", 0L); - OK(b); -} - -/* 28. ACQ_RELEASE - 보류 해제. */ -void ACQ_RELEASE(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - long pid = getl(b, T_PURCHASE_ID); - if (acdb_update_status(pid, "A") < 0) FAIL(b); - Bchg(b, T_STATUS, 0, "A", 0L); - OK(b); -} - -/* 29. ACQ_VOID - 매입 무효화 (당일 취소). */ -void ACQ_VOID(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char bizdate[16]; - EXEC SQL BEGIN DECLARE SECTION; - long h_id, h_pid, h_amt; - 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, bizdate, sizeof(bizdate)); - strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; - 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, 'VOID', :h_amt, 'VOID', :h_bizdate); - if (sqlca.sqlcode < 0) FAIL(b); - if (acdb_update_status(h_pid, "V") < 0) FAIL(b); - setl(b, T_CANCEL_ID, h_id); - Bchg(b, T_STATUS, 0, "V", 0L); - OK(b); -} - -/* 30. ACQ_REFUND - 환불 기록. */ -void ACQ_REFUND(TPSVCINFO *p) -{ - UBFH *b = (UBFH *)p->data; - char bizdate[16], reason[128]; - EXEC SQL BEGIN DECLARE SECTION; - long h_id, h_pid, h_amt; - char h_bizdate[16], h_reason[128]; - EXEC SQL END DECLARE SECTION; - h_pid = getl(b, T_PURCHASE_ID); h_amt = getl(b, T_AMOUNT); - gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); - gets_(b, T_REASON, reason, sizeof(reason)); if (reason[0] == 0) strcpy(reason, "REFUND"); - strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; - strncpy(h_reason, reason, sizeof(h_reason)-1); h_reason[sizeof(h_reason)-1] = 0; - 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, 'REFUND', :h_amt, :h_reason, :h_bizdate); - if (sqlca.sqlcode < 0) FAIL(b); - setl(b, T_CANCEL_ID, h_id); - OK(b); -} - /* vim: set ts=4 sw=4 et smartindent: */ diff --git a/app/src/ac/batch/ac_cancel_batch.pgc b/app/src/ac/batch/ac_cancel_batch.pgc new file mode 100644 index 0000000..c8de856 --- /dev/null +++ b/app/src/ac/batch/ac_cancel_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_cancel_batch.pgc - ac 취소 집계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_cancel_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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_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(*), coalesce(sum(amount),0) INTO :h_cnt, :h_amt FROM ac_cancel WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_cancel_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_cancel_batch COMMIT: bizdate=%s cancels=%ld amount=%ld\n", bizdate, h_cnt, h_amt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_channel_batch.pgc b/app/src/ac/batch/ac_channel_batch.pgc new file mode 100644 index 0000000..a5f75e9 --- /dev/null +++ b/app/src/ac/batch/ac_channel_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_channel_batch.pgc - ac 채널별 집계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_channel_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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_pos, h_edc; + 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 channel='POS'), count(*) FILTER (WHERE channel='EDC') INTO :h_pos, :h_edc FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_channel_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_channel_batch COMMIT: bizdate=%s pos=%ld edc=%ld\n", bizdate, h_pos, h_edc); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_daily_batch.pgc b/app/src/ac/batch/ac_daily_batch.pgc new file mode 100644 index 0000000..e6c3f69 --- /dev/null +++ b/app/src/ac/batch/ac_daily_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_daily_batch.pgc - ac 일일 매입 총계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_daily_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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_gross, h_fee, 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 SELECT count(*), coalesce(sum(amount),0), coalesce(sum(fee),0), coalesce(sum(net),0) INTO :h_cnt, :h_gross, :h_fee, :h_net FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_daily_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_daily_batch COMMIT: bizdate=%s txns=%ld gross=%ld fee=%ld net=%ld\n", bizdate, h_cnt, h_gross, h_fee, h_net); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_edi_batch.pgc b/app/src/ac/batch/ac_edi_batch.pgc new file mode 100644 index 0000000..fcb32a5 --- /dev/null +++ b/app/src/ac/batch/ac_edi_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_edi_batch.pgc - ac EDI 청구 집계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_edi_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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_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(*), coalesce(sum(amount),0) INTO :h_cnt, :h_amt FROM ac_edi WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_edi_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_edi_batch COMMIT: bizdate=%s edi_rows=%ld amount=%ld\n", bizdate, h_cnt, h_amt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_feeadj_batch.pgc b/app/src/ac/batch/ac_feeadj_batch.pgc new file mode 100644 index 0000000..ca1178c --- /dev/null +++ b/app/src/ac/batch/ac_feeadj_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_feeadj_batch.pgc - ac 수수료조정 집계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_feeadj_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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_delta; + 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_delta FROM ac_fee_adj WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_feeadj_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_feeadj_batch COMMIT: bizdate=%s feeadj_rows=%ld delta_sum=%ld\n", bizdate, h_cnt, h_delta); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_fx_batch.pgc b/app/src/ac/batch/ac_fx_batch.pgc new file mode 100644 index 0000000..b233c16 --- /dev/null +++ b/app/src/ac/batch/ac_fx_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_fx_batch.pgc - ac 해외(FX) 집계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_fx_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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_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(*), coalesce(sum(krw_amount),0) INTO :h_cnt, :h_krw FROM ac_fx WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_fx_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_fx_batch COMMIT: bizdate=%s fx_rows=%ld krw=%ld\n", bizdate, h_cnt, h_krw); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_hold_batch.pgc b/app/src/ac/batch/ac_hold_batch.pgc new file mode 100644 index 0000000..418a01f --- /dev/null +++ b/app/src/ac/batch/ac_hold_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_hold_batch.pgc - ac 보류 집계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_hold_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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; + 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_cnt FROM purchase WHERE biz_date = :h_bizdate AND status = 'H'; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_hold_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_hold_batch COMMIT: bizdate=%s holds=%ld\n", bizdate, h_cnt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_install_batch.pgc b/app/src/ac/batch/ac_install_batch.pgc new file mode 100644 index 0000000..e8ad800 --- /dev/null +++ b/app/src/ac/batch/ac_install_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_install_batch.pgc - ac 할부 집계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_install_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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_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(*), coalesce(sum(month_amount),0) INTO :h_cnt, :h_amt FROM ac_install WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_install_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_install_batch COMMIT: bizdate=%s install_rows=%ld month_amt=%ld\n", bizdate, h_cnt, h_amt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_issuer_batch.pgc b/app/src/ac/batch/ac_issuer_batch.pgc new file mode 100644 index 0000000..ee7e559 --- /dev/null +++ b/app/src/ac/batch/ac_issuer_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_issuer_batch.pgc - ac 발급사별 집계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_issuer_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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; + 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_cnt FROM purchase WHERE biz_date = :h_bizdate AND issuer = 'BC'; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_issuer_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_issuer_batch COMMIT: bizdate=%s bc_issuer_txns=%ld\n", bizdate, h_cnt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_refund_batch.pgc b/app/src/ac/batch/ac_refund_batch.pgc new file mode 100644 index 0000000..3833b57 --- /dev/null +++ b/app/src/ac/batch/ac_refund_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_refund_batch.pgc - ac 환불 집계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_refund_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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_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(*), 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) { fprintf(stderr, "ac_refund_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_refund_batch COMMIT: bizdate=%s refunds=%ld amount=%ld\n", bizdate, h_cnt, h_amt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_reproc_batch.pgc b/app/src/ac/batch/ac_reproc_batch.pgc new file mode 100644 index 0000000..b6207a0 --- /dev/null +++ b/app/src/ac/batch/ac_reproc_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_reproc_batch.pgc - ac 재처리 대상 집계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_reproc_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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; + 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_cnt FROM purchase WHERE biz_date = :h_bizdate AND status = 'A'; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_reproc_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_reproc_batch COMMIT: bizdate=%s reproc_candidates=%ld\n", bizdate, h_cnt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_settle_batch.pgc b/app/src/ac/batch/ac_settle_batch.pgc new file mode 100644 index 0000000..3ad5e51 --- /dev/null +++ b/app/src/ac/batch/ac_settle_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_settle_batch.pgc - ac 정산 집계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_settle_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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_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 SELECT count(*), coalesce(sum(net),0) INTO :h_cnt, :h_net FROM settlement WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_settle_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_settle_batch COMMIT: bizdate=%s settlements=%ld net=%ld\n", bizdate, h_cnt, h_net); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_taxinv_batch.pgc b/app/src/ac/batch/ac_taxinv_batch.pgc new file mode 100644 index 0000000..2d59be0 --- /dev/null +++ b/app/src/ac/batch/ac_taxinv_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_taxinv_batch.pgc - ac 세금계산서 집계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_taxinv_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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_vat; + 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(vat),0) INTO :h_cnt, :h_vat FROM ac_tax_invoice WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_taxinv_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_taxinv_batch COMMIT: bizdate=%s taxinv_rows=%ld vat=%ld\n", bizdate, h_cnt, h_vat); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_unmatch_batch.pgc b/app/src/ac/batch/ac_unmatch_batch.pgc new file mode 100644 index 0000000..32f1b19 --- /dev/null +++ b/app/src/ac/batch/ac_unmatch_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_unmatch_batch.pgc - ac 미매입 집계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_unmatch_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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; + 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_cnt FROM ac_unmatch WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_unmatch_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_unmatch_batch COMMIT: bizdate=%s unmatch_rows=%ld\n", bizdate, h_cnt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/batch/ac_void_batch.pgc b/app/src/ac/batch/ac_void_batch.pgc new file mode 100644 index 0000000..15100f0 --- /dev/null +++ b/app/src/ac/batch/ac_void_batch.pgc @@ -0,0 +1,37 @@ +/* + * ac_void_batch.pgc - ac 무효(VOID) 집계 (단건 집계 배치, XA). + * + * ATMI client: tpinit/tpopen open the ECPG XA RM, tpbegin starts ONE global + * transaction, an EXEC SQL aggregate runs on the biz_date, then tpcommit drives + * XA 2PC. Usage: ac_void_batch [YYYY-MM-DD] + */ +#include +#include +#include +#include +#include "ac_dbio.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; + 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_cnt FROM ac_cancel WHERE biz_date = :h_bizdate AND cancel_type = 'VOID'; + if (sqlca.sqlcode < 0) { fprintf(stderr, "ac_void_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_void_batch COMMIT: bizdate=%s voids=%ld\n", bizdate, h_cnt); + + tpclose(); tpterm(); + return 0; +} diff --git a/app/src/ac/dbio/ac_approval_query_dbio.h b/app/src/ac/dbio/ac_approval_query_dbio.h new file mode 100644 index 0000000..212898c --- /dev/null +++ b/app/src/ac/dbio/ac_approval_query_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_approval_query_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_approval_query_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_APPROVAL_QUERY_DBIO_H +#define AC_APPROVAL_QUERY_DBIO_H + +typedef struct { long approval_id; long purchase_id; int approved; } ac_approval_q_rec_t; + +long acdb_approval_count_all(long pid); +long acdb_approval_ok_count(long pid); + +#endif /* AC_APPROVAL_QUERY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_approval_query_dbio.pgc b/app/src/ac/dbio/ac_approval_query_dbio.pgc new file mode 100644 index 0000000..c43728f --- /dev/null +++ b/app/src/ac/dbio/ac_approval_query_dbio.pgc @@ -0,0 +1,29 @@ +/* + * ac_approval_query_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_approval_query_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_approval_query_dbio.h" + +long acdb_approval_count_all(long pid) +{ + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_v = 0; + EXEC SQL END DECLARE SECTION; + h_pid = pid; + EXEC SQL SELECT count(*) INTO :h_v FROM approval WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0) { userlog("acdb_approval_count_all FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); return -1; } + return h_v; +} + +long acdb_approval_ok_count(long pid) +{ + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_v = 0; + EXEC SQL END DECLARE SECTION; + h_pid = pid; + EXEC SQL SELECT count(*) INTO :h_v FROM approval WHERE purchase_id = :h_pid AND approved = true; + if (sqlca.sqlcode < 0) { userlog("acdb_approval_ok_count FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); return -1; } + return h_v; +} diff --git a/app/src/ac/dbio/ac_cancel_query_dbio.h b/app/src/ac/dbio/ac_cancel_query_dbio.h new file mode 100644 index 0000000..4373e64 --- /dev/null +++ b/app/src/ac/dbio/ac_cancel_query_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_cancel_query_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_cancel_query_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_CANCEL_QUERY_DBIO_H +#define AC_CANCEL_QUERY_DBIO_H + +typedef struct { char cancel_type[16]; long amount; long cancel_cnt; long purchase_cnt; } ac_cancel_q_rec_t; + +long acdb_cancel_typed_sum(const char *bizdate); +long acdb_cancel_ratio_bps(const char *bizdate); + +#endif /* AC_CANCEL_QUERY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_cancel_query_dbio.pgc b/app/src/ac/dbio/ac_cancel_query_dbio.pgc new file mode 100644 index 0000000..4234703 --- /dev/null +++ b/app/src/ac/dbio/ac_cancel_query_dbio.pgc @@ -0,0 +1,46 @@ +/* + * ac_cancel_query_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_cancel_query_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_cancel_query_dbio.h" + +/* 취소유형(FULL/PARTIAL/REFUND/VOID)별 합계를 커서로 누적하여 총 취소금액 반환. */ +long acdb_cancel_typed_sum(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_type[16]; + long h_amt, total = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL DECLARE cxc CURSOR FOR + SELECT cancel_type, coalesce(sum(amount),0) FROM ac_cancel + WHERE biz_date = :h_bizdate GROUP BY cancel_type; + EXEC SQL OPEN cxc; + if (sqlca.sqlcode < 0) { userlog("acdb_cancel_typed_sum OPEN [%d]", sqlca.sqlcode); return -1; } + for (;;) { + EXEC SQL FETCH cxc INTO :h_type, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE cxc; return -1; } + total += h_amt; + } + EXEC SQL CLOSE cxc; + return total; +} + +/* 취소율(bps) = 취소건수 * 10000 / 매입건수 (두 카운트 비교). */ +long acdb_cancel_ratio_bps(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_canc = 0, h_purc = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT count(*) INTO :h_canc FROM ac_cancel WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + EXEC SQL SELECT count(*) INTO :h_purc FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + if (h_purc <= 0) return 0; + return h_canc * 10000 / h_purc; +} diff --git a/app/src/ac/dbio/ac_category_dbio.h b/app/src/ac/dbio/ac_category_dbio.h new file mode 100644 index 0000000..0d11089 --- /dev/null +++ b/app/src/ac/dbio/ac_category_dbio.h @@ -0,0 +1,13 @@ +/* + * ac_category_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_category_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_CATEGORY_DBIO_H +#define AC_CATEGORY_DBIO_H + +typedef struct { char category[32]; long variety; } ac_category_rec_t; + +long acdb_category_variety(const char *bizdate); + +#endif /* AC_CATEGORY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_category_dbio.pgc b/app/src/ac/dbio/ac_category_dbio.pgc new file mode 100644 index 0000000..6443097 --- /dev/null +++ b/app/src/ac/dbio/ac_category_dbio.pgc @@ -0,0 +1,20 @@ +/* + * ac_category_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_category_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_category_dbio.h" + +/* 당일 사용된 업종/분류 가짓수 (distinct). */ +long acdb_category_variety(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT count(DISTINCT category) INTO :h_v FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { userlog("acdb_category_variety FAIL [%d]", sqlca.sqlcode); return -1; } + return h_v; +} diff --git a/app/src/ac/dbio/ac_daily_dbio.h b/app/src/ac/dbio/ac_daily_dbio.h new file mode 100644 index 0000000..7bb5e10 --- /dev/null +++ b/app/src/ac/dbio/ac_daily_dbio.h @@ -0,0 +1,13 @@ +/* + * ac_daily_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_daily_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_DAILY_DBIO_H +#define AC_DAILY_DBIO_H + +typedef struct { char biz_date[16]; long max_amount; long min_amount; } ac_daily_rec_t; + +long acdb_daily_amount_spread(const char *bizdate); + +#endif /* AC_DAILY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_daily_dbio.pgc b/app/src/ac/dbio/ac_daily_dbio.pgc new file mode 100644 index 0000000..266d2b6 --- /dev/null +++ b/app/src/ac/dbio/ac_daily_dbio.pgc @@ -0,0 +1,21 @@ +/* + * ac_daily_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_daily_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_daily_dbio.h" + +/* 당일 매입 금액 스프레드 = max(amount) - min(amount). */ +long acdb_daily_amount_spread(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_max = 0, h_min = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(max(amount),0), coalesce(min(amount),0) + INTO :h_max, :h_min FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + return h_max - h_min; +} diff --git a/app/src/ac/dbio/ac_edi_query_dbio.h b/app/src/ac/dbio/ac_edi_query_dbio.h new file mode 100644 index 0000000..1122a62 --- /dev/null +++ b/app/src/ac/dbio/ac_edi_query_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_edi_query_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_edi_query_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_EDI_QUERY_DBIO_H +#define AC_EDI_QUERY_DBIO_H + +typedef struct { long edi_id; char merchant_id[64]; long amount; } ac_edi_q_rec_t; + +long acdb_edi_count(const char *bizdate); +long acdb_edi_sum(const char *bizdate); + +#endif /* AC_EDI_QUERY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_edi_query_dbio.pgc b/app/src/ac/dbio/ac_edi_query_dbio.pgc new file mode 100644 index 0000000..d832061 --- /dev/null +++ b/app/src/ac/dbio/ac_edi_query_dbio.pgc @@ -0,0 +1,31 @@ +/* + * ac_edi_query_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_edi_query_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_edi_query_dbio.h" + +long acdb_edi_count(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT count(*) INTO :h_v FROM ac_edi WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { userlog("acdb_edi_count FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); return -1; } + return h_v; +} + +long acdb_edi_sum(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_v FROM ac_edi WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { userlog("acdb_edi_sum FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); return -1; } + return h_v; +} diff --git a/app/src/ac/dbio/ac_feeadj_query_dbio.h b/app/src/ac/dbio/ac_feeadj_query_dbio.h new file mode 100644 index 0000000..a3a9ab6 --- /dev/null +++ b/app/src/ac/dbio/ac_feeadj_query_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_feeadj_query_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_feeadj_query_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_FEEADJ_QUERY_DBIO_H +#define AC_FEEADJ_QUERY_DBIO_H + +typedef struct { long adj_id; long purchase_id; long delta; char status[8]; } ac_feeadj_q_rec_t; + +long acdb_feeadj_settled_delta(const char *bizdate); +long acdb_feeadj_max_abs(const char *bizdate); + +#endif /* AC_FEEADJ_QUERY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_feeadj_query_dbio.pgc b/app/src/ac/dbio/ac_feeadj_query_dbio.pgc new file mode 100644 index 0000000..0fa9968 --- /dev/null +++ b/app/src/ac/dbio/ac_feeadj_query_dbio.pgc @@ -0,0 +1,35 @@ +/* + * ac_feeadj_query_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_feeadj_query_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_feeadj_query_dbio.h" + +/* 정산완료(status='S') 매입에 걸린 수수료조정 delta 합계 (JOIN). */ +long acdb_feeadj_settled_delta(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(a.delta),0) INTO :h_v + FROM ac_fee_adj a JOIN purchase p ON p.purchase_id = a.purchase_id + WHERE a.biz_date = :h_bizdate AND p.status = 'S'; + if (sqlca.sqlcode < 0) { userlog("acdb_feeadj_settled_delta FAIL [%d]", sqlca.sqlcode); return -1; } + return h_v; +} + +/* 당일 최대 절대 조정폭 (max(abs(delta))). */ +long acdb_feeadj_max_abs(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(max(abs(delta)),0) INTO :h_v FROM ac_fee_adj WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + return h_v; +} diff --git a/app/src/ac/dbio/ac_fx_query_dbio.h b/app/src/ac/dbio/ac_fx_query_dbio.h new file mode 100644 index 0000000..5a618c4 --- /dev/null +++ b/app/src/ac/dbio/ac_fx_query_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_fx_query_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_fx_query_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_FX_QUERY_DBIO_H +#define AC_FX_QUERY_DBIO_H + +typedef struct { long fx_id; char ccy[8]; long fx_amount; long krw_amount; long rate_bps; } ac_fx_q_rec_t; + +long acdb_fx_krw_total(const char *bizdate); +long acdb_fx_wavg_rate(const char *bizdate); + +#endif /* AC_FX_QUERY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_fx_query_dbio.pgc b/app/src/ac/dbio/ac_fx_query_dbio.pgc new file mode 100644 index 0000000..126e158 --- /dev/null +++ b/app/src/ac/dbio/ac_fx_query_dbio.pgc @@ -0,0 +1,37 @@ +/* + * ac_fx_query_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_fx_query_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_fx_query_dbio.h" + +/* 원통화합/원화합을 함께 조회하고 원화 환산 총액 반환. */ +long acdb_fx_krw_total(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_krw = 0, h_cnt = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(krw_amount),0), count(*) + INTO :h_krw, :h_cnt FROM ac_fx WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + if (h_cnt == 0) return 0; + return h_krw; +} + +/* 가중평균 환율(1/100 KRW) = sum(krw)*100 / sum(fx). */ +long acdb_fx_wavg_rate(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_krw = 0, h_fx = 0; + EXEC SQL END DECLARE SECTION; + 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),0) + INTO :h_krw, :h_fx FROM ac_fx WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + if (h_fx <= 0) return 0; + return h_krw * 100 / h_fx; +} diff --git a/app/src/ac/dbio/ac_install_query_dbio.h b/app/src/ac/dbio/ac_install_query_dbio.h new file mode 100644 index 0000000..206ae6b --- /dev/null +++ b/app/src/ac/dbio/ac_install_query_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_install_query_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_install_query_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_INSTALL_QUERY_DBIO_H +#define AC_INSTALL_QUERY_DBIO_H + +typedef struct { long install_id; long purchase_id; long months; long month_amount; } ac_install_q_rec_t; + +long acdb_install_count(long pid); +long acdb_install_sum(long pid); + +#endif /* AC_INSTALL_QUERY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_install_query_dbio.pgc b/app/src/ac/dbio/ac_install_query_dbio.pgc new file mode 100644 index 0000000..6d3993f --- /dev/null +++ b/app/src/ac/dbio/ac_install_query_dbio.pgc @@ -0,0 +1,29 @@ +/* + * ac_install_query_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_install_query_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_install_query_dbio.h" + +long acdb_install_count(long pid) +{ + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_v = 0; + EXEC SQL END DECLARE SECTION; + h_pid = pid; + EXEC SQL SELECT count(*) INTO :h_v FROM ac_install WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0) { userlog("acdb_install_count FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); return -1; } + return h_v; +} + +long acdb_install_sum(long pid) +{ + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_v = 0; + EXEC SQL END DECLARE SECTION; + h_pid = pid; + EXEC SQL SELECT coalesce(sum(month_amount),0) INTO :h_v FROM ac_install WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0) { userlog("acdb_install_sum FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); return -1; } + return h_v; +} diff --git a/app/src/ac/dbio/ac_issuer_dbio.h b/app/src/ac/dbio/ac_issuer_dbio.h new file mode 100644 index 0000000..56aceec --- /dev/null +++ b/app/src/ac/dbio/ac_issuer_dbio.h @@ -0,0 +1,13 @@ +/* + * ac_issuer_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_issuer_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_ISSUER_DBIO_H +#define AC_ISSUER_DBIO_H + +typedef struct { char issuer[32]; long bc_cnt; long all_cnt; } ac_issuer_rec_t; + +long acdb_issuer_bc_share_bps(const char *bizdate); + +#endif /* AC_ISSUER_DBIO_H */ diff --git a/app/src/ac/dbio/ac_issuer_dbio.pgc b/app/src/ac/dbio/ac_issuer_dbio.pgc new file mode 100644 index 0000000..85f4981 --- /dev/null +++ b/app/src/ac/dbio/ac_issuer_dbio.pgc @@ -0,0 +1,22 @@ +/* + * ac_issuer_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_issuer_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_issuer_dbio.h" + +/* 발급사 'BC' 점유율(bps) = BC건수 * 10000 / 전체건수 (FILTER 비교). */ +long acdb_issuer_bc_share_bps(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_bc = 0, h_all = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT count(*) FILTER (WHERE issuer = 'BC'), count(*) + INTO :h_bc, :h_all FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + if (h_all <= 0) return 0; + return h_bc * 10000 / h_all; +} diff --git a/app/src/ac/dbio/ac_ledger_query_dbio.h b/app/src/ac/dbio/ac_ledger_query_dbio.h new file mode 100644 index 0000000..fd99fbd --- /dev/null +++ b/app/src/ac/dbio/ac_ledger_query_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_ledger_query_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_ledger_query_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_LEDGER_QUERY_DBIO_H +#define AC_LEDGER_QUERY_DBIO_H + +typedef struct { long ledger_id; long purchase_id; char entry_type[16]; long amount; } ac_ledger_q_rec_t; + +long acdb_ledger_settle_sum(const char *bizdate); +long acdb_ledger_balance(long pid); + +#endif /* AC_LEDGER_QUERY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_ledger_query_dbio.pgc b/app/src/ac/dbio/ac_ledger_query_dbio.pgc new file mode 100644 index 0000000..ad3bf6f --- /dev/null +++ b/app/src/ac/dbio/ac_ledger_query_dbio.pgc @@ -0,0 +1,46 @@ +/* + * ac_ledger_query_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_ledger_query_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_ledger_query_dbio.h" + +/* RECON/SETTLE 엔트리를 한 번에 분리 집계하고 SETTLE 합 반환. */ +long acdb_ledger_settle_sum(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_recon = 0, h_settle = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT + coalesce(sum(amount) FILTER (WHERE entry_type = 'RECON'), 0), + coalesce(sum(amount) FILTER (WHERE entry_type = 'SETTLE'), 0) + INTO :h_recon, :h_settle FROM ledger WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + if (h_recon < 0) return -1; + return h_settle; +} + +/* 단일 매입 원장 잔액 = SETTLE(+) - XFER(-), 커서 부호 누적. */ +long acdb_ledger_balance(long pid) +{ + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_amt, bal = 0; + char h_type[16]; + EXEC SQL END DECLARE SECTION; + h_pid = pid; + EXEC SQL DECLARE lbc CURSOR FOR + SELECT entry_type, amount FROM ledger WHERE purchase_id = :h_pid; + EXEC SQL OPEN lbc; + if (sqlca.sqlcode < 0) return -1; + for (;;) { + EXEC SQL FETCH lbc INTO :h_type, :h_amt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE lbc; return -1; } + bal += (h_type[0] == 'X') ? -h_amt : h_amt; + } + EXEC SQL CLOSE lbc; + return bal; +} diff --git a/app/src/ac/dbio/ac_merchant_query_dbio.h b/app/src/ac/dbio/ac_merchant_query_dbio.h new file mode 100644 index 0000000..0999697 --- /dev/null +++ b/app/src/ac/dbio/ac_merchant_query_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_merchant_query_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_merchant_query_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_MERCHANT_QUERY_DBIO_H +#define AC_MERCHANT_QUERY_DBIO_H + +typedef struct { char merchant_id[64]; long daily_limit; char status[16]; } ac_merchant_q_rec_t; + +long acdb_merchant_limit(const char *merch); +int acdb_merchant_active(const char *merch); + +#endif /* AC_MERCHANT_QUERY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_merchant_query_dbio.pgc b/app/src/ac/dbio/ac_merchant_query_dbio.pgc new file mode 100644 index 0000000..3c7abbc --- /dev/null +++ b/app/src/ac/dbio/ac_merchant_query_dbio.pgc @@ -0,0 +1,29 @@ +/* + * ac_merchant_query_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_merchant_query_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_merchant_query_dbio.h" + +long acdb_merchant_limit(const char *merch) +{ + EXEC SQL BEGIN DECLARE SECTION; + long h_v = 0; char h_merch[64]; + EXEC SQL END DECLARE SECTION; + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + EXEC SQL SELECT daily_limit INTO :h_v FROM merchant WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) { userlog("acdb_merchant_limit FAIL [%d]", sqlca.sqlcode); return -1; } + return h_v; +} + +int acdb_merchant_active(const char *merch) +{ + EXEC SQL BEGIN DECLARE SECTION; + long h_v = 0; char h_merch[64]; + EXEC SQL END DECLARE SECTION; + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + EXEC SQL SELECT count(*) INTO :h_v FROM merchant WHERE merchant_id = :h_merch AND status = 'ACTIVE'; + if (sqlca.sqlcode < 0) return -1; + return (int)h_v; +} diff --git a/app/src/ac/dbio/ac_merchsum_query_dbio.h b/app/src/ac/dbio/ac_merchsum_query_dbio.h new file mode 100644 index 0000000..b768250 --- /dev/null +++ b/app/src/ac/dbio/ac_merchsum_query_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_merchsum_query_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_merchsum_query_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_MERCHSUM_QUERY_DBIO_H +#define AC_MERCHSUM_QUERY_DBIO_H + +typedef struct { char merchant_id[64]; long gross_amount; long fee_amount; long txn_count; } ac_merchsum_q_rec_t; + +long acdb_merchsum_peak_gross(const char *bizdate); +long acdb_merchsum_avg_fee(const char *bizdate); + +#endif /* AC_MERCHSUM_QUERY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_merchsum_query_dbio.pgc b/app/src/ac/dbio/ac_merchsum_query_dbio.pgc new file mode 100644 index 0000000..7d82f53 --- /dev/null +++ b/app/src/ac/dbio/ac_merchsum_query_dbio.pgc @@ -0,0 +1,35 @@ +/* + * ac_merchsum_query_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_merchsum_query_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_merchsum_query_dbio.h" + +/* 최대 gross 를 기록한 가맹점의 gross 반환. */ +long acdb_merchsum_peak_gross(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(max(gross_amount),0) INTO :h_v FROM merchant_summary WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + return h_v; +} + +/* 건당 평균 수수료 = sum(fee)/sum(txn_count). */ +long acdb_merchsum_avg_fee(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_fee = 0, h_txn = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(fee_amount),0), coalesce(sum(txn_count),0) + INTO :h_fee, :h_txn FROM merchant_summary WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + if (h_txn <= 0) return 0; + return h_fee / h_txn; +} diff --git a/app/src/ac/dbio/ac_purchase_agg_dbio.h b/app/src/ac/dbio/ac_purchase_agg_dbio.h new file mode 100644 index 0000000..4d3f9da --- /dev/null +++ b/app/src/ac/dbio/ac_purchase_agg_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_purchase_agg_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_purchase_agg_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_PURCHASE_AGG_DBIO_H +#define AC_PURCHASE_AGG_DBIO_H + +typedef struct { char biz_date[16]; long gross; long fee; long net; } ac_purchase_agg_rec_t; + +long acdb_purchase_gross_by_date(const char *bizdate); +long acdb_purchase_net_by_date(const char *bizdate); + +#endif /* AC_PURCHASE_AGG_DBIO_H */ diff --git a/app/src/ac/dbio/ac_purchase_agg_dbio.pgc b/app/src/ac/dbio/ac_purchase_agg_dbio.pgc new file mode 100644 index 0000000..c169b2e --- /dev/null +++ b/app/src/ac/dbio/ac_purchase_agg_dbio.pgc @@ -0,0 +1,31 @@ +/* + * ac_purchase_agg_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_purchase_agg_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_purchase_agg_dbio.h" + +long acdb_purchase_gross_by_date(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_v FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { userlog("acdb_purchase_gross_by_date FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); return -1; } + return h_v; +} + +long acdb_purchase_net_by_date(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_v FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { userlog("acdb_purchase_net_by_date FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); return -1; } + return h_v; +} diff --git a/app/src/ac/dbio/ac_purchase_channel_dbio.h b/app/src/ac/dbio/ac_purchase_channel_dbio.h new file mode 100644 index 0000000..ca25a5e --- /dev/null +++ b/app/src/ac/dbio/ac_purchase_channel_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_purchase_channel_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_purchase_channel_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_PURCHASE_CHANNEL_DBIO_H +#define AC_PURCHASE_CHANNEL_DBIO_H + +typedef struct { char channel[16]; long gross; long variety; } ac_purchase_channel_rec_t; + +long acdb_channel_gross(const char *channel, const char *bizdate); +long acdb_channel_variety(const char *bizdate); + +#endif /* AC_PURCHASE_CHANNEL_DBIO_H */ diff --git a/app/src/ac/dbio/ac_purchase_channel_dbio.pgc b/app/src/ac/dbio/ac_purchase_channel_dbio.pgc new file mode 100644 index 0000000..ae80e0c --- /dev/null +++ b/app/src/ac/dbio/ac_purchase_channel_dbio.pgc @@ -0,0 +1,35 @@ +/* + * ac_purchase_channel_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_purchase_channel_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_purchase_channel_dbio.h" + +/* 지정 채널의 당일 매입 총액 (channel 인자). */ +long acdb_channel_gross(const char *channel, const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_ch[16], h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_ch, channel, sizeof(h_ch)-1); h_ch[sizeof(h_ch)-1] = 0; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_v FROM purchase + WHERE channel = :h_ch AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + return h_v; +} + +/* 당일 사용된 채널 가짓수 (POS/EDC/EDI/FOREIGN 중 몇 종). */ +long acdb_channel_variety(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT count(DISTINCT channel) INTO :h_v FROM purchase WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + return h_v; +} diff --git a/app/src/ac/dbio/ac_purchase_query_dbio.h b/app/src/ac/dbio/ac_purchase_query_dbio.h new file mode 100644 index 0000000..a2184e4 --- /dev/null +++ b/app/src/ac/dbio/ac_purchase_query_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_purchase_query_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_purchase_query_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_PURCHASE_QUERY_DBIO_H +#define AC_PURCHASE_QUERY_DBIO_H + +typedef struct { long purchase_id; long amount; long fee; long net; char status[8]; } ac_purchase_q_rec_t; + +long acdb_purchase_amount(long pid); +long acdb_purchase_fee(long pid); + +#endif /* AC_PURCHASE_QUERY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_purchase_query_dbio.pgc b/app/src/ac/dbio/ac_purchase_query_dbio.pgc new file mode 100644 index 0000000..14cfa45 --- /dev/null +++ b/app/src/ac/dbio/ac_purchase_query_dbio.pgc @@ -0,0 +1,29 @@ +/* + * ac_purchase_query_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_purchase_query_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_purchase_query_dbio.h" + +long acdb_purchase_amount(long pid) +{ + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_v = 0; + EXEC SQL END DECLARE SECTION; + h_pid = pid; + EXEC SQL SELECT coalesce(amount,0) INTO :h_v FROM purchase WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0) { userlog("acdb_purchase_amount FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); return -1; } + return h_v; +} + +long acdb_purchase_fee(long pid) +{ + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_v = 0; + EXEC SQL END DECLARE SECTION; + h_pid = pid; + EXEC SQL SELECT coalesce(fee,0) INTO :h_v FROM purchase WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0) { userlog("acdb_purchase_fee FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); return -1; } + return h_v; +} diff --git a/app/src/ac/dbio/ac_purchase_status_dbio.h b/app/src/ac/dbio/ac_purchase_status_dbio.h new file mode 100644 index 0000000..a311bd9 --- /dev/null +++ b/app/src/ac/dbio/ac_purchase_status_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_purchase_status_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_purchase_status_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_PURCHASE_STATUS_DBIO_H +#define AC_PURCHASE_STATUS_DBIO_H + +typedef struct { char status[8]; long cnt; } ac_purchase_status_rec_t; + +long acdb_status_settled_count(const char *bizdate); +long acdb_status_open_count(const char *bizdate); + +#endif /* AC_PURCHASE_STATUS_DBIO_H */ diff --git a/app/src/ac/dbio/ac_purchase_status_dbio.pgc b/app/src/ac/dbio/ac_purchase_status_dbio.pgc new file mode 100644 index 0000000..d63a747 --- /dev/null +++ b/app/src/ac/dbio/ac_purchase_status_dbio.pgc @@ -0,0 +1,43 @@ +/* + * ac_purchase_status_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_purchase_status_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_purchase_status_dbio.h" + +/* 상태별 건수를 커서로 순회하여 정산완료(S) 건수만 추출. */ +long acdb_status_settled_count(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16], h_st[8]; + long h_cnt, settled = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL DECLARE psc CURSOR FOR + SELECT status, count(*) FROM purchase WHERE biz_date = :h_bizdate GROUP BY status; + EXEC SQL OPEN psc; + if (sqlca.sqlcode < 0) return -1; + for (;;) { + EXEC SQL FETCH psc INTO :h_st, :h_cnt; + if (sqlca.sqlcode == 100) break; + if (sqlca.sqlcode < 0) { EXEC SQL CLOSE psc; return -1; } + if (h_st[0] == 'S') settled = h_cnt; + } + EXEC SQL CLOSE psc; + return settled; +} + +/* 미완결(A/H/M) 건수 - IN 집합 필터. */ +long acdb_status_open_count(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT count(*) INTO :h_v FROM purchase + WHERE biz_date = :h_bizdate AND status IN ('A', 'H', 'M'); + if (sqlca.sqlcode < 0) return -1; + return h_v; +} diff --git a/app/src/ac/dbio/ac_refund_dbio.h b/app/src/ac/dbio/ac_refund_dbio.h new file mode 100644 index 0000000..74afeb8 --- /dev/null +++ b/app/src/ac/dbio/ac_refund_dbio.h @@ -0,0 +1,13 @@ +/* + * ac_refund_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_refund_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_REFUND_DBIO_H +#define AC_REFUND_DBIO_H + +typedef struct { long cancel_id; long purchase_id; long amount; } ac_refund_rec_t; + +long acdb_refund_sum(const char *bizdate); + +#endif /* AC_REFUND_DBIO_H */ diff --git a/app/src/ac/dbio/ac_refund_dbio.pgc b/app/src/ac/dbio/ac_refund_dbio.pgc new file mode 100644 index 0000000..850c475 --- /dev/null +++ b/app/src/ac/dbio/ac_refund_dbio.pgc @@ -0,0 +1,19 @@ +/* + * ac_refund_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_refund_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_refund_dbio.h" + +long acdb_refund_sum(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_v FROM ac_cancel WHERE biz_date = :h_bizdate AND cancel_type = 'REFUND'; + if (sqlca.sqlcode < 0) { userlog("acdb_refund_sum FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); return -1; } + return h_v; +} diff --git a/app/src/ac/dbio/ac_settle_dbio.h b/app/src/ac/dbio/ac_settle_dbio.h new file mode 100644 index 0000000..39c8d6b --- /dev/null +++ b/app/src/ac/dbio/ac_settle_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_settle_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_settle_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_SETTLE_DBIO_H +#define AC_SETTLE_DBIO_H + +typedef struct { long settlement_id; long purchase_id; char merchant_id[64]; long net; char biz_date[16]; } ac_settle_rec_t; + +long acdb_insert_settlement(long pid, const char *merch, long net, const char *bizdate); +long acdb_settle_net_sum(const char *bizdate); + +#endif /* AC_SETTLE_DBIO_H */ diff --git a/app/src/ac/dbio/ac_settle_dbio.pgc b/app/src/ac/dbio/ac_settle_dbio.pgc new file mode 100644 index 0000000..b784d6e --- /dev/null +++ b/app/src/ac/dbio/ac_settle_dbio.pgc @@ -0,0 +1,35 @@ +/* + * ac_settle_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_settle_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_settle_dbio.h" + +long acdb_insert_settlement(long pid, const char *merch, long net, const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_pid = pid, h_net = net; + char h_merch[64], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + 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 nextval('settlement_seq') INTO :h_id; + if (sqlca.sqlcode < 0) return -1; + EXEC SQL INSERT INTO settlement (settlement_id, purchase_id, merchant_id, net, biz_date) + VALUES (:h_id, :h_pid, :h_merch, :h_net, :h_bizdate); + if (sqlca.sqlcode < 0) { userlog("acdb_insert_settlement FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); return -1; } + return h_id; +} + +long acdb_settle_net_sum(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(net),0) INTO :h_v FROM settlement WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) { userlog("acdb_settle_net_sum FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); return -1; } + return h_v; +} diff --git a/app/src/ac/dbio/ac_taxinv_query_dbio.h b/app/src/ac/dbio/ac_taxinv_query_dbio.h new file mode 100644 index 0000000..bc895e9 --- /dev/null +++ b/app/src/ac/dbio/ac_taxinv_query_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_taxinv_query_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_taxinv_query_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_TAXINV_QUERY_DBIO_H +#define AC_TAXINV_QUERY_DBIO_H + +typedef struct { long inv_id; char merchant_id[64]; long supply; long vat; } ac_taxinv_q_rec_t; + +long acdb_taxinv_vat_total(const char *bizdate); +long acdb_taxinv_merchant_count(const char *merch, const char *bizdate); + +#endif /* AC_TAXINV_QUERY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_taxinv_query_dbio.pgc b/app/src/ac/dbio/ac_taxinv_query_dbio.pgc new file mode 100644 index 0000000..9e60e8d --- /dev/null +++ b/app/src/ac/dbio/ac_taxinv_query_dbio.pgc @@ -0,0 +1,37 @@ +/* + * ac_taxinv_query_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_taxinv_query_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_taxinv_query_dbio.h" + +/* 부가세합/공급가합을 함께 조회, 부가세 총액 반환. */ +long acdb_taxinv_vat_total(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_vat = 0, h_supply = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(vat),0), coalesce(sum(supply),0) + INTO :h_vat, :h_supply FROM ac_tax_invoice WHERE biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + if (h_supply < 0) return -1; + return h_vat; +} + +/* 특정 가맹점의 세금계산서 발행 건수 (merch + bizdate). */ +long acdb_taxinv_merchant_count(const char *merch, const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[64], h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + 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(*) INTO :h_v FROM ac_tax_invoice + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + return h_v; +} diff --git a/app/src/ac/dbio/ac_unmatch_query_dbio.h b/app/src/ac/dbio/ac_unmatch_query_dbio.h new file mode 100644 index 0000000..c2e3727 --- /dev/null +++ b/app/src/ac/dbio/ac_unmatch_query_dbio.h @@ -0,0 +1,13 @@ +/* + * ac_unmatch_query_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_unmatch_query_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_UNMATCH_QUERY_DBIO_H +#define AC_UNMATCH_QUERY_DBIO_H + +typedef struct { long unmatch_id; long purchase_id; char biz_date[16]; } ac_unmatch_q_rec_t; + +long acdb_unmatch_backlog(const char *bizdate); + +#endif /* AC_UNMATCH_QUERY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_unmatch_query_dbio.pgc b/app/src/ac/dbio/ac_unmatch_query_dbio.pgc new file mode 100644 index 0000000..596617c --- /dev/null +++ b/app/src/ac/dbio/ac_unmatch_query_dbio.pgc @@ -0,0 +1,22 @@ +/* + * ac_unmatch_query_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_unmatch_query_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_unmatch_query_dbio.h" + +/* 당일 이하 미매입 중 정산이 안 잡힌 누적 건수 (aging, NOT EXISTS settlement). */ +long acdb_unmatch_backlog(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT count(*) INTO :h_v FROM ac_unmatch u + WHERE u.biz_date <= :h_bizdate + AND NOT EXISTS (SELECT 1 FROM settlement s WHERE s.purchase_id = u.purchase_id); + if (sqlca.sqlcode < 0) return -1; + return h_v; +} diff --git a/app/src/ac/dbio/ac_void_dbio.h b/app/src/ac/dbio/ac_void_dbio.h new file mode 100644 index 0000000..bd25f3c --- /dev/null +++ b/app/src/ac/dbio/ac_void_dbio.h @@ -0,0 +1,13 @@ +/* + * ac_void_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_void_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_VOID_DBIO_H +#define AC_VOID_DBIO_H + +typedef struct { long cancel_id; long purchase_id; long amount; } ac_void_q_rec_t; + +long acdb_void_unsettled_amount(const char *bizdate); + +#endif /* AC_VOID_DBIO_H */ diff --git a/app/src/ac/dbio/ac_void_dbio.pgc b/app/src/ac/dbio/ac_void_dbio.pgc new file mode 100644 index 0000000..2c1bf8b --- /dev/null +++ b/app/src/ac/dbio/ac_void_dbio.pgc @@ -0,0 +1,23 @@ +/* + * ac_void_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_void_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_void_dbio.h" + +/* VOID 처리된 매입 중 원장 SETTLE 이 없는 건의 금액 합 (정합성 점검, NOT EXISTS). */ +long acdb_void_unsettled_amount(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(c.amount),0) INTO :h_v FROM ac_cancel c + WHERE c.biz_date = :h_bizdate AND c.cancel_type = 'VOID' + AND NOT EXISTS (SELECT 1 FROM ledger l + WHERE l.purchase_id = c.purchase_id AND l.entry_type = 'SETTLE'); + if (sqlca.sqlcode < 0) return -1; + return h_v; +} diff --git a/app/src/ac/dbio/ac_wht_query_dbio.h b/app/src/ac/dbio/ac_wht_query_dbio.h new file mode 100644 index 0000000..53be1ab --- /dev/null +++ b/app/src/ac/dbio/ac_wht_query_dbio.h @@ -0,0 +1,14 @@ +/* + * ac_wht_query_dbio.h - ac 모듈 DB 접근 계층 copybook (ac_wht_query_dbio, part of libacdbio.a). + * ECPG functions; run on the caller's XA branch (no EXEC SQL CONNECT). + * Return convention: >=0 ok, -1 = SQL error. + */ +#ifndef AC_WHT_QUERY_DBIO_H +#define AC_WHT_QUERY_DBIO_H + +typedef struct { long wht_id; long purchase_id; long tax_amount; long fee; } ac_wht_q_rec_t; + +long acdb_wht_settled_sum(const char *bizdate); +long acdb_wht_effective_bps(const char *bizdate); + +#endif /* AC_WHT_QUERY_DBIO_H */ diff --git a/app/src/ac/dbio/ac_wht_query_dbio.pgc b/app/src/ac/dbio/ac_wht_query_dbio.pgc new file mode 100644 index 0000000..a6cf4e0 --- /dev/null +++ b/app/src/ac/dbio/ac_wht_query_dbio.pgc @@ -0,0 +1,38 @@ +/* + * ac_wht_query_dbio.pgc - ac 모듈 DB 접근 함수 세트 (ac_wht_query_dbio), archived into libacdbio.a. + * ECPG (EXEC SQL) functions; run on the caller's XA branch. No EXEC SQL CONNECT. + */ +#include +#include +#include "ac_wht_query_dbio.h" + +/* 정산완료 매입에 대한 원천징수 세액 합계 (JOIN + 상태필터). */ +long acdb_wht_settled_sum(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_v = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(w.tax_amount),0) INTO :h_v + FROM ac_wht w JOIN purchase p ON p.purchase_id = w.purchase_id + WHERE w.biz_date = :h_bizdate AND p.status = 'S'; + if (sqlca.sqlcode < 0) return -1; + return h_v; +} + +/* 실효 원천세율(bps) = sum(tax)*10000 / sum(fee) over joined purchases. */ +long acdb_wht_effective_bps(const char *bizdate) +{ + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[16]; + long h_tax = 0, h_fee = 0; + EXEC SQL END DECLARE SECTION; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT coalesce(sum(w.tax_amount),0), coalesce(sum(p.fee),0) + INTO :h_tax, :h_fee FROM ac_wht w JOIN purchase p ON p.purchase_id = w.purchase_id + WHERE w.biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) return -1; + if (h_fee <= 0) return 0; + return h_tax * 10000 / h_fee; +} diff --git a/app/src/ac/run/run_acquire.sh b/app/src/ac/run/run_acquire.sh new file mode 100755 index 0000000..3ca1439 --- /dev/null +++ b/app/src/ac/run/run_acquire.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_acquire.sh - 매입 체인(ACQUIRE->RECONCILE->SETTLE) 1건 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_acquire.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_acquire.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/acqdrv "$MERCH" "$AMOUNT" "$BIZDATE" diff --git a/app/src/ac/run/run_cancel.sh b/app/src/ac/run/run_cancel.sh new file mode 100755 index 0000000..82f3f88 --- /dev/null +++ b/app/src/ac/run/run_cancel.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_cancel.sh - 취소 집계 배치 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_cancel.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_cancel.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_cancel_batch "$BIZDATE" diff --git a/app/src/ac/run/run_channel.sh b/app/src/ac/run/run_channel.sh new file mode 100755 index 0000000..cbe979b --- /dev/null +++ b/app/src/ac/run/run_channel.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_channel.sh - 채널별 집계 배치 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_channel.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_channel.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_channel_batch "$BIZDATE" diff --git a/app/src/ac/run/run_daily.sh b/app/src/ac/run/run_daily.sh new file mode 100755 index 0000000..2c16bbe --- /dev/null +++ b/app/src/ac/run/run_daily.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_daily.sh - 일일 매입 총계 배치 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_daily.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_daily.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_daily_batch "$BIZDATE" diff --git a/app/src/ac/run/run_edi.sh b/app/src/ac/run/run_edi.sh new file mode 100755 index 0000000..09c51d8 --- /dev/null +++ b/app/src/ac/run/run_edi.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_edi.sh - EDI 청구 집계 배치 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_edi.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_edi.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_edi_batch "$BIZDATE" diff --git a/app/src/ac/run/run_eod.sh b/app/src/ac/run/run_eod.sh new file mode 100755 index 0000000..19230e5 --- /dev/null +++ b/app/src/ac/run/run_eod.sh @@ -0,0 +1,20 @@ +#!/bin/bash +## +## run_eod.sh - 일마감(EOD): 집계 배치 일괄 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_eod.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_eod.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +for b in ac_daily_batch ac_merchsum_batch ac_wht_batch ac_settle_batch \ + ac_cancel_batch ac_fx_batch ac_taxinv_batch ac_feeadj_batch; do + echo "--- $b $BIZDATE ---" + "/app/bin/$b" "$BIZDATE" || exit 1 +done diff --git a/app/src/ac/run/run_feeadj.sh b/app/src/ac/run/run_feeadj.sh new file mode 100755 index 0000000..e9a1de5 --- /dev/null +++ b/app/src/ac/run/run_feeadj.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_feeadj.sh - 수수료조정 집계 배치 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_feeadj.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_feeadj.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_feeadj_batch "$BIZDATE" diff --git a/app/src/ac/run/run_fx.sh b/app/src/ac/run/run_fx.sh new file mode 100755 index 0000000..0080d62 --- /dev/null +++ b/app/src/ac/run/run_fx.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_fx.sh - 해외(FX) 집계 배치 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_fx.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_fx.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_fx_batch "$BIZDATE" diff --git a/app/src/ac/run/run_install.sh b/app/src/ac/run/run_install.sh new file mode 100755 index 0000000..c60f379 --- /dev/null +++ b/app/src/ac/run/run_install.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_install.sh - 할부 집계 배치 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_install.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_install.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_install_batch "$BIZDATE" diff --git a/app/src/ac/run/run_merchsum.sh b/app/src/ac/run/run_merchsum.sh new file mode 100755 index 0000000..e42f514 --- /dev/null +++ b/app/src/ac/run/run_merchsum.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_merchsum.sh - 가맹점 일집계 배치 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_merchsum.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_merchsum.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_merchsum_batch "$BIZDATE" diff --git a/app/src/ac/run/run_ops.sh b/app/src/ac/run/run_ops.sh new file mode 100755 index 0000000..aace3a4 --- /dev/null +++ b/app/src/ac/run/run_ops.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_ops.sh - ac 온라인 서비스 ops 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_ops.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_ops.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_ops "$PID" "$MERCH" "$AMOUNT" "$BIZDATE" diff --git a/app/src/ac/run/run_refund.sh b/app/src/ac/run/run_refund.sh new file mode 100755 index 0000000..4064416 --- /dev/null +++ b/app/src/ac/run/run_refund.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_refund.sh - 환불 집계 배치 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_refund.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_refund.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_refund_batch "$BIZDATE" diff --git a/app/src/ac/run/run_settle.sh b/app/src/ac/run/run_settle.sh new file mode 100755 index 0000000..f6ea5ce --- /dev/null +++ b/app/src/ac/run/run_settle.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_settle.sh - 정산 집계 배치 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_settle.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_settle.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_settle_batch "$BIZDATE" diff --git a/app/src/ac/run/run_taxinv.sh b/app/src/ac/run/run_taxinv.sh new file mode 100755 index 0000000..a5467c4 --- /dev/null +++ b/app/src/ac/run/run_taxinv.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_taxinv.sh - 세금계산서 집계 배치 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_taxinv.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_taxinv.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_taxinv_batch "$BIZDATE" diff --git a/app/src/ac/run/run_unmatch.sh b/app/src/ac/run/run_unmatch.sh new file mode 100755 index 0000000..53ff004 --- /dev/null +++ b/app/src/ac/run/run_unmatch.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_unmatch.sh - 미매입 집계 배치 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_unmatch.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_unmatch.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_unmatch_batch "$BIZDATE" diff --git a/app/src/ac/run/run_void.sh b/app/src/ac/run/run_void.sh new file mode 100755 index 0000000..0cc9d43 --- /dev/null +++ b/app/src/ac/run/run_void.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_void.sh - 무효(VOID) 집계 배치 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_void.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_void.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_void_batch "$BIZDATE" diff --git a/app/src/ac/run/run_wht.sh b/app/src/ac/run/run_wht.sh new file mode 100755 index 0000000..9dc83e1 --- /dev/null +++ b/app/src/ac/run/run_wht.sh @@ -0,0 +1,16 @@ +#!/bin/bash +## +## run_wht.sh - 원천징수 배치 실행. +## 운영 스크립트: Enduro/X + ECPG/XA 런타임 환경을 설정하고 배치/서비스를 실행. +## Usage: run_wht.sh [BIZDATE] [MERCH] [AMOUNT] [PID] +## +set -e +. /app/conf/setapp.sh + +BIZDATE="${1:-2026-07-19}" +MERCH="${2:-M0001}" +AMOUNT="${3:-1000000}" +PID="${4:-1}" + +echo "[run_wht.sh] BIZDATE=$BIZDATE MERCH=$MERCH AMOUNT=$AMOUNT" +exec /app/bin/ac_wht_batch "$BIZDATE" diff --git a/app/src/ac/svc/ACQUIRE.h b/app/src/ac/svc/ACQUIRE.h new file mode 100644 index 0000000..5a2d1c9 --- /dev/null +++ b/app/src/ac/svc/ACQUIRE.h @@ -0,0 +1,24 @@ +/* + * ACQUIRE.h - ac 서비스 ACQUIRE 카피북 (copybook / record header). + * Request fields: T_MERCHANT, T_AMOUNT, T_BIZDATE + * Response fields: T_PURCHASE_ID, T_FEE, T_NET, T_STATUS + */ +#ifndef ACQUIRE_H +#define ACQUIRE_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + char merchant_id[64]; + long amount; + long fee; + long net; + char status[8]; + char channel[16]; + char biz_date[16]; +} ac_acquire_rec_t; + +void ACQUIRE(TPSVCINFO *p); + +#endif /* ACQUIRE_H */ diff --git a/app/src/ac/svc/ACQUIRE.pgc b/app/src/ac/svc/ACQUIRE.pgc new file mode 100644 index 0000000..987e19a --- /dev/null +++ b/app/src/ac/svc/ACQUIRE.pgc @@ -0,0 +1,45 @@ +/* + * ACQUIRE.pgc - ac 모듈 서비스 ACQUIRE (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQUIRE.h" + +void ACQUIRE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + long amount, bps = 25, fee, net, pid, rlen = 0; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + if (merch[0] == 0) { userlog("ACQUIRE: missing T_MERCHANT"); FAIL(b); } + amount = getl(b, T_AMOUNT); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + + if (acdb_merchant_mdr(merch, &bps) < 0) bps = 25; /* keep 25bps default */ + fee = acq_fee(amount, bps); + net = amount - fee; + + pid = acdb_next_purchase_id(); + if (pid < 0) FAIL(b); + if (acdb_insert_purchase(pid, merch, amount, fee, net, "A", "POS", bizdate) < 0) FAIL(b); + if (acdb_insert_approval(pid, 1) < 0) FAIL(b); + + setl(b, T_PURCHASE_ID, pid); + setl(b, T_FEE, fee); + setl(b, T_NET, net); + Bchg(b, T_STATUS, 0, "A", 0L); + + userlog("ACQUIRE pid=%ld merch=%s amount=%ld fee=%ld net=%ld -> RECONCILE", + pid, merch, amount, fee, net); + + /* continue the SAME global tx into rc_svr (distinct branch) */ + if (tpcall("RECONCILE", (char *)b, 0L, (char **)&b, &rlen, 0L) < 0) { + userlog("ACQUIRE: tpcall(RECONCILE) FAIL: %s", tpstrerror(tperrno)); + FAIL(b); + } + /* our branch resumes: settle-complete the purchase we own */ + if (acdb_update_status(pid, "S") < 0) FAIL(b); + Bchg(b, T_STATUS, 0, "S", 0L); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_AMTFIX.h b/app/src/ac/svc/ACQ_AMTFIX.h new file mode 100644 index 0000000..040ed25 --- /dev/null +++ b/app/src/ac/svc/ACQ_AMTFIX.h @@ -0,0 +1,21 @@ +/* + * ACQ_AMTFIX.h - ac 서비스 ACQ_AMTFIX 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_AMOUNT, T_MERCHANT + * Response fields: T_FEE, T_NET + */ +#ifndef ACQ_AMTFIX_H +#define ACQ_AMTFIX_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + char merchant_id[64]; + long amount; + long fee; + long net; +} ac_amtfix_rec_t; + +void ACQ_AMTFIX(TPSVCINFO *p); + +#endif /* ACQ_AMTFIX_H */ diff --git a/app/src/ac/svc/ACQ_AMTFIX.pgc b/app/src/ac/svc/ACQ_AMTFIX.pgc new file mode 100644 index 0000000..af82085 --- /dev/null +++ b/app/src/ac/svc/ACQ_AMTFIX.pgc @@ -0,0 +1,31 @@ +/* + * ACQ_AMTFIX.pgc - ac 모듈 서비스 ACQ_AMTFIX (one service = one file). + * 금액 확정 정정: 직전 금액 조회 후 하우스 25bps 로 절대 재계산, 증감(delta) 회신. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_AMTFIX.h" + +void ACQ_AMTFIX(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long amount, fee, net; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_old = 0, h_amount, h_fee, h_net; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + amount = getl(b, T_AMOUNT); + /* absolute fix: read prior amount, recompute fee at flat house 25bps (no MDR lookup) */ + EXEC SQL SELECT amount INTO :h_old FROM purchase WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + fee = acq_fee(amount, 25); + net = amount - fee; + h_amount = amount; h_fee = fee; h_net = net; + EXEC SQL UPDATE purchase SET amount = :h_amount, fee = :h_fee, net = :h_net, updated_at = now() + WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + setl(b, T_DELTA, amount - h_old); + setl(b, T_FEE, fee); + setl(b, T_NET, net); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_APPRLINK.h b/app/src/ac/svc/ACQ_APPRLINK.h new file mode 100644 index 0000000..ea28fab --- /dev/null +++ b/app/src/ac/svc/ACQ_APPRLINK.h @@ -0,0 +1,19 @@ +/* + * ACQ_APPRLINK.h - ac 서비스 ACQ_APPRLINK 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID + * Response fields: (none) + */ +#ifndef ACQ_APPRLINK_H +#define ACQ_APPRLINK_H + +#include "ac_svc.h" + +typedef struct { + long approval_id; + long purchase_id; + int approved; +} ac_apprlink_rec_t; + +void ACQ_APPRLINK(TPSVCINFO *p); + +#endif /* ACQ_APPRLINK_H */ diff --git a/app/src/ac/svc/ACQ_APPRLINK.pgc b/app/src/ac/svc/ACQ_APPRLINK.pgc new file mode 100644 index 0000000..026d2f1 --- /dev/null +++ b/app/src/ac/svc/ACQ_APPRLINK.pgc @@ -0,0 +1,14 @@ +/* + * ACQ_APPRLINK.pgc - ac 모듈 서비스 ACQ_APPRLINK (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_APPRLINK.h" + +void ACQ_APPRLINK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long pid = getl(b, T_PURCHASE_ID); + if (acdb_insert_approval(pid, 1) < 0) FAIL(b); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_APPRMAP.h b/app/src/ac/svc/ACQ_APPRMAP.h new file mode 100644 index 0000000..f92f689 --- /dev/null +++ b/app/src/ac/svc/ACQ_APPRMAP.h @@ -0,0 +1,18 @@ +/* + * ACQ_APPRMAP.h - ac 서비스 ACQ_APPRMAP 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID + * Response fields: T_COUNT + */ +#ifndef ACQ_APPRMAP_H +#define ACQ_APPRMAP_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long count; +} ac_apprmap_rec_t; + +void ACQ_APPRMAP(TPSVCINFO *p); + +#endif /* ACQ_APPRMAP_H */ diff --git a/app/src/ac/svc/ACQ_APPRMAP.pgc b/app/src/ac/svc/ACQ_APPRMAP.pgc new file mode 100644 index 0000000..f83b2aa --- /dev/null +++ b/app/src/ac/svc/ACQ_APPRMAP.pgc @@ -0,0 +1,19 @@ +/* + * ACQ_APPRMAP.pgc - ac 모듈 서비스 ACQ_APPRMAP (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_APPRMAP.h" + +void ACQ_APPRMAP(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_cnt; + EXEC SQL END DECLARE SECTION; + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL SELECT count(*) INTO :h_cnt FROM approval WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CANCEL.h b/app/src/ac/svc/ACQ_CANCEL.h new file mode 100644 index 0000000..2a09052 --- /dev/null +++ b/app/src/ac/svc/ACQ_CANCEL.h @@ -0,0 +1,22 @@ +/* + * ACQ_CANCEL.h - ac 서비스 ACQ_CANCEL 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_AMOUNT, T_BIZDATE, T_REASON + * Response fields: T_CANCEL_ID, T_STATUS + */ +#ifndef ACQ_CANCEL_H +#define ACQ_CANCEL_H + +#include "ac_svc.h" + +typedef struct { + long cancel_id; + long purchase_id; + char cancel_type[8]; + long amount; + char reason[128]; + char biz_date[16]; +} ac_cancel_rec_t; + +void ACQ_CANCEL(TPSVCINFO *p); + +#endif /* ACQ_CANCEL_H */ diff --git a/app/src/ac/svc/ACQ_CANCEL.pgc b/app/src/ac/svc/ACQ_CANCEL.pgc new file mode 100644 index 0000000..732b698 --- /dev/null +++ b/app/src/ac/svc/ACQ_CANCEL.pgc @@ -0,0 +1,33 @@ +/* + * ACQ_CANCEL.pgc - ac 모듈 서비스 ACQ_CANCEL (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_CANCEL.h" + +void ACQ_CANCEL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], reason[128]; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_pid, h_amt; + char h_bizdate[16], h_reason[128]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + h_amt = getl(b, T_AMOUNT); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_REASON, reason, sizeof(reason)); if (reason[0] == 0) strcpy(reason, "CUSTOMER"); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strncpy(h_reason, reason, sizeof(h_reason)-1); h_reason[sizeof(h_reason)-1] = 0; + + 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, 'FULL', :h_amt, :h_reason, :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + if (acdb_update_status(h_pid, "C") < 0) FAIL(b); + setl(b, T_CANCEL_ID, h_id); + Bchg(b, T_STATUS, 0, "C", 0L); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_CORRECT.h b/app/src/ac/svc/ACQ_CORRECT.h new file mode 100644 index 0000000..c9d8591 --- /dev/null +++ b/app/src/ac/svc/ACQ_CORRECT.h @@ -0,0 +1,21 @@ +/* + * ACQ_CORRECT.h - ac 서비스 ACQ_CORRECT 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_AMOUNT, T_MERCHANT + * Response fields: T_FEE, T_NET + */ +#ifndef ACQ_CORRECT_H +#define ACQ_CORRECT_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + char merchant_id[64]; + long amount; + long fee; + long net; +} ac_correct_rec_t; + +void ACQ_CORRECT(TPSVCINFO *p); + +#endif /* ACQ_CORRECT_H */ diff --git a/app/src/ac/svc/ACQ_CORRECT.pgc b/app/src/ac/svc/ACQ_CORRECT.pgc new file mode 100644 index 0000000..9c801d7 --- /dev/null +++ b/app/src/ac/svc/ACQ_CORRECT.pgc @@ -0,0 +1,20 @@ +/* + * ACQ_CORRECT.pgc - ac 모듈 서비스 ACQ_CORRECT (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_CORRECT.h" + +void ACQ_CORRECT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64]; + long pid, amount, bps = 25, fee, net; + pid = getl(b, T_PURCHASE_ID); amount = getl(b, T_AMOUNT); + gets_(b, T_MERCHANT, merch, sizeof(merch)); + if (merch[0] && acdb_merchant_mdr(merch, &bps) < 0) bps = 25; + fee = acq_fee(amount, bps); net = amount - fee; + if (acdb_update_amount(pid, amount, fee, net) < 0) FAIL(b); + setl(b, T_FEE, fee); setl(b, T_NET, net); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_DATECHG.h b/app/src/ac/svc/ACQ_DATECHG.h new file mode 100644 index 0000000..2126af3 --- /dev/null +++ b/app/src/ac/svc/ACQ_DATECHG.h @@ -0,0 +1,18 @@ +/* + * ACQ_DATECHG.h - ac 서비스 ACQ_DATECHG 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_BIZDATE + * Response fields: (none) + */ +#ifndef ACQ_DATECHG_H +#define ACQ_DATECHG_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + char biz_date[16]; +} ac_datechg_rec_t; + +void ACQ_DATECHG(TPSVCINFO *p); + +#endif /* ACQ_DATECHG_H */ diff --git a/app/src/ac/svc/ACQ_DATECHG.pgc b/app/src/ac/svc/ACQ_DATECHG.pgc new file mode 100644 index 0000000..278472f --- /dev/null +++ b/app/src/ac/svc/ACQ_DATECHG.pgc @@ -0,0 +1,22 @@ +/* + * ACQ_DATECHG.pgc - ac 모듈 서비스 ACQ_DATECHG (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_DATECHG.h" + +void ACQ_DATECHG(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + h_pid = getl(b, T_PURCHASE_ID); + 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 purchase SET biz_date = :h_bizdate, updated_at = now() WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_DDC.h b/app/src/ac/svc/ACQ_DDC.h new file mode 100644 index 0000000..da47e9e --- /dev/null +++ b/app/src/ac/svc/ACQ_DDC.h @@ -0,0 +1,23 @@ +/* + * ACQ_DDC.h - ac 서비스 ACQ_DDC 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_FX_AMT, T_CCY, T_BIZDATE + * Response fields: T_AMOUNT + */ +#ifndef ACQ_DDC_H +#define ACQ_DDC_H + +#include "ac_svc.h" + +typedef struct { + long fx_id; + long purchase_id; + char ccy[8]; + long fx_amount; + long krw_amount; + long rate_bps; + char biz_date[16]; +} ac_ddc_rec_t; + +void ACQ_DDC(TPSVCINFO *p); + +#endif /* ACQ_DDC_H */ diff --git a/app/src/ac/svc/ACQ_DDC.pgc b/app/src/ac/svc/ACQ_DDC.pgc new file mode 100644 index 0000000..a67b73e --- /dev/null +++ b/app/src/ac/svc/ACQ_DDC.pgc @@ -0,0 +1,33 @@ +/* + * ACQ_DDC.pgc - ac 모듈 서비스 ACQ_DDC (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_DDC.h" + +void ACQ_DDC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], ccy[8]; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_pid, h_fx, h_krw, h_rate; + char h_ccy[8], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + h_fx = getl(b, T_FX_AMT); + gets_(b, T_CCY, ccy, sizeof(ccy)); if (ccy[0] == 0) strcpy(ccy, "USD"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + h_rate = 1350; /* 13.50 KRW per unit (bps of 100) */ + h_krw = h_fx * h_rate / 100; + strncpy(h_ccy, ccy, sizeof(h_ccy)-1); h_ccy[sizeof(h_ccy)-1] = 0; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL SELECT nextval('ac_fx_seq') INTO :h_id; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO ac_fx (fx_id, purchase_id, ccy, fx_amount, krw_amount, rate_bps, biz_date) + VALUES (:h_id, :h_pid, :h_ccy, :h_fx, :h_krw, :h_rate, :h_bizdate); + if (sqlca.sqlcode < 0) { userlog("ACQ_DDC INSERT FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + setl(b, T_AMOUNT, h_krw); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_DUPCHK.h b/app/src/ac/svc/ACQ_DUPCHK.h new file mode 100644 index 0000000..d66a41e --- /dev/null +++ b/app/src/ac/svc/ACQ_DUPCHK.h @@ -0,0 +1,20 @@ +/* + * ACQ_DUPCHK.h - ac 서비스 ACQ_DUPCHK 카피북 (copybook / record header). + * Request fields: T_MERCHANT, T_AMOUNT, T_BIZDATE + * Response fields: T_COUNT + */ +#ifndef ACQ_DUPCHK_H +#define ACQ_DUPCHK_H + +#include "ac_svc.h" + +typedef struct { + char merchant_id[64]; + long amount; + char biz_date[16]; + long count; +} ac_dupchk_rec_t; + +void ACQ_DUPCHK(TPSVCINFO *p); + +#endif /* ACQ_DUPCHK_H */ diff --git a/app/src/ac/svc/ACQ_DUPCHK.pgc b/app/src/ac/svc/ACQ_DUPCHK.pgc new file mode 100644 index 0000000..92790d7 --- /dev/null +++ b/app/src/ac/svc/ACQ_DUPCHK.pgc @@ -0,0 +1,26 @@ +/* + * ACQ_DUPCHK.pgc - ac 모듈 서비스 ACQ_DUPCHK (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_DUPCHK.h" + +void ACQ_DUPCHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_amt; + 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)); + h_amt = getl(b, T_AMOUNT); + 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(*) INTO :h_cnt FROM purchase + WHERE merchant_id = :h_merch AND amount = :h_amt AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_EDC.h b/app/src/ac/svc/ACQ_EDC.h new file mode 100644 index 0000000..286c302 --- /dev/null +++ b/app/src/ac/svc/ACQ_EDC.h @@ -0,0 +1,22 @@ +/* + * ACQ_EDC.h - ac 서비스 ACQ_EDC 카피북 (copybook / record header). + * Request fields: T_MERCHANT, T_AMOUNT, T_BIZDATE + * Response fields: T_PURCHASE_ID, T_FEE, T_NET + */ +#ifndef ACQ_EDC_H +#define ACQ_EDC_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + char merchant_id[64]; + long amount; + long fee; + long net; + char biz_date[16]; +} ac_edc_rec_t; + +void ACQ_EDC(TPSVCINFO *p); + +#endif /* ACQ_EDC_H */ diff --git a/app/src/ac/svc/ACQ_EDC.pgc b/app/src/ac/svc/ACQ_EDC.pgc new file mode 100644 index 0000000..5f62cda --- /dev/null +++ b/app/src/ac/svc/ACQ_EDC.pgc @@ -0,0 +1,25 @@ +/* + * ACQ_EDC.pgc - ac 모듈 서비스 ACQ_EDC (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_EDC.h" + +void ACQ_EDC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + long amount, bps = 25, fee, net, pid; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + amount = getl(b, T_AMOUNT); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (acdb_merchant_mdr(merch, &bps) < 0) bps = 25; + fee = acq_fee(amount, bps); net = amount - fee; + pid = acdb_next_purchase_id(); + if (pid < 0) FAIL(b); + if (acdb_insert_purchase(pid, merch, amount, fee, net, "A", "EDC", bizdate) < 0) FAIL(b); + if (acdb_insert_approval(pid, 1) < 0) FAIL(b); + setl(b, T_PURCHASE_ID, pid); setl(b, T_FEE, fee); setl(b, T_NET, net); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_EDI.h b/app/src/ac/svc/ACQ_EDI.h new file mode 100644 index 0000000..2dc9355 --- /dev/null +++ b/app/src/ac/svc/ACQ_EDI.h @@ -0,0 +1,21 @@ +/* + * ACQ_EDI.h - ac 서비스 ACQ_EDI 카피북 (copybook / record header). + * Request fields: T_MERCHANT, T_DOCNO, T_AMOUNT, T_BIZDATE + * Response fields: (none) + */ +#ifndef ACQ_EDI_H +#define ACQ_EDI_H + +#include "ac_svc.h" + +typedef struct { + long edi_id; + char merchant_id[64]; + char doc_no[32]; + long amount; + char biz_date[16]; +} ac_edi_rec_t; + +void ACQ_EDI(TPSVCINFO *p); + +#endif /* ACQ_EDI_H */ diff --git a/app/src/ac/svc/ACQ_EDI.pgc b/app/src/ac/svc/ACQ_EDI.pgc new file mode 100644 index 0000000..1d161bf --- /dev/null +++ b/app/src/ac/svc/ACQ_EDI.pgc @@ -0,0 +1,31 @@ +/* + * ACQ_EDI.pgc - ac 모듈 서비스 ACQ_EDI (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_EDI.h" + +void ACQ_EDI(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], docno[32], bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_amount; + char h_merch[64], h_doc[32], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_DOCNO, docno, sizeof(docno)); if (docno[0] == 0) strcpy(docno, "EDI-AUTO"); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + h_amount = getl(b, T_AMOUNT); + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + strncpy(h_doc, docno, sizeof(h_doc)-1); h_doc[sizeof(h_doc)-1] = 0; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + + EXEC SQL SELECT nextval('ac_edi_seq') INTO :h_id; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO ac_edi (edi_id, merchant_id, doc_no, amount, biz_date) + VALUES (:h_id, :h_merch, :h_doc, :h_amount, :h_bizdate); + if (sqlca.sqlcode < 0) { userlog("ACQ_EDI INSERT FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + OK(b); +} diff --git a/app/src/ac/svc/ACQ_FEEADJ.h b/app/src/ac/svc/ACQ_FEEADJ.h new file mode 100644 index 0000000..c18a37e --- /dev/null +++ b/app/src/ac/svc/ACQ_FEEADJ.h @@ -0,0 +1,21 @@ +/* + * ACQ_FEEADJ.h - ac 서비스 ACQ_FEEADJ 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_DELTA, T_BIZDATE, T_REASON + * Response fields: T_ADJ + */ +#ifndef ACQ_FEEADJ_H +#define ACQ_FEEADJ_H + +#include "ac_svc.h" + +typedef struct { + long adj_id; + long purchase_id; + long delta; + char reason[128]; + char biz_date[16]; +} ac_feeadj_rec_t; + +void ACQ_FEEADJ(TPSVCINFO *p); + +#endif /* ACQ_FEEADJ_H */ diff --git a/app/src/ac/svc/ACQ_FEEADJ.pgc b/app/src/ac/svc/ACQ_FEEADJ.pgc new file mode 100644 index 0000000..2ea194d --- /dev/null +++ b/app/src/ac/svc/ACQ_FEEADJ.pgc @@ -0,0 +1,30 @@ +/* + * ACQ_FEEADJ.pgc - ac 모듈 서비스 ACQ_FEEADJ (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_FEEADJ.h" + +void ACQ_FEEADJ(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], reason[128]; + long pid, delta, adj; + EXEC SQL BEGIN DECLARE SECTION; + long e_pid, e_delta; + EXEC SQL END DECLARE SECTION; + + pid = getl(b, T_PURCHASE_ID); + delta = getl(b, T_DELTA); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_REASON, reason, sizeof(reason)); if (reason[0] == 0) strcpy(reason, "ADJ"); + adj = acdb_insert_fee_adj(pid, delta, reason, bizdate); + if (adj < 0) FAIL(b); + + e_pid = pid; e_delta = delta; + EXEC SQL UPDATE purchase SET fee = fee + :e_delta, net = net - :e_delta, updated_at = now() + WHERE purchase_id = :e_pid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + setl(b, T_ADJ, adj); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_FOREIGN.h b/app/src/ac/svc/ACQ_FOREIGN.h new file mode 100644 index 0000000..218c0c7 --- /dev/null +++ b/app/src/ac/svc/ACQ_FOREIGN.h @@ -0,0 +1,24 @@ +/* + * ACQ_FOREIGN.h - ac 서비스 ACQ_FOREIGN 카피북 (copybook / record header). + * Request fields: T_MERCHANT, T_AMOUNT, T_BIZDATE, T_CCY + * Response fields: T_PURCHASE_ID, T_FEE + */ +#ifndef ACQ_FOREIGN_H +#define ACQ_FOREIGN_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + char merchant_id[64]; + long amount; + long fee; + long net; + char ccy[8]; + long fx_id; + char biz_date[16]; +} ac_foreign_rec_t; + +void ACQ_FOREIGN(TPSVCINFO *p); + +#endif /* ACQ_FOREIGN_H */ diff --git a/app/src/ac/svc/ACQ_FOREIGN.pgc b/app/src/ac/svc/ACQ_FOREIGN.pgc new file mode 100644 index 0000000..d788547 --- /dev/null +++ b/app/src/ac/svc/ACQ_FOREIGN.pgc @@ -0,0 +1,38 @@ +/* + * ACQ_FOREIGN.pgc - ac 모듈 서비스 ACQ_FOREIGN (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_FOREIGN.h" + +void ACQ_FOREIGN(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16], ccy[8]; + long amount, bps = 30, fee, net, pid; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_pid, h_fx, h_krw, h_rate; + char h_ccy[8], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + gets_(b, T_MERCHANT, merch, sizeof(merch)); + amount = getl(b, T_AMOUNT); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_CCY, ccy, sizeof(ccy)); if (ccy[0] == 0) strcpy(ccy, "USD"); + if (acdb_merchant_mdr(merch, &bps) < 0) bps = 30; + fee = acq_fee(amount, bps); net = amount - fee; + pid = acdb_next_purchase_id(); + if (pid < 0) FAIL(b); + if (acdb_insert_purchase(pid, merch, amount, fee, net, "A", "FOREIGN", bizdate) < 0) FAIL(b); + + h_pid = pid; h_fx = amount; h_rate = 1350; h_krw = amount; + strncpy(h_ccy, ccy, sizeof(h_ccy)-1); h_ccy[sizeof(h_ccy)-1] = 0; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT nextval('ac_fx_seq') INTO :h_id; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO ac_fx (fx_id, purchase_id, ccy, fx_amount, krw_amount, rate_bps, biz_date) + VALUES (:h_id, :h_pid, :h_ccy, :h_fx, :h_krw, :h_rate, :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_PURCHASE_ID, pid); setl(b, T_FEE, fee); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_HOLD.h b/app/src/ac/svc/ACQ_HOLD.h new file mode 100644 index 0000000..da8a3d6 --- /dev/null +++ b/app/src/ac/svc/ACQ_HOLD.h @@ -0,0 +1,18 @@ +/* + * ACQ_HOLD.h - ac 서비스 ACQ_HOLD 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID + * Response fields: T_STATUS + */ +#ifndef ACQ_HOLD_H +#define ACQ_HOLD_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + char status[8]; +} ac_hold_rec_t; + +void ACQ_HOLD(TPSVCINFO *p); + +#endif /* ACQ_HOLD_H */ diff --git a/app/src/ac/svc/ACQ_HOLD.pgc b/app/src/ac/svc/ACQ_HOLD.pgc new file mode 100644 index 0000000..dc607e4 --- /dev/null +++ b/app/src/ac/svc/ACQ_HOLD.pgc @@ -0,0 +1,24 @@ +/* + * ACQ_HOLD.pgc - ac 모듈 서비스 ACQ_HOLD (one service = one file). + * 매입 보류: 접수/대사(A,M) 상태만 보류로 전이, 반영 건수를 T_RC 로. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_HOLD.h" + +void ACQ_HOLD(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); + /* only an approved(A)/matched(M) purchase may be put on hold */ + EXEC SQL UPDATE purchase SET status = 'H', updated_at = now() + WHERE purchase_id = :h_pid AND status IN ('A', 'M'); + if (sqlca.sqlcode < 0) { userlog("ACQ_HOLD FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + h_aff = sqlca.sqlerrd[2]; + if (h_aff > 0) Bchg(b, T_STATUS, 0, "H", 0L); + setl(b, T_RC, h_aff > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_INSTALL.h b/app/src/ac/svc/ACQ_INSTALL.h new file mode 100644 index 0000000..50ad209 --- /dev/null +++ b/app/src/ac/svc/ACQ_INSTALL.h @@ -0,0 +1,22 @@ +/* + * ACQ_INSTALL.h - ac 서비스 ACQ_INSTALL 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_INSTALL_N, T_AMOUNT, T_BIZDATE + * Response fields: T_COUNT + */ +#ifndef ACQ_INSTALL_H +#define ACQ_INSTALL_H + +#include "ac_svc.h" + +typedef struct { + long install_id; + long purchase_id; + long seq_no; + long months; + long month_amount; + char biz_date[16]; +} ac_install_rec_t; + +void ACQ_INSTALL(TPSVCINFO *p); + +#endif /* ACQ_INSTALL_H */ diff --git a/app/src/ac/svc/ACQ_INSTALL.pgc b/app/src/ac/svc/ACQ_INSTALL.pgc new file mode 100644 index 0000000..c427152 --- /dev/null +++ b/app/src/ac/svc/ACQ_INSTALL.pgc @@ -0,0 +1,34 @@ +/* + * ACQ_INSTALL.pgc - ac 모듈 서비스 ACQ_INSTALL (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_INSTALL.h" + +void ACQ_INSTALL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long months, amount, i; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_pid, h_seq, h_months, h_mamt; + char h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + months = getl(b, T_INSTALL_N); if (months < 1) months = 3; + amount = getl(b, T_AMOUNT); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + h_months = months; h_mamt = amount / months; + for (i = 1; i <= months; i++) { + h_seq = i; + EXEC SQL SELECT nextval('ac_install_seq') INTO :h_id; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO ac_install (install_id, purchase_id, seq_no, months, month_amount, biz_date) + VALUES (:h_id, :h_pid, :h_seq, :h_months, :h_mamt, :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + } + setl(b, T_COUNT, months); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_ISSUERCLS.h b/app/src/ac/svc/ACQ_ISSUERCLS.h new file mode 100644 index 0000000..68db44f --- /dev/null +++ b/app/src/ac/svc/ACQ_ISSUERCLS.h @@ -0,0 +1,18 @@ +/* + * ACQ_ISSUERCLS.h - ac 서비스 ACQ_ISSUERCLS 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_ISSUER + * Response fields: (none) + */ +#ifndef ACQ_ISSUERCLS_H +#define ACQ_ISSUERCLS_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + char issuer[32]; +} ac_issuercls_rec_t; + +void ACQ_ISSUERCLS(TPSVCINFO *p); + +#endif /* ACQ_ISSUERCLS_H */ diff --git a/app/src/ac/svc/ACQ_ISSUERCLS.pgc b/app/src/ac/svc/ACQ_ISSUERCLS.pgc new file mode 100644 index 0000000..5914c4b --- /dev/null +++ b/app/src/ac/svc/ACQ_ISSUERCLS.pgc @@ -0,0 +1,22 @@ +/* + * ACQ_ISSUERCLS.pgc - ac 모듈 서비스 ACQ_ISSUERCLS (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_ISSUERCLS.h" + +void ACQ_ISSUERCLS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char issuer[32]; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid; + char h_issuer[32]; + EXEC SQL END DECLARE SECTION; + h_pid = getl(b, T_PURCHASE_ID); + gets_(b, T_ISSUER, issuer, sizeof(issuer)); if (issuer[0] == 0) strcpy(issuer, "BC"); + strncpy(h_issuer, issuer, sizeof(h_issuer)-1); h_issuer[sizeof(h_issuer)-1] = 0; + EXEC SQL UPDATE purchase SET issuer = :h_issuer, updated_at = now() WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_LIMITCHK.h b/app/src/ac/svc/ACQ_LIMITCHK.h new file mode 100644 index 0000000..e8f5901 --- /dev/null +++ b/app/src/ac/svc/ACQ_LIMITCHK.h @@ -0,0 +1,21 @@ +/* + * ACQ_LIMITCHK.h - ac 서비스 ACQ_LIMITCHK 카피북 (copybook / record header). + * Request fields: T_MERCHANT, T_BIZDATE + * Response fields: T_GROSS, T_RC + */ +#ifndef ACQ_LIMITCHK_H +#define ACQ_LIMITCHK_H + +#include "ac_svc.h" + +typedef struct { + char merchant_id[64]; + char biz_date[16]; + long gross; + long daily_limit; + long rc; +} ac_limitchk_rec_t; + +void ACQ_LIMITCHK(TPSVCINFO *p); + +#endif /* ACQ_LIMITCHK_H */ diff --git a/app/src/ac/svc/ACQ_LIMITCHK.pgc b/app/src/ac/svc/ACQ_LIMITCHK.pgc new file mode 100644 index 0000000..730aadc --- /dev/null +++ b/app/src/ac/svc/ACQ_LIMITCHK.pgc @@ -0,0 +1,28 @@ +/* + * ACQ_LIMITCHK.pgc - ac 모듈 서비스 ACQ_LIMITCHK (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_LIMITCHK.h" + +void ACQ_LIMITCHK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_sum, h_lim; + 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) INTO :h_sum FROM purchase + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL SELECT daily_limit INTO :h_lim FROM merchant WHERE merchant_id = :h_merch; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + setl(b, T_GROSS, h_sum); + setl(b, T_RC, h_sum > h_lim ? 1 : 0); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_MERCHSUM.h b/app/src/ac/svc/ACQ_MERCHSUM.h new file mode 100644 index 0000000..d31662d --- /dev/null +++ b/app/src/ac/svc/ACQ_MERCHSUM.h @@ -0,0 +1,22 @@ +/* + * ACQ_MERCHSUM.h - ac 서비스 ACQ_MERCHSUM 카피북 (copybook / record header). + * Request fields: T_MERCHANT, T_BIZDATE + * Response fields: T_COUNT, T_GROSS + */ +#ifndef ACQ_MERCHSUM_H +#define ACQ_MERCHSUM_H + +#include "ac_svc.h" + +typedef struct { + char merchant_id[64]; + char biz_date[16]; + long count; + long gross; + long fee; + long net; +} ac_merchsum_rec_t; + +void ACQ_MERCHSUM(TPSVCINFO *p); + +#endif /* ACQ_MERCHSUM_H */ diff --git a/app/src/ac/svc/ACQ_MERCHSUM.pgc b/app/src/ac/svc/ACQ_MERCHSUM.pgc new file mode 100644 index 0000000..197fa16 --- /dev/null +++ b/app/src/ac/svc/ACQ_MERCHSUM.pgc @@ -0,0 +1,27 @@ +/* + * ACQ_MERCHSUM.pgc - ac 모듈 서비스 ACQ_MERCHSUM (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_MERCHSUM.h" + +void ACQ_MERCHSUM(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_cnt, h_gross, h_fee, h_net; + char h_merch[64], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + EXEC SQL SELECT count(*), coalesce(sum(amount),0), coalesce(sum(fee),0), coalesce(sum(net),0) + INTO :h_cnt, :h_gross, :h_fee, :h_net FROM purchase + WHERE merchant_id = :h_merch AND biz_date = :h_bizdate; + if (sqlca.sqlcode < 0) FAIL(b); + if (acdb_upsert_merch_sum(merch, bizdate, h_gross, h_fee, h_net, h_cnt) < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); setl(b, T_GROSS, h_gross); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_PARTIAL.h b/app/src/ac/svc/ACQ_PARTIAL.h new file mode 100644 index 0000000..ba99427 --- /dev/null +++ b/app/src/ac/svc/ACQ_PARTIAL.h @@ -0,0 +1,20 @@ +/* + * ACQ_PARTIAL.h - ac 서비스 ACQ_PARTIAL 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_AMOUNT, T_BIZDATE + * Response fields: T_CANCEL_ID + */ +#ifndef ACQ_PARTIAL_H +#define ACQ_PARTIAL_H + +#include "ac_svc.h" + +typedef struct { + long cancel_id; + long purchase_id; + long amount; + char biz_date[16]; +} ac_partial_rec_t; + +void ACQ_PARTIAL(TPSVCINFO *p); + +#endif /* ACQ_PARTIAL_H */ diff --git a/app/src/ac/svc/ACQ_PARTIAL.pgc b/app/src/ac/svc/ACQ_PARTIAL.pgc new file mode 100644 index 0000000..2f34ca9 --- /dev/null +++ b/app/src/ac/svc/ACQ_PARTIAL.pgc @@ -0,0 +1,26 @@ +/* + * ACQ_PARTIAL.pgc - ac 모듈 서비스 ACQ_PARTIAL (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_PARTIAL.h" + +void ACQ_PARTIAL(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_pid, h_amt; + 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, bizdate, sizeof(bizdate)); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + 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, 'PARTIAL', :h_amt, 'PARTIAL', :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_CANCEL_ID, h_id); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_RECLASS.h b/app/src/ac/svc/ACQ_RECLASS.h new file mode 100644 index 0000000..0525381 --- /dev/null +++ b/app/src/ac/svc/ACQ_RECLASS.h @@ -0,0 +1,18 @@ +/* + * ACQ_RECLASS.h - ac 서비스 ACQ_RECLASS 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_CATEGORY + * Response fields: (none) + */ +#ifndef ACQ_RECLASS_H +#define ACQ_RECLASS_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + char category[32]; +} ac_reclass_rec_t; + +void ACQ_RECLASS(TPSVCINFO *p); + +#endif /* ACQ_RECLASS_H */ diff --git a/app/src/ac/svc/ACQ_RECLASS.pgc b/app/src/ac/svc/ACQ_RECLASS.pgc new file mode 100644 index 0000000..ddffe74 --- /dev/null +++ b/app/src/ac/svc/ACQ_RECLASS.pgc @@ -0,0 +1,30 @@ +/* + * ACQ_RECLASS.pgc - ac 모듈 서비스 ACQ_RECLASS (one service = one file). + * 업종/분류 재지정: category 갱신 후 같은 가맹점에서 동일 분류 건수를 집계 회신. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_RECLASS.h" + +void ACQ_RECLASS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char cat[32]; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_cnt = 0; + char h_cat[32]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + 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 UPDATE purchase SET category = :h_cat, updated_at = now() WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + /* how many of this merchant's purchases now carry the new 분류 */ + EXEC SQL SELECT count(*) INTO :h_cnt FROM purchase + WHERE merchant_id = (SELECT merchant_id FROM purchase WHERE purchase_id = :h_pid) + AND category = :h_cat; + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_COUNT, h_cnt); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_REFUND.h b/app/src/ac/svc/ACQ_REFUND.h new file mode 100644 index 0000000..9d753ca --- /dev/null +++ b/app/src/ac/svc/ACQ_REFUND.h @@ -0,0 +1,21 @@ +/* + * ACQ_REFUND.h - ac 서비스 ACQ_REFUND 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_AMOUNT, T_BIZDATE, T_REASON + * Response fields: T_CANCEL_ID + */ +#ifndef ACQ_REFUND_H +#define ACQ_REFUND_H + +#include "ac_svc.h" + +typedef struct { + long cancel_id; + long purchase_id; + long amount; + char reason[128]; + char biz_date[16]; +} ac_refund_rec_t; + +void ACQ_REFUND(TPSVCINFO *p); + +#endif /* ACQ_REFUND_H */ diff --git a/app/src/ac/svc/ACQ_REFUND.pgc b/app/src/ac/svc/ACQ_REFUND.pgc new file mode 100644 index 0000000..6273ab6 --- /dev/null +++ b/app/src/ac/svc/ACQ_REFUND.pgc @@ -0,0 +1,28 @@ +/* + * ACQ_REFUND.pgc - ac 모듈 서비스 ACQ_REFUND (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_REFUND.h" + +void ACQ_REFUND(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16], reason[128]; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_pid, h_amt; + char h_bizdate[16], h_reason[128]; + EXEC SQL END DECLARE SECTION; + h_pid = getl(b, T_PURCHASE_ID); h_amt = getl(b, T_AMOUNT); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + gets_(b, T_REASON, reason, sizeof(reason)); if (reason[0] == 0) strcpy(reason, "REFUND"); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + strncpy(h_reason, reason, sizeof(h_reason)-1); h_reason[sizeof(h_reason)-1] = 0; + 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, 'REFUND', :h_amt, :h_reason, :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + setl(b, T_CANCEL_ID, h_id); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_RELEASE.h b/app/src/ac/svc/ACQ_RELEASE.h new file mode 100644 index 0000000..c69d7ca --- /dev/null +++ b/app/src/ac/svc/ACQ_RELEASE.h @@ -0,0 +1,18 @@ +/* + * ACQ_RELEASE.h - ac 서비스 ACQ_RELEASE 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID + * Response fields: T_STATUS + */ +#ifndef ACQ_RELEASE_H +#define ACQ_RELEASE_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + char status[8]; +} ac_release_rec_t; + +void ACQ_RELEASE(TPSVCINFO *p); + +#endif /* ACQ_RELEASE_H */ diff --git a/app/src/ac/svc/ACQ_RELEASE.pgc b/app/src/ac/svc/ACQ_RELEASE.pgc new file mode 100644 index 0000000..3bd2655 --- /dev/null +++ b/app/src/ac/svc/ACQ_RELEASE.pgc @@ -0,0 +1,27 @@ +/* + * ACQ_RELEASE.pgc - ac 모듈 서비스 ACQ_RELEASE (one service = one file). + * 보류 해제: 직전 상태를 조회한 뒤 H->A 전이, 이전 상태를 함께 회신. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_RELEASE.h" + +void ACQ_RELEASE(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + EXEC SQL BEGIN DECLARE SECTION; + long h_pid, h_aff; + char h_prev[8]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + EXEC SQL SELECT status INTO :h_prev FROM purchase WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + /* release only from HOLD; report whether the transition actually fired */ + EXEC SQL UPDATE purchase SET status = 'A', updated_at = now() + WHERE purchase_id = :h_pid AND status = 'H'; + if (sqlca.sqlcode < 0) FAIL(b); + h_aff = sqlca.sqlerrd[2]; + Bchg(b, T_STATUS, 0, h_aff > 0 ? "A" : h_prev, 0L); + setl(b, T_RC, h_aff > 0 ? 0 : 1); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_REPROC.h b/app/src/ac/svc/ACQ_REPROC.h new file mode 100644 index 0000000..b8d8955 --- /dev/null +++ b/app/src/ac/svc/ACQ_REPROC.h @@ -0,0 +1,18 @@ +/* + * ACQ_REPROC.h - ac 서비스 ACQ_REPROC 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID + * Response fields: T_STATUS + */ +#ifndef ACQ_REPROC_H +#define ACQ_REPROC_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + char status[8]; +} ac_reproc_rec_t; + +void ACQ_REPROC(TPSVCINFO *p); + +#endif /* ACQ_REPROC_H */ diff --git a/app/src/ac/svc/ACQ_REPROC.pgc b/app/src/ac/svc/ACQ_REPROC.pgc new file mode 100644 index 0000000..9ba2e16 --- /dev/null +++ b/app/src/ac/svc/ACQ_REPROC.pgc @@ -0,0 +1,15 @@ +/* + * ACQ_REPROC.pgc - ac 모듈 서비스 ACQ_REPROC (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_REPROC.h" + +void ACQ_REPROC(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long pid = getl(b, T_PURCHASE_ID); + if (acdb_update_status(pid, "A") < 0) FAIL(b); + Bchg(b, T_STATUS, 0, "A", 0L); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_SETTLELINK.h b/app/src/ac/svc/ACQ_SETTLELINK.h new file mode 100644 index 0000000..a4c4d6e --- /dev/null +++ b/app/src/ac/svc/ACQ_SETTLELINK.h @@ -0,0 +1,19 @@ +/* + * ACQ_SETTLELINK.h - ac 서비스 ACQ_SETTLELINK 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_NET, T_BIZDATE + * Response fields: (none) + */ +#ifndef ACQ_SETTLELINK_H +#define ACQ_SETTLELINK_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + long net; + char biz_date[16]; +} ac_settlelink_rec_t; + +void ACQ_SETTLELINK(TPSVCINFO *p); + +#endif /* ACQ_SETTLELINK_H */ diff --git a/app/src/ac/svc/ACQ_SETTLELINK.pgc b/app/src/ac/svc/ACQ_SETTLELINK.pgc new file mode 100644 index 0000000..9dd0a77 --- /dev/null +++ b/app/src/ac/svc/ACQ_SETTLELINK.pgc @@ -0,0 +1,36 @@ +/* + * ACQ_SETTLELINK.pgc - ac 모듈 서비스 ACQ_SETTLELINK (one service = one file). + * 정산 연계: 가맹점 미지정 시 매입에서 조회, settlement 링크 행(status='LINKED') 생성. + * Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr. + */ +#include "ACQ_SETTLELINK.h" + +void ACQ_SETTLELINK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_pid, h_net; + char h_merch[64], h_bizdate[16]; + EXEC SQL END DECLARE SECTION; + + h_pid = getl(b, T_PURCHASE_ID); + h_net = getl(b, T_NET); + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (merch[0] == 0) { + EXEC SQL SELECT merchant_id INTO :h_merch FROM purchase WHERE purchase_id = :h_pid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + } else { + 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 nextval('settlement_seq') INTO :h_id; + if (sqlca.sqlcode < 0) FAIL(b); + EXEC SQL INSERT INTO settlement (settlement_id, purchase_id, merchant_id, net, biz_date, status) + VALUES (:h_id, :h_pid, :h_merch, :h_net, :h_bizdate, 'LINKED'); + if (sqlca.sqlcode < 0) { userlog("ACQ_SETTLELINK FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + setl(b, T_SETTLE_ID, h_id); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_STATUS.h b/app/src/ac/svc/ACQ_STATUS.h new file mode 100644 index 0000000..7ddb03d --- /dev/null +++ b/app/src/ac/svc/ACQ_STATUS.h @@ -0,0 +1,18 @@ +/* + * ACQ_STATUS.h - ac 서비스 ACQ_STATUS 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID + * Response fields: T_STATUS + */ +#ifndef ACQ_STATUS_H +#define ACQ_STATUS_H + +#include "ac_svc.h" + +typedef struct { + long purchase_id; + char status[8]; +} ac_status_rec_t; + +void ACQ_STATUS(TPSVCINFO *p); + +#endif /* ACQ_STATUS_H */ diff --git a/app/src/ac/svc/ACQ_STATUS.pgc b/app/src/ac/svc/ACQ_STATUS.pgc new file mode 100644 index 0000000..424adc5 --- /dev/null +++ b/app/src/ac/svc/ACQ_STATUS.pgc @@ -0,0 +1,16 @@ +/* + * ACQ_STATUS.pgc - ac 모듈 서비스 ACQ_STATUS (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_STATUS.h" + +void ACQ_STATUS(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + long pid = getl(b, T_PURCHASE_ID); + char st[4]; + if (acdb_get_status(pid, st) < 0) FAIL(b); + Bchg(b, T_STATUS, 0, st, 0L); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_TAXINV.h b/app/src/ac/svc/ACQ_TAXINV.h new file mode 100644 index 0000000..1dd486d --- /dev/null +++ b/app/src/ac/svc/ACQ_TAXINV.h @@ -0,0 +1,22 @@ +/* + * ACQ_TAXINV.h - ac 서비스 ACQ_TAXINV 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_AMOUNT, T_MERCHANT, T_BIZDATE + * Response fields: T_TAX + */ +#ifndef ACQ_TAXINV_H +#define ACQ_TAXINV_H + +#include "ac_svc.h" + +typedef struct { + long inv_id; + long purchase_id; + char merchant_id[64]; + long supply; + long vat; + char biz_date[16]; +} ac_taxinv_rec_t; + +void ACQ_TAXINV(TPSVCINFO *p); + +#endif /* ACQ_TAXINV_H */ diff --git a/app/src/ac/svc/ACQ_TAXINV.pgc b/app/src/ac/svc/ACQ_TAXINV.pgc new file mode 100644 index 0000000..a925d39 --- /dev/null +++ b/app/src/ac/svc/ACQ_TAXINV.pgc @@ -0,0 +1,22 @@ +/* + * ACQ_TAXINV.pgc - ac 모듈 서비스 ACQ_TAXINV (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_TAXINV.h" + +void ACQ_TAXINV(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char merch[64], bizdate[16]; + long pid, supply, vat, inv; + pid = getl(b, T_PURCHASE_ID); + supply = getl(b, T_AMOUNT); + gets_(b, T_MERCHANT, merch, sizeof(merch)); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + vat = acq_vat(supply); + inv = acdb_insert_tax_invoice(pid, merch, supply, vat, bizdate); + if (inv < 0) FAIL(b); + setl(b, T_TAX, vat); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_UNMATCH.h b/app/src/ac/svc/ACQ_UNMATCH.h new file mode 100644 index 0000000..8229877 --- /dev/null +++ b/app/src/ac/svc/ACQ_UNMATCH.h @@ -0,0 +1,21 @@ +/* + * ACQ_UNMATCH.h - ac 서비스 ACQ_UNMATCH 카피북 (copybook / record header). + * Request fields: T_BIZDATE + * Response fields: T_COUNT + */ +#ifndef ACQ_UNMATCH_H +#define ACQ_UNMATCH_H + +#include "ac_svc.h" + +typedef struct { + long unmatch_id; + long purchase_id; + char reason[16]; + char biz_date[16]; + long count; +} ac_unmatch_rec_t; + +void ACQ_UNMATCH(TPSVCINFO *p); + +#endif /* ACQ_UNMATCH_H */ diff --git a/app/src/ac/svc/ACQ_UNMATCH.pgc b/app/src/ac/svc/ACQ_UNMATCH.pgc new file mode 100644 index 0000000..bd9c9fc --- /dev/null +++ b/app/src/ac/svc/ACQ_UNMATCH.pgc @@ -0,0 +1,29 @@ +/* + * ACQ_UNMATCH.pgc - ac 모듈 서비스 ACQ_UNMATCH (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_UNMATCH.h" + +void ACQ_UNMATCH(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 INSERT INTO ac_unmatch (unmatch_id, purchase_id, reason, biz_date) + SELECT nextval('ac_unmatch_seq'), p.purchase_id, 'NO_LEDGER', p.biz_date + 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 NOT EXISTS (SELECT 1 FROM ac_unmatch u WHERE u.purchase_id = p.purchase_id); + if (sqlca.sqlcode < 0) { userlog("ACQ_UNMATCH FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); } + h_cnt = sqlca.sqlerrd[2]; + setl(b, T_COUNT, h_cnt); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_VOID.h b/app/src/ac/svc/ACQ_VOID.h new file mode 100644 index 0000000..4582afd --- /dev/null +++ b/app/src/ac/svc/ACQ_VOID.h @@ -0,0 +1,21 @@ +/* + * ACQ_VOID.h - ac 서비스 ACQ_VOID 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_AMOUNT, T_BIZDATE + * Response fields: T_CANCEL_ID, T_STATUS + */ +#ifndef ACQ_VOID_H +#define ACQ_VOID_H + +#include "ac_svc.h" + +typedef struct { + long cancel_id; + long purchase_id; + long amount; + char status[8]; + char biz_date[16]; +} ac_void_rec_t; + +void ACQ_VOID(TPSVCINFO *p); + +#endif /* ACQ_VOID_H */ diff --git a/app/src/ac/svc/ACQ_VOID.pgc b/app/src/ac/svc/ACQ_VOID.pgc new file mode 100644 index 0000000..15b8fff --- /dev/null +++ b/app/src/ac/svc/ACQ_VOID.pgc @@ -0,0 +1,28 @@ +/* + * ACQ_VOID.pgc - ac 모듈 서비스 ACQ_VOID (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_VOID.h" + +void ACQ_VOID(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + EXEC SQL BEGIN DECLARE SECTION; + long h_id, h_pid, h_amt; + 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, bizdate, sizeof(bizdate)); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate)-1); h_bizdate[sizeof(h_bizdate)-1] = 0; + 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, 'VOID', :h_amt, 'VOID', :h_bizdate); + if (sqlca.sqlcode < 0) FAIL(b); + if (acdb_update_status(h_pid, "V") < 0) FAIL(b); + setl(b, T_CANCEL_ID, h_id); + Bchg(b, T_STATUS, 0, "V", 0L); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_WHT.h b/app/src/ac/svc/ACQ_WHT.h new file mode 100644 index 0000000..4f74c58 --- /dev/null +++ b/app/src/ac/svc/ACQ_WHT.h @@ -0,0 +1,21 @@ +/* + * ACQ_WHT.h - ac 서비스 ACQ_WHT 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_BIZDATE + * Response fields: T_TAX + */ +#ifndef ACQ_WHT_H +#define ACQ_WHT_H + +#include "ac_svc.h" + +typedef struct { + long wht_id; + long purchase_id; + long fee; + long tax_amount; + char biz_date[16]; +} ac_wht_rec_t; + +void ACQ_WHT(TPSVCINFO *p); + +#endif /* ACQ_WHT_H */ diff --git a/app/src/ac/svc/ACQ_WHT.pgc b/app/src/ac/svc/ACQ_WHT.pgc new file mode 100644 index 0000000..9b8942a --- /dev/null +++ b/app/src/ac/svc/ACQ_WHT.pgc @@ -0,0 +1,26 @@ +/* + * ACQ_WHT.pgc - ac 모듈 서비스 ACQ_WHT (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_WHT.h" + +void ACQ_WHT(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long pid, tax, wht_id; + EXEC SQL BEGIN DECLARE SECTION; + long e_pid, e_fee; + EXEC SQL END DECLARE SECTION; + pid = getl(b, T_PURCHASE_ID); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + e_pid = pid; + EXEC SQL SELECT fee INTO :e_fee FROM purchase WHERE purchase_id = :e_pid; + if (sqlca.sqlcode < 0 || sqlca.sqlcode == 100) FAIL(b); + tax = acq_wht_amt(e_fee, 2200); /* 22% */ + wht_id = acdb_insert_wht(pid, tax, bizdate); + if (wht_id < 0) FAIL(b); + setl(b, T_TAX, tax); + OK(b); +} diff --git a/app/src/ac/svc/ACQ_XFERLINK.h b/app/src/ac/svc/ACQ_XFERLINK.h new file mode 100644 index 0000000..813affc --- /dev/null +++ b/app/src/ac/svc/ACQ_XFERLINK.h @@ -0,0 +1,21 @@ +/* + * ACQ_XFERLINK.h - ac 서비스 ACQ_XFERLINK 카피북 (copybook / record header). + * Request fields: T_PURCHASE_ID, T_AMOUNT, T_BIZDATE + * Response fields: (none) + */ +#ifndef ACQ_XFERLINK_H +#define ACQ_XFERLINK_H + +#include "ac_svc.h" + +typedef struct { + long ledger_id; + long purchase_id; + char entry_type[16]; + long amount; + char biz_date[16]; +} ac_xferlink_rec_t; + +void ACQ_XFERLINK(TPSVCINFO *p); + +#endif /* ACQ_XFERLINK_H */ diff --git a/app/src/ac/svc/ACQ_XFERLINK.pgc b/app/src/ac/svc/ACQ_XFERLINK.pgc new file mode 100644 index 0000000..1b12b36 --- /dev/null +++ b/app/src/ac/svc/ACQ_XFERLINK.pgc @@ -0,0 +1,16 @@ +/* + * ACQ_XFERLINK.pgc - ac 모듈 서비스 ACQ_XFERLINK (one service = one file). + * Moved verbatim from the former inline ac_svr.pgc; runs on the caller's XA + * branch (no EXEC SQL CONNECT). Advertised by ac_svr at tpsvrinit. + */ +#include "ACQ_XFERLINK.h" + +void ACQ_XFERLINK(TPSVCINFO *p) +{ + UBFH *b = (UBFH *)p->data; + char bizdate[16]; + long pid = getl(b, T_PURCHASE_ID), amt = getl(b, T_AMOUNT); + gets_(b, T_BIZDATE, bizdate, sizeof(bizdate)); + if (acdb_insert_ledger(pid, "XFER", amt, bizdate) < 0) FAIL(b); + OK(b); +} diff --git a/app/src/ac/svc/ac_svc.h b/app/src/ac/svc/ac_svc.h new file mode 100644 index 0000000..9ee4fd4 --- /dev/null +++ b/app/src/ac/svc/ac_svc.h @@ -0,0 +1,33 @@ +/* + * ac_svc.h - ac (매입) 서비스 공통 카피북 (shared copybook for all ac svc/*.pgc). + * + * Every per-service file (svc/.pgc) includes its own copybook header + * (svc/.h), which in turn includes THIS shared base. It pulls in the + * ATMI/UBF runtime, the acq.fd field ids, the ac dbio layer (libacdbio.a) and the + * common math (libacqcommon.a), and provides the tiny UBF helpers + tpreturn + * macros every service uses. No business logic and no service function lives here. + */ +#ifndef AC_SVC_H +#define AC_SVC_H + +#include +#include +#include +#include +#include +#include +#include +#include "acq.fd.h" +#include "ac_dbio.h" +#include "acq_common.h" + +/* ----- small UBF helpers (one copy per service TU) ----------------------- */ +static long getl(UBFH *b, BFLDID f) { long v = 0; Bget(b, f, 0, (char *)&v, 0L); return v; } +static void gets_(UBFH *b, BFLDID f, char *out, int cap) +{ BFLDLEN l = cap; out[0] = 0; Bget(b, f, 0, out, &l); } +static void setl(UBFH *b, BFLDID f, long v) { Bchg(b, f, 0, (char *)&v, 0L); } + +#define FAIL(b) do { tpreturn(TPFAIL, 0, (char *)(b), 0L, 0L); return; } while (0) +#define OK(b) do { tpreturn(TPSUCCESS, 0, (char *)(b), 0L, 0L); return; } while (0) + +#endif /* AC_SVC_H */