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

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

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

View file

@ -0,0 +1,63 @@
/*
* mm_acct_reverify_batch.pgc - 정산계좌 정기 재검증 배치 (전수 검증 이력 적재 + XA).
*
* 활성 가맹점의 정산계좌를 커서로 전수 재검증하여 결과(OK/EMPTY/FORMAT)를
* 가맹점별 ACCT_VRF 검증 이력으로 남긴다 (온라인 MM_ACCT_VERIFY 와 동일 판정 기준).
* 전 구간 ONE global XA transaction.
* host 변수는 전용 카피북 mm_acct_reverify_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_acct_reverify_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "mm_dbio.h"
static const char *acct_verdict(const char *acct)
{
size_t i, len = strlen(acct);
if (len == 0) return "EMPTY";
if (len < 10 || len > 16) return "FORMAT";
for (i = 0; i < len; i++)
if (acct[i] < '0' || acct[i] > '9') return "FORMAT";
return "OK";
}
int main(int argc, char **argv)
{
long total = 0, failed = 0;
const char *result;
(void)argc; (void)argv;
EXEC SQL INCLUDE mm_acct_reverify_cpy;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE arv CURSOR FOR
SELECT merchant_id, settle_acct FROM mm_merchant_ext
WHERE status = 'ACTIVE'
ORDER BY merchant_id;
EXEC SQL OPEN arv;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH arv INTO :b_merch, :b_acct;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE arv; tpabort(0); return 1; }
result = acct_verdict(b_acct);
if (mmdb_insert_merch_hist(b_merch, "ACCT_VRF", "", result, "정기 배치 재검증") < 0) {
EXEC SQL CLOSE arv; tpabort(0); return 1;
}
total++;
if (strcmp(result, "OK") != 0) failed++;
}
EXEC SQL CLOSE arv;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_acct_reverify_batch COMMIT: 재검증=%ld건 불량=%ld건\n", total, failed);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,5 @@
/* mm_acct_reverify_cpy.h - mm_acct_reverify_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
char b_merch[64];
char b_acct[40];
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,55 @@
/*
* mm_apply_expire_batch.pgc - 장기 미처리 가입신청 자동반려 배치 (심사 워크플로 + XA).
*
* APPLIED/REVIEW 상태로 기한을 넘긴 신청 건을 커서로 훑어 REJECTED 로 전환하고
* 자동반려 사유를 이력(REJECT)으로 남긴다. 전 구간 ONE global XA transaction.
* host 변수는 전용 카피북 mm_apply_expire_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_apply_expire_batch [DAYS=30]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "mm_dbio.h"
int main(int argc, char **argv)
{
long expired = 0;
EXEC SQL INCLUDE mm_apply_expire_cpy;
b_days = (argc > 1) ? atol(argv[1]) : 30;
if (b_days < 1) b_days = 30;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE aex CURSOR FOR
SELECT merchant_id, status FROM mm_merchant_ext
WHERE status IN ('APPLIED', 'REVIEW')
AND updated_at < now() - :b_days * interval '1 day'
ORDER BY merchant_id;
EXEC SQL OPEN aex;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH aex INTO :b_merch, :b_stat;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE aex; tpabort(0); return 1; }
EXEC SQL UPDATE mm_merchant_ext SET status = 'REJECTED', updated_at = now()
WHERE merchant_id = :b_merch;
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPDATE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE aex; tpabort(0); return 1; }
if (mmdb_insert_merch_hist(b_merch, "REJECT", b_stat, "REJECTED", "심사기한 초과 자동반려") < 0) {
EXEC SQL CLOSE aex; tpabort(0); return 1;
}
expired++;
}
EXEC SQL CLOSE aex;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_apply_expire_batch COMMIT: 기한=%ld일 자동반려=%ld건\n", b_days, expired);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,6 @@
/* mm_apply_expire_cpy.h - mm_apply_expire_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
long b_days;
char b_merch[64];
char b_stat[16];
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,65 @@
/*
* mm_audit_compact_batch.pgc - 감사 이력 압축 배치 (요약행 생성 후 원본 삭제 + XA).
*
* 보존일수를 넘긴 가맹점별 이력이 임계 건수를 초과하면 건수를 담은 AUDIT_SUM
* 요약행 1건을 남기고 해당 구간의 원본 이력을 삭제해 mm_merch_hist 를 압축한다.
* (AUDIT_SUM 자신은 삭제 대상에서 제외.) 전 구간 ONE global XA transaction.
* host 변수는 전용 카피북 mm_audit_compact_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_audit_compact_batch [DAYS=365] [KEEP_THRESHOLD=10]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "mm_dbio.h"
int main(int argc, char **argv)
{
long merchants = 0, removed = 0;
char summary[96];
EXEC SQL INCLUDE mm_audit_compact_cpy;
b_days = (argc > 1) ? atol(argv[1]) : 365;
b_keep = (argc > 2) ? atol(argv[2]) : 10;
if (b_days < 30) b_days = 365;
if (b_keep < 1) b_keep = 10;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(120, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE acp CURSOR FOR
SELECT merchant_id, count(*) FROM mm_merch_hist
WHERE created_at < now() - :b_days * interval '1 day'
AND action <> 'AUDIT_SUM'
GROUP BY merchant_id
HAVING count(*) > :b_keep
ORDER BY merchant_id;
EXEC SQL OPEN acp;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH acp INTO :b_merch, :b_cnt;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE acp; tpabort(0); return 1; }
snprintf(summary, sizeof(summary), "보존기한 경과 이력 %ld건 압축 요약", b_cnt);
if (mmdb_insert_merch_hist(b_merch, "AUDIT_SUM", "", "", summary) < 0) {
EXEC SQL CLOSE acp; tpabort(0); return 1;
}
EXEC SQL DELETE FROM mm_merch_hist
WHERE merchant_id = :b_merch
AND created_at < now() - :b_days * interval '1 day'
AND action <> 'AUDIT_SUM';
if (sqlca.sqlcode < 0) { fprintf(stderr, "DELETE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE acp; tpabort(0); return 1; }
removed += sqlca.sqlerrd[2];
merchants++;
}
EXEC SQL CLOSE acp;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_audit_compact_batch COMMIT: 압축 가맹점=%ld 삭제 이력=%ld건\n", merchants, removed);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,7 @@
/* mm_audit_compact_cpy.h - mm_audit_compact_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
long b_days;
long b_keep;
char b_merch[64];
long b_cnt;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,68 @@
/*
* mm_bizno_audit_batch.pgc - 사업자등록번호 전수 점검 배치 (형식+검증공식 + XA).
*
* 활성 가맹점의 사업자번호를 커서로 전수 검사한다: 10자리 숫자 형식과
* 국세청 검증공식(가중치 1,3,7,1,3,7,1,3,5) checksum. 위반 건은 BIZNO_ERR
* 이력으로 적재한다. 전 구간 ONE global XA transaction.
* host 변수는 전용 카피북 mm_bizno_audit_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_bizno_audit_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "mm_dbio.h"
/* 0=정상, 1=형식 오류, 2=검증숫자 불일치 */
static int bizno_verdict(const char *no)
{
static const int w[9] = { 1, 3, 7, 1, 3, 7, 1, 3, 5 };
int i, sum = 0;
if (strlen(no) != 10) return 1;
for (i = 0; i < 10; i++) if (no[i] < '0' || no[i] > '9') return 1;
for (i = 0; i < 9; i++) sum += (no[i] - '0') * w[i];
sum += ((no[8] - '0') * 5) / 10;
return ((10 - sum % 10) % 10) == (no[9] - '0') ? 0 : 2;
}
int main(int argc, char **argv)
{
long checked = 0, bad = 0;
int verdict;
(void)argc; (void)argv;
EXEC SQL INCLUDE mm_bizno_audit_cpy;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE bza CURSOR FOR
SELECT merchant_id, biz_reg_no FROM mm_merchant_ext
WHERE status = 'ACTIVE' AND biz_reg_no <> ''
ORDER BY merchant_id;
EXEC SQL OPEN bza;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH bza INTO :b_merch, :b_bizno;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE bza; tpabort(0); return 1; }
checked++;
verdict = bizno_verdict(b_bizno);
if (verdict != 0) {
if (mmdb_insert_merch_hist(b_merch, "BIZNO_ERR", "", b_bizno,
verdict == 1 ? "사업자번호 형식 오류" : "사업자번호 검증숫자 불일치") < 0) {
EXEC SQL CLOSE bza; tpabort(0); return 1;
}
bad++;
}
}
EXEC SQL CLOSE bza;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_bizno_audit_batch COMMIT: 점검=%ld건 위반=%ld건\n", checked, bad);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,5 @@
/* mm_bizno_audit_cpy.h - mm_bizno_audit_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
char b_merch[64];
char b_bizno[16];
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,47 @@
/*
* mm_close_sweep_batch.pgc - 폐업 가맹점 부속 마스터 정리 배치 (IN-subquery 일괄 + XA).
*
* DELETED 가맹점에 딸린 부속 데이터를 일괄 정리한다:
* 미폐기 단말 SCRAPPED 전환, 수수료율 행 삭제, 한도 행 삭제.
* 커서 없이 서브쿼리 3방으로 처리. 전 구간 ONE global XA transaction.
* host 변수는 전용 카피북 mm_close_sweep_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_close_sweep_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
(void)argc; (void)argv;
EXEC SQL INCLUDE mm_close_sweep_cpy;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL UPDATE mm_terminal SET status = 'SCRAPPED'
WHERE status <> 'SCRAPPED'
AND merchant_id IN (SELECT merchant_id FROM mm_merchant_ext WHERE status = 'DELETED');
if (sqlca.sqlcode < 0) { fprintf(stderr, "TERM FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
b_terms = sqlca.sqlerrd[2];
EXEC SQL DELETE FROM mm_fee_rate
WHERE merchant_id IN (SELECT merchant_id FROM mm_merchant_ext WHERE status = 'DELETED');
if (sqlca.sqlcode < 0) { fprintf(stderr, "FEE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
b_fees = sqlca.sqlerrd[2];
EXEC SQL DELETE FROM mm_limit
WHERE merchant_id IN (SELECT merchant_id FROM mm_merchant_ext WHERE status = 'DELETED');
if (sqlca.sqlcode < 0) { fprintf(stderr, "LIMIT FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
b_limits = sqlca.sqlerrd[2];
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_close_sweep_batch COMMIT: 단말 폐기=%ld 요율 삭제=%ld 한도 삭제=%ld\n",
b_terms, b_fees, b_limits);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,6 @@
/* mm_close_sweep_cpy.h - mm_close_sweep_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
long b_terms;
long b_fees;
long b_limits;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,46 @@
/*
* mm_codemap_gc_batch.pgc - 비활성 코드 매핑 정리(GC) 배치 (집합 DELETE 2단 + XA).
*
* (1) MAP_* 네임스페이스의 INACTIVE 매핑 행을 즉시 삭제하고,
* (2) MERCH_REL 관계의 INACTIVE 행은 보존일수 경과분만 삭제한다.
* 커서 없이 집합 DELETE 두 번으로 처리, 삭제 건수는 sqlca.sqlerrd 로 집계.
* 전 구간 ONE global XA transaction.
* host 변수는 전용 카피북 mm_codemap_gc_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_codemap_gc_batch [REL_KEEP_DAYS=90]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
long map_gone, rel_gone;
EXEC SQL INCLUDE mm_codemap_gc_cpy;
b_days = (argc > 1) ? atol(argv[1]) : 90;
if (b_days < 1) b_days = 90;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DELETE FROM mm_code
WHERE substr(code_group, 1, 4) = 'MAP_' AND status = 'INACTIVE';
if (sqlca.sqlcode < 0) { fprintf(stderr, "DELETE(MAP) FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
map_gone = sqlca.sqlerrd[2];
EXEC SQL DELETE FROM mm_code
WHERE code_group = 'MERCH_REL' AND status = 'INACTIVE'
AND created_at < now() - :b_days * interval '1 day';
if (sqlca.sqlcode < 0) { fprintf(stderr, "DELETE(REL) FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
rel_gone = sqlca.sqlerrd[2];
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_codemap_gc_batch COMMIT: 매핑 삭제=%ld건 관계 삭제=%ld건 (보존 %ld일)\n",
map_gone, rel_gone, b_days);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,4 @@
/* mm_codemap_gc_cpy.h - mm_codemap_gc_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
long b_days;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,63 @@
/*
* mm_consist_report_batch.pgc - 마스터 정합성 종합 리포트 배치 (read-only 점검 5종 + XA).
*
* 마스터 전반의 불일치를 한 트랜잭션 스냅샷에서 집계해 리포트로 출력한다:
* 고아 단말 / 요율 없는 활성 가맹점 / 한도 없는 활성 가맹점 / 중복 사업자번호 /
* 심사 적체(APPLIED+REVIEW). 불일치가 하나라도 있으면 exit 2 (모니터링 연동용).
* host 변수는 전용 카피북 mm_consist_report_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_consist_report_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
long faults;
(void)argc; (void)argv;
EXEC SQL INCLUDE mm_consist_report_cpy;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL SELECT count(*) INTO :r_orphan_term FROM mm_terminal t
WHERE NOT EXISTS (SELECT 1 FROM mm_merchant_ext m WHERE m.merchant_id = t.merchant_id);
if (sqlca.sqlcode < 0) { fprintf(stderr, "CHK1 FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
EXEC SQL SELECT count(*) INTO :r_no_fee FROM mm_merchant_ext m
WHERE m.status = 'ACTIVE'
AND NOT EXISTS (SELECT 1 FROM mm_fee_rate f WHERE f.merchant_id = m.merchant_id);
if (sqlca.sqlcode < 0) { fprintf(stderr, "CHK2 FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
EXEC SQL SELECT count(*) INTO :r_no_limit FROM mm_merchant_ext m
WHERE m.status = 'ACTIVE'
AND NOT EXISTS (SELECT 1 FROM mm_limit l WHERE l.merchant_id = m.merchant_id);
if (sqlca.sqlcode < 0) { fprintf(stderr, "CHK3 FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
EXEC SQL SELECT count(*) INTO :r_dup_bizno FROM (
SELECT biz_reg_no FROM mm_merchant_ext
WHERE biz_reg_no <> '' AND status <> 'DELETED'
GROUP BY biz_reg_no HAVING count(*) > 1) d;
if (sqlca.sqlcode < 0) { fprintf(stderr, "CHK4 FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
EXEC SQL SELECT count(*) INTO :r_backlog FROM mm_merchant_ext
WHERE status IN ('APPLIED', 'REVIEW');
if (sqlca.sqlcode < 0) { fprintf(stderr, "CHK5 FAIL [%d]\n", sqlca.sqlcode); tpabort(0); return 1; }
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
faults = r_orphan_term + r_no_fee + r_no_limit + r_dup_bizno;
printf("=== mm 마스터 정합성 리포트 ===\n");
printf("고아 단말 : %ld\n", r_orphan_term);
printf("요율 없는 활성 가맹점: %ld\n", r_no_fee);
printf("한도 없는 활성 가맹점: %ld\n", r_no_limit);
printf("중복 사업자번호 : %ld\n", r_dup_bizno);
printf("심사 적체(참고) : %ld\n", r_backlog);
printf(">>> mm_consist_report_batch DONE: 불일치=%ld\n", faults);
tpclose(); tpterm();
return faults > 0 ? 2 : 0;
}

View file

@ -0,0 +1,8 @@
/* mm_consist_report_cpy.h - mm_consist_report_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
long r_orphan_term;
long r_no_fee;
long r_no_limit;
long r_dup_bizno;
long r_backlog;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,62 @@
/*
* mm_contract_renew_batch.pgc - 만기 도래 가맹 계약 자동갱신 배치 (GROUP BY 커서 + XA).
*
* 가맹점별 최종 계약 만기(after_val 최대값)가 오늘 이하인 건을 커서로 훑어
* 만기일 기준 개월수 연장한 새 CONTRACT 이력을 적재한다 (해지 이력이 뒤따르는
* 가맹점은 갱신 대상에서 제외). 전 구간 ONE global XA transaction.
* host 변수는 전용 카피북 mm_contract_renew_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_contract_renew_batch [MONTHS=12]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "mm_dbio.h"
int main(int argc, char **argv)
{
long renewed = 0;
EXEC SQL INCLUDE mm_contract_renew_cpy;
b_mon = (argc > 1) ? atol(argv[1]) : 12;
if (b_mon < 1 || b_mon > 120) b_mon = 12;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE crn CURSOR FOR
SELECT h.merchant_id, max(h.after_val)
FROM mm_merch_hist h
JOIN mm_merchant_ext m ON m.merchant_id = h.merchant_id AND m.status = 'ACTIVE'
WHERE h.action = 'CONTRACT'
AND NOT EXISTS (SELECT 1 FROM mm_merch_hist e
WHERE e.merchant_id = h.merchant_id AND e.action = 'CONT_END'
AND e.hist_id > h.hist_id)
GROUP BY h.merchant_id
HAVING max(h.after_val) <= to_char(current_date, 'YYYYMMDD')
ORDER BY h.merchant_id;
EXEC SQL OPEN crn;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH crn INTO :b_merch, :b_old;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE crn; tpabort(0); return 1; }
EXEC SQL SELECT to_char(to_date(:b_old, 'YYYYMMDD') + :b_mon * interval '1 month', 'YYYYMMDD')
INTO :b_new;
if (sqlca.sqlcode < 0) { fprintf(stderr, "CALC FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE crn; tpabort(0); return 1; }
if (mmdb_insert_merch_hist(b_merch, "CONTRACT", b_old, b_new, "만기 도래 배치 자동갱신") < 0) {
EXEC SQL CLOSE crn; tpabort(0); return 1;
}
renewed++;
}
EXEC SQL CLOSE crn;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_contract_renew_batch COMMIT: 연장=%ld개월 갱신=%ld건\n", b_mon, renewed);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,7 @@
/* mm_contract_renew_cpy.h - mm_contract_renew_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
long b_mon;
char b_merch[64];
char b_old[16];
char b_new[16];
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,76 @@
/*
* mm_grade_demote_batch.pgc - 매출 미달 가맹점 등급 강등 배치 (LEFT JOIN 집계 커서 + XA).
*
* 최근 N일 매출 합계가 현재 등급의 유지 기준(S>=5억, A>=1억, B>=1천만)에 미달하는
* 활성 가맹점을 한 단계씩 강등(S->A->B->C)하고 GRADE_DEM 이력을 남긴다.
* 승격은 하지 않는다 (승격은 mm_grade_recalc_batch 소관). ONE global XA transaction.
* host 변수는 전용 카피북 mm_grade_demote_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_grade_demote_batch [DAYS=90]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "mm_dbio.h"
/* 등급 유지 최소 매출 (원). 미달 시 한 단계 강등 */
static long keep_floor(const char *grade)
{
if (grade[0] == 'S') return 500000000L;
if (grade[0] == 'A') return 100000000L;
if (grade[0] == 'B') return 10000000L;
return 0;
}
static const char *one_down(const char *grade)
{
if (grade[0] == 'S') return "A";
if (grade[0] == 'A') return "B";
return "C";
}
int main(int argc, char **argv)
{
long demoted = 0;
EXEC SQL INCLUDE mm_grade_demote_cpy;
b_days = (argc > 1) ? atol(argv[1]) : 90;
if (b_days < 1) b_days = 90;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE gdm CURSOR FOR
SELECT me.merchant_id, me.grade, coalesce(sum(ms.gross_amount), 0)
FROM mm_merchant_ext me
LEFT JOIN merchant_summary ms
ON ms.merchant_id = me.merchant_id
AND ms.biz_date >= current_date - :b_days
WHERE me.status = 'ACTIVE' AND me.grade IN ('S', 'A', 'B')
GROUP BY me.merchant_id, me.grade
ORDER BY me.merchant_id;
EXEC SQL OPEN gdm;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH gdm INTO :b_merch, :b_grade, :b_gross;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE gdm; tpabort(0); return 1; }
if (b_gross >= keep_floor(b_grade)) continue;
strncpy(b_next, one_down(b_grade), sizeof(b_next)-1); b_next[sizeof(b_next)-1] = 0;
if (mmdb_update_merchant_grade(b_merch, b_next) < 0) { EXEC SQL CLOSE gdm; tpabort(0); return 1; }
if (mmdb_insert_merch_hist(b_merch, "GRADE_DEM", b_grade, b_next, "매출 미달 배치 강등") < 0) {
EXEC SQL CLOSE gdm; tpabort(0); return 1;
}
demoted++;
}
EXEC SQL CLOSE gdm;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_grade_demote_batch COMMIT: 기간=%ld일 강등=%ld건\n", b_days, demoted);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,8 @@
/* mm_grade_demote_cpy.h - mm_grade_demote_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
long b_days;
char b_merch[64];
char b_grade[8];
long b_gross;
char b_next[8];
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,56 @@
/*
* mm_mcc_reclass_batch.pgc - MCC 위험도 재분류 배치 (업종별 폐업률 기반 + XA).
*
* MCC 별 소속 가맹점의 폐업(DELETED) 비율로 위험도를 재산정한다:
* 신위험도 = 1 + (폐업수 * 4 / 전체수), 1~5 범위. 기존 값과 다를 때만 UPDATE.
* 전 구간 ONE global XA transaction.
* host 변수는 전용 카피북 mm_mcc_reclass_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_mcc_reclass_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
long scanned = 0, changed = 0;
(void)argc; (void)argv;
EXEC SQL INCLUDE mm_mcc_reclass_cpy;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE mrc CURSOR FOR
SELECT c.mcc, c.risk_level, count(me.merchant_id),
coalesce(sum(CASE WHEN me.status = 'DELETED' THEN 1 ELSE 0 END), 0)
FROM mm_mcc c
JOIN mm_merchant_ext me ON me.mcc = c.mcc
GROUP BY c.mcc, c.risk_level
ORDER BY c.mcc;
EXEC SQL OPEN mrc;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH mrc INTO :b_mcc, :b_risk, :b_total, :b_closed;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE mrc; tpabort(0); return 1; }
scanned++;
if (b_total <= 0) continue;
b_newrisk = 1 + (b_closed * 4) / b_total;
if (b_newrisk > 5) b_newrisk = 5;
if (b_newrisk == b_risk) continue;
EXEC SQL UPDATE mm_mcc SET risk_level = :b_newrisk WHERE mcc = :b_mcc;
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPDATE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE mrc; tpabort(0); return 1; }
changed++;
}
EXEC SQL CLOSE mrc;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_mcc_reclass_batch COMMIT: 점검 MCC=%ld 위험도 변경=%ld건\n", scanned, changed);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,8 @@
/* mm_mcc_reclass_cpy.h - mm_mcc_reclass_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
char b_mcc[8];
long b_total;
long b_closed;
long b_risk;
long b_newrisk;
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,58 @@
/*
* mm_partner_sync_batch.pgc - 제휴사 마스터-발급사 동기화 배치 (upsert 커서 + XA).
*
* 활성 발급사(mm_issuer)를 커서로 훑어 제휴사 코드(PARTNER 코드그룹)로 upsert 하고,
* 발급사 마스터에서 사라졌거나 비활성인 제휴사 코드는 INACTIVE 로 내린다.
* 전 구간 ONE global XA transaction.
* host 변수는 전용 카피북 mm_partner_sync_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_partner_sync_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
int main(int argc, char **argv)
{
long synced = 0, retired;
(void)argc; (void)argv;
EXEC SQL INCLUDE mm_partner_sync_cpy;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE psy CURSOR FOR
SELECT issuer_cd, issuer_name FROM mm_issuer
WHERE status = 'ACTIVE' ORDER BY issuer_cd;
EXEC SQL OPEN psy;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH psy INTO :b_cd, :b_nm;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE psy; tpabort(0); return 1; }
EXEC SQL INSERT INTO mm_code (code_group, code, code_name, status)
VALUES ('PARTNER', :b_cd, :b_nm, 'ACTIVE')
ON CONFLICT (code_group, code)
DO UPDATE SET code_name = EXCLUDED.code_name, status = 'ACTIVE';
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPSERT FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE psy; tpabort(0); return 1; }
synced++;
}
EXEC SQL CLOSE psy;
/* 발급사 마스터에 없는(또는 비활성) 제휴사 코드는 은퇴 처리 */
EXEC SQL UPDATE mm_code SET status = 'INACTIVE'
WHERE code_group = 'PARTNER' AND status = 'ACTIVE'
AND NOT EXISTS (SELECT 1 FROM mm_issuer i
WHERE i.issuer_cd = mm_code.code AND i.status = 'ACTIVE');
if (sqlca.sqlcode < 0) { fprintf(stderr, "RETIRE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
retired = sqlca.sqlerrd[2];
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_partner_sync_batch COMMIT: 동기화=%ld건 은퇴=%ld건\n", synced, retired);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,5 @@
/* mm_partner_sync_cpy.h - mm_partner_sync_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
char b_cd[16];
char b_nm[64];
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,55 @@
/*
* mm_rel_orphan_batch.pgc - 대표-하위 가맹점 관계 정합성 정리 배치 (NOT EXISTS 커서 + XA).
*
* 대표가맹점(code_name)이 더 이상 활성이 아닌 MERCH_REL 관계 행을 커서로 찾아
* INACTIVE 로 내리고 하위가맹점 이력(REL_UNLINK)을 남긴다.
* 전 구간 ONE global XA transaction.
* host 변수는 전용 카피북 mm_rel_orphan_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_rel_orphan_batch
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "mm_dbio.h"
int main(int argc, char **argv)
{
long cleaned = 0;
(void)argc; (void)argv;
EXEC SQL INCLUDE mm_rel_orphan_cpy;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE rox CURSOR FOR
SELECT c.code, c.code_name FROM mm_code c
WHERE c.code_group = 'MERCH_REL' AND c.status = 'ACTIVE'
AND NOT EXISTS (SELECT 1 FROM mm_merchant_ext m
WHERE m.merchant_id = c.code_name AND m.status = 'ACTIVE')
ORDER BY c.code;
EXEC SQL OPEN rox;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH rox INTO :b_child, :b_parent;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE rox; tpabort(0); return 1; }
EXEC SQL UPDATE mm_code SET status = 'INACTIVE'
WHERE code_group = 'MERCH_REL' AND code = :b_child;
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPDATE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE rox; tpabort(0); return 1; }
if (mmdb_insert_merch_hist(b_child, "REL_UNLINK", b_parent, "", "대표가맹점 비활성 배치 해제") < 0) {
EXEC SQL CLOSE rox; tpabort(0); return 1;
}
cleaned++;
}
EXEC SQL CLOSE rox;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_rel_orphan_batch COMMIT: 관계 해제=%ld건\n", cleaned);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,5 @@
/* mm_rel_orphan_cpy.h - mm_rel_orphan_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
char b_child[64];
char b_parent[64];
EXEC SQL END DECLARE SECTION;

View file

@ -0,0 +1,54 @@
/*
* mm_term_scrap_batch.pgc - 장기 정지 단말 일괄 폐기 배치 (커서 + UPDATE + 이력 + XA).
*
* SUSPENDED 상태로 보존일수 이상 경과한 단말을 커서로 훑어 SCRAPPED 로 전환하고,
* 소속 가맹점 이력(TERM_SCRAP)을 남긴다. 전 구간 ONE global XA transaction.
* host 변수는 전용 카피북 mm_term_scrap_cpy.h 를 EXEC SQL INCLUDE.
* Usage: mm_term_scrap_batch [DAYS=180]
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atmi.h>
#include "mm_dbio.h"
int main(int argc, char **argv)
{
long scrapped = 0;
EXEC SQL INCLUDE mm_term_scrap_cpy;
b_days = (argc > 1) ? atol(argv[1]) : 180;
if (b_days < 1) b_days = 180;
if (tpinit(NULL) < 0) { fprintf(stderr, "tpinit FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpopen() < 0) { fprintf(stderr, "tpopen FAIL: %s\n", tpstrerror(tperrno)); return 1; }
if (tpbegin(60, 0) < 0) { fprintf(stderr, "tpbegin FAIL: %s\n", tpstrerror(tperrno)); return 1; }
EXEC SQL DECLARE tsc CURSOR FOR
SELECT terminal_id, merchant_id FROM mm_terminal
WHERE status = 'SUSPENDED'
AND created_at < now() - :b_days * interval '1 day'
ORDER BY terminal_id;
EXEC SQL OPEN tsc;
if (sqlca.sqlcode < 0) { fprintf(stderr, "OPEN FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); tpabort(0); return 1; }
for (;;) {
EXEC SQL FETCH tsc INTO :b_term, :b_merch;
if (sqlca.sqlcode == 100) break;
if (sqlca.sqlcode < 0) { fprintf(stderr, "FETCH FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE tsc; tpabort(0); return 1; }
EXEC SQL UPDATE mm_terminal SET status = 'SCRAPPED' WHERE terminal_id = :b_term;
if (sqlca.sqlcode < 0) { fprintf(stderr, "UPDATE FAIL [%d] %s\n", sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); EXEC SQL CLOSE tsc; tpabort(0); return 1; }
if (mmdb_insert_merch_hist(b_merch, "TERM_SCRAP", "SUSPENDED", "SCRAPPED", "장기 정지 단말 배치 폐기") < 0) {
EXEC SQL CLOSE tsc; tpabort(0); return 1;
}
scrapped++;
}
EXEC SQL CLOSE tsc;
if (tpcommit(0) < 0) { fprintf(stderr, "tpcommit FAIL: %s\n", tpstrerror(tperrno)); tpabort(0); return 1; }
printf(">>> mm_term_scrap_batch COMMIT: 기준=%ld일 폐기=%ld대\n", b_days, scrapped);
tpclose(); tpterm();
return 0;
}

View file

@ -0,0 +1,6 @@
/* mm_term_scrap_cpy.h - mm_term_scrap_batch 전용 카피북 (host 변수 선언). */
EXEC SQL BEGIN DECLARE SECTION;
long b_days;
char b_term[64];
char b_merch[64];
EXEC SQL END DECLARE SECTION;

View file

@ -2,7 +2,7 @@
* mm_svr.pgc - mm (마스터/master) MODULE SERVER (thin dispatcher).
*
* The legacy "one module server, many services" pattern: this binary owns NO
* business logic. Each of mm's ~30 online services now lives in its own file
* business logic. Each of mm's ~75 online services now lives in its own file
* (app/src/mm/svc/<SVCNAME>.pgc) with its own copybook header
* (app/src/mm/svc/<SVCNAME>.h); those objects are archived into libmmsvc.a and
* linked into this server. Here we only include every service copybook, tpopen()
@ -46,6 +46,51 @@
#include "MM_CODE_CACHE.h"
#include "MM_STATUS.h"
#include "MM_CONFIRM.h"
#include "MM_TERM_MOVE.h"
#include "MM_TERM_SCRAP.h"
#include "MM_TERM_SUSPEND.h"
#include "MM_TERM_CNT.h"
#include "MM_APPLY_REG.h"
#include "MM_APPLY_REVIEW.h"
#include "MM_APPLY_APPROVE.h"
#include "MM_APPLY_REJECT.h"
#include "MM_APPLY_STATUS.h"
#include "MM_CONTRACT_NEW.h"
#include "MM_CONTRACT_RENEW.h"
#include "MM_CONTRACT_END.h"
#include "MM_CONTRACT_INQ.h"
#include "MM_PARTNER_REG.h"
#include "MM_PARTNER_INQ.h"
#include "MM_PARTNER_STATUS.h"
#include "MM_REL_LINK.h"
#include "MM_REL_UNLINK.h"
#include "MM_REL_LIST.h"
#include "MM_MCC_UPD.h"
#include "MM_MCC_INQ.h"
#include "MM_MCC_RISKLIST.h"
#include "MM_RATE_REQ.h"
#include "MM_RATE_APPROVE.h"
#include "MM_RATE_REJECT.h"
#include "MM_MERCH_PAUSE.h"
#include "MM_MERCH_RESUME.h"
#include "MM_MERCH_CLOSE.h"
#include "MM_ACCT_VERIFY.h"
#include "MM_ACCT_VRF_HIST.h"
#include "MM_ACCT_VRF_STAT.h"
#include "MM_GRADE_PREVIEW.h"
#include "MM_GRADE_ADJUST.h"
#include "MM_GRADE_DIST.h"
#include "MM_AUDIT_TRAIL.h"
#include "MM_AUDIT_RECENT.h"
#include "MM_AUDIT_PURGE.h"
#include "MM_CHK_TERM.h"
#include "MM_CHK_FEERATE.h"
#include "MM_CHK_LIMIT.h"
#include "MM_CODEMAP_SET.h"
#include "MM_CODEMAP_GET.h"
#include "MM_CODEMAP_DEL.h"
#include "MM_BIZNO_CHECK.h"
#include "MM_BIZNO_UPD.h"
/* ----- advertise table: {service name, function} (fn is extern from its file) */
static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = {
@ -79,6 +124,51 @@ static struct { const char *name; void (*fn)(TPSVCINFO *); } SVCS[] = {
{"MM_CODE_CACHE", MM_CODE_CACHE},
{"MM_STATUS", MM_STATUS},
{"MM_CONFIRM", MM_CONFIRM},
{"MM_TERM_MOVE", MM_TERM_MOVE},
{"MM_TERM_SCRAP", MM_TERM_SCRAP},
{"MM_TERM_SUSPEND", MM_TERM_SUSPEND},
{"MM_TERM_CNT", MM_TERM_CNT},
{"MM_APPLY_REG", MM_APPLY_REG},
{"MM_APPLY_REVIEW", MM_APPLY_REVIEW},
{"MM_APPLY_APPROVE", MM_APPLY_APPROVE},
{"MM_APPLY_REJECT", MM_APPLY_REJECT},
{"MM_APPLY_STATUS", MM_APPLY_STATUS},
{"MM_CONTRACT_NEW", MM_CONTRACT_NEW},
{"MM_CONTRACT_RENEW", MM_CONTRACT_RENEW},
{"MM_CONTRACT_END", MM_CONTRACT_END},
{"MM_CONTRACT_INQ", MM_CONTRACT_INQ},
{"MM_PARTNER_REG", MM_PARTNER_REG},
{"MM_PARTNER_INQ", MM_PARTNER_INQ},
{"MM_PARTNER_STATUS", MM_PARTNER_STATUS},
{"MM_REL_LINK", MM_REL_LINK},
{"MM_REL_UNLINK", MM_REL_UNLINK},
{"MM_REL_LIST", MM_REL_LIST},
{"MM_MCC_UPD", MM_MCC_UPD},
{"MM_MCC_INQ", MM_MCC_INQ},
{"MM_MCC_RISKLIST", MM_MCC_RISKLIST},
{"MM_RATE_REQ", MM_RATE_REQ},
{"MM_RATE_APPROVE", MM_RATE_APPROVE},
{"MM_RATE_REJECT", MM_RATE_REJECT},
{"MM_MERCH_PAUSE", MM_MERCH_PAUSE},
{"MM_MERCH_RESUME", MM_MERCH_RESUME},
{"MM_MERCH_CLOSE", MM_MERCH_CLOSE},
{"MM_ACCT_VERIFY", MM_ACCT_VERIFY},
{"MM_ACCT_VRF_HIST", MM_ACCT_VRF_HIST},
{"MM_ACCT_VRF_STAT", MM_ACCT_VRF_STAT},
{"MM_GRADE_PREVIEW", MM_GRADE_PREVIEW},
{"MM_GRADE_ADJUST", MM_GRADE_ADJUST},
{"MM_GRADE_DIST", MM_GRADE_DIST},
{"MM_AUDIT_TRAIL", MM_AUDIT_TRAIL},
{"MM_AUDIT_RECENT", MM_AUDIT_RECENT},
{"MM_AUDIT_PURGE", MM_AUDIT_PURGE},
{"MM_CHK_TERM", MM_CHK_TERM},
{"MM_CHK_FEERATE", MM_CHK_FEERATE},
{"MM_CHK_LIMIT", MM_CHK_LIMIT},
{"MM_CODEMAP_SET", MM_CODEMAP_SET},
{"MM_CODEMAP_GET", MM_CODEMAP_GET},
{"MM_CODEMAP_DEL", MM_CODEMAP_DEL},
{"MM_BIZNO_CHECK", MM_BIZNO_CHECK},
{"MM_BIZNO_UPD", MM_BIZNO_UPD},
{NULL, NULL}
};

View file

@ -0,0 +1,12 @@
/*
* MM_ACCT_VERIFY.h - mm MM_ACCT_VERIFY (copybook / record header).
* UBF : T_MERCHANT, T_STR1( ), T_RC
*/
#ifndef MM_ACCT_VERIFY_H
#define MM_ACCT_VERIFY_H
#include "mm_svc.h"
void MM_ACCT_VERIFY(TPSVCINFO *p);
#endif /* MM_ACCT_VERIFY_H */

View file

@ -0,0 +1,42 @@
/*
* MM_ACCT_VERIFY.pgc - mm 모듈 서비스 MM_ACCT_VERIFY (one service = one file).
* 기능: 정산계좌 검증 실행 (형식 검사 + ACCT_VRF 검증 이력 적재).
* 입력: T_MERCHANT. 출력: T_STR1=결과(OK/EMPTY/FORMAT), T_RC=0정상 1미존재 2계좌미등록 3형식오류.
* 검증 결과는 성공/실패 모두 이력으로 남긴다 (감사 추적용).
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_ACCT_VERIFY.h"
void MM_ACCT_VERIFY(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64];
const char *result;
long rc = 0;
size_t i, len;
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_acct[40];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT settle_acct INTO :h_acct FROM mm_merchant_ext
WHERE merchant_id = :h_merch;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
len = strlen(h_acct);
if (len == 0) { result = "EMPTY"; rc = 2; }
else {
result = "OK";
if (len < 10 || len > 16) { result = "FORMAT"; rc = 3; }
else for (i = 0; i < len; i++)
if (h_acct[i] < '0' || h_acct[i] > '9') { result = "FORMAT"; rc = 3; break; }
}
if (mmdb_insert_merch_hist(h_merch, "ACCT_VRF", "", result, "정산계좌 검증 수행") < 0) FAIL(b);
Bchg(b, T_STR1, 0, (char *)result, 0L);
setl(b, T_RC, rc);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_ACCT_VRF_HIST.h - mm MM_ACCT_VRF_HIST (copybook / record header).
* UBF : T_MERCHANT, T_COUNT, T_STR1( ), T_STR2( ), T_RC
*/
#ifndef MM_ACCT_VRF_HIST_H
#define MM_ACCT_VRF_HIST_H
#include "mm_svc.h"
void MM_ACCT_VRF_HIST(TPSVCINFO *p);
#endif /* MM_ACCT_VRF_HIST_H */

View file

@ -0,0 +1,39 @@
/*
* MM_ACCT_VRF_HIST.pgc - mm 모듈 서비스 MM_ACCT_VRF_HIST (one service = one file).
* 기능: 정산계좌 검증 이력 조회 (누적 횟수 + 최근 결과/일시).
* 입력: T_MERCHANT. 출력: T_COUNT, T_STR1=최근 결과, T_STR2=최근 검증일(YYYYMMDD).
* RC: 1=검증 이력 없음.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_ACCT_VRF_HIST.h"
void MM_ACCT_VRF_HIST(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64];
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_last[16], h_when[16];
long h_cnt;
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT count(*) INTO :h_cnt FROM mm_merch_hist
WHERE merchant_id = :h_merch AND action = 'ACCT_VRF';
if (sqlca.sqlcode < 0) FAIL(b);
if (h_cnt == 0) { setl(b, T_RC, 1); OK(b); }
EXEC SQL SELECT after_val, to_char(created_at, 'YYYYMMDD')
INTO :h_last, :h_when
FROM mm_merch_hist
WHERE merchant_id = :h_merch AND action = 'ACCT_VRF'
ORDER BY hist_id DESC LIMIT 1;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_cnt);
Bchg(b, T_STR1, 0, h_last, 0L);
Bchg(b, T_STR2, 0, h_when, 0L);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_ACCT_VRF_STAT.h - mm MM_ACCT_VRF_STAT (copybook / record header).
* UBF : T_COUNT( ), T_ARG1( ), T_ARG2( ), T_RC
*/
#ifndef MM_ACCT_VRF_STAT_H
#define MM_ACCT_VRF_STAT_H
#include "mm_svc.h"
void MM_ACCT_VRF_STAT(TPSVCINFO *p);
#endif /* MM_ACCT_VRF_STAT_H */

View file

@ -0,0 +1,28 @@
/*
* MM_ACCT_VRF_STAT.pgc - mm 모듈 서비스 MM_ACCT_VRF_STAT (one service = one file).
* 기능: 정산계좌 검증 전사 통계 (총 검증 수, 검증 대상 가맹점 수, 실패 건수).
* 출력: T_COUNT, T_ARG1, T_ARG2. 실패 = 결과가 OK가 아닌 검증.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_ACCT_VRF_STAT.h"
void MM_ACCT_VRF_STAT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_tot, h_merch, h_bad;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT count(*),
count(DISTINCT merchant_id),
coalesce(sum(CASE WHEN after_val <> 'OK' THEN 1 ELSE 0 END), 0)
INTO :h_tot, :h_merch, :h_bad
FROM mm_merch_hist WHERE action = 'ACCT_VRF';
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_tot);
setl(b, T_ARG1, h_merch);
setl(b, T_ARG2, h_bad);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_APPLY_APPROVE.h - mm MM_APPLY_APPROVE (copybook / record header).
* UBF : T_MERCHANT, T_ARG1( bps), T_RC
*/
#ifndef MM_APPLY_APPROVE_H
#define MM_APPLY_APPROVE_H
#include "mm_svc.h"
void MM_APPLY_APPROVE(TPSVCINFO *p);
#endif /* MM_APPLY_APPROVE_H */

View file

@ -0,0 +1,43 @@
/*
* MM_APPLY_APPROVE.pgc - mm 모듈 서비스 MM_APPLY_APPROVE (one service = one file).
* 기능: 가입 심사 승인 (REVIEW -> ACTIVE, 기본 수수료율/한도 자동 개설).
* 입력: T_MERCHANT, T_ARG1=기본 수수료율 bps(없으면 25).
* RC: 1=가맹점 없음, 2=REVIEW 상태가 아님.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_APPLY_APPROVE.h"
void MM_APPLY_APPROVE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64];
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_stat[16];
long h_bps;
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
h_bps = getl(b, T_ARG1);
if (h_bps <= 0 || h_bps > 500) h_bps = 25;
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT status INTO :h_stat FROM mm_merchant_ext WHERE merchant_id = :h_merch;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
if (strcmp(h_stat, "REVIEW") != 0) { setl(b, T_RC, 2); OK(b); }
EXEC SQL UPDATE mm_merchant_ext SET status = 'ACTIVE', updated_at = now()
WHERE merchant_id = :h_merch;
if (sqlca.sqlcode < 0) FAIL(b);
/* 승인과 동시에 기본 수수료율/한도 레코드 개설 (이미 있으면 유지) */
EXEC SQL INSERT INTO mm_fee_rate (merchant_id, card_type, rate_bps)
VALUES (:h_merch, 'CREDIT', :h_bps)
ON CONFLICT (merchant_id, card_type) DO NOTHING;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL INSERT INTO mm_limit (merchant_id) VALUES (:h_merch)
ON CONFLICT (merchant_id) DO NOTHING;
if (sqlca.sqlcode < 0) FAIL(b);
if (mmdb_insert_merch_hist(h_merch, "APPROVE", "REVIEW", "ACTIVE", "심사 승인, 기본 요율/한도 개설") < 0) FAIL(b);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_APPLY_REG.h - mm MM_APPLY_REG (copybook / record header).
* UBF : T_MERCHANT, T_STR1(), T_STR2(), T_STR3(MCC), T_RC
*/
#ifndef MM_APPLY_REG_H
#define MM_APPLY_REG_H
#include "mm_svc.h"
void MM_APPLY_REG(TPSVCINFO *p);
#endif /* MM_APPLY_REG_H */

View file

@ -0,0 +1,45 @@
/*
* MM_APPLY_REG.pgc - mm 모듈 서비스 MM_APPLY_REG (one service = one file).
* 기능: 가맹점 가입 신청 접수 (심사 워크플로 시작, 상태 APPLIED).
* 입력: T_MERCHANT, T_STR1=상호, T_STR2=사업자번호(10자리), T_STR3=MCC.
* RC: 1=이미 존재하는 가맹점, 2=사업자번호 형식 오류, 3=필수값 누락.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_APPLY_REG.h"
void MM_APPLY_REG(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], name[64], bizno[16], mcc[8];
int i;
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_name[64], h_bizno[16], h_mcc[8];
long h_dup;
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_STR1, name, sizeof(name));
gets_(b, T_STR2, bizno, sizeof(bizno));
gets_(b, T_STR3, mcc, sizeof(mcc));
if (mcc[0] == 0) strcpy(mcc, "0000");
if (merch[0] == 0 || name[0] == 0) { setl(b, T_RC, 3); OK(b); }
if (strlen(bizno) != 10) { setl(b, T_RC, 2); OK(b); }
for (i = 0; i < 10; i++)
if (bizno[i] < '0' || bizno[i] > '9') { setl(b, T_RC, 2); OK(b); }
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_name, name, sizeof(h_name)-1); h_name[sizeof(h_name)-1] = 0;
strncpy(h_bizno, bizno, sizeof(h_bizno)-1); h_bizno[sizeof(h_bizno)-1] = 0;
strncpy(h_mcc, mcc, sizeof(h_mcc)-1); h_mcc[sizeof(h_mcc)-1] = 0;
EXEC SQL SELECT count(*) INTO :h_dup FROM mm_merchant_ext WHERE merchant_id = :h_merch;
if (sqlca.sqlcode < 0) FAIL(b);
if (h_dup > 0) { setl(b, T_RC, 1); OK(b); }
EXEC SQL INSERT INTO mm_merchant_ext (merchant_id, name, biz_reg_no, mcc, status)
VALUES (:h_merch, :h_name, :h_bizno, :h_mcc, 'APPLIED');
if (sqlca.sqlcode < 0) FAIL(b);
if (mmdb_insert_merch_hist(h_merch, "APPLY", "", "APPLIED", "가입 신청 접수") < 0) FAIL(b);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_APPLY_REJECT.h - mm MM_APPLY_REJECT (copybook / record header).
* UBF : T_MERCHANT, T_REASON, T_RC
*/
#ifndef MM_APPLY_REJECT_H
#define MM_APPLY_REJECT_H
#include "mm_svc.h"
void MM_APPLY_REJECT(TPSVCINFO *p);
#endif /* MM_APPLY_REJECT_H */

View file

@ -0,0 +1,36 @@
/*
* MM_APPLY_REJECT.pgc - mm 모듈 서비스 MM_APPLY_REJECT (one service = one file).
* 기능: 가입 심사 반려 (APPLIED/REVIEW -> REJECTED, 반려 사유 필수).
* 입력: T_MERCHANT, T_REASON=반려 사유(필수).
* RC: 1=가맹점 없음, 2=심사중 상태가 아님, 3=반려 사유 누락.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_APPLY_REJECT.h"
void MM_APPLY_REJECT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], reason[128];
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_stat[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_REASON, reason, sizeof(reason));
if (reason[0] == 0) { setl(b, T_RC, 3); OK(b); }
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT status INTO :h_stat FROM mm_merchant_ext WHERE merchant_id = :h_merch;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
if (strcmp(h_stat, "APPLIED") != 0 && strcmp(h_stat, "REVIEW") != 0) {
setl(b, T_RC, 2); OK(b);
}
EXEC SQL UPDATE mm_merchant_ext SET status = 'REJECTED', updated_at = now()
WHERE merchant_id = :h_merch;
if (sqlca.sqlcode < 0) FAIL(b);
if (mmdb_insert_merch_hist(h_merch, "REJECT", h_stat, "REJECTED", reason) < 0) FAIL(b);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_APPLY_REVIEW.h - mm MM_APPLY_REVIEW (copybook / record header).
* UBF : T_MERCHANT, T_ARG1(MCC ), T_RC
*/
#ifndef MM_APPLY_REVIEW_H
#define MM_APPLY_REVIEW_H
#include "mm_svc.h"
void MM_APPLY_REVIEW(TPSVCINFO *p);
#endif /* MM_APPLY_REVIEW_H */

View file

@ -0,0 +1,40 @@
/*
* MM_APPLY_REVIEW.pgc - mm 모듈 서비스 MM_APPLY_REVIEW (one service = one file).
* 기능: 가입 신청 심사 개시 (APPLIED -> REVIEW, MCC 유효성 사전점검).
* 입력: T_MERCHANT. 출력: T_ARG1=신청 MCC의 마스터 등록 여부(1/0).
* RC: 1=가맹점 없음, 2=APPLIED 상태가 아님.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_APPLY_REVIEW.h"
void MM_APPLY_REVIEW(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], memo[128];
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_stat[16], h_mcc[8];
long h_mccok;
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT status, mcc INTO :h_stat, :h_mcc
FROM mm_merchant_ext WHERE merchant_id = :h_merch;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
if (strcmp(h_stat, "APPLIED") != 0) { setl(b, T_RC, 2); OK(b); }
EXEC SQL SELECT count(*) INTO :h_mccok FROM mm_mcc WHERE mcc = :h_mcc;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL UPDATE mm_merchant_ext SET status = 'REVIEW', updated_at = now()
WHERE merchant_id = :h_merch;
if (sqlca.sqlcode < 0) FAIL(b);
snprintf(memo, sizeof(memo), "심사 개시 (MCC %s %s)", h_mcc,
h_mccok > 0 ? "등록됨" : "미등록");
if (mmdb_insert_merch_hist(h_merch, "REVIEW", "APPLIED", "REVIEW", memo) < 0) FAIL(b);
setl(b, T_ARG1, h_mccok > 0 ? 1 : 0);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_APPLY_STATUS.h - mm MM_APPLY_STATUS (copybook / record header).
* UBF : T_MERCHANT, T_STATUS, T_ARG1( ), T_COUNT, T_RC
*/
#ifndef MM_APPLY_STATUS_H
#define MM_APPLY_STATUS_H
#include "mm_svc.h"
void MM_APPLY_STATUS(TPSVCINFO *p);
#endif /* MM_APPLY_STATUS_H */

View file

@ -0,0 +1,42 @@
/*
* MM_APPLY_STATUS.pgc - mm 모듈 서비스 MM_APPLY_STATUS (one service = one file).
* 기능: 가입 심사 진행 상태 조회 (단계 코드 + 워크플로 이력 건수).
* 입력: T_MERCHANT. 출력: T_STATUS=현재 상태, T_ARG1=단계(1접수 2심사 3승인 9반려), T_COUNT=워크플로 이력 수.
* RC: 1=가맹점 없음.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_APPLY_STATUS.h"
void MM_APPLY_STATUS(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64];
long stage = 0;
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_stat[16];
long h_cnt;
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT status INTO :h_stat FROM mm_merchant_ext WHERE merchant_id = :h_merch;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
if (strcmp(h_stat, "APPLIED") == 0) stage = 1;
else if (strcmp(h_stat, "REVIEW") == 0) stage = 2;
else if (strcmp(h_stat, "ACTIVE") == 0) stage = 3;
else if (strcmp(h_stat, "REJECTED") == 0) stage = 9;
EXEC SQL SELECT count(*) INTO :h_cnt FROM mm_merch_hist
WHERE merchant_id = :h_merch
AND action IN ('APPLY', 'REVIEW', 'APPROVE', 'REJECT');
if (sqlca.sqlcode < 0) FAIL(b);
Bchg(b, T_STATUS, 0, h_stat, 0L);
setl(b, T_ARG1, stage);
setl(b, T_COUNT, h_cnt);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_AUDIT_PURGE.h - mm MM_AUDIT_PURGE (copybook / record header).
* UBF : T_ARG1(), T_COUNT( ), T_RC
*/
#ifndef MM_AUDIT_PURGE_H
#define MM_AUDIT_PURGE_H
#include "mm_svc.h"
void MM_AUDIT_PURGE(TPSVCINFO *p);
#endif /* MM_AUDIT_PURGE_H */

View file

@ -0,0 +1,36 @@
/*
* MM_AUDIT_PURGE.pgc - mm 모듈 서비스 MM_AUDIT_PURGE (one service = one file).
* 기능: 감사 이력 보존기한 경과분 삭제 (삭제 내역 자체도 감사 기록으로 남김).
* 입력: T_ARG1=보존일수(기본 365, 최소 30). 출력: T_COUNT=삭제 건수.
* RC: 3=보존일수 30일 미만 불가.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_AUDIT_PURGE.h"
void MM_AUDIT_PURGE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char memo[96];
long purged;
EXEC SQL BEGIN DECLARE SECTION;
long h_days;
EXEC SQL END DECLARE SECTION;
h_days = getl(b, T_ARG1);
if (h_days == 0) h_days = 365;
if (h_days < 30) { setl(b, T_RC, 3); OK(b); }
EXEC SQL DELETE FROM mm_merch_hist
WHERE created_at < now() - :h_days * interval '1 day'
AND action <> 'AUDIT_PRG';
if (sqlca.sqlcode < 0) FAIL(b);
purged = sqlca.sqlerrd[2];
if (purged > 0) {
snprintf(memo, sizeof(memo), "보존기한 %ld일 경과 이력 %ld건 삭제", h_days, purged);
if (mmdb_insert_merch_hist("*", "AUDIT_PRG", "", "", memo) < 0) FAIL(b);
}
setl(b, T_COUNT, purged);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_AUDIT_RECENT.h - mm MM_AUDIT_RECENT (copybook / record header).
* UBF : T_MERCHANT, T_STR1(), T_STR2( ), T_MSG(), T_BIZDATE(), T_RC
*/
#ifndef MM_AUDIT_RECENT_H
#define MM_AUDIT_RECENT_H
#include "mm_svc.h"
void MM_AUDIT_RECENT(TPSVCINFO *p);
#endif /* MM_AUDIT_RECENT_H */

View file

@ -0,0 +1,35 @@
/*
* MM_AUDIT_RECENT.pgc - mm 모듈 서비스 MM_AUDIT_RECENT (one service = one file).
* 기능: 가맹점 최근 변경 감사 조회 (최신 이력 1건: 액션/변경값/메모/일자).
* 입력: T_MERCHANT. 출력: T_STR1, T_STR2, T_MSG, T_BIZDATE.
* RC: 1=변경 이력 없음.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_AUDIT_RECENT.h"
void MM_AUDIT_RECENT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64];
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_act[32], h_after[128], h_memo[128], h_when[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT action, after_val, memo, to_char(created_at, 'YYYYMMDD')
INTO :h_act, :h_after, :h_memo, :h_when
FROM mm_merch_hist
WHERE merchant_id = :h_merch
ORDER BY hist_id DESC LIMIT 1;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
Bchg(b, T_STR1, 0, h_act, 0L);
Bchg(b, T_STR2, 0, h_after, 0L);
Bchg(b, T_MSG, 0, h_memo, 0L);
Bchg(b, T_BIZDATE, 0, h_when, 0L);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_AUDIT_TRAIL.h - mm MM_AUDIT_TRAIL (copybook / record header).
* UBF : T_MERCHANT, T_STR1(action ), T_COUNT, T_ARG1( ), T_RC
*/
#ifndef MM_AUDIT_TRAIL_H
#define MM_AUDIT_TRAIL_H
#include "mm_svc.h"
void MM_AUDIT_TRAIL(TPSVCINFO *p);
#endif /* MM_AUDIT_TRAIL_H */

View file

@ -0,0 +1,35 @@
/*
* MM_AUDIT_TRAIL.pgc - mm 모듈 서비스 MM_AUDIT_TRAIL (one service = one file).
* 기능: 가맹점 마스터 변경 감사 추적 (액션 필터별 이력 건수 + 발생 액션 종류 수).
* 입력: T_MERCHANT, T_STR1=액션 필터(빈값이면 전체).
* 출력: T_COUNT=이력 건수, T_ARG1=서로 다른 액션 종류 수.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_AUDIT_TRAIL.h"
void MM_AUDIT_TRAIL(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], act[32];
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_act[32];
long h_cnt, h_kinds;
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_STR1, act, sizeof(act));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_act, act, sizeof(h_act)-1); h_act[sizeof(h_act)-1] = 0;
EXEC SQL SELECT count(*), count(DISTINCT action)
INTO :h_cnt, :h_kinds
FROM mm_merch_hist
WHERE merchant_id = :h_merch
AND (:h_act = '' OR action = :h_act);
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_cnt);
setl(b, T_ARG1, h_kinds);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_BIZNO_CHECK.h - mm MM_BIZNO_CHECK (copybook / record header).
* UBF : T_STR2(), T_COUNT( ), T_RC
*/
#ifndef MM_BIZNO_CHECK_H
#define MM_BIZNO_CHECK_H
#include "mm_svc.h"
void MM_BIZNO_CHECK(TPSVCINFO *p);
#endif /* MM_BIZNO_CHECK_H */

View file

@ -0,0 +1,44 @@
/*
* MM_BIZNO_CHECK.pgc - mm 모듈 서비스 MM_BIZNO_CHECK (one service = one file).
* 기능: 사업자등록번호 검증 (10자리 형식 + 국세청 검증공식 checksum + 마스터 중복 사용 확인).
* checksum: 가중치 1,3,7,1,3,7,1,3,5 곱 합 + floor(9번째*5/10), (10-합%10)%10 == 검증숫자.
* 입력: T_STR2=사업자번호. 출력: T_COUNT=해당 번호를 쓰는 기존 가맹점 수.
* RC: 0=유효, 2=형식 오류, 3=검증숫자 불일치.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_BIZNO_CHECK.h"
static int bizno_checksum_ok(const char *no)
{
static const int w[9] = { 1, 3, 7, 1, 3, 7, 1, 3, 5 };
int i, sum = 0;
for (i = 0; i < 9; i++) sum += (no[i] - '0') * w[i];
sum += ((no[8] - '0') * 5) / 10;
return ((10 - sum % 10) % 10) == (no[9] - '0');
}
void MM_BIZNO_CHECK(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char bizno[16];
int i;
EXEC SQL BEGIN DECLARE SECTION;
char h_bizno[16];
long h_used;
EXEC SQL END DECLARE SECTION;
gets_(b, T_STR2, bizno, sizeof(bizno));
if (strlen(bizno) != 10) { setl(b, T_RC, 2); OK(b); }
for (i = 0; i < 10; i++)
if (bizno[i] < '0' || bizno[i] > '9') { setl(b, T_RC, 2); OK(b); }
if (!bizno_checksum_ok(bizno)) { setl(b, T_RC, 3); OK(b); }
strncpy(h_bizno, bizno, sizeof(h_bizno)-1); h_bizno[sizeof(h_bizno)-1] = 0;
EXEC SQL SELECT count(*) INTO :h_used FROM mm_merchant_ext
WHERE biz_reg_no = :h_bizno AND status <> 'DELETED';
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_used);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_BIZNO_UPD.h - mm MM_BIZNO_UPD (copybook / record header).
* UBF : T_MERCHANT, T_STR2( ), T_STR1( ), T_RC
*/
#ifndef MM_BIZNO_UPD_H
#define MM_BIZNO_UPD_H
#include "mm_svc.h"
void MM_BIZNO_UPD(TPSVCINFO *p);
#endif /* MM_BIZNO_UPD_H */

View file

@ -0,0 +1,46 @@
/*
* MM_BIZNO_UPD.pgc - mm 모듈 서비스 MM_BIZNO_UPD (one service = one file).
* 기능: 사업자등록번호 변경 (형식 검사 + 타 가맹점 중복 차단 + 변경 이력).
* 입력: T_MERCHANT, T_STR2=신규 번호. 출력: T_STR1=기존 번호.
* RC: 1=가맹점 없음, 2=형식 오류, 4=타 가맹점이 이미 사용, 5=기존 번호와 동일.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_BIZNO_UPD.h"
void MM_BIZNO_UPD(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], bizno[16];
int i;
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_new[16], h_old[16];
long h_other;
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_STR2, bizno, sizeof(bizno));
if (strlen(bizno) != 10) { setl(b, T_RC, 2); OK(b); }
for (i = 0; i < 10; i++)
if (bizno[i] < '0' || bizno[i] > '9') { setl(b, T_RC, 2); OK(b); }
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_new, bizno, sizeof(h_new)-1); h_new[sizeof(h_new)-1] = 0;
EXEC SQL SELECT biz_reg_no INTO :h_old FROM mm_merchant_ext
WHERE merchant_id = :h_merch;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
if (strcmp(h_old, h_new) == 0) { setl(b, T_RC, 5); OK(b); }
EXEC SQL SELECT count(*) INTO :h_other FROM mm_merchant_ext
WHERE biz_reg_no = :h_new AND merchant_id <> :h_merch AND status <> 'DELETED';
if (sqlca.sqlcode < 0) FAIL(b);
if (h_other > 0) { setl(b, T_RC, 4); OK(b); }
EXEC SQL UPDATE mm_merchant_ext SET biz_reg_no = :h_new, updated_at = now()
WHERE merchant_id = :h_merch;
if (sqlca.sqlcode < 0) FAIL(b);
if (mmdb_insert_merch_hist(h_merch, "BIZNO", h_old, h_new, "사업자번호 변경") < 0) FAIL(b);
Bchg(b, T_STR1, 0, h_old, 0L);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_CHK_FEERATE.h - mm MM_CHK_FEERATE (copybook / record header).
* UBF : T_COUNT( ), T_ARG1( ), T_ARG2( ), T_RC
*/
#ifndef MM_CHK_FEERATE_H
#define MM_CHK_FEERATE_H
#include "mm_svc.h"
void MM_CHK_FEERATE(TPSVCINFO *p);
#endif /* MM_CHK_FEERATE_H */

View file

@ -0,0 +1,37 @@
/*
* MM_CHK_FEERATE.pgc - mm 모듈 서비스 MM_CHK_FEERATE (one service = one file).
* 기능: 수수료율 마스터 정합성 점검 (활성 가맹점 요율 누락 / bps 범위 이탈 / 고아 요율).
* 출력: T_COUNT=요율 없는 활성 가맹점, T_ARG1=bps 0 이하 또는 500 초과, T_ARG2=가맹점 없는 요율.
* T_RC=0 정상 / 9 불일치 존재.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_CHK_FEERATE.h"
void MM_CHK_FEERATE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_missing, h_range, h_orphan;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT count(*) INTO :h_missing FROM mm_merchant_ext m
WHERE m.status = 'ACTIVE'
AND NOT EXISTS (SELECT 1 FROM mm_fee_rate f
WHERE f.merchant_id = m.merchant_id);
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT count(*) INTO :h_range FROM mm_fee_rate
WHERE rate_bps <= 0 OR rate_bps > 500;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT count(*) INTO :h_orphan FROM mm_fee_rate f
WHERE NOT EXISTS (SELECT 1 FROM mm_merchant_ext m
WHERE m.merchant_id = f.merchant_id);
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_missing);
setl(b, T_ARG1, h_range);
setl(b, T_ARG2, h_orphan);
setl(b, T_RC, (h_missing + h_range + h_orphan) > 0 ? 9 : 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_CHK_LIMIT.h - mm MM_CHK_LIMIT (copybook / record header).
* UBF : T_COUNT( ), T_ARG1( ), T_RC
*/
#ifndef MM_CHK_LIMIT_H
#define MM_CHK_LIMIT_H
#include "mm_svc.h"
void MM_CHK_LIMIT(TPSVCINFO *p);
#endif /* MM_CHK_LIMIT_H */

View file

@ -0,0 +1,31 @@
/*
* MM_CHK_LIMIT.pgc - mm 모듈 서비스 MM_CHK_LIMIT (one service = one file).
* 기능: 한도 마스터 정합성 점검 (활성 가맹점 한도 누락 + 한도 체계 역전 검출).
* 역전 = 건당한도 > 일한도 또는 일한도 > 월한도.
* 출력: T_COUNT=한도 없는 활성 가맹점, T_ARG1=역전 한도 행. T_RC=0 정상 / 9 불일치 존재.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_CHK_LIMIT.h"
void MM_CHK_LIMIT(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_missing, h_invert;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT count(*) INTO :h_missing FROM mm_merchant_ext m
WHERE m.status = 'ACTIVE'
AND NOT EXISTS (SELECT 1 FROM mm_limit l
WHERE l.merchant_id = m.merchant_id);
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT count(*) INTO :h_invert FROM mm_limit
WHERE per_txn_limit > daily_limit OR daily_limit > monthly_limit;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_missing);
setl(b, T_ARG1, h_invert);
setl(b, T_RC, (h_missing + h_invert) > 0 ? 9 : 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_CHK_TERM.h - mm MM_CHK_TERM (copybook / record header).
* UBF : T_COUNT( ), T_ARG1( ), T_RC
*/
#ifndef MM_CHK_TERM_H
#define MM_CHK_TERM_H
#include "mm_svc.h"
void MM_CHK_TERM(TPSVCINFO *p);
#endif /* MM_CHK_TERM_H */

View file

@ -0,0 +1,30 @@
/*
* MM_CHK_TERM.pgc - mm 모듈 서비스 MM_CHK_TERM (one service = one file).
* 기능: 단말 마스터 정합성 점검 (소속 가맹점 없는 고아 단말 + 폐업 가맹점의 가동 단말).
* 출력: T_COUNT=고아 단말 수, T_ARG1=폐업 가맹점 소속 미폐기 단말 수, T_RC=0 정상 / 9 불일치 존재.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_CHK_TERM.h"
void MM_CHK_TERM(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_orphan, h_zombie;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT count(*) INTO :h_orphan FROM mm_terminal t
WHERE NOT EXISTS (SELECT 1 FROM mm_merchant_ext m
WHERE m.merchant_id = t.merchant_id);
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT count(*) INTO :h_zombie FROM mm_terminal t
JOIN mm_merchant_ext m ON m.merchant_id = t.merchant_id
WHERE m.status = 'DELETED' AND t.status <> 'SCRAPPED';
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_orphan);
setl(b, T_ARG1, h_zombie);
setl(b, T_RC, (h_orphan + h_zombie) > 0 ? 9 : 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_CODEMAP_DEL.h - mm MM_CODEMAP_DEL (copybook / record header).
* UBF : T_KEY1(), T_STR1( ; = ), T_COUNT, T_RC
*/
#ifndef MM_CODEMAP_DEL_H
#define MM_CODEMAP_DEL_H
#include "mm_svc.h"
void MM_CODEMAP_DEL(TPSVCINFO *p);
#endif /* MM_CODEMAP_DEL_H */

View file

@ -0,0 +1,37 @@
/*
* MM_CODEMAP_DEL.pgc - mm 모듈 서비스 MM_CODEMAP_DEL (one service = one file).
* 기능: 코드 매핑 삭제 (단건 또는 네임스페이스 전체 일괄 삭제).
* 입력: T_KEY1=네임스페이스, T_STR1=원본 코드(빈값이면 네임스페이스 전체 삭제).
* 출력: T_COUNT=삭제 건수. RC: 1=대상 없음, 3=네임스페이스 누락.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_CODEMAP_DEL.h"
void MM_CODEMAP_DEL(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char ns[16], src[32];
long ndel;
EXEC SQL BEGIN DECLARE SECTION;
char h_grp[24], h_src[32];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, ns, sizeof(ns));
gets_(b, T_STR1, src, sizeof(src));
if (ns[0] == 0) { setl(b, T_RC, 3); OK(b); }
snprintf(h_grp, sizeof(h_grp), "MAP_%s", ns);
strncpy(h_src, src, sizeof(h_src)-1); h_src[sizeof(h_src)-1] = 0;
if (h_src[0] == 0) {
EXEC SQL DELETE FROM mm_code WHERE code_group = :h_grp;
} else {
EXEC SQL DELETE FROM mm_code WHERE code_group = :h_grp AND code = :h_src;
}
if (sqlca.sqlcode < 0) FAIL(b);
ndel = sqlca.sqlerrd[2];
if (ndel == 0) { setl(b, T_RC, 1); OK(b); }
setl(b, T_COUNT, ndel);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_CODEMAP_GET.h - mm MM_CODEMAP_GET (copybook / record header).
* UBF : T_KEY1(), T_STR1( ), T_STR2( ), T_RC
*/
#ifndef MM_CODEMAP_GET_H
#define MM_CODEMAP_GET_H
#include "mm_svc.h"
void MM_CODEMAP_GET(TPSVCINFO *p);
#endif /* MM_CODEMAP_GET_H */

View file

@ -0,0 +1,36 @@
/*
* MM_CODEMAP_GET.pgc - mm 모듈 서비스 MM_CODEMAP_GET (one service = one file).
* 기능: 코드 매핑 변환 조회 (네임스페이스의 원본 코드를 대상 코드로 변환).
* 입력: T_KEY1=네임스페이스, T_STR1=원본 코드. 출력: T_STR2=변환된 대상 코드.
* RC: 1=활성 매핑 없음 (이때 T_STR2에는 원본 코드를 그대로 반환 - passthrough).
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_CODEMAP_GET.h"
void MM_CODEMAP_GET(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char ns[16], src[32];
EXEC SQL BEGIN DECLARE SECTION;
char h_grp[24], h_src[32], h_dst[32];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, ns, sizeof(ns));
gets_(b, T_STR1, src, sizeof(src));
snprintf(h_grp, sizeof(h_grp), "MAP_%s", ns);
strncpy(h_src, src, sizeof(h_src)-1); h_src[sizeof(h_src)-1] = 0;
EXEC SQL SELECT code_name INTO :h_dst FROM mm_code
WHERE code_group = :h_grp AND code = :h_src AND status = 'ACTIVE';
if (sqlca.sqlcode == 100) {
/* 매핑이 없으면 원본 코드 그대로 통과 */
Bchg(b, T_STR2, 0, h_src, 0L);
setl(b, T_RC, 1);
OK(b);
}
if (sqlca.sqlcode < 0) FAIL(b);
Bchg(b, T_STR2, 0, h_dst, 0L);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_CODEMAP_SET.h - mm MM_CODEMAP_SET (copybook / record header).
* UBF : T_KEY1( ), T_STR1( ), T_STR2( ), T_RC
*/
#ifndef MM_CODEMAP_SET_H
#define MM_CODEMAP_SET_H
#include "mm_svc.h"
void MM_CODEMAP_SET(TPSVCINFO *p);
#endif /* MM_CODEMAP_SET_H */

View file

@ -0,0 +1,33 @@
/*
* MM_CODEMAP_SET.pgc - mm 모듈 서비스 MM_CODEMAP_SET (one service = one file).
* 기능: 코드 매핑 등록/갱신 (외부코드->내부코드 변환표, MAP_<네임스페이스> 코드그룹 upsert).
* 입력: T_KEY1=네임스페이스(예: VAN1), T_STR1=원본 코드, T_STR2=대상 코드.
* RC: 3=필수값 누락. 재등록 시 대상 코드 교체 + 상태 ACTIVE 복원.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_CODEMAP_SET.h"
void MM_CODEMAP_SET(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char ns[16], src[32], dst[32];
EXEC SQL BEGIN DECLARE SECTION;
char h_grp[24], h_src[32], h_dst[32];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, ns, sizeof(ns));
gets_(b, T_STR1, src, sizeof(src));
gets_(b, T_STR2, dst, sizeof(dst));
if (ns[0] == 0 || src[0] == 0 || dst[0] == 0) { setl(b, T_RC, 3); OK(b); }
snprintf(h_grp, sizeof(h_grp), "MAP_%s", ns);
strncpy(h_src, src, sizeof(h_src)-1); h_src[sizeof(h_src)-1] = 0;
strncpy(h_dst, dst, sizeof(h_dst)-1); h_dst[sizeof(h_dst)-1] = 0;
EXEC SQL INSERT INTO mm_code (code_group, code, code_name, status)
VALUES (:h_grp, :h_src, :h_dst, 'ACTIVE')
ON CONFLICT (code_group, code)
DO UPDATE SET code_name = EXCLUDED.code_name, status = 'ACTIVE';
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_CONTRACT_END.h - mm MM_CONTRACT_END (copybook / record header).
* UBF : T_MERCHANT, T_REASON, T_RC
*/
#ifndef MM_CONTRACT_END_H
#define MM_CONTRACT_END_H
#include "mm_svc.h"
void MM_CONTRACT_END(TPSVCINFO *p);
#endif /* MM_CONTRACT_END_H */

View file

@ -0,0 +1,43 @@
/*
* MM_CONTRACT_END.pgc - mm 모듈 서비스 MM_CONTRACT_END (one service = one file).
* 기능: 가맹 계약 해지 (CONT_END 이력 + 가맹점 상태 HOLD 전환).
* 입력: T_MERCHANT, T_REASON=해지 사유(필수).
* RC: 1=체결된 계약 없음, 2=이미 해지됨, 3=해지 사유 누락.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_CONTRACT_END.h"
void MM_CONTRACT_END(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], reason[128];
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_end[16];
long h_ended;
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_REASON, reason, sizeof(reason));
if (reason[0] == 0) { setl(b, T_RC, 3); OK(b); }
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT after_val INTO :h_end FROM mm_merch_hist
WHERE merchant_id = :h_merch AND action = 'CONTRACT'
ORDER BY hist_id DESC LIMIT 1;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT count(*) INTO :h_ended FROM mm_merch_hist
WHERE merchant_id = :h_merch AND action = 'CONT_END'
AND hist_id > (SELECT max(hist_id) FROM mm_merch_hist
WHERE merchant_id = :h_merch AND action = 'CONTRACT');
if (sqlca.sqlcode < 0) FAIL(b);
if (h_ended > 0) { setl(b, T_RC, 2); OK(b); }
EXEC SQL UPDATE mm_merchant_ext SET status = 'HOLD', updated_at = now()
WHERE merchant_id = :h_merch AND status = 'ACTIVE';
if (sqlca.sqlcode < 0) FAIL(b);
if (mmdb_insert_merch_hist(h_merch, "CONT_END", h_end, "", reason) < 0) FAIL(b);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_CONTRACT_INQ.h - mm MM_CONTRACT_INQ (copybook / record header).
* UBF : T_MERCHANT, T_COUNT( ), T_STR1( ), T_ARG1( ), T_RC
*/
#ifndef MM_CONTRACT_INQ_H
#define MM_CONTRACT_INQ_H
#include "mm_svc.h"
void MM_CONTRACT_INQ(TPSVCINFO *p);
#endif /* MM_CONTRACT_INQ_H */

View file

@ -0,0 +1,37 @@
/*
* MM_CONTRACT_INQ.pgc - mm 모듈 서비스 MM_CONTRACT_INQ (one service = one file).
* 기능: 가맹 계약 현황 조회 (체결/갱신 횟수, 최종 만기일, 유효 여부).
* 입력: T_MERCHANT. 출력: T_COUNT=체결·갱신 누계, T_STR1=최종 만기, T_ARG1=1이면 만기 미도래.
* RC: 1=계약 이력 없음.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_CONTRACT_INQ.h"
void MM_CONTRACT_INQ(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64];
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_end[16], h_today[16];
long h_cnt;
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT count(*), coalesce(max(after_val), '')
INTO :h_cnt, :h_end
FROM mm_merch_hist
WHERE merchant_id = :h_merch AND action = 'CONTRACT';
if (sqlca.sqlcode < 0) FAIL(b);
if (h_cnt == 0) { setl(b, T_RC, 1); OK(b); }
EXEC SQL SELECT to_char(current_date, 'YYYYMMDD') INTO :h_today;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_cnt);
Bchg(b, T_STR1, 0, h_end, 0L);
setl(b, T_ARG1, strcmp(h_end, h_today) > 0 ? 1 : 0);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_CONTRACT_NEW.h - mm MM_CONTRACT_NEW (copybook / record header).
* UBF : T_MERCHANT, T_ARG1( ), T_STR1(), T_RC
*/
#ifndef MM_CONTRACT_NEW_H
#define MM_CONTRACT_NEW_H
#include "mm_svc.h"
void MM_CONTRACT_NEW(TPSVCINFO *p);
#endif /* MM_CONTRACT_NEW_H */

View file

@ -0,0 +1,44 @@
/*
* MM_CONTRACT_NEW.pgc - mm 모듈 서비스 MM_CONTRACT_NEW (one service = one file).
* 기능: 가맹 계약 체결 (기간 개월수로 만기일 산출, CONTRACT 이력 기록).
* 입력: T_MERCHANT, T_ARG1=계약기간 개월(1~120, 기본 12). 출력: T_STR1=만기일(YYYYMMDD).
* RC: 1=활성 가맹점 아님, 3=기간 범위 오류, 4=미만기 계약 이미 존재.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_CONTRACT_NEW.h"
void MM_CONTRACT_NEW(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64];
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_end[16];
long h_mon, h_alive, h_open;
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
h_mon = getl(b, T_ARG1);
if (h_mon == 0) h_mon = 12;
if (h_mon < 1 || h_mon > 120) { setl(b, T_RC, 3); OK(b); }
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT count(*) INTO :h_alive FROM mm_merchant_ext
WHERE merchant_id = :h_merch AND status = 'ACTIVE';
if (sqlca.sqlcode < 0) FAIL(b);
if (h_alive == 0) { setl(b, T_RC, 1); OK(b); }
/* 만기 미도래 계약이 남아 있으면 신규 체결 불가 */
EXEC SQL SELECT count(*) INTO :h_open FROM mm_merch_hist
WHERE merchant_id = :h_merch AND action = 'CONTRACT'
AND after_val > to_char(current_date, 'YYYYMMDD');
if (sqlca.sqlcode < 0) FAIL(b);
if (h_open > 0) { setl(b, T_RC, 4); OK(b); }
EXEC SQL SELECT to_char(current_date + :h_mon * interval '1 month', 'YYYYMMDD')
INTO :h_end;
if (sqlca.sqlcode < 0) FAIL(b);
if (mmdb_insert_merch_hist(h_merch, "CONTRACT", "", h_end, "가맹 계약 체결") < 0) FAIL(b);
Bchg(b, T_STR1, 0, h_end, 0L);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_CONTRACT_RENEW.h - mm MM_CONTRACT_RENEW (copybook / record header).
* UBF : T_MERCHANT, T_ARG1( ), T_STR1( ), T_STR2( ), T_RC
*/
#ifndef MM_CONTRACT_RENEW_H
#define MM_CONTRACT_RENEW_H
#include "mm_svc.h"
void MM_CONTRACT_RENEW(TPSVCINFO *p);
#endif /* MM_CONTRACT_RENEW_H */

View file

@ -0,0 +1,47 @@
/*
* MM_CONTRACT_RENEW.pgc - mm 모듈 서비스 MM_CONTRACT_RENEW (one service = one file).
* 기능: 가맹 계약 갱신 (직전 만기일 기준 개월수 연장).
* 입력: T_MERCHANT, T_ARG1=연장 개월(기본 12). 출력: T_STR1=기존 만기, T_STR2=신규 만기.
* RC: 1=체결된 계약 없음, 2=해지된 계약(갱신 불가), 3=기간 범위 오류.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_CONTRACT_RENEW.h"
void MM_CONTRACT_RENEW(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64];
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_old[16], h_new[16];
long h_mon, h_ended;
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
h_mon = getl(b, T_ARG1);
if (h_mon == 0) h_mon = 12;
if (h_mon < 1 || h_mon > 120) { setl(b, T_RC, 3); OK(b); }
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT after_val INTO :h_old FROM mm_merch_hist
WHERE merchant_id = :h_merch AND action = 'CONTRACT'
ORDER BY hist_id DESC LIMIT 1;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
/* 만기 이후 해지 이력이 있으면 갱신 불가 */
EXEC SQL SELECT count(*) INTO :h_ended FROM mm_merch_hist
WHERE merchant_id = :h_merch AND action = 'CONT_END'
AND hist_id > (SELECT max(hist_id) FROM mm_merch_hist
WHERE merchant_id = :h_merch AND action = 'CONTRACT');
if (sqlca.sqlcode < 0) FAIL(b);
if (h_ended > 0) { setl(b, T_RC, 2); OK(b); }
EXEC SQL SELECT to_char(to_date(:h_old, 'YYYYMMDD') + :h_mon * interval '1 month', 'YYYYMMDD')
INTO :h_new;
if (sqlca.sqlcode < 0) FAIL(b);
if (mmdb_insert_merch_hist(h_merch, "CONTRACT", h_old, h_new, "계약 갱신 연장") < 0) FAIL(b);
Bchg(b, T_STR1, 0, h_old, 0L);
Bchg(b, T_STR2, 0, h_new, 0L);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_GRADE_ADJUST.h - mm MM_GRADE_ADJUST (copybook / record header).
* UBF : T_MERCHANT, T_STR2( ), T_REASON(), T_STR1( ), T_RC
*/
#ifndef MM_GRADE_ADJUST_H
#define MM_GRADE_ADJUST_H
#include "mm_svc.h"
void MM_GRADE_ADJUST(TPSVCINFO *p);
#endif /* MM_GRADE_ADJUST_H */

View file

@ -0,0 +1,38 @@
/*
* MM_GRADE_ADJUST.pgc - mm 모듈 서비스 MM_GRADE_ADJUST (one service = one file).
* 기능: 가맹점 등급 수동 보정 (산정 결과 예외 조정, 조정 사유 필수 감사 기록).
* 입력: T_MERCHANT, T_STR2=조정 등급(S/A/B/C), T_REASON=조정 사유(필수).
* 출력: T_STR1=이전 등급. RC: 1=가맹점 없음, 2=동일 등급, 3=등급값 오류, 4=사유 누락.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_GRADE_ADJUST.h"
void MM_GRADE_ADJUST(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], grade[8], reason[128];
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_old[8], h_new[8];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_STR2, grade, sizeof(grade));
gets_(b, T_REASON, reason, sizeof(reason));
if (strlen(grade) != 1 || strchr("SABC", grade[0]) == NULL) { setl(b, T_RC, 3); OK(b); }
if (reason[0] == 0) { setl(b, T_RC, 4); OK(b); }
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
strncpy(h_new, grade, sizeof(h_new)-1); h_new[sizeof(h_new)-1] = 0;
EXEC SQL SELECT grade INTO :h_old FROM mm_merchant_ext WHERE merchant_id = :h_merch;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
if (strcmp(h_old, h_new) == 0) { setl(b, T_RC, 2); OK(b); }
EXEC SQL UPDATE mm_merchant_ext SET grade = :h_new, updated_at = now()
WHERE merchant_id = :h_merch;
if (sqlca.sqlcode < 0) FAIL(b);
if (mmdb_insert_merch_hist(h_merch, "GRADE_ADJ", h_old, h_new, reason) < 0) FAIL(b);
Bchg(b, T_STR1, 0, h_old, 0L);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_GRADE_DIST.h - mm MM_GRADE_DIST (copybook / record header).
* UBF : T_BIZDATE(), T_ARG1~T_ARG4(S/A/B/C ), T_COUNT(), T_RC
*/
#ifndef MM_GRADE_DIST_H
#define MM_GRADE_DIST_H
#include "mm_svc.h"
void MM_GRADE_DIST(TPSVCINFO *p);
#endif /* MM_GRADE_DIST_H */

View file

@ -0,0 +1,42 @@
/*
* MM_GRADE_DIST.pgc - mm 모듈 서비스 MM_GRADE_DIST (one service = one file).
* 기능: 가맹점 등급 분포 조회 (최근 등급 통계일 기준 S/A/B/C 세분 현황).
* 출력: T_BIZDATE=통계 기준일, T_ARG1=S, T_ARG2=A, T_ARG3=B, T_ARG4=C, T_COUNT=합계.
* RC: 1=통계 미생성 (mm_stat_batch 선행 필요).
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_GRADE_DIST.h"
void MM_GRADE_DIST(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
char h_date[16];
long h_s, h_a, h_b, h_c, h_rows;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT count(*) INTO :h_rows FROM mm_grade_stat;
if (sqlca.sqlcode < 0) FAIL(b);
if (h_rows == 0) { setl(b, T_RC, 1); OK(b); }
EXEC SQL SELECT to_char(max(stat_date), 'YYYYMMDD') INTO :h_date FROM mm_grade_stat;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT coalesce(sum(CASE WHEN grade = 'S' THEN merch_cnt ELSE 0 END), 0),
coalesce(sum(CASE WHEN grade = 'A' THEN merch_cnt ELSE 0 END), 0),
coalesce(sum(CASE WHEN grade = 'B' THEN merch_cnt ELSE 0 END), 0),
coalesce(sum(CASE WHEN grade = 'C' THEN merch_cnt ELSE 0 END), 0)
INTO :h_s, :h_a, :h_b, :h_c
FROM mm_grade_stat
WHERE stat_date = (SELECT max(stat_date) FROM mm_grade_stat);
if (sqlca.sqlcode < 0) FAIL(b);
Bchg(b, T_BIZDATE, 0, h_date, 0L);
setl(b, T_ARG1, h_s);
setl(b, T_ARG2, h_a);
setl(b, T_ARG3, h_b);
setl(b, T_ARG4, h_c);
setl(b, T_COUNT, h_s + h_a + h_b + h_c);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_GRADE_PREVIEW.h - mm MM_GRADE_PREVIEW (copybook / record header).
* UBF : T_MERCHANT, T_STR1( ), T_STR2( ), T_GROSS, T_ARG1( ), T_RC
*/
#ifndef MM_GRADE_PREVIEW_H
#define MM_GRADE_PREVIEW_H
#include "mm_svc.h"
void MM_GRADE_PREVIEW(TPSVCINFO *p);
#endif /* MM_GRADE_PREVIEW_H */

View file

@ -0,0 +1,43 @@
/*
* MM_GRADE_PREVIEW.pgc - mm 모듈 서비스 MM_GRADE_PREVIEW (one service = one file).
* 기능: 가맹점 등급 산정 시뮬레이션 (누적 매출로 등급 산출, 마스터는 불변).
* 임계: S>=10억, A>=3억, B>=5천만, 그 외 C (배치 재산정과 동일 기준).
* 입력: T_MERCHANT. 출력: T_STR1=현재, T_STR2=산정, T_GROSS=누적 매출, T_ARG1=1이면 등급 변경 대상.
* RC: 1=가맹점 없음.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_GRADE_PREVIEW.h"
void MM_GRADE_PREVIEW(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64];
const char *calc;
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_cur[8];
long h_gross;
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT grade INTO :h_cur FROM mm_merchant_ext WHERE merchant_id = :h_merch;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT coalesce(sum(gross_amount), 0) INTO :h_gross
FROM merchant_summary WHERE merchant_id = :h_merch;
if (sqlca.sqlcode < 0) FAIL(b);
if (h_gross >= 1000000000L) calc = "S";
else if (h_gross >= 300000000L) calc = "A";
else if (h_gross >= 50000000L) calc = "B";
else calc = "C";
Bchg(b, T_STR1, 0, h_cur, 0L);
Bchg(b, T_STR2, 0, (char *)calc, 0L);
setl(b, T_GROSS, h_gross);
setl(b, T_ARG1, strcmp(h_cur, calc) != 0 ? 1 : 0);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_MCC_INQ.h - mm MM_MCC_INQ (copybook / record header).
* UBF : T_CATEGORY(MCC), T_STR1(), T_ARG1(), T_COUNT( ), T_RC
*/
#ifndef MM_MCC_INQ_H
#define MM_MCC_INQ_H
#include "mm_svc.h"
void MM_MCC_INQ(TPSVCINFO *p);
#endif /* MM_MCC_INQ_H */

View file

@ -0,0 +1,36 @@
/*
* MM_MCC_INQ.pgc - mm 모듈 서비스 MM_MCC_INQ (one service = one file).
* 기능: MCC 업종 조회 (명칭/위험도 + 해당 업종 활성 가맹점 수).
* 입력: T_CATEGORY=MCC. 출력: T_STR1, T_ARG1, T_COUNT.
* RC: 1=미등록 MCC.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_MCC_INQ.h"
void MM_MCC_INQ(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char mcc[8];
EXEC SQL BEGIN DECLARE SECTION;
char h_mcc[8], h_name[64];
long h_risk, h_merch;
EXEC SQL END DECLARE SECTION;
gets_(b, T_CATEGORY, mcc, sizeof(mcc));
strncpy(h_mcc, mcc, sizeof(h_mcc)-1); h_mcc[sizeof(h_mcc)-1] = 0;
EXEC SQL SELECT mcc_name, risk_level INTO :h_name, :h_risk
FROM mm_mcc WHERE mcc = :h_mcc;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT count(*) INTO :h_merch FROM mm_merchant_ext
WHERE mcc = :h_mcc AND status = 'ACTIVE';
if (sqlca.sqlcode < 0) FAIL(b);
Bchg(b, T_STR1, 0, h_name, 0L);
setl(b, T_ARG1, h_risk);
setl(b, T_COUNT, h_merch);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_MCC_RISKLIST.h - mm MM_MCC_RISKLIST (copybook / record header).
* UBF : T_ARG1( ), T_COUNT(MCC ), T_ARG2( ), T_ARG3( ), T_RC
*/
#ifndef MM_MCC_RISKLIST_H
#define MM_MCC_RISKLIST_H
#include "mm_svc.h"
void MM_MCC_RISKLIST(TPSVCINFO *p);
#endif /* MM_MCC_RISKLIST_H */

View file

@ -0,0 +1,36 @@
/*
* MM_MCC_RISKLIST.pgc - mm 모듈 서비스 MM_MCC_RISKLIST (one service = one file).
* 기능: 고위험 MCC 현황 (기준 위험도 이상 MCC 수, 최고 위험도, 소속 활성 가맹점 수).
* 입력: T_ARG1=최소 위험도(기본 3). 출력: T_COUNT, T_ARG2, T_ARG3.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_MCC_RISKLIST.h"
void MM_MCC_RISKLIST(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
EXEC SQL BEGIN DECLARE SECTION;
long h_min, h_cnt, h_max, h_merch;
EXEC SQL END DECLARE SECTION;
h_min = getl(b, T_ARG1);
if (h_min <= 0) h_min = 3;
EXEC SQL SELECT count(*), coalesce(max(risk_level), 0)
INTO :h_cnt, :h_max
FROM mm_mcc WHERE risk_level >= :h_min;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL SELECT count(*) INTO :h_merch
FROM mm_merchant_ext me
WHERE me.status = 'ACTIVE'
AND EXISTS (SELECT 1 FROM mm_mcc c
WHERE c.mcc = me.mcc AND c.risk_level >= :h_min);
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_COUNT, h_cnt);
setl(b, T_ARG2, h_max);
setl(b, T_ARG3, h_merch);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_MCC_UPD.h - mm MM_MCC_UPD (copybook / record header).
* UBF : T_CATEGORY(MCC), T_STR1(), T_ARG1(), T_RC
*/
#ifndef MM_MCC_UPD_H
#define MM_MCC_UPD_H
#include "mm_svc.h"
void MM_MCC_UPD(TPSVCINFO *p);
#endif /* MM_MCC_UPD_H */

View file

@ -0,0 +1,37 @@
/*
* MM_MCC_UPD.pgc - mm 모듈 서비스 MM_MCC_UPD (one service = one file).
* 기능: MCC 업종 마스터 변경 (명칭 선택 변경 + 위험도 1~5 재설정).
* 입력: T_CATEGORY=MCC, T_STR1=명칭(빈값이면 유지), T_ARG1=위험도(1~5).
* RC: 1=미등록 MCC, 3=위험도 범위 오류.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_MCC_UPD.h"
void MM_MCC_UPD(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char mcc[8], name[64];
EXEC SQL BEGIN DECLARE SECTION;
char h_mcc[8], h_name[64];
long h_risk, h_found;
EXEC SQL END DECLARE SECTION;
gets_(b, T_CATEGORY, mcc, sizeof(mcc));
gets_(b, T_STR1, name, sizeof(name));
h_risk = getl(b, T_ARG1);
if (h_risk < 1 || h_risk > 5) { setl(b, T_RC, 3); OK(b); }
strncpy(h_mcc, mcc, sizeof(h_mcc)-1); h_mcc[sizeof(h_mcc)-1] = 0;
strncpy(h_name, name, sizeof(h_name)-1); h_name[sizeof(h_name)-1] = 0;
EXEC SQL SELECT count(*) INTO :h_found FROM mm_mcc WHERE mcc = :h_mcc;
if (sqlca.sqlcode < 0) FAIL(b);
if (h_found == 0) { setl(b, T_RC, 1); OK(b); }
EXEC SQL UPDATE mm_mcc
SET mcc_name = CASE WHEN :h_name = '' THEN mcc_name ELSE :h_name END,
risk_level = :h_risk
WHERE mcc = :h_mcc;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_MERCH_CLOSE.h - mm MM_MERCH_CLOSE (copybook / record header).
* UBF : T_MERCHANT, T_REASON, T_COUNT( ), T_RC
*/
#ifndef MM_MERCH_CLOSE_H
#define MM_MERCH_CLOSE_H
#include "mm_svc.h"
void MM_MERCH_CLOSE(TPSVCINFO *p);
#endif /* MM_MERCH_CLOSE_H */

View file

@ -0,0 +1,46 @@
/*
* MM_MERCH_CLOSE.pgc - mm 모듈 서비스 MM_MERCH_CLOSE (one service = one file).
* 기능: 가맹점 폐업 처리 (DELETED 전환 + 전체 단말 폐기 + 하위 관계 해제).
* 입력: T_MERCHANT, T_REASON=폐업 사유(없으면 '폐업 신고'). 출력: T_COUNT=폐기된 단말 수.
* RC: 1=가맹점 없음, 2=이미 폐업됨.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_MERCH_CLOSE.h"
void MM_MERCH_CLOSE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], reason[128], memo[160];
long nterm;
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_stat[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_REASON, reason, sizeof(reason));
if (reason[0] == 0) strcpy(reason, "폐업 신고");
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT status INTO :h_stat FROM mm_merchant_ext WHERE merchant_id = :h_merch;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
if (strcmp(h_stat, "DELETED") == 0) { setl(b, T_RC, 2); OK(b); }
EXEC SQL UPDATE mm_merchant_ext SET status = 'DELETED', updated_at = now()
WHERE merchant_id = :h_merch;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL UPDATE mm_terminal SET status = 'SCRAPPED'
WHERE merchant_id = :h_merch AND status <> 'SCRAPPED';
if (sqlca.sqlcode < 0) FAIL(b);
nterm = sqlca.sqlerrd[2];
/* 대표-하위 관계도 함께 해제 (이 가맹점이 하위이거나 대표인 경우 모두) */
EXEC SQL UPDATE mm_code SET status = 'INACTIVE'
WHERE code_group = 'MERCH_REL'
AND (code = :h_merch OR code_name = :h_merch) AND status = 'ACTIVE';
if (sqlca.sqlcode < 0) FAIL(b);
snprintf(memo, sizeof(memo), "%s (단말 %ld대 폐기)", reason, nterm);
if (mmdb_insert_merch_hist(h_merch, "CLOSE", h_stat, "DELETED", memo) < 0) FAIL(b);
setl(b, T_COUNT, nterm);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_MERCH_PAUSE.h - mm MM_MERCH_PAUSE (copybook / record header).
* UBF : T_MERCHANT, T_REASON, T_COUNT( ), T_RC
*/
#ifndef MM_MERCH_PAUSE_H
#define MM_MERCH_PAUSE_H
#include "mm_svc.h"
void MM_MERCH_PAUSE(TPSVCINFO *p);
#endif /* MM_MERCH_PAUSE_H */

View file

@ -0,0 +1,40 @@
/*
* MM_MERCH_PAUSE.pgc - mm 모듈 서비스 MM_MERCH_PAUSE (one service = one file).
* 기능: 가맹점 휴업 처리 (ACTIVE -> HOLD, 가동 단말 일괄 SUSPENDED).
* 입력: T_MERCHANT, T_REASON=휴업 사유(없으면 '자진 휴업'). 출력: T_COUNT=정지된 단말 수.
* RC: 1=가맹점 없음, 2=활성 상태가 아님.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_MERCH_PAUSE.h"
void MM_MERCH_PAUSE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], reason[128];
long nterm;
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_stat[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
gets_(b, T_REASON, reason, sizeof(reason));
if (reason[0] == 0) strcpy(reason, "자진 휴업");
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT status INTO :h_stat FROM mm_merchant_ext WHERE merchant_id = :h_merch;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
if (strcmp(h_stat, "ACTIVE") != 0) { setl(b, T_RC, 2); OK(b); }
EXEC SQL UPDATE mm_merchant_ext SET status = 'HOLD', updated_at = now()
WHERE merchant_id = :h_merch;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL UPDATE mm_terminal SET status = 'SUSPENDED'
WHERE merchant_id = :h_merch AND status = 'ACTIVE';
if (sqlca.sqlcode < 0) FAIL(b);
nterm = sqlca.sqlerrd[2];
if (mmdb_insert_merch_hist(h_merch, "PAUSE", "ACTIVE", "HOLD", reason) < 0) FAIL(b);
setl(b, T_COUNT, nterm);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_MERCH_RESUME.h - mm MM_MERCH_RESUME (copybook / record header).
* UBF : T_MERCHANT, T_COUNT( ), T_RC
*/
#ifndef MM_MERCH_RESUME_H
#define MM_MERCH_RESUME_H
#include "mm_svc.h"
void MM_MERCH_RESUME(TPSVCINFO *p);
#endif /* MM_MERCH_RESUME_H */

View file

@ -0,0 +1,38 @@
/*
* MM_MERCH_RESUME.pgc - mm 모듈 서비스 MM_MERCH_RESUME (one service = one file).
* 기능: 가맹점 영업 재개 (HOLD -> ACTIVE, 정지 단말 재가동).
* 입력: T_MERCHANT. 출력: T_COUNT=재가동된 단말 수.
* RC: 1=가맹점 없음, 2=휴업 상태가 아님.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_MERCH_RESUME.h"
void MM_MERCH_RESUME(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64];
long nterm;
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_stat[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
EXEC SQL SELECT status INTO :h_stat FROM mm_merchant_ext WHERE merchant_id = :h_merch;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
if (strcmp(h_stat, "HOLD") != 0) { setl(b, T_RC, 2); OK(b); }
EXEC SQL UPDATE mm_merchant_ext SET status = 'ACTIVE', updated_at = now()
WHERE merchant_id = :h_merch;
if (sqlca.sqlcode < 0) FAIL(b);
EXEC SQL UPDATE mm_terminal SET status = 'ACTIVE'
WHERE merchant_id = :h_merch AND status = 'SUSPENDED';
if (sqlca.sqlcode < 0) FAIL(b);
nterm = sqlca.sqlerrd[2];
if (mmdb_insert_merch_hist(h_merch, "RESUME", "HOLD", "ACTIVE", "영업 재개") < 0) FAIL(b);
setl(b, T_COUNT, nterm);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_PARTNER_INQ.h - mm MM_PARTNER_INQ (copybook / record header).
* UBF : T_KEY1, T_STR1(), T_STATUS, T_ARG1(), T_RC
*/
#ifndef MM_PARTNER_INQ_H
#define MM_PARTNER_INQ_H
#include "mm_svc.h"
void MM_PARTNER_INQ(TPSVCINFO *p);
#endif /* MM_PARTNER_INQ_H */

View file

@ -0,0 +1,32 @@
/*
* MM_PARTNER_INQ.pgc - mm 모듈 서비스 MM_PARTNER_INQ (one service = one file).
* 기능: 제휴사 마스터 단건 조회 (명칭/상태/표시순서).
* 입력: T_KEY1=제휴사 코드. 출력: T_STR1, T_STATUS, T_ARG1.
* RC: 1=미등록 제휴사.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_PARTNER_INQ.h"
void MM_PARTNER_INQ(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char code[16];
EXEC SQL BEGIN DECLARE SECTION;
char h_code[16], h_name[64], h_stat[16];
long h_sort;
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, code, sizeof(code));
strncpy(h_code, code, sizeof(h_code)-1); h_code[sizeof(h_code)-1] = 0;
EXEC SQL SELECT code_name, status, sort_order INTO :h_name, :h_stat, :h_sort
FROM mm_code WHERE code_group = 'PARTNER' AND code = :h_code;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
Bchg(b, T_STR1, 0, h_name, 0L);
Bchg(b, T_STATUS, 0, h_stat, 0L);
setl(b, T_ARG1, h_sort);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_PARTNER_REG.h - mm MM_PARTNER_REG (copybook / record header).
* UBF : T_KEY1( ), T_STR1(), T_ARG1(), T_RC
*/
#ifndef MM_PARTNER_REG_H
#define MM_PARTNER_REG_H
#include "mm_svc.h"
void MM_PARTNER_REG(TPSVCINFO *p);
#endif /* MM_PARTNER_REG_H */

View file

@ -0,0 +1,33 @@
/*
* MM_PARTNER_REG.pgc - mm 모듈 서비스 MM_PARTNER_REG (one service = one file).
* 기능: 제휴사 마스터 등록/갱신 (공통코드 PARTNER 그룹 upsert).
* 입력: T_KEY1=제휴사 코드, T_STR1=제휴사명, T_ARG1=표시순서.
* RC: 3=필수값 누락. 재등록 시 명칭/순서 갱신 (기존 상태 유지).
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_PARTNER_REG.h"
void MM_PARTNER_REG(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char code[16], name[64];
EXEC SQL BEGIN DECLARE SECTION;
char h_code[16], h_name[64];
long h_sort;
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, code, sizeof(code));
gets_(b, T_STR1, name, sizeof(name));
h_sort = getl(b, T_ARG1);
if (code[0] == 0 || name[0] == 0) { setl(b, T_RC, 3); OK(b); }
strncpy(h_code, code, sizeof(h_code)-1); h_code[sizeof(h_code)-1] = 0;
strncpy(h_name, name, sizeof(h_name)-1); h_name[sizeof(h_name)-1] = 0;
EXEC SQL INSERT INTO mm_code (code_group, code, code_name, sort_order)
VALUES ('PARTNER', :h_code, :h_name, :h_sort)
ON CONFLICT (code_group, code)
DO UPDATE SET code_name = EXCLUDED.code_name, sort_order = EXCLUDED.sort_order;
if (sqlca.sqlcode < 0) FAIL(b);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_PARTNER_STATUS.h - mm MM_PARTNER_STATUS (copybook / record header).
* UBF : T_KEY1, T_STATUS(ACTIVE/INACTIVE), T_RC
*/
#ifndef MM_PARTNER_STATUS_H
#define MM_PARTNER_STATUS_H
#include "mm_svc.h"
void MM_PARTNER_STATUS(TPSVCINFO *p);
#endif /* MM_PARTNER_STATUS_H */

View file

@ -0,0 +1,32 @@
/*
* MM_PARTNER_STATUS.pgc - mm 모듈 서비스 MM_PARTNER_STATUS (one service = one file).
* 기능: 제휴사 사용 상태 변경 (ACTIVE/INACTIVE 만 허용).
* 입력: T_KEY1=제휴사 코드, T_STATUS=변경할 상태.
* RC: 1=미등록 제휴사, 3=허용되지 않는 상태값.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_PARTNER_STATUS.h"
void MM_PARTNER_STATUS(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char code[16], stat[16];
EXEC SQL BEGIN DECLARE SECTION;
char h_code[16], h_stat[16];
EXEC SQL END DECLARE SECTION;
gets_(b, T_KEY1, code, sizeof(code));
gets_(b, T_STATUS, stat, sizeof(stat));
if (strcmp(stat, "ACTIVE") != 0 && strcmp(stat, "INACTIVE") != 0) {
setl(b, T_RC, 3); OK(b);
}
strncpy(h_code, code, sizeof(h_code)-1); h_code[sizeof(h_code)-1] = 0;
strncpy(h_stat, stat, sizeof(h_stat)-1); h_stat[sizeof(h_stat)-1] = 0;
EXEC SQL UPDATE mm_code SET status = :h_stat
WHERE code_group = 'PARTNER' AND code = :h_code;
if (sqlca.sqlcode < 0) FAIL(b);
if (sqlca.sqlerrd[2] == 0) { setl(b, T_RC, 1); OK(b); }
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_RATE_APPROVE.h - mm MM_RATE_APPROVE (copybook / record header).
* UBF : T_MERCHANT, T_AMT1( bps), T_STR2(), T_RC
*/
#ifndef MM_RATE_APPROVE_H
#define MM_RATE_APPROVE_H
#include "mm_svc.h"
void MM_RATE_APPROVE(TPSVCINFO *p);
#endif /* MM_RATE_APPROVE_H */

View file

@ -0,0 +1,47 @@
/*
* MM_RATE_APPROVE.pgc - mm 모듈 서비스 MM_RATE_APPROVE (one service = one file).
* 기능: 수수료율 변경 승인 (대기 요청을 실제 요율 마스터에 반영, RATE_APV 이력).
* 입력: T_MERCHANT. 출력: T_STR2=카드종류, T_AMT1=적용 bps.
* RC: 1=대기중 요청 없음, 5=요청 데이터 형식 오류.
* caller의 XA 브랜치에서 실행(EXEC SQL CONNECT 없음); mm_svr가 tpadvertise 한다.
*/
#include "MM_RATE_APPROVE.h"
void MM_RATE_APPROVE(TPSVCINFO *p)
{
UBFH *b = (UBFH *)p->data;
char merch[64], card[16];
char *colon;
long bps;
EXEC SQL BEGIN DECLARE SECTION;
char h_merch[64], h_req[32];
EXEC SQL END DECLARE SECTION;
gets_(b, T_MERCHANT, merch, sizeof(merch));
strncpy(h_merch, merch, sizeof(h_merch)-1); h_merch[sizeof(h_merch)-1] = 0;
/* 최신 미처리 RATE_REQ 를 찾는다 (형식 "CARD:bps") */
EXEC SQL SELECT r.after_val INTO :h_req FROM mm_merch_hist r
WHERE r.merchant_id = :h_merch AND r.action = 'RATE_REQ'
AND NOT EXISTS (SELECT 1 FROM mm_merch_hist d
WHERE d.merchant_id = r.merchant_id
AND d.action IN ('RATE_APV', 'RATE_REJ')
AND d.hist_id > r.hist_id)
ORDER BY r.hist_id DESC LIMIT 1;
if (sqlca.sqlcode == 100) { setl(b, T_RC, 1); OK(b); }
if (sqlca.sqlcode < 0) FAIL(b);
colon = strchr(h_req, ':');
if (colon == NULL) { setl(b, T_RC, 5); OK(b); }
*colon = 0;
strncpy(card, h_req, sizeof(card)-1); card[sizeof(card)-1] = 0;
bps = atol(colon + 1);
if (bps < 1 || bps > 500) { setl(b, T_RC, 5); OK(b); }
if (mmdb_set_fee_rate(h_merch, card, bps) < 0) FAIL(b);
if (mmdb_insert_merch_hist(h_merch, "RATE_APV", "", colon + 1, "요율 변경 승인 반영") < 0) FAIL(b);
Bchg(b, T_STR2, 0, card, 0L);
setl(b, T_AMT1, bps);
setl(b, T_RC, 0);
OK(b);
}

View file

@ -0,0 +1,12 @@
/*
* MM_RATE_REJECT.h - mm MM_RATE_REJECT (copybook / record header).
* UBF : T_MERCHANT, T_REASON, T_STR1( ), T_RC
*/
#ifndef MM_RATE_REJECT_H
#define MM_RATE_REJECT_H
#include "mm_svc.h"
void MM_RATE_REJECT(TPSVCINFO *p);
#endif /* MM_RATE_REJECT_H */

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