규모확장 Wave B: 전 11모듈 svc 75→133·batch 35→46 (BAIS 온라인 1:1)

- svc 825→1,463 (모듈당 133), batch 385→506 (모듈당 46) — 전부 새 고유 실업무 로직
  + 카피북 헤더, 디스패처 133 advertise, 한글 기능설명(매니페스트 추출)
- BAIS As-Is 대비: 온라인 1,463 vs 1,466(99.8%), 배치 506 vs 503(초과)
- 화면 매니페스트 1,463 재생성
- 검증: build DONE modules=11 batches=506, 12서버 runok, xadmin psc 1,466 AVAIL,
  매입체인 XA 커밋, 신규서비스 실데이터 스모크, 전 코퍼스 .pgc 클론 0

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

View file

@ -0,0 +1,18 @@
/*
* ACQ_ALERTAMT.h - ac ACQ_ALERTAMT (copybook / record header).
* : (T_AMOUNT)
*/
#ifndef ACQ_ALERTAMT_H
#define ACQ_ALERTAMT_H
#include "ac_svc.h"
typedef struct {
long threshold;
long over_count;
long max_amount;
} acq_alertamt_rec_t;
void ACQ_ALERTAMT(TPSVCINFO *p);
#endif /* ACQ_ALERTAMT_H */

View file

@ -0,0 +1,30 @@
/*
* ACQ_ALERTAMT.pgc - ac 모듈 서비스 ACQ_ALERTAMT (one service = one file).
* 매입 금액초과 알림: 가맹점의 영업일 매입 중 임계금액(T_AMOUNT)을 초과한
* 고액 건수와 최고 매입액을 조회해 경보 여부를 판정한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_ALERTAMT.h"
void ACQ_ALERTAMT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_lim, h_over = 0, h_max = 0;
char h_merch[64], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
h_lim = getl(b, T_AMOUNT); if (h_lim <= 0) h_lim = 1000000;
gets_(b, T_MERCHANT, h_merch, sizeof(h_merch));
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT count(*) FILTER (WHERE amount > :h_lim), coalesce(max(amount),0)
INTO :h_over, :h_max FROM purchase
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_over);
setl(b, T_AMT1, h_max);
setl(b, T_RC, h_over > 0 ? 1 : 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_ALERTDUP.h - ac ACQ_ALERTDUP (copybook / record header).
* : 2
*/
#ifndef ACQ_ALERTDUP_H
#define ACQ_ALERTDUP_H
#include "ac_svc.h"
typedef struct {
char merchant[64];
long dup_groups;
long rc;
} acq_alertdup_rec_t;
void ACQ_ALERTDUP(TPSVCINFO *p);
#endif /* ACQ_ALERTDUP_H */

View file

@ -0,0 +1,28 @@
/*
* ACQ_ALERTDUP.pgc - ac 모듈 서비스 ACQ_ALERTDUP (one service = one file).
* 매입 중복 알림: 가맹점의 영업일 매입 중 동일 금액이 2건 이상 반복된
* 금액 그룹 수를 세어 중복 의심 여부를 알린다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_ALERTDUP.h"
void ACQ_ALERTDUP(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_grp = 0;
char h_merch[64], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, h_merch, sizeof(h_merch));
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT count(*) INTO :h_grp FROM (
SELECT amount FROM purchase WHERE merchant_id = :h_merch
AND biz_date = :h_bizdate GROUP BY amount HAVING count(*) > 1) d;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_grp);
setl(b, T_RC, h_grp > 0 ? 1 : 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_ALERTQRY.h - ac ACQ_ALERTQRY (copybook / record header).
* : (//)
*/
#ifndef ACQ_ALERTQRY_H
#define ACQ_ALERTQRY_H
#include "ac_svc.h"
typedef struct {
long velocity;
long limit;
long dup;
} acq_alertqry_rec_t;
void ACQ_ALERTQRY(TPSVCINFO *p);
#endif /* ACQ_ALERTQRY_H */

View file

@ -0,0 +1,30 @@
/*
* ACQ_ALERTQRY.pgc - ac 모듈 서비스 ACQ_ALERTQRY (one service = one file).
* 매입 알림 유형별 조회: 미매입 원장을 알림 유형(속도/한도/중복) 접두어로
* 한 번에 분해 집계해 유형별 미해소 알림 건수를 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_ALERTQRY.h"
void ACQ_ALERTQRY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_vel = 0, h_lim = 0, h_dup = 0;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT count(*) FILTER (WHERE reason LIKE 'VEL%'),
count(*) FILTER (WHERE reason LIKE 'LIMIT%'),
count(*) FILTER (WHERE reason LIKE 'DUP%')
INTO :h_vel, :h_lim, :h_dup FROM ac_unmatch WHERE biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_ARG1, h_vel);
setl(b, T_ARG2, h_lim);
setl(b, T_ARG3, h_dup);
setl(b, T_COUNT, h_vel + h_lim + h_dup);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_ALERTVEL.h - ac ACQ_ALERTVEL (copybook / record header).
* : 5
*/
#ifndef ACQ_ALERTVEL_H
#define ACQ_ALERTVEL_H
#include "ac_svc.h"
typedef struct {
char channel[16];
long win5m;
long win30m;
} acq_alertvel_rec_t;
void ACQ_ALERTVEL(TPSVCINFO *p);
#endif /* ACQ_ALERTVEL_H */

View file

@ -0,0 +1,30 @@
/*
* ACQ_ALERTVEL.pgc - ac 모듈 서비스 ACQ_ALERTVEL (one service = one file).
* 매입 속도 알림: 채널의 최근 5분간 유입 건수를 재어 분당 허용치를 넘으면
* 경보한다. 참고로 직전 30분 건수도 함께 조회한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_ALERTVEL.h"
void ACQ_ALERTVEL(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long allow;
EXEC SQL BEGIN DECLARE SECTION;
long h_5m = 0, h_30m = 0;
char h_chan[16];
EXEC SQL END DECLARE SECTION;
allow = getl(b, T_ARG1); if (allow <= 0) allow = 50;
gets_(b, T_CHANNEL, h_chan, sizeof(h_chan));
EXEC SQL SELECT count(*) FILTER (WHERE created_at >= now() - interval '5 minutes'),
count(*) FILTER (WHERE created_at >= now() - interval '30 minutes')
INTO :h_5m, :h_30m FROM purchase WHERE channel = :h_chan;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_ARG2, h_5m);
setl(b, T_ARG3, h_30m);
setl(b, T_RC, h_5m > allow ? 1 : 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_BRANDQRY.h - ac ACQ_BRANDQRY (copybook / record header).
* : (T_ISSUER) ,
*/
#ifndef ACQ_BRANDQRY_H
#define ACQ_BRANDQRY_H
#include "ac_svc.h"
typedef struct {
char issuer[32];
long count;
long gross;
} acq_brandqry_rec_t;
void ACQ_BRANDQRY(TPSVCINFO *p);
#endif /* ACQ_BRANDQRY_H */

View file

@ -0,0 +1,30 @@
/*
* ACQ_BRANDQRY.pgc - ac 모듈 서비스 ACQ_BRANDQRY (one service = one file).
* 매입 브랜드별 조회: 지정 발급사(T_ISSUER)의 영업일 매입 건수와 총액,
* 평균 수수료를 단건으로 조회해 응답 버퍼에 채운다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_BRANDQRY.h"
void ACQ_BRANDQRY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_n = 0, h_sum = 0, h_fee = 0;
char h_iss[32], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_ISSUER, h_iss, sizeof(h_iss));
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT count(*), coalesce(sum(amount),0), coalesce(sum(fee),0)
INTO :h_n, :h_sum, :h_fee FROM purchase
WHERE issuer = :h_iss AND biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_n);
setl(b, T_GROSS, h_sum);
setl(b, T_FEE, h_fee);
setl(b, T_RC, h_n > 0 ? 0 : 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_BRANDRANK.h - ac ACQ_BRANDRANK (copybook / record header).
* :
*/
#ifndef ACQ_BRANDRANK_H
#define ACQ_BRANDRANK_H
#include "ac_svc.h"
typedef struct {
char issuer[32];
long grand;
long half_at;
} acq_brandrank_rec_t;
void ACQ_BRANDRANK(TPSVCINFO *p);
#endif /* ACQ_BRANDRANK_H */

View file

@ -0,0 +1,42 @@
/*
* ACQ_BRANDRANK.pgc - ac 모듈 서비스 ACQ_BRANDRANK (one service = one file).
* 매입 브랜드별 순위: 발급사 매출 상위 순으로 커서 조회하며 누적 점유율을
* 계산해 상위 브랜드가 전체의 절반을 넘는 지점(집중도)을 함께 낸다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_BRANDRANK.h"
void ACQ_BRANDRANK(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long rank = 0, run = 0, half_at = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_sum, h_grand = 0;
char h_iss[32], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_grand FROM purchase WHERE biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL DECLARE brr_cur CURSOR FOR
SELECT issuer, sum(amount) FROM purchase WHERE biz_date = :h_bizdate
GROUP BY issuer ORDER BY sum(amount) DESC;
EXEC SQL OPEN brr_cur;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH brr_cur INTO :h_iss, :h_sum;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE brr_cur; FAIL(b); }
rank++; run += h_sum;
if (half_at == 0 && h_grand > 0 && run * 2 >= h_grand) half_at = rank;
if (rank <= 6) Badd(b, T_ISSUER, h_iss, 0L);
}
EXEC SQL CLOSE brr_cur;
setl(b, T_GROSS, h_grand);
setl(b, T_ARG1, rank);
setl(b, T_ARG2, half_at);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_BRANDSUM.h - ac ACQ_BRANDSUM (copybook / record header).
* : ( )
*/
#ifndef ACQ_BRANDSUM_H
#define ACQ_BRANDSUM_H
#include "ac_svc.h"
typedef struct {
char issuer[32];
long amount;
long kinds;
} acq_brandsum_rec_t;
void ACQ_BRANDSUM(TPSVCINFO *p);
#endif /* ACQ_BRANDSUM_H */

View file

@ -0,0 +1,37 @@
/*
* ACQ_BRANDSUM.pgc - ac 모듈 서비스 ACQ_BRANDSUM (one service = one file).
* 매입 브랜드별 집계: 발급사(카드 브랜드) 기준으로 매출 합계를 그룹 집계해
* 브랜드명·매출액 쌍을 occurrence 로 내리고 최다 브랜드 건수를 함께 계산한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_BRANDSUM.h"
void ACQ_BRANDSUM(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long grand = 0, kinds = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_sum, h_n;
char h_iss[32], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL DECLARE brs_cur CURSOR FOR
SELECT issuer, sum(amount), count(*) FROM purchase
WHERE biz_date = :h_bizdate GROUP BY issuer ORDER BY sum(amount) DESC;
EXEC SQL OPEN brs_cur;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH brs_cur INTO :h_iss, :h_sum, :h_n;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE brs_cur; FAIL(b); }
if (kinds < 8) { Badd(b, T_ISSUER, h_iss, 0L); Badd(b, T_AMT1, (char *)&h_sum, 0L); }
grand += h_sum; kinds++;
}
EXEC SQL CLOSE brs_cur;
setl(b, T_GROSS, grand);
setl(b, T_ARG1, kinds);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_BULKCHK.h - ac ACQ_BULKCHK (copybook / record header).
* : (A)
*/
#ifndef ACQ_BULKCHK_H
#define ACQ_BULKCHK_H
#include "ac_svc.h"
typedef struct {
long ok_count;
long bad_count;
long rc;
} acq_bulkchk_rec_t;
void ACQ_BULKCHK(TPSVCINFO *p);
#endif /* ACQ_BULKCHK_H */

View file

@ -0,0 +1,38 @@
/*
* ACQ_BULKCHK.pgc - ac 모듈 서비스 ACQ_BULKCHK (one service = one file).
* 대량매입 배치검증: 영업일 접수(A) 매입을 커서로 순회하며 금액 양수 및
* 수수료 정합(net=amount-fee)을 점검해 정상/오류 건수를 분리 집계한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_BULKCHK.h"
void ACQ_BULKCHK(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long ok = 0, bad = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_amt, h_fee, h_net;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL DECLARE blk_cur CURSOR FOR
SELECT purchase_id, amount, fee, net FROM purchase
WHERE biz_date = :h_bizdate AND status = 'A';
EXEC SQL OPEN blk_cur;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH blk_cur INTO :h_pid, :h_amt, :h_fee, :h_net;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE blk_cur; FAIL(b); }
if (h_amt > 0 && h_net == h_amt - h_fee) ok++;
else { bad++; if (bad <= 8) Badd(b, T_PURCHASE_ID, (char *)&h_pid, 0L); }
}
EXEC SQL CLOSE blk_cur;
setl(b, T_ARG1, ok);
setl(b, T_ARG2, bad);
setl(b, T_RC, bad > 0 ? 1 : 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_BULKRPT.h - ac ACQ_BULKRPT (copybook / record header).
* : (///)
*/
#ifndef ACQ_BULKRPT_H
#define ACQ_BULKRPT_H
#include "ac_svc.h"
typedef struct {
long a;
long m;
long settled;
} acq_bulkrpt_rec_t;
void ACQ_BULKRPT(TPSVCINFO *p);
#endif /* ACQ_BULKRPT_H */

View file

@ -0,0 +1,32 @@
/*
* ACQ_BULKRPT.pgc - ac 모듈 서비스 ACQ_BULKRPT (one service = one file).
* 대량매입 리포트: 영업일 매입을 상태(접수/대사/정산/취소)별로 한 번에 분해
* 집계해 상태별 건수와 총 매출·순정산 금액을 요약 리포트로 낸다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_BULKRPT.h"
void ACQ_BULKRPT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_a = 0, h_m = 0, h_s = 0, h_c = 0, h_g = 0, h_net = 0;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT count(*) FILTER (WHERE status = 'A'), count(*) FILTER (WHERE status = 'M'),
count(*) FILTER (WHERE status = 'S'), count(*) FILTER (WHERE status = 'C'),
coalesce(sum(amount),0), coalesce(sum(net) FILTER (WHERE status = 'S'),0)
INTO :h_a, :h_m, :h_s, :h_c, :h_g, :h_net FROM purchase WHERE biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_ARG1, h_a);
setl(b, T_ARG2, h_m);
setl(b, T_ARG3, h_s);
setl(b, T_ARG4, h_c);
setl(b, T_GROSS, h_g);
setl(b, T_NET, h_net);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_CB_ARB.h - ac ACQ_CB_ARB (copybook / record header).
* : 2 . CB
*/
#ifndef ACQ_CB_ARB_H
#define ACQ_CB_ARB_H
#include "ac_svc.h"
typedef struct {
long cancel_id;
long purchase_id;
long rc;
} acq_cb_arb_rec_t;
void ACQ_CB_ARB(TPSVCINFO *p);
#endif /* ACQ_CB_ARB_H */

View file

@ -0,0 +1,36 @@
/*
* ACQ_CB_ARB.pgc - ac 모듈 서비스 ACQ_CB_ARB (one service = one file).
* 지불거절 중재: 라운드2까지 미해결 건을 카드사 중재로 넘긴다. 해당 CB
* 행 사유를 ARB 로 갱신하고 원매입을 보류(H)로 유지한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_CB_ARB.h"
void ACQ_CB_ARB(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char note[80];
EXEC SQL BEGIN DECLARE SECTION;
long h_cid, h_pid = 0;
char h_reason[100];
EXEC SQL END DECLARE SECTION;
h_cid = getl(b, T_CANCEL_ID);
gets_(b, T_REASON, note, sizeof(note));
snprintf(h_reason, sizeof(h_reason), "ARB:%s", note[0] ? note : "PENDING");
EXEC SQL SELECT purchase_id INTO :h_pid FROM ac_cancel
WHERE cancel_id = :h_cid AND cancel_type IN ('CB', 'CB2');
if (sqlca.sqlcode == 100) { setl(b, T_RC, 4); FAIL(b); }
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL UPDATE ac_cancel SET reason = :h_reason WHERE cancel_id = :h_cid;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL UPDATE purchase SET status = 'H', updated_at = now()
WHERE purchase_id = :h_pid AND status <> 'C';
if (sqlca.sqlcode < 0) { userlog("ACQ_CB_ARB FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
setl(b, T_PURCHASE_ID, h_pid);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_CB_FINAL.h - ac ACQ_CB_FINAL (copybook / record header).
* : (C)
*/
#ifndef ACQ_CB_FINAL_H
#define ACQ_CB_FINAL_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long affected;
long rc;
} acq_cb_final_rec_t;
void ACQ_CB_FINAL(TPSVCINFO *p);
#endif /* ACQ_CB_FINAL_H */

View file

@ -0,0 +1,26 @@
/*
* ACQ_CB_FINAL.pgc - ac 모듈 서비스 ACQ_CB_FINAL (one service = one file).
* 지불거절 최종확정: 중재 결과로 매입사 패소가 확정되면 원매입을 취소(C)로
* 확정 전이하고, 확정 여부(반영 건수)를 결과코드로 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_CB_FINAL.h"
void ACQ_CB_FINAL(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_aff;
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL UPDATE purchase SET status = 'C', updated_at = now()
WHERE purchase_id = :h_pid AND status = 'H';
if (sqlca.sqlcode < 0) { userlog("ACQ_CB_FINAL FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
h_aff = sqlca.sqlerrd[2];
if (h_aff > 0) Bchg(b, T_STATUS, 0, "C", 0L);
setl(b, T_COUNT, h_aff);
setl(b, T_RC, h_aff > 0 ? 0 : 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_CB_REVERSE.h - ac ACQ_CB_REVERSE (copybook / record header).
* : CB . CB/CB2
*/
#ifndef ACQ_CB_REVERSE_H
#define ACQ_CB_REVERSE_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long cancel_id;
long restored;
} acq_cb_reverse_rec_t;
void ACQ_CB_REVERSE(TPSVCINFO *p);
#endif /* ACQ_CB_REVERSE_H */

View file

@ -0,0 +1,33 @@
/*
* ACQ_CB_REVERSE.pgc - ac 모듈 서비스 ACQ_CB_REVERSE (one service = one file).
* 지불거절 역전: 방어 승소로 CB 를 무효화한다. 최근 CB/CB2 행을 조회해
* 취소 사유를 REVERSED 로 바꾸고 보류 원매입을 대사완료(M)로 복원한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_CB_REVERSE.h"
void ACQ_CB_REVERSE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long restored;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_cid = 0, h_aff;
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL SELECT max(cancel_id) INTO :h_cid FROM ac_cancel
WHERE purchase_id = :h_pid AND cancel_type IN ('CB', 'CB2');
if (sqlca.sqlcode < 0 || h_cid == 0) { setl(b, T_RC, 4); FAIL(b); }
EXEC SQL UPDATE ac_cancel SET reason = 'REVERSED' WHERE cancel_id = :h_cid;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL UPDATE purchase SET status = 'M', updated_at = now()
WHERE purchase_id = :h_pid AND status = 'H';
if (sqlca.sqlcode < 0) { userlog("ACQ_CB_REVERSE FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
h_aff = sqlca.sqlerrd[2];
restored = h_aff;
setl(b, T_CANCEL_ID, h_cid);
setl(b, T_RC, restored > 0 ? 0 : 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_CB_RND2.h - ac ACQ_CB_RND2 (copybook / record header).
* 2(2): 1 .
*/
#ifndef ACQ_CB_RND2_H
#define ACQ_CB_RND2_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long cancel_id;
long prev_cb;
} acq_cb_rnd2_rec_t;
void ACQ_CB_RND2(TPSVCINFO *p);
#endif /* ACQ_CB_RND2_H */

View file

@ -0,0 +1,38 @@
/*
* ACQ_CB_RND2.pgc - ac 모듈 서비스 ACQ_CB_RND2 (one service = one file).
* 지불거절 2차(라운드2): 1차 방어 이후 재청구를 접수한다. 원매입에 이미
* 1차 CB 이력이 있어야 하며, ac_cancel 에 CB2 유형 행을 추가한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_CB_RND2.h"
void ACQ_CB_RND2(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char why[80];
EXEC SQL BEGIN DECLARE SECTION;
long h_id, h_pid, h_amt, h_prev = 0;
char h_reason[100], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
h_amt = getl(b, T_AMOUNT);
gets_(b, T_REASON, why, sizeof(why));
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT count(*) INTO :h_prev FROM ac_cancel
WHERE purchase_id = :h_pid AND cancel_type = 'CB';
if (sqlca.sqlcode < 0) FAIL(b);
if (h_prev == 0) { setl(b, T_RC, 4); FAIL(b); }
snprintf(h_reason, sizeof(h_reason), "CB2:%s", why[0] ? why : "REPRESENT");
EXEC SQL SELECT nextval('ac_cancel_seq') INTO :h_id;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO ac_cancel (cancel_id, purchase_id, cancel_type, amount, reason, biz_date)
VALUES (:h_id, :h_pid, 'CB2', :h_amt, :h_reason, :h_bizdate);
if (sqlca.sqlcode < 0) { userlog("ACQ_CB_RND2 FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
setl(b, T_CANCEL_ID, h_id);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_CCYQRY.h - ac ACQ_CCYQRY (copybook / record header).
* : (T_CCY)
*/
#ifndef ACQ_CCYQRY_H
#define ACQ_CCYQRY_H
#include "ac_svc.h"
typedef struct {
char ccy[8];
long fx_amount;
long krw_amount;
} acq_ccyqry_rec_t;
void ACQ_CCYQRY(TPSVCINFO *p);
#endif /* ACQ_CCYQRY_H */

View file

@ -0,0 +1,29 @@
/*
* ACQ_CCYQRY.pgc - ac 모듈 서비스 ACQ_CCYQRY (one service = one file).
* 매입 통화별 조회: 지정 통화(T_CCY)의 원통화 합계와 원화 환산 합계를
* 해외매입 원장에서 조회해 환산차(원화-원통화)를 계산한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_CCYQRY.h"
void ACQ_CCYQRY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long diff;
EXEC SQL BEGIN DECLARE SECTION;
long h_fx = 0, h_krw = 0;
char h_ccy[8];
EXEC SQL END DECLARE SECTION;
gets_(b, T_CCY, h_ccy, sizeof(h_ccy));
EXEC SQL SELECT coalesce(sum(fx_amount),0), coalesce(sum(krw_amount),0)
INTO :h_fx, :h_krw FROM ac_fx WHERE ccy = :h_ccy;
if (sqlca.sqlcode < 0) FAIL(b);
diff = h_krw - h_fx;
setl(b, T_FX_AMT, h_fx);
setl(b, T_GROSS, h_krw);
setl(b, T_DELTA, diff);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_CCYRANK.h - ac ACQ_CCYRANK (copybook / record header).
* :
*/
#ifndef ACQ_CCYRANK_H
#define ACQ_CCYRANK_H
#include "ac_svc.h"
typedef struct {
char top_ccy[8];
long ranks;
long krw;
} acq_ccyrank_rec_t;
void ACQ_CCYRANK(TPSVCINFO *p);
#endif /* ACQ_CCYRANK_H */

View file

@ -0,0 +1,40 @@
/*
* ACQ_CCYRANK.pgc - ac 모듈 서비스 ACQ_CCYRANK (one service = one file).
* 매입 통화별 순위: 해외매입 통화 그룹을 원화 합계 내림차순으로 커서 조회해
* 상위 통화코드·원화액 쌍을 occurrence 로 내리고 1위 통화를 표시한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_CCYRANK.h"
void ACQ_CCYRANK(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long rank = 0;
char top[8];
EXEC SQL BEGIN DECLARE SECTION;
long h_krw;
char h_ccy[8], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
top[0] = 0;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL DECLARE ccr_cur CURSOR FOR
SELECT ccy, sum(krw_amount) FROM ac_fx WHERE biz_date = :h_bizdate
GROUP BY ccy ORDER BY sum(krw_amount) DESC;
EXEC SQL OPEN ccr_cur;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH ccr_cur INTO :h_ccy, :h_krw;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ccr_cur; FAIL(b); }
if (rank == 0) { strncpy(top, h_ccy, sizeof(top)-1); top[sizeof(top)-1] = 0; }
if (rank < 5) { Badd(b, T_CCY, h_ccy, 0L); Badd(b, T_AMT1, (char *)&h_krw, 0L); }
rank++;
}
EXEC SQL CLOSE ccr_cur;
if (top[0]) Bchg(b, T_STR1, 0, top, 0L);
setl(b, T_ARG1, rank);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_CHANDETAIL.h - ac ACQ_CHANDETAIL (copybook / record header).
* : //
*/
#ifndef ACQ_CHANDETAIL_H
#define ACQ_CHANDETAIL_H
#include "ac_svc.h"
typedef struct {
char channel[16];
long avg_amount;
long kinds;
} acq_chandetail_rec_t;
void ACQ_CHANDETAIL(TPSVCINFO *p);
#endif /* ACQ_CHANDETAIL_H */

View file

@ -0,0 +1,39 @@
/*
* ACQ_CHANDETAIL.pgc - ac 모듈 서비스 ACQ_CHANDETAIL (one service = one file).
* 매입 채널별 상세 조회: 영업일 채널 그룹의 건수/총액/평균을 커서로 훑어
* 채널명·평균단가 쌍을 occurrence 로 내리고 전체 평균을 함께 반환한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_CHANDETAIL.h"
void ACQ_CHANDETAIL(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long tot = 0, cnt = 0, kinds = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_n, h_sum, h_avg;
char h_chan[16], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL DECLARE chd_cur CURSOR FOR
SELECT channel, count(*), sum(amount), avg(amount)::bigint FROM purchase
WHERE biz_date = :h_bizdate AND status <> 'C' GROUP BY channel ORDER BY channel;
EXEC SQL OPEN chd_cur;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH chd_cur INTO :h_chan, :h_n, :h_sum, :h_avg;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE chd_cur; FAIL(b); }
if (kinds < 8) { Badd(b, T_STR1, h_chan, 0L); Badd(b, T_AMT1, (char *)&h_avg, 0L); }
tot += h_sum; cnt += h_n; kinds++;
}
EXEC SQL CLOSE chd_cur;
setl(b, T_GROSS, tot);
setl(b, T_COUNT, cnt);
setl(b, T_ARG1, kinds);
setl(b, T_AMT2, cnt > 0 ? tot / cnt : 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_CHANRANK.h - ac ACQ_CHANRANK (copybook / record header).
* :
*/
#ifndef ACQ_CHANRANK_H
#define ACQ_CHANRANK_H
#include "ac_svc.h"
typedef struct {
char busiest[16];
long count;
long ranks;
} acq_chanrank_rec_t;
void ACQ_CHANRANK(TPSVCINFO *p);
#endif /* ACQ_CHANRANK_H */

View file

@ -0,0 +1,41 @@
/*
* ACQ_CHANRANK.pgc - ac 모듈 서비스 ACQ_CHANRANK (one service = one file).
* 매입 채널별 순위: 영업일 채널을 건수 내림차순으로 정렬해 커서 조회하고
* 채널명·건수 쌍을 occurrence 로 내려 최다 유입 채널을 판별한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_CHANRANK.h"
void ACQ_CHANRANK(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long rank = 0, busiest = 0;
char topchan[16];
EXEC SQL BEGIN DECLARE SECTION;
long h_n;
char h_chan[16], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
topchan[0] = 0;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL DECLARE chr_cur CURSOR FOR
SELECT channel, count(*) FROM purchase WHERE biz_date = :h_bizdate
GROUP BY channel ORDER BY count(*) DESC, channel;
EXEC SQL OPEN chr_cur;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH chr_cur INTO :h_chan, :h_n;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE chr_cur; FAIL(b); }
if (rank == 0) { strncpy(topchan, h_chan, sizeof(topchan)-1); topchan[sizeof(topchan)-1] = 0; busiest = h_n; }
if (rank < 6) { Badd(b, T_CHANNEL, h_chan, 0L); Badd(b, T_ARG3, (char *)&h_n, 0L); }
rank++;
}
EXEC SQL CLOSE chr_cur;
if (topchan[0]) Bchg(b, T_STR2, 0, topchan, 0L);
setl(b, T_COUNT, busiest);
setl(b, T_ARG1, rank);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_CORRAPPR.h - ac ACQ_CORRAPPR (copybook / record header).
* : (ac_fee_adj) OK
*/
#ifndef ACQ_CORRAPPR_H
#define ACQ_CORRAPPR_H
#include "ac_svc.h"
typedef struct {
long adj_id;
long affected;
long rc;
} acq_corrappr_rec_t;
void ACQ_CORRAPPR(TPSVCINFO *p);
#endif /* ACQ_CORRAPPR_H */

View file

@ -0,0 +1,30 @@
/*
* ACQ_CORRAPPR.pgc - ac 모듈 서비스 ACQ_CORRAPPR (one service = one file).
* 매입 정정 승인: 접수된 조정(ac_fee_adj) 행의 사유 앞에 OK 승인 표식을
* 덧대어 확정한다. 이미 승인된 건은 중복 승인하지 않는다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_CORRAPPR.h"
void ACQ_CORRAPPR(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_id, h_aff;
char h_reason[128];
EXEC SQL END DECLARE SECTION;
h_id = getl(b, T_ADJ);
EXEC SQL SELECT reason INTO :h_reason FROM ac_fee_adj WHERE adj_id = :h_id;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 5); FAIL(b); }
if (sqlca.sqlcode < 0) FAIL(b);
if (strncmp(h_reason, "OK:", 3) == 0) { setl(b, T_RC, 2); FAIL(b); }
EXEC SQL UPDATE ac_fee_adj SET reason = 'OK:' || reason WHERE adj_id = :h_id;
if (sqlca.sqlcode < 0) { userlog("ACQ_CORRAPPR FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
h_aff = sqlca.sqlerrd[2];
setl(b, T_ADJ, h_id);
setl(b, T_RC, h_aff > 0 ? 0 : 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_CORRRSN.h - ac ACQ_CORRRSN (copybook / record header).
* : (ac_fee_adj)
*/
#ifndef ACQ_CORRRSN_H
#define ACQ_CORRRSN_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long adj_id;
long delta;
} acq_corrrsn_rec_t;
void ACQ_CORRRSN(TPSVCINFO *p);
#endif /* ACQ_CORRRSN_H */

View file

@ -0,0 +1,34 @@
/*
* ACQ_CORRRSN.pgc - ac 모듈 서비스 ACQ_CORRRSN (one service = one file).
* 매입 정정 사유별 처리: 수수료 조정 원장(ac_fee_adj)에 사유 코드가 붙은
* 증감 행을 남긴다. 증감액(T_DELTA)과 사유(T_REASON)를 그대로 기록한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_CORRRSN.h"
void ACQ_CORRRSN(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char why[80];
EXEC SQL BEGIN DECLARE SECTION;
long h_id, h_pid, h_delta;
char h_reason[100], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
h_delta = getl(b, T_DELTA);
gets_(b, T_REASON, why, sizeof(why));
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
if (h_delta == 0) { setl(b, T_RC, 3); FAIL(b); }
snprintf(h_reason, sizeof(h_reason), "CORR:%s", why[0] ? why : "MISC");
EXEC SQL SELECT nextval('ac_feeadj_seq') INTO :h_id;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO ac_fee_adj (adj_id, purchase_id, delta, reason, biz_date)
VALUES (:h_id, :h_pid, :h_delta, :h_reason, :h_bizdate);
if (sqlca.sqlcode < 0) { userlog("ACQ_CORRRSN FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
setl(b, T_ADJ, h_id);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_CORRSTAT.h - ac ACQ_CORRSTAT (copybook / record header).
* : ·
*/
#ifndef ACQ_CORRSTAT_H
#define ACQ_CORRSTAT_H
#include "ac_svc.h"
typedef struct {
char reason[64];
long net;
long kinds;
} acq_corrstat_rec_t;
void ACQ_CORRSTAT(TPSVCINFO *p);
#endif /* ACQ_CORRSTAT_H */

View file

@ -0,0 +1,37 @@
/*
* ACQ_CORRSTAT.pgc - ac 모듈 서비스 ACQ_CORRSTAT (one service = one file).
* 매입 정정 사유별 통계: 조정 원장을 사유 접두어별로 그룹 집계해 사유코드·
* 증감합계 쌍을 occurrence 로 내리고 순증감 총액을 계산한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_CORRSTAT.h"
void ACQ_CORRSTAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long net = 0, kinds = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_sum;
char h_reason[64], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL DECLARE crs_cur CURSOR FOR
SELECT split_part(reason, ':', 1), sum(delta) FROM ac_fee_adj
WHERE biz_date = :h_bizdate GROUP BY split_part(reason, ':', 1) ORDER BY 1;
EXEC SQL OPEN crs_cur;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH crs_cur INTO :h_reason, :h_sum;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE crs_cur; FAIL(b); }
if (kinds < 8) { Badd(b, T_REASON, h_reason, 0L); Badd(b, T_DELTA, (char *)&h_sum, 0L); }
net += h_sum; kinds++;
}
EXEC SQL CLOSE crs_cur;
setl(b, T_GROSS, net);
setl(b, T_ARG1, kinds);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_CTRYQRY.h - ac ACQ_CTRYQRY (copybook / record header).
* : (T_CCY) (ac_fx)
*/
#ifndef ACQ_CTRYQRY_H
#define ACQ_CTRYQRY_H
#include "ac_svc.h"
typedef struct {
char ccy[8];
long krw;
long rate_bps;
} acq_ctryqry_rec_t;
void ACQ_CTRYQRY(TPSVCINFO *p);
#endif /* ACQ_CTRYQRY_H */

View file

@ -0,0 +1,29 @@
/*
* ACQ_CTRYQRY.pgc - ac 모듈 서비스 ACQ_CTRYQRY (one service = one file).
* 매입 국가별 조회: 통화권역 대표 통화(T_CCY)로 해외매입(ac_fx)을 필터해
* 권역 원화 합계와 건수를 조회하고 평균 환율(bps)을 함께 낸다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_CTRYQRY.h"
void ACQ_CTRYQRY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_n = 0, h_krw = 0, h_rate = 0;
char h_ccy[8], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_CCY, h_ccy, sizeof(h_ccy));
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT count(*), coalesce(sum(krw_amount),0), coalesce(avg(rate_bps),0)::bigint
INTO :h_n, :h_krw, :h_rate FROM ac_fx
WHERE ccy = :h_ccy AND biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_n);
setl(b, T_GROSS, h_krw);
setl(b, T_ARG1, h_rate);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_CTRYRANK.h - ac ACQ_CTRYRANK (copybook / record header).
* : (///) C
*/
#ifndef ACQ_CTRYRANK_H
#define ACQ_CTRYRANK_H
#include "ac_svc.h"
typedef struct {
long buckets[4];
long best;
long rc;
} acq_ctryrank_rec_t;
void ACQ_CTRYRANK(TPSVCINFO *p);
#endif /* ACQ_CTRYRANK_H */

View file

@ -0,0 +1,49 @@
/*
* ACQ_CTRYRANK.pgc - ac 모듈 서비스 ACQ_CTRYRANK (one service = one file).
* 매입 국가별 순위: 통화코드를 권역(미주/일본/유럽/기타)으로 분류하는 C
* 분류기로 원화 합계를 권역 버킷에 모아 최대 권역 인덱스를 판정한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_CTRYRANK.h"
/* 통화 → 권역 인덱스 (0=미주 1=일본 2=유럽 3=기타) */
static int ac_region(const char *c)
{
if (strcmp(c, "USD") == 0 || strcmp(c, "CAD") == 0) return 0;
if (strcmp(c, "JPY") == 0) return 1;
if (strcmp(c, "EUR") == 0 || strcmp(c, "GBP") == 0) return 2;
return 3;
}
void ACQ_CTRYRANK(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long bucket[4] = {0, 0, 0, 0};
int i, best = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_krw;
char h_ccy[8], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL DECLARE ctr_rk CURSOR FOR
SELECT ccy, sum(krw_amount) FROM ac_fx WHERE biz_date = :h_bizdate GROUP BY ccy;
EXEC SQL OPEN ctr_rk;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH ctr_rk INTO :h_ccy, :h_krw;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE ctr_rk; FAIL(b); }
bucket[ac_region(h_ccy)] += h_krw;
}
EXEC SQL CLOSE ctr_rk;
for (i = 1; i < 4; i++) if (bucket[i] > bucket[best]) best = i;
setl(b, T_ARG1, bucket[0]);
setl(b, T_ARG2, bucket[1]);
setl(b, T_ARG3, bucket[2]);
setl(b, T_ARG4, bucket[3]);
setl(b, T_RC, best);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_DOWSTAT.h - ac ACQ_DOWSTAT (copybook / record header).
* : (T_STR1~T_STR2) (0~6)
*/
#ifndef ACQ_DOWSTAT_H
#define ACQ_DOWSTAT_H
#include "ac_svc.h"
typedef struct {
long total;
long weekend;
long rc;
} acq_dowstat_rec_t;
void ACQ_DOWSTAT(TPSVCINFO *p);
#endif /* ACQ_DOWSTAT_H */

View file

@ -0,0 +1,41 @@
/*
* ACQ_DOWSTAT.pgc - ac 모듈 서비스 ACQ_DOWSTAT (one service = one file).
* 매입 요일별 통계: 지정 기간(T_STR1~T_STR2)의 매입을 요일(0~6)별로 그룹
* 집계해 요일번호·건수 쌍을 occurrence 로 내리고 주말 비중을 계산한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_DOWSTAT.h"
void ACQ_DOWSTAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long total = 0, weekend = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_dow, h_n;
char h_from[16], h_to[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_STR1, h_from, sizeof(h_from));
gets_(b, T_STR2, h_to, sizeof(h_to));
EXEC SQL DECLARE dow_cur CURSOR FOR
SELECT extract(dow from biz_date)::int, count(*) FROM purchase
WHERE biz_date BETWEEN :h_from AND :h_to GROUP BY 1 ORDER BY 1;
EXEC SQL OPEN dow_cur;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH dow_cur INTO :h_dow, :h_n;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE dow_cur; FAIL(b); }
Badd(b, T_ARG5, (char *)&h_dow, 0L);
Badd(b, T_ARG6, (char *)&h_n, 0L);
total += h_n;
if (h_dow == 0 || h_dow == 6) weekend += h_n;
}
EXEC SQL CLOSE dow_cur;
setl(b, T_COUNT, total);
setl(b, T_ARG1, weekend);
setl(b, T_RC, total > 0 ? 0 : 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_EDIQRY.h - ac ACQ_EDIQRY (copybook / record header).
* EDI : (T_MERCHANT) EDI
*/
#ifndef ACQ_EDIQRY_H
#define ACQ_EDIQRY_H
#include "ac_svc.h"
typedef struct {
char merchant[64];
long count;
long sum;
} acq_ediqry_rec_t;
void ACQ_EDIQRY(TPSVCINFO *p);
#endif /* ACQ_EDIQRY_H */

View file

@ -0,0 +1,28 @@
/*
* ACQ_EDIQRY.pgc - ac 모듈 서비스 ACQ_EDIQRY (one service = one file).
* EDI 조회: 지정 가맹점(T_MERCHANT)의 영업일 EDI 접수 문서 건수와 금액
* 합계를 조회해 응답한다. 접수분이 없으면 결과코드 1.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_EDIQRY.h"
void ACQ_EDIQRY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_n = 0, h_sum = 0;
char h_merch[64], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, h_merch, sizeof(h_merch));
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT count(*), coalesce(sum(amount),0) INTO :h_n, :h_sum
FROM ac_edi WHERE merchant_id = :h_merch AND biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_n);
setl(b, T_GROSS, h_sum);
setl(b, T_RC, h_n > 0 ? 0 : 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_EDIRSNDCH.h - ac ACQ_EDIRSNDCH (copybook / record header).
* EDI : EDI
*/
#ifndef ACQ_EDIRSNDCH_H
#define ACQ_EDIRSNDCH_H
#include "ac_svc.h"
typedef struct {
char doc_no[32];
long count;
long total;
} acq_edirsndch_rec_t;
void ACQ_EDIRSNDCH(TPSVCINFO *p);
#endif /* ACQ_EDIRSNDCH_H */

View file

@ -0,0 +1,37 @@
/*
* ACQ_EDIRSNDCH.pgc - ac 모듈 서비스 ACQ_EDIRSNDCH (one service = one file).
* EDI 재전송 채널별: 영업일 미수신 EDI 문서를 가맹점별로 커서 조회해
* 문서번호·금액 쌍을 occurrence 로 내리고 재전송 대상 건수를 집계한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_EDIRSNDCH.h"
void ACQ_EDIRSNDCH(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long n = 0, tot = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_amt;
char h_doc[32], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL DECLARE edr_cur CURSOR FOR
SELECT doc_no, amount FROM ac_edi WHERE biz_date = :h_bizdate ORDER BY doc_no;
EXEC SQL OPEN edr_cur;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH edr_cur INTO :h_doc, :h_amt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE edr_cur; FAIL(b); }
if (n < 8) { Badd(b, T_DOCNO, h_doc, 0L); Badd(b, T_AMT1, (char *)&h_amt, 0L); }
n++; tot += h_amt;
}
EXEC SQL CLOSE edr_cur;
setl(b, T_COUNT, n);
setl(b, T_GROSS, tot);
setl(b, T_RC, n > 0 ? 0 : 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_EDISTAT.h - ac ACQ_EDISTAT (copybook / record header).
* EDI : EDI (//)
*/
#ifndef ACQ_EDISTAT_H
#define ACQ_EDISTAT_H
#include "ac_svc.h"
typedef struct {
long lo;
long mid;
long hi;
} acq_edistat_rec_t;
void ACQ_EDISTAT(TPSVCINFO *p);
#endif /* ACQ_EDISTAT_H */

View file

@ -0,0 +1,30 @@
/*
* ACQ_EDISTAT.pgc - ac 모듈 서비스 ACQ_EDISTAT (one service = one file).
* EDI 통계: 영업일 EDI 문서를 금액대(소액/중액/고액)로 나눠 대역별 건수를
* 한 번의 집계에서 산출하고 전체 문서 금액 합계를 함께 낸다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_EDISTAT.h"
void ACQ_EDISTAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_lo = 0, h_mid = 0, h_hi = 0, h_sum = 0;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT count(*) FILTER (WHERE amount < 100000),
count(*) FILTER (WHERE amount >= 100000 AND amount < 1000000),
count(*) FILTER (WHERE amount >= 1000000), coalesce(sum(amount),0)
INTO :h_lo, :h_mid, :h_hi, :h_sum FROM ac_edi WHERE biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_ARG1, h_lo);
setl(b, T_ARG2, h_mid);
setl(b, T_ARG3, h_hi);
setl(b, T_GROSS, h_sum);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_ERRCLASS.h - ac ACQ_ERRCLASS (copybook / record header).
* : (ac_unmatch)
*/
#ifndef ACQ_ERRCLASS_H
#define ACQ_ERRCLASS_H
#include "ac_svc.h"
typedef struct {
long classes[4];
long count;
long rc;
} acq_errclass_rec_t;
void ACQ_ERRCLASS(TPSVCINFO *p);
#endif /* ACQ_ERRCLASS_H */

View file

@ -0,0 +1,46 @@
/*
* ACQ_ERRCLASS.pgc - ac 모듈 서비스 ACQ_ERRCLASS (one service = one file).
* 매입 오류유형별 분류: 미매입 원장(ac_unmatch)의 사유 접두어를 읽어
* 중복/한도/환율/기타 4개 오류군으로 분류하고 군별 건수를 집계한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_ERRCLASS.h"
/* 사유 접두어 → 오류군 (0=중복 1=한도 2=환율 3=기타) */
static int ac_errkind(const char *r)
{
if (strncmp(r, "DUP", 3) == 0) return 0;
if (strncmp(r, "LIMIT", 5) == 0) return 1;
if (strncmp(r, "FX", 2) == 0) return 2;
return 3;
}
void ACQ_ERRCLASS(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long cls[4] = {0, 0, 0, 0};
EXEC SQL BEGIN DECLARE SECTION;
char h_reason[64], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL DECLARE erc_cur CURSOR FOR
SELECT reason FROM ac_unmatch WHERE biz_date = :h_bizdate;
EXEC SQL OPEN erc_cur;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH erc_cur INTO :h_reason;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE erc_cur; FAIL(b); }
cls[ac_errkind(h_reason)]++;
}
EXEC SQL CLOSE erc_cur;
setl(b, T_ARG1, cls[0]);
setl(b, T_ARG2, cls[1]);
setl(b, T_ARG3, cls[2]);
setl(b, T_ARG4, cls[3]);
setl(b, T_COUNT, cls[0] + cls[1] + cls[2] + cls[3]);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_ERRQRY.h - ac ACQ_ERRQRY (copybook / record header).
* : (T_REASON)
*/
#ifndef ACQ_ERRQRY_H
#define ACQ_ERRQRY_H
#include "ac_svc.h"
typedef struct {
char pattern[64];
long count;
long rc;
} acq_errqry_rec_t;
void ACQ_ERRQRY(TPSVCINFO *p);
#endif /* ACQ_ERRQRY_H */

View file

@ -0,0 +1,28 @@
/*
* ACQ_ERRQRY.pgc - ac 모듈 서비스 ACQ_ERRQRY (one service = one file).
* 매입 오류유형별 조회: 사유 접두어(T_REASON)로 시작하는 미매입 건을
* 패턴 조회해 해당 오류유형의 건수를 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_ERRQRY.h"
void ACQ_ERRQRY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char pat[64];
EXEC SQL BEGIN DECLARE SECTION;
long h_n = 0;
char h_like[70];
EXEC SQL END DECLARE SECTION;
gets_(b, T_REASON, pat, sizeof(pat));
if (pat[0] == 0) strcpy(pat, "DUP");
snprintf(h_like, sizeof(h_like), "%s%%", pat);
EXEC SQL SELECT count(*) INTO :h_n FROM ac_unmatch WHERE reason LIKE :h_like;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_n);
setl(b, T_RC, h_n > 0 ? 0 : 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_ERRRETRY.h - ac ACQ_ERRRETRY (copybook / record header).
* : (H) (A)
*/
#ifndef ACQ_ERRRETRY_H
#define ACQ_ERRRETRY_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long affected;
long rc;
} acq_errretry_rec_t;
void ACQ_ERRRETRY(TPSVCINFO *p);
#endif /* ACQ_ERRRETRY_H */

View file

@ -0,0 +1,25 @@
/*
* ACQ_ERRRETRY.pgc - ac 모듈 서비스 ACQ_ERRRETRY (one service = one file).
* 매입 오류 재처리: 보류(H) 상태로 묶여 있던 오류 매입을 접수(A)로 되돌려
* 재처리 큐에 태우고 반영 건수를 결과코드로 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_ERRRETRY.h"
void ACQ_ERRRETRY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_aff;
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL UPDATE purchase SET status = 'A', updated_at = now()
WHERE purchase_id = :h_pid AND status = 'H';
if (sqlca.sqlcode < 0) { userlog("ACQ_ERRRETRY FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
h_aff = sqlca.sqlerrd[2];
if (h_aff > 0) Bchg(b, T_STATUS, 0, "A", 0L);
setl(b, T_RC, h_aff > 0 ? 0 : 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_ERRSTAT.h - ac ACQ_ERRSTAT (copybook / record header).
* :
*/
#ifndef ACQ_ERRSTAT_H
#define ACQ_ERRSTAT_H
#include "ac_svc.h"
typedef struct {
long dup;
long limit;
long fx;
} acq_errstat_rec_t;
void ACQ_ERRSTAT(TPSVCINFO *p);
#endif /* ACQ_ERRSTAT_H */

View file

@ -0,0 +1,30 @@
/*
* ACQ_ERRSTAT.pgc - ac 모듈 서비스 ACQ_ERRSTAT (one service = one file).
* 매입 오류유형별 통계: 미매입 원장을 사유 접두어 기준으로 한 번의 집계에서
* 중복/한도/환율 오류 건수를 FILTER 로 분해해 동시에 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_ERRSTAT.h"
void ACQ_ERRSTAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_dup = 0, h_lim = 0, h_fx = 0, h_all = 0;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT count(*) FILTER (WHERE reason LIKE 'DUP%'),
count(*) FILTER (WHERE reason LIKE 'LIMIT%'),
count(*) FILTER (WHERE reason LIKE 'FX%'), count(*)
INTO :h_dup, :h_lim, :h_fx, :h_all FROM ac_unmatch WHERE biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_ARG1, h_dup);
setl(b, T_ARG2, h_lim);
setl(b, T_ARG3, h_fx);
setl(b, T_COUNT, h_all);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_ESCROW.h - ac ACQ_ESCROW (copybook / record header).
* :
*/
#ifndef ACQ_ESCROW_H
#define ACQ_ESCROW_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long ledger_id;
long amount;
} acq_escrow_rec_t;
void ACQ_ESCROW(TPSVCINFO *p);
#endif /* ACQ_ESCROW_H */

View file

@ -0,0 +1,31 @@
/*
* ACQ_ESCROW.pgc - ac 모듈 서비스 ACQ_ESCROW (one service = one file).
* 매입 에스크로 예치: 분쟁 소지 매입 금액을 정산 전 유보하기 위해 원장에
* ESCROW 항목으로 예치액을 적립한다. 금액이 0 이하면 거절.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_ESCROW.h"
void ACQ_ESCROW(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_amt, h_lid;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
h_amt = getl(b, T_AMOUNT);
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
if (h_amt <= 0) { setl(b, T_RC, 3); FAIL(b); }
EXEC SQL SELECT nextval('ledger_seq') INTO :h_lid;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date)
VALUES (:h_lid, :h_pid, 'ESCROW', :h_amt, :h_bizdate);
if (sqlca.sqlcode < 0) { userlog("ACQ_ESCROW FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
setl(b, T_SETTLE_ID, h_lid);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_ESCROWREL.h - ac ACQ_ESCROWREL (copybook / record header).
* : (ESCROW) (ESCROW_REL)
*/
#ifndef ACQ_ESCROWREL_H
#define ACQ_ESCROWREL_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long balance;
long rc;
} acq_escrowrel_rec_t;
void ACQ_ESCROWREL(TPSVCINFO *p);
#endif /* ACQ_ESCROWREL_H */

View file

@ -0,0 +1,36 @@
/*
* ACQ_ESCROWREL.pgc - ac 모듈 서비스 ACQ_ESCROWREL (one service = one file).
* 매입 에스크로 해제: 예치(ESCROW)와 해제(ESCROW_REL) 원장 잔액을 계산해
* 잔여 예치금을 해제 항목으로 전액 반제하고 잔액을 0 으로 만든다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_ESCROWREL.h"
void ACQ_ESCROWREL(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_esc = 0, h_rel = 0, h_lid, h_bal;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT coalesce(sum(amount) FILTER (WHERE entry_type = 'ESCROW'),0),
coalesce(sum(amount) FILTER (WHERE entry_type = 'ESCROW_REL'),0)
INTO :h_esc, :h_rel FROM ledger WHERE purchase_id = :h_pid;
if (sqlca.sqlcode < 0) FAIL(b);
h_bal = h_esc - h_rel;
if (h_bal <= 0) { setl(b, T_GROSS, 0); setl(b, T_RC, 1); OK(b); }
EXEC SQL SELECT nextval('ledger_seq') INTO :h_lid;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date)
VALUES (:h_lid, :h_pid, 'ESCROW_REL', :h_bal, :h_bizdate);
if (sqlca.sqlcode < 0) { userlog("ACQ_ESCROWREL FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
setl(b, T_GROSS, h_bal);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_EVENTBUY.h - ac ACQ_EVENTBUY (copybook / record header).
* : EVENT .
*/
#ifndef ACQ_EVENTBUY_H
#define ACQ_EVENTBUY_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long fee;
long net;
} acq_eventbuy_rec_t;
void ACQ_EVENTBUY(TPSVCINFO *p);
#endif /* ACQ_EVENTBUY_H */

View file

@ -0,0 +1,41 @@
/*
* ACQ_EVENTBUY.pgc - ac 모듈 서비스 ACQ_EVENTBUY (one service = one file).
* 이벤트 매입: 프로모션 행사 매입을 EVENT 채널로 접수한다. 활성 가맹점만
* 허용하며 이벤트 할인율(T_ARG1, bps)만큼 수수료를 감면해 net 을 올린다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_EVENTBUY.h"
void ACQ_EVENTBUY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long fee, disc;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_amt, h_bps = 0, h_disc, h_fee, h_net;
char h_merch[64], h_mst[16], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
h_amt = getl(b, T_AMOUNT);
disc = getl(b, T_ARG1); if (disc < 0) disc = 0;
gets_(b, T_MERCHANT, h_merch, sizeof(h_merch));
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
if (h_amt <= 0) { setl(b, T_RC, 3); FAIL(b); }
EXEC SQL SELECT status, mdr_bps INTO :h_mst, :h_bps FROM merchant WHERE merchant_id = :h_merch;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 5); FAIL(b); }
if (sqlca.sqlcode < 0) FAIL(b);
if (strcmp(h_mst, "ACTIVE") != 0) { setl(b, T_RC, 6); FAIL(b); }
h_disc = (h_bps > disc) ? h_bps - disc : 0;
fee = h_amt * h_disc / 10000; h_fee = fee; h_net = h_amt - fee;
EXEC SQL SELECT nextval('purchase_seq') INTO :h_pid;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO purchase (purchase_id, merchant_id, amount, fee, net, status, biz_date, channel, category)
VALUES (:h_pid, :h_merch, :h_amt, :h_fee, :h_net, 'A', :h_bizdate, 'EVENT', 'PROMO');
if (sqlca.sqlcode < 0) { userlog("ACQ_EVENTBUY FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
setl(b, T_PURCHASE_ID, h_pid);
setl(b, T_FEE, h_fee);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_EXCHANGE.h - ac ACQ_EXCHANGE (copybook / record header).
* : .
*/
#ifndef ACQ_EXCHANGE_H
#define ACQ_EXCHANGE_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long cancel_id;
long amount;
} acq_exchange_rec_t;
void ACQ_EXCHANGE(TPSVCINFO *p);
#endif /* ACQ_EXCHANGE_H */

View file

@ -0,0 +1,38 @@
/*
* ACQ_EXCHANGE.pgc - ac 모듈 서비스 ACQ_EXCHANGE (one service = one file).
* 매입 교환 처리: 반품 후 동일가 재구매를 한 번에 처리한다. 원매입에 대해
* EXCHANGE 취소 행을 남기고 원매입 상태를 대사완료(M)로 유지한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_EXCHANGE.h"
void ACQ_EXCHANGE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char why[80];
EXEC SQL BEGIN DECLARE SECTION;
long h_id, h_pid, h_amt, h_orig = 0;
char h_reason[100], h_st[4], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
h_amt = getl(b, T_AMOUNT);
gets_(b, T_REASON, why, sizeof(why));
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT status, amount INTO :h_st, :h_orig FROM purchase WHERE purchase_id = :h_pid;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 5); FAIL(b); }
if (sqlca.sqlcode < 0) FAIL(b);
if (h_amt <= 0 || h_amt > h_orig) { setl(b, T_RC, 3); FAIL(b); }
snprintf(h_reason, sizeof(h_reason), "EXCHANGE:%s", why[0] ? why : "SWAP");
EXEC SQL SELECT nextval('ac_cancel_seq') INTO :h_id;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO ac_cancel (cancel_id, purchase_id, cancel_type, amount, reason, biz_date)
VALUES (:h_id, :h_pid, 'EXCHANGE', :h_amt, :h_reason, :h_bizdate);
if (sqlca.sqlcode < 0) { userlog("ACQ_EXCHANGE FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
setl(b, T_CANCEL_ID, h_id);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_GRADESTAT.h - ac ACQ_GRADESTAT (copybook / record header).
* : MDR(bps) //
*/
#ifndef ACQ_GRADESTAT_H
#define ACQ_GRADESTAT_H
#include "ac_svc.h"
typedef struct {
long grades[3];
long gross;
long rc;
} acq_gradestat_rec_t;
void ACQ_GRADESTAT(TPSVCINFO *p);
#endif /* ACQ_GRADESTAT_H */

View file

@ -0,0 +1,47 @@
/*
* ACQ_GRADESTAT.pgc - ac 모듈 서비스 ACQ_GRADESTAT (one service = one file).
* 매입 가맹점등급별 통계: 가맹점 MDR(bps)을 프리미엄/일반/기본 등급으로 나눠
* 등급별 매출 합계를 버킷 집계한다. 등급 경계는 C 분류기로 판정한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_GRADESTAT.h"
/* MDR(bps) → 등급 (0=프리미엄 1=일반 2=기본) */
static int ac_grade(long bps)
{
if (bps <= 20) return 0;
if (bps <= 28) return 1;
return 2;
}
void ACQ_GRADESTAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long g[3] = {0, 0, 0};
EXEC SQL BEGIN DECLARE SECTION;
long h_bps, h_sum;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL DECLARE grd_cur CURSOR FOR
SELECT m.mdr_bps, coalesce(sum(p.amount),0) FROM merchant m
JOIN purchase p ON p.merchant_id = m.merchant_id AND p.biz_date = :h_bizdate
GROUP BY m.merchant_id, m.mdr_bps;
EXEC SQL OPEN grd_cur;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH grd_cur INTO :h_bps, :h_sum;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE grd_cur; FAIL(b); }
g[ac_grade(h_bps)] += h_sum;
}
EXEC SQL CLOSE grd_cur;
setl(b, T_ARG1, g[0]);
setl(b, T_ARG2, g[1]);
setl(b, T_ARG3, g[2]);
setl(b, T_GROSS, g[0] + g[1] + g[2]);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_GUARANTEE.h - ac ACQ_GUARANTEE (copybook / record header).
* : GUARANTEE
*/
#ifndef ACQ_GUARANTEE_H
#define ACQ_GUARANTEE_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long guarantee;
long ledger_id;
} acq_guarantee_rec_t;
void ACQ_GUARANTEE(TPSVCINFO *p);
#endif /* ACQ_GUARANTEE_H */

View file

@ -0,0 +1,34 @@
/*
* ACQ_GUARANTEE.pgc - ac 모듈 서비스 ACQ_GUARANTEE (one service = one file).
* 매입 보증 등록: 대형 매입에 대한 이행보증금을 원장에 GUARANTEE 항목으로
* 적립한다. 보증률(T_ARG1, bps)로 보증금을 산출하며 기본 10%.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_GUARANTEE.h"
void ACQ_GUARANTEE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_amt, h_bps, h_lid, h_guar;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
h_amt = getl(b, T_AMOUNT);
h_bps = getl(b, T_ARG1); if (h_bps <= 0) h_bps = 1000;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
h_guar = h_amt * h_bps / 10000;
if (h_guar <= 0) { setl(b, T_RC, 3); FAIL(b); }
EXEC SQL SELECT nextval('ledger_seq') INTO :h_lid;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date)
VALUES (:h_lid, :h_pid, 'GUARANTEE', :h_guar, :h_bizdate);
if (sqlca.sqlcode < 0) { userlog("ACQ_GUARANTEE FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
setl(b, T_AMT1, h_guar);
setl(b, T_SETTLE_ID, h_lid);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_GUARQRY.h - ac ACQ_GUARQRY (copybook / record header).
* : (GUARANTEE)
*/
#ifndef ACQ_GUARQRY_H
#define ACQ_GUARQRY_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long guarantee;
long count;
} acq_guarqry_rec_t;
void ACQ_GUARQRY(TPSVCINFO *p);
#endif /* ACQ_GUARQRY_H */

View file

@ -0,0 +1,26 @@
/*
* ACQ_GUARQRY.pgc - ac 모듈 서비스 ACQ_GUARQRY (one service = one file).
* 매입 보증 조회: 한 매입에 적립된 보증금(GUARANTEE) 총액과 건수를 원장에서
* 조회해 응답한다. 보증 이력이 없으면 결과코드 1.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_GUARQRY.h"
void ACQ_GUARQRY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_sum = 0, h_n = 0;
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL SELECT coalesce(sum(amount),0), count(*) INTO :h_sum, :h_n
FROM ledger WHERE purchase_id = :h_pid AND entry_type = 'GUARANTEE';
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_AMT1, h_sum);
setl(b, T_COUNT, h_n);
setl(b, T_RC, h_n > 0 ? 0 : 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_HALFSTAT.h - ac ACQ_HALFSTAT (copybook / record header).
* : (T_ARG1) (1~6)/(7~12)
*/
#ifndef ACQ_HALFSTAT_H
#define ACQ_HALFSTAT_H
#include "ac_svc.h"
typedef struct {
long h1;
long h2;
long share;
} acq_halfstat_rec_t;
void ACQ_HALFSTAT(TPSVCINFO *p);
#endif /* ACQ_HALFSTAT_H */

View file

@ -0,0 +1,32 @@
/*
* ACQ_HALFSTAT.pgc - ac 모듈 서비스 ACQ_HALFSTAT (one service = one file).
* 매입 반기별 통계: 지정 연도(T_ARG1) 매입을 상반기(1~6월)/하반기(7~12월)로
* 나눠 반기별 매출과 건수를 산출하고 상반기 비중을 계산한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_HALFSTAT.h"
void ACQ_HALFSTAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long share = 0, total;
EXEC SQL BEGIN DECLARE SECTION;
long h_h1 = 0, h_h2 = 0, h_n = 0;
int h_yr;
EXEC SQL END DECLARE SECTION;
h_yr = (int)getl(b, T_ARG1); if (h_yr < 2000) h_yr = 2026;
EXEC SQL SELECT coalesce(sum(amount) FILTER (WHERE extract(month from biz_date) <= 6),0),
coalesce(sum(amount) FILTER (WHERE extract(month from biz_date) > 6),0), count(*)
INTO :h_h1, :h_h2, :h_n FROM purchase WHERE extract(year from biz_date) = :h_yr;
if (sqlca.sqlcode < 0) FAIL(b);
total = h_h1 + h_h2;
if (total > 0) share = h_h1 * 100 / total;
setl(b, T_ARG2, h_h1);
setl(b, T_ARG3, h_h2);
setl(b, T_COUNT, h_n);
setl(b, T_RC, share);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_HOURSTAT.h - ac ACQ_HOURSTAT (copybook / record header).
* : ///
*/
#ifndef ACQ_HOURSTAT_H
#define ACQ_HOURSTAT_H
#include "ac_svc.h"
typedef struct {
long bands[4];
long peak;
long rc;
} acq_hourstat_rec_t;
void ACQ_HOURSTAT(TPSVCINFO *p);
#endif /* ACQ_HOURSTAT_H */

View file

@ -0,0 +1,35 @@
/*
* ACQ_HOURSTAT.pgc - ac 모듈 서비스 ACQ_HOURSTAT (one service = one file).
* 매입 시간대별 통계: 접수시각을 새벽/오전/오후/야간 네 구간으로 나눠 구간별
* 매입 건수를 한 번의 집계로 분해하고 피크 구간 인덱스를 판정한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_HOURSTAT.h"
void ACQ_HOURSTAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long v[4]; int i, peak = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_d = 0, h_m = 0, h_a = 0, h_n = 0;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT count(*) FILTER (WHERE extract(hour from created_at) < 6),
count(*) FILTER (WHERE extract(hour from created_at) BETWEEN 6 AND 11),
count(*) FILTER (WHERE extract(hour from created_at) BETWEEN 12 AND 17),
count(*) FILTER (WHERE extract(hour from created_at) >= 18)
INTO :h_d, :h_m, :h_a, :h_n FROM purchase WHERE biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
v[0] = h_d; v[1] = h_m; v[2] = h_a; v[3] = h_n;
for (i = 1; i < 4; i++) if (v[i] > v[peak]) peak = i;
setl(b, T_ARG1, h_d);
setl(b, T_ARG2, h_m);
setl(b, T_ARG3, h_a);
setl(b, T_ARG4, h_n);
setl(b, T_RC, peak);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_INSTCXL.h - ac ACQ_INSTCXL (copybook / record header).
* : .
*/
#ifndef ACQ_INSTCXL_H
#define ACQ_INSTCXL_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long deleted;
long rc;
} acq_instcxl_rec_t;
void ACQ_INSTCXL(TPSVCINFO *p);
#endif /* ACQ_INSTCXL_H */

View file

@ -0,0 +1,24 @@
/*
* ACQ_INSTCXL.pgc - ac 모듈 서비스 ACQ_INSTCXL (one service = one file).
* 할부 취소: 한 매입의 할부 스케줄 전 회차를 원장에서 삭제해 할부를 해지한다.
* 삭제된 회차수를 건수로 응답하며, 없으면 결과코드 1.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_INSTCXL.h"
void ACQ_INSTCXL(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_del;
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL DELETE FROM ac_install WHERE purchase_id = :h_pid;
if (sqlca.sqlcode < 0) { userlog("ACQ_INSTCXL FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
h_del = sqlca.sqlerrd[2];
setl(b, T_COUNT, h_del);
setl(b, T_RC, h_del > 0 ? 0 : 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_INSTPLAN.h - ac ACQ_INSTPLAN (copybook / record header).
* : ,
*/
#ifndef ACQ_INSTPLAN_H
#define ACQ_INSTPLAN_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long months;
long total;
} acq_instplan_rec_t;
void ACQ_INSTPLAN(TPSVCINFO *p);
#endif /* ACQ_INSTPLAN_H */

View file

@ -0,0 +1,28 @@
/*
* ACQ_INSTPLAN.pgc - ac 모듈 서비스 ACQ_INSTPLAN (one service = one file).
* 할부 계획 조회: 한 매입의 약정 개월수와 회차 합계, 회차당 금액을 단건
* 조회해 계획 개요를 응답한다. 할부가 없으면 결과코드 1.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_INSTPLAN.h"
void ACQ_INSTPLAN(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_months = 0, h_sum = 0, h_first = 0;
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL SELECT coalesce(max(months),0), coalesce(sum(month_amount),0),
coalesce(min(month_amount),0)
INTO :h_months, :h_sum, :h_first FROM ac_install WHERE purchase_id = :h_pid;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_INSTALL_N, h_months);
setl(b, T_GROSS, h_sum);
setl(b, T_AMT1, h_first);
setl(b, T_RC, h_months > 0 ? 0 : 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_INSTPREPAY.h - ac ACQ_INSTPREPAY (copybook / record header).
* : (T_ARG1)
*/
#ifndef ACQ_INSTPREPAY_H
#define ACQ_INSTPREPAY_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long remaining;
long count;
} acq_instprepay_rec_t;
void ACQ_INSTPREPAY(TPSVCINFO *p);
#endif /* ACQ_INSTPREPAY_H */

View file

@ -0,0 +1,36 @@
/*
* ACQ_INSTPREPAY.pgc - ac 모듈 서비스 ACQ_INSTPREPAY (one service = one file).
* 할부 조기상환: 지정 회차(T_ARG1) 이후 남은 할부금 합계를 계산해 원장에
* PREPAY 항목으로 선지급을 기록하고 잔여 회차수를 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_INSTPREPAY.h"
void ACQ_INSTPREPAY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_from, h_left = 0, h_cnt = 0, h_lid;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
h_from = getl(b, T_ARG1);
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT coalesce(sum(month_amount),0), count(*) INTO :h_left, :h_cnt
FROM ac_install WHERE purchase_id = :h_pid AND seq_no > :h_from;
if (sqlca.sqlcode < 0) FAIL(b);
if (h_cnt == 0) { setl(b, T_RC, 1); OK(b); }
EXEC SQL SELECT nextval('ledger_seq') INTO :h_lid;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO ledger (ledger_id, purchase_id, entry_type, amount, biz_date)
VALUES (:h_lid, :h_pid, 'PREPAY', :h_left, :h_bizdate);
if (sqlca.sqlcode < 0) { userlog("ACQ_INSTPREPAY FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
setl(b, T_GROSS, h_left);
setl(b, T_COUNT, h_cnt);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_INSTQRY.h - ac ACQ_INSTQRY (copybook / record header).
* : (ac_install)
*/
#ifndef ACQ_INSTQRY_H
#define ACQ_INSTQRY_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long months;
long total;
} acq_instqry_rec_t;
void ACQ_INSTQRY(TPSVCINFO *p);
#endif /* ACQ_INSTQRY_H */

View file

@ -0,0 +1,38 @@
/*
* ACQ_INSTQRY.pgc - ac 모듈 서비스 ACQ_INSTQRY (one service = one file).
* 할부 회차조회: 한 매입의 할부 스케줄(ac_install)을 회차 순으로 커서 조회해
* 회차·회차금액 쌍을 occurrence 로 내리고 총 회차수와 합계를 계산한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_INSTQRY.h"
void ACQ_INSTQRY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long n = 0, tot = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_seq, h_mamt;
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL DECLARE isq_cur CURSOR FOR
SELECT seq_no, month_amount FROM ac_install WHERE purchase_id = :h_pid
ORDER BY seq_no;
EXEC SQL OPEN isq_cur;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH isq_cur INTO :h_seq, :h_mamt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE isq_cur; FAIL(b); }
Badd(b, T_ARG5, (char *)&h_seq, 0L);
Badd(b, T_AMT1, (char *)&h_mamt, 0L);
n++; tot += h_mamt;
}
EXEC SQL CLOSE isq_cur;
setl(b, T_INSTALL_N, n);
setl(b, T_GROSS, tot);
setl(b, T_RC, n > 0 ? 0 : 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_LIMITEXC.h - ac ACQ_LIMITEXC (copybook / record header).
* :
*/
#ifndef ACQ_LIMITEXC_H
#define ACQ_LIMITEXC_H
#include "ac_svc.h"
typedef struct {
char merchant[64];
long room;
long rc;
} acq_limitexc_rec_t;
void ACQ_LIMITEXC(TPSVCINFO *p);
#endif /* ACQ_LIMITEXC_H */

View file

@ -0,0 +1,40 @@
/*
* ACQ_LIMITEXC.pgc - ac 모듈 서비스 ACQ_LIMITEXC (one service = one file).
* 매입 한도 예외처리: 가맹점 일한도와 당일 누적 매입을 비교해 한도 초과 시
* 미매입 원장에 LIMIT_EXC 예외를 기록하고, 여유 한도를 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_LIMITEXC.h"
void ACQ_LIMITEXC(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long room;
EXEC SQL BEGIN DECLARE SECTION;
long h_id, h_lim = 0, h_used = 0;
char h_merch[64], h_bizdate[16], h_reason[64];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, h_merch, sizeof(h_merch));
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT daily_limit INTO :h_lim FROM merchant WHERE merchant_id = :h_merch;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 5); FAIL(b); }
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT coalesce(sum(amount),0) INTO :h_used FROM purchase
WHERE merchant_id = :h_merch AND biz_date = :h_bizdate AND status <> 'C';
if (sqlca.sqlcode < 0) FAIL(b);
room = h_lim - h_used;
if (room >= 0) { setl(b, T_DELTA, room); setl(b, T_RC, 0); OK(b); }
snprintf(h_reason, sizeof(h_reason), "LIMIT_EXC:%ld", -room);
EXEC SQL SELECT nextval('ac_unmatch_seq') INTO :h_id;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO ac_unmatch (unmatch_id, purchase_id, reason, biz_date)
VALUES (:h_id, 0, :h_reason, :h_bizdate);
if (sqlca.sqlcode < 0) { userlog("ACQ_LIMITEXC FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
setl(b, T_DELTA, room);
setl(b, T_RC, 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_LIMITOVR.h - ac ACQ_LIMITOVR (copybook / record header).
* : (H)
*/
#ifndef ACQ_LIMITOVR_H
#define ACQ_LIMITOVR_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long affected;
long rc;
} acq_limitovr_rec_t;
void ACQ_LIMITOVR(TPSVCINFO *p);
#endif /* ACQ_LIMITOVR_H */

View file

@ -0,0 +1,26 @@
/*
* ACQ_LIMITOVR.pgc - ac 모듈 서비스 ACQ_LIMITOVR (one service = one file).
* 매입 한도 초과 승인: 한도 초과로 보류(H)된 매입을 관리자가 예외 승인해
* 접수(A)로 되살린다. 승인 표식(net 유지)만 남기고 반영 건수를 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_LIMITOVR.h"
void ACQ_LIMITOVR(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_aff;
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL UPDATE purchase SET status = 'A', updated_at = now()
WHERE purchase_id = :h_pid AND status = 'H' AND channel <> 'RSV';
if (sqlca.sqlcode < 0) { userlog("ACQ_LIMITOVR FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
h_aff = sqlca.sqlerrd[2];
if (h_aff > 0) Bchg(b, T_STATUS, 0, "A", 0L);
setl(b, T_COUNT, h_aff);
setl(b, T_RC, h_aff > 0 ? 0 : 2);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_PROMOQRY.h - ac ACQ_PROMOQRY (copybook / record header).
* : (category=PROMO) ,
*/
#ifndef ACQ_PROMOQRY_H
#define ACQ_PROMOQRY_H
#include "ac_svc.h"
typedef struct {
long count;
long gross;
long fee;
} acq_promoqry_rec_t;
void ACQ_PROMOQRY(TPSVCINFO *p);
#endif /* ACQ_PROMOQRY_H */

View file

@ -0,0 +1,29 @@
/*
* ACQ_PROMOQRY.pgc - ac 모듈 서비스 ACQ_PROMOQRY (one service = one file).
* 프로모션 조회: 영업일 프로모션(category=PROMO) 매입의 건수와 매출, 감면
* 수수료 합계를 조회해 행사 성과를 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_PROMOQRY.h"
void ACQ_PROMOQRY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_n = 0, h_sum = 0, h_fee = 0;
char h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL SELECT count(*), coalesce(sum(amount),0), coalesce(sum(fee),0)
INTO :h_n, :h_sum, :h_fee FROM purchase
WHERE category = 'PROMO' AND biz_date = :h_bizdate;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_n);
setl(b, T_GROSS, h_sum);
setl(b, T_FEE, h_fee);
setl(b, T_RC, h_n > 0 ? 0 : 1);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_PROMOSTAT.h - ac ACQ_PROMOSTAT (copybook / record header).
* : / ·
*/
#ifndef ACQ_PROMOSTAT_H
#define ACQ_PROMOSTAT_H
#include "ac_svc.h"
typedef struct {
char category[32];
long grand;
long promo;
} acq_promostat_rec_t;
void ACQ_PROMOSTAT(TPSVCINFO *p);
#endif /* ACQ_PROMOSTAT_H */

View file

@ -0,0 +1,40 @@
/*
* ACQ_PROMOSTAT.pgc - ac 모듈 서비스 ACQ_PROMOSTAT (one service = one file).
* 프로모션 통계: 영업일 업종/분류별 매출을 그룹 집계해 분류코드·매출 쌍을
* occurrence 로 내리고 프로모션 분류가 차지하는 비중을 계산한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_PROMOSTAT.h"
void ACQ_PROMOSTAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long grand = 0, promo = 0, kinds = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_sum;
char h_cat[32], h_bizdate[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_BIZDATE, h_bizdate, sizeof(h_bizdate));
EXEC SQL DECLARE pms_cur CURSOR FOR
SELECT category, sum(amount) FROM purchase WHERE biz_date = :h_bizdate
GROUP BY category ORDER BY sum(amount) DESC;
EXEC SQL OPEN pms_cur;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH pms_cur INTO :h_cat, :h_sum;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE pms_cur; FAIL(b); }
if (kinds < 8) { Badd(b, T_CATEGORY, h_cat, 0L); Badd(b, T_AMT1, (char *)&h_sum, 0L); }
grand += h_sum;
if (strcmp(h_cat, "PROMO") == 0) promo = h_sum;
kinds++;
}
EXEC SQL CLOSE pms_cur;
setl(b, T_GROSS, grand);
setl(b, T_AMT2, promo);
setl(b, T_RC, grand > 0 ? promo * 100 / grand : 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_QTRSTAT.h - ac ACQ_QTRSTAT (copybook / record header).
* : (T_ARG1) 1~4
*/
#ifndef ACQ_QTRSTAT_H
#define ACQ_QTRSTAT_H
#include "ac_svc.h"
typedef struct {
long year;
long q[4];
long gross;
} acq_qtrstat_rec_t;
void ACQ_QTRSTAT(TPSVCINFO *p);
#endif /* ACQ_QTRSTAT_H */

View file

@ -0,0 +1,35 @@
/*
* ACQ_QTRSTAT.pgc - ac 모듈 서비스 ACQ_QTRSTAT (one service = one file).
* 매입 분기별 통계: 지정 연도(T_ARG1)의 매입 매출을 1~4분기로 나눠 분기별
* 합계를 한 번의 집계로 산출하고 연간 총매출을 함께 응답한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_QTRSTAT.h"
void ACQ_QTRSTAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long yr;
EXEC SQL BEGIN DECLARE SECTION;
long h_q1 = 0, h_q2 = 0, h_q3 = 0, h_q4 = 0;
int h_yr;
EXEC SQL END DECLARE SECTION;
yr = getl(b, T_ARG1); if (yr < 2000) yr = 2026;
h_yr = (int)yr;
EXEC SQL SELECT coalesce(sum(amount) FILTER (WHERE extract(quarter from biz_date) = 1),0),
coalesce(sum(amount) FILTER (WHERE extract(quarter from biz_date) = 2),0),
coalesce(sum(amount) FILTER (WHERE extract(quarter from biz_date) = 3),0),
coalesce(sum(amount) FILTER (WHERE extract(quarter from biz_date) = 4),0)
INTO :h_q1, :h_q2, :h_q3, :h_q4 FROM purchase
WHERE extract(year from biz_date) = :h_yr;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_ARG2, h_q1);
setl(b, T_ARG3, h_q2);
setl(b, T_ARG4, h_q3);
setl(b, T_ARG5, h_q4);
setl(b, T_GROSS, h_q1 + h_q2 + h_q3 + h_q4);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_REBUYAPPR.h - ac ACQ_REBUYAPPR (copybook / record header).
* : (C) (approval)
*/
#ifndef ACQ_REBUYAPPR_H
#define ACQ_REBUYAPPR_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long approval_id;
long rc;
} acq_rebuyappr_rec_t;
void ACQ_REBUYAPPR(TPSVCINFO *p);
#endif /* ACQ_REBUYAPPR_H */

View file

@ -0,0 +1,34 @@
/*
* ACQ_REBUYAPPR.pgc - ac 모듈 서비스 ACQ_REBUYAPPR (one service = one file).
* 재매입 승인: 취소(C)된 원매입에 한해 승인 원장(approval)에 재승인 행을
* 남기고 원매입을 접수(A)로 되살려 재매입을 개시한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_REBUYAPPR.h"
void ACQ_REBUYAPPR(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_aid;
char st[4];
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL SELECT status INTO :st FROM purchase WHERE purchase_id = :h_pid;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 5); FAIL(b); }
if (sqlca.sqlcode < 0) FAIL(b);
if (st[0] != 'C') { setl(b, T_RC, 2); FAIL(b); }
EXEC SQL SELECT nextval('approval_seq') INTO :h_aid;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO approval (approval_id, purchase_id, approved) VALUES (:h_aid, :h_pid, true);
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL UPDATE purchase SET status = 'A', updated_at = now() WHERE purchase_id = :h_pid;
if (sqlca.sqlcode < 0) { userlog("ACQ_REBUYAPPR FAIL [%d] %s", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); FAIL(b); }
setl(b, T_ID1, h_aid);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,18 @@
/*
* ACQ_REBUYHIST.h - ac ACQ_REBUYHIST (copybook / record header).
* : ID를
*/
#ifndef ACQ_REBUYHIST_H
#define ACQ_REBUYHIST_H
#include "ac_svc.h"
typedef struct {
long purchase_id;
long count;
long rc;
} acq_rebuyhist_rec_t;
void ACQ_REBUYHIST(TPSVCINFO *p);
#endif /* ACQ_REBUYHIST_H */

View file

@ -0,0 +1,37 @@
/*
* ACQ_REBUYHIST.pgc - ac 모듈 서비스 ACQ_REBUYHIST (one service = one file).
* 재매입 이력: 한 매입의 승인 원장을 승인시각 순으로 커서 조회해 승인 ID를
* occurrence 로 내리고 총 승인 횟수와 최근 승인 여부를 계산한다.
* Runs on the caller's XA branch (no EXEC SQL CONNECT). Advertised by ac_svr.
*/
#include "ACQ_REBUYHIST.h"
void ACQ_REBUYHIST(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
long n = 0; int last = 0;
EXEC SQL BEGIN DECLARE SECTION;
long h_pid, h_aid;
int h_ok;
EXEC SQL END DECLARE SECTION;
h_pid = getl(b, T_PURCHASE_ID);
EXEC SQL DECLARE rbh_cur CURSOR FOR
SELECT approval_id, approved FROM approval WHERE purchase_id = :h_pid
ORDER BY approved_at;
EXEC SQL OPEN rbh_cur;
if (sqlca.sqlcode < 0) FAIL(b);
for (;;) {
EXEC SQL FETCH rbh_cur INTO :h_aid, :h_ok;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { EXEC SQL CLOSE rbh_cur; FAIL(b); }
Badd(b, T_ID1, (char *)&h_aid, 0L);
last = h_ok; n++;
}
EXEC SQL CLOSE rbh_cur;
setl(b, T_COUNT, n);
setl(b, T_RC, last ? 0 : 1);
OK(b);
}

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