diff --git a/app/batch/ac_bt_0001.c b/app/batch/ac_bt_0001.c index 914f440..b833f04 100644 --- a/app/batch/ac_bt_0001.c +++ b/app/batch/ac_bt_0001.c @@ -8,10 +8,11 @@ #line 1 "app/batch/ac_bt_0001.pgc" /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0001.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0001.pgc - 매입 배치마감 (일마감 상태전이) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 금액 유효성에 + * 따라 완료('M')/불일치('U') 로 상태를 전이하고, 완료 건에 대해 + * 총매입금액/총수수료를 집계한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0001 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0001.pgc" +#line 20 "app/batch/ac_bt_0001.pgc" static int run_ac_bt_0001(const char *biz_date) @@ -104,42 +105,48 @@ static int run_ac_bt_0001(const char *biz_date) + -#line 24 "app/batch/ac_bt_0001.pgc" +#line 25 "app/batch/ac_bt_0001.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0001.pgc" +#line 26 "app/batch/ac_bt_0001.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/ac_bt_0001.pgc" +#line 27 "app/batch/ac_bt_0001.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/ac_bt_0001.pgc" +#line 28 "app/batch/ac_bt_0001.pgc" long h_amount ; -#line 28 "app/batch/ac_bt_0001.pgc" +#line 29 "app/batch/ac_bt_0001.pgc" + long h_fee ; + +#line 30 "app/batch/ac_bt_0001.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/ac_bt_0001.pgc" +#line 31 "app/batch/ac_bt_0001.pgc" long total = 0, done = 0, unmatch = 0, errcnt = 0; + long sum_amount = 0, sum_fee = 0; + char buf_amt[32], buf_fee[32]; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0001 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0001.pgc" + /* declare cur_ac_bt_0001 cursor for select key , merch_id , amount , fee from ac_ledger where biz_date = $1 and status = 'R' order by key */ +#line 46 "app/batch/ac_bt_0001.pgc" -#line 42 "app/batch/ac_bt_0001.pgc" +#line 46 "app/batch/ac_bt_0001.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0001 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0001 cursor for select key , merch_id , amount , fee from ac_ledger where biz_date = $1 and status = 'R' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0001.pgc" +#line 48 "app/batch/ac_bt_0001.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0001"); @@ -156,8 +163,10 @@ static int run_ac_bt_0001(const char *biz_date) ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0001.pgc" +#line 58 "app/batch/ac_bt_0001.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -174,6 +183,8 @@ static int run_ac_bt_0001(const char *biz_date) if (amount_is_valid(h_amount)) { strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); done++; + sum_amount += h_amount; + sum_fee += h_fee; } else { strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); unmatch++; @@ -187,7 +198,7 @@ static int run_ac_bt_0001(const char *biz_date) } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0001", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0001.pgc" +#line 88 "app/batch/ac_bt_0001.pgc" if (errcnt == 0) @@ -195,17 +206,22 @@ static int run_ac_bt_0001(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[ac_bt_0001] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_amount, buf_amt, sizeof(buf_amt)); + amount_format_won(sum_fee, buf_fee, sizeof(buf_fee)); - printf("========== 매입 배치 결과 [ac_bt_0001] ==========\n"); + tx_log(TX_LOG_INFO, + "[ac_bt_0001] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld 매입금액=%ld 수수료=%ld", + biz_date, total, done, unmatch, errcnt, sum_amount, sum_fee); + + printf("========== 매입 배치마감 결과 [ac_bt_0001] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); printf(" 완료(M) : %ld\n", done); printf(" 불일치(U) : %ld\n", unmatch); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" 총매입금액 : %s\n", buf_amt); + printf(" 총수수료 : %s\n", buf_fee); + printf("=====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0001.pgc b/app/batch/ac_bt_0001.pgc index e753bf3..1e164c0 100644 --- a/app/batch/ac_bt_0001.pgc +++ b/app/batch/ac_bt_0001.pgc @@ -1,9 +1,10 @@ /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0001.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0001.pgc - 매입 배치마감 (일마감 상태전이) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 금액 유효성에 + * 따라 완료('M')/불일치('U') 로 상태를 전이하고, 완료 건에 대해 + * 총매입금액/총수수료를 집계한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0001 [db@host] */ @@ -25,17 +26,20 @@ static int run_ac_bt_0001(const char *biz_date) char h_key[16]; char h_merch_id[16]; long h_amount; + long h_fee; char h_new_status[2]; EXEC SQL END DECLARE SECTION; long total = 0, done = 0, unmatch = 0, errcnt = 0; + long sum_amount = 0, sum_fee = 0; + char buf_amt[32], buf_fee[32]; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_ac_bt_0001 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, merch_id, amount, fee FROM ac_ledger WHERE biz_date = :h_biz_date AND status = 'R' @@ -51,7 +55,7 @@ static int run_ac_bt_0001(const char *biz_date) tx_log(TX_LOG_WARN, "[ac_bt_0001] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_ac_bt_0001 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_ac_bt_0001 INTO :h_key, :h_merch_id, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -67,6 +71,8 @@ static int run_ac_bt_0001(const char *biz_date) if (amount_is_valid(h_amount)) { strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); done++; + sum_amount += h_amount; + sum_fee += h_fee; } else { strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); unmatch++; @@ -86,17 +92,22 @@ static int run_ac_bt_0001(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[ac_bt_0001] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_amount, buf_amt, sizeof(buf_amt)); + amount_format_won(sum_fee, buf_fee, sizeof(buf_fee)); - printf("========== 매입 배치 결과 [ac_bt_0001] ==========\n"); + tx_log(TX_LOG_INFO, + "[ac_bt_0001] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld 매입금액=%ld 수수료=%ld", + biz_date, total, done, unmatch, errcnt, sum_amount, sum_fee); + + printf("========== 매입 배치마감 결과 [ac_bt_0001] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); printf(" 완료(M) : %ld\n", done); printf(" 불일치(U) : %ld\n", unmatch); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" 총매입금액 : %s\n", buf_amt); + printf(" 총수수료 : %s\n", buf_fee); + printf("=====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0002.c b/app/batch/ac_bt_0002.c index 0dc1688..3e76097 100644 --- a/app/batch/ac_bt_0002.c +++ b/app/batch/ac_bt_0002.c @@ -8,10 +8,10 @@ #line 1 "app/batch/ac_bt_0002.pgc" /* @tier medium @module ac @transform acquire-batch */ /* - * ac_bt_0002.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=medium] + * ac_bt_0002.pgc - 미매입 추출 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 불일치(status='U') 건을 커서로 순회하며 미매입목록 + * (ac_claim, status='X') 으로 추출/적재한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0002 [db@host] */ @@ -104,6 +104,8 @@ static int run_ac_bt_0002(const char *biz_date) + + #line 24 "app/batch/ac_bt_0002.pgc" char h_biz_date [ 9 ] ; @@ -118,28 +120,35 @@ static int run_ac_bt_0002(const char *biz_date) long h_amount ; #line 28 "app/batch/ac_bt_0002.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + char h_claim_no [ 20 ] ; + #line 29 "app/batch/ac_bt_0002.pgc" + int h_file_seq ; + +#line 30 "app/batch/ac_bt_0002.pgc" + char h_status [ 2 ] ; +/* exec sql end declare section */ +#line 31 "app/batch/ac_bt_0002.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, extracted = 0, errcnt = 0; + long sum_amount = 0; + char buf_amt[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0002 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0002.pgc" + /* declare cur_ac_bt_0002 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'U' order by key */ +#line 45 "app/batch/ac_bt_0002.pgc" -#line 42 "app/batch/ac_bt_0002.pgc" +#line 45 "app/batch/ac_bt_0002.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0002 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0002 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'U' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0002.pgc" +#line 47 "app/batch/ac_bt_0002.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0002"); @@ -149,6 +158,9 @@ static int run_ac_bt_0002(const char *biz_date) if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[ac_bt_0002] tx_begin 경고"); + strncpy(h_status, "X", sizeof(h_status)); + h_status[sizeof(h_status) - 1] = '\0'; + for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_bt_0002", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), @@ -157,7 +169,7 @@ static int run_ac_bt_0002(const char *biz_date) ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0002.pgc" +#line 60 "app/batch/ac_bt_0002.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,25 +181,40 @@ static int run_ac_bt_0002(const char *biz_date) } total++; + h_file_seq = 0; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* 미매입목록 파일번호(claim_no) 는 원거래키 기반으로 채번한다 */ + snprintf(h_claim_no, sizeof(h_claim_no), "UNM%s", h_key); - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into ac_claim ( claim_no , merch_id , biz_date , amount , file_seq , status ) values ( $1 , $2 , $3 , $4 , $5 , $6 )", + ECPGt_char,(h_claim_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_file_seq),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 79 "app/batch/ac_bt_0002.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT cur_ac_bt_0002"); + tx_log(TX_LOG_ERROR, "[ac_bt_0002] 미매입 추출 실패 key=%s", h_key); errcnt++; + continue; } + + extracted++; + sum_amount += h_amount; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0002", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0002.pgc" +#line 92 "app/batch/ac_bt_0002.pgc" if (errcnt == 0) @@ -195,17 +222,19 @@ static int run_ac_bt_0002(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[ac_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_amount, buf_amt, sizeof(buf_amt)); - printf("========== 매입 배치 결과 [ac_bt_0002] ==========\n"); + tx_log(TX_LOG_INFO, + "[ac_bt_0002] 요약 date=%s 대상=%ld 추출=%ld 오류=%ld 금액=%ld", + biz_date, total, extracted, errcnt, sum_amount); + + printf("========== 미매입 추출 결과 [ac_bt_0002] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 추출 건수 : %ld\n", extracted); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" 추출 총액 : %s\n", buf_amt); + printf("====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0002.pgc b/app/batch/ac_bt_0002.pgc index ae365be..999374c 100644 --- a/app/batch/ac_bt_0002.pgc +++ b/app/batch/ac_bt_0002.pgc @@ -1,9 +1,9 @@ /* @tier medium @module ac @transform acquire-batch */ /* - * ac_bt_0002.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=medium] + * ac_bt_0002.pgc - 미매입 추출 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 불일치(status='U') 건을 커서로 순회하며 미매입목록 + * (ac_claim, status='X') 으로 추출/적재한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0002 [db@host] */ @@ -25,11 +25,14 @@ static int run_ac_bt_0002(const char *biz_date) char h_key[16]; char h_merch_id[16]; long h_amount; - char h_new_status[2]; + char h_claim_no[20]; + int h_file_seq; + char h_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, extracted = 0, errcnt = 0; + long sum_amount = 0; + char buf_amt[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; @@ -38,7 +41,7 @@ static int run_ac_bt_0002(const char *biz_date) SELECT key, merch_id, amount FROM ac_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'U' ORDER BY key; EXEC SQL OPEN cur_ac_bt_0002; @@ -50,6 +53,9 @@ static int run_ac_bt_0002(const char *biz_date) if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[ac_bt_0002] tx_begin 경고"); + strncpy(h_status, "X", sizeof(h_status)); + h_status[sizeof(h_status) - 1] = '\0'; + for (;;) { EXEC SQL FETCH cur_ac_bt_0002 INTO :h_key, :h_merch_id, :h_amount; @@ -62,21 +68,25 @@ static int run_ac_bt_0002(const char *biz_date) } total++; + h_file_seq = 0; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* 미매입목록 파일번호(claim_no) 는 원거래키 기반으로 채번한다 */ + snprintf(h_claim_no, sizeof(h_claim_no), "UNM%s", h_key); - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL INSERT INTO ac_claim + (claim_no, merch_id, biz_date, amount, file_seq, status) + VALUES + (:h_claim_no, :h_merch_id, :h_biz_date, :h_amount, :h_file_seq, :h_status); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT cur_ac_bt_0002"); + tx_log(TX_LOG_ERROR, "[ac_bt_0002] 미매입 추출 실패 key=%s", h_key); errcnt++; + continue; } + + extracted++; + sum_amount += h_amount; } EXEC SQL CLOSE cur_ac_bt_0002; @@ -86,17 +96,19 @@ static int run_ac_bt_0002(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[ac_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_amount, buf_amt, sizeof(buf_amt)); - printf("========== 매입 배치 결과 [ac_bt_0002] ==========\n"); + tx_log(TX_LOG_INFO, + "[ac_bt_0002] 요약 date=%s 대상=%ld 추출=%ld 오류=%ld 금액=%ld", + biz_date, total, extracted, errcnt, sum_amount); + + printf("========== 미매입 추출 결과 [ac_bt_0002] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 추출 건수 : %ld\n", extracted); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" 추출 총액 : %s\n", buf_amt); + printf("====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0003.c b/app/batch/ac_bt_0003.c index f9d5e00..68352d6 100644 --- a/app/batch/ac_bt_0003.c +++ b/app/batch/ac_bt_0003.c @@ -8,10 +8,11 @@ #line 1 "app/batch/ac_bt_0003.pgc" /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0003.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0003.pgc - 미매입 재처리 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 불일치(status='U') 건을 커서로 순회하며 금액 유효성을 + * 재검증하여 완료('M') 로 재전이한다. 여전히 유효하지 않으면 상태를 + * 그대로 둔다(재실패). 배치 main 엔트리 포함. * * 실행: ac_bt_0003 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0003.pgc" +#line 20 "app/batch/ac_bt_0003.pgc" static int run_ac_bt_0003(const char *biz_date) @@ -105,41 +106,41 @@ static int run_ac_bt_0003(const char *biz_date) -#line 24 "app/batch/ac_bt_0003.pgc" +#line 25 "app/batch/ac_bt_0003.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0003.pgc" +#line 26 "app/batch/ac_bt_0003.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/ac_bt_0003.pgc" +#line 27 "app/batch/ac_bt_0003.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/ac_bt_0003.pgc" +#line 28 "app/batch/ac_bt_0003.pgc" long h_amount ; -#line 28 "app/batch/ac_bt_0003.pgc" +#line 29 "app/batch/ac_bt_0003.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/ac_bt_0003.pgc" +#line 30 "app/batch/ac_bt_0003.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, reprocessed = 0, still_fail = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0003 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0003.pgc" + /* declare cur_ac_bt_0003 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'U' order by key */ +#line 43 "app/batch/ac_bt_0003.pgc" -#line 42 "app/batch/ac_bt_0003.pgc" +#line 43 "app/batch/ac_bt_0003.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0003 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0003 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'U' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0003.pgc" +#line 45 "app/batch/ac_bt_0003.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0003"); @@ -157,7 +158,7 @@ static int run_ac_bt_0003(const char *biz_date) ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0003.pgc" +#line 55 "app/batch/ac_bt_0003.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +171,26 @@ static int run_ac_bt_0003(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 업무규칙: 금액 유효성 재검증 - 유효하면 재처리 성공 */ + if (!amount_is_valid(h_amount)) { + still_fail++; + tx_log(TX_LOG_WARN, "[ac_bt_0003] 재처리 보류 key=%s amount=%ld", h_key, h_amount); + continue; } + strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); rc = ac_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[ac_bt_0003] 재처리 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + + reprocessed++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0003", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0003.pgc" +#line 85 "app/batch/ac_bt_0003.pgc" if (errcnt == 0) @@ -196,16 +199,16 @@ static int run_ac_bt_0003(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0003] 요약 date=%s 대상=%ld 재처리성공=%ld 재실패=%ld 오류=%ld", + biz_date, total, reprocessed, still_fail, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 미매입 재처리 결과 [ac_bt_0003] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 재처리성공(M) : %ld\n", reprocessed); + printf(" 재실패(U) : %ld\n", still_fail); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0003.pgc b/app/batch/ac_bt_0003.pgc index db09cba..65e6304 100644 --- a/app/batch/ac_bt_0003.pgc +++ b/app/batch/ac_bt_0003.pgc @@ -1,9 +1,10 @@ /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0003.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0003.pgc - 미매입 재처리 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 불일치(status='U') 건을 커서로 순회하며 금액 유효성을 + * 재검증하여 완료('M') 로 재전이한다. 여전히 유효하지 않으면 상태를 + * 그대로 둔다(재실패). 배치 main 엔트리 포함. * * 실행: ac_bt_0003 [db@host] */ @@ -28,7 +29,7 @@ static int run_ac_bt_0003(const char *biz_date) char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, reprocessed = 0, still_fail = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); @@ -38,7 +39,7 @@ static int run_ac_bt_0003(const char *biz_date) SELECT key, merch_id, amount FROM ac_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'U' ORDER BY key; EXEC SQL OPEN cur_ac_bt_0003; @@ -63,20 +64,22 @@ static int run_ac_bt_0003(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 업무규칙: 금액 유효성 재검증 - 유효하면 재처리 성공 */ + if (!amount_is_valid(h_amount)) { + still_fail++; + tx_log(TX_LOG_WARN, "[ac_bt_0003] 재처리 보류 key=%s amount=%ld", h_key, h_amount); + continue; } + strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); rc = ac_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[ac_bt_0003] 재처리 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + + reprocessed++; } EXEC SQL CLOSE cur_ac_bt_0003; @@ -87,16 +90,16 @@ static int run_ac_bt_0003(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0003] 요약 date=%s 대상=%ld 재처리성공=%ld 재실패=%ld 오류=%ld", + biz_date, total, reprocessed, still_fail, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 미매입 재처리 결과 [ac_bt_0003] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 재처리성공(M) : %ld\n", reprocessed); + printf(" 재실패(U) : %ld\n", still_fail); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0004.c b/app/batch/ac_bt_0004.c index 90b942b..65e9bed 100644 --- a/app/batch/ac_bt_0004.c +++ b/app/batch/ac_bt_0004.c @@ -8,10 +8,11 @@ #line 1 "app/batch/ac_bt_0004.pgc" /* @tier medium @module ac @transform acquire-batch */ /* - * ac_bt_0004.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=medium] + * ac_bt_0004.pgc - EDI 매입청구 파일생성 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 완료(status='M') 건을 커서로 순회하며 청구파일 SEQ 를 + * 채번하여 ac_claim(status='C') 에 청구 라인으로 적재한다. + * 배치 main 엔트리 포함. * * 실행: ac_bt_0004 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0004.pgc" +#line 20 "app/batch/ac_bt_0004.pgc" static int run_ac_bt_0004(const char *biz_date) @@ -104,42 +105,52 @@ static int run_ac_bt_0004(const char *biz_date) + + -#line 24 "app/batch/ac_bt_0004.pgc" +#line 25 "app/batch/ac_bt_0004.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0004.pgc" +#line 26 "app/batch/ac_bt_0004.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/ac_bt_0004.pgc" +#line 27 "app/batch/ac_bt_0004.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/ac_bt_0004.pgc" +#line 28 "app/batch/ac_bt_0004.pgc" long h_amount ; -#line 28 "app/batch/ac_bt_0004.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ #line 29 "app/batch/ac_bt_0004.pgc" + char h_claim_no [ 20 ] ; + +#line 30 "app/batch/ac_bt_0004.pgc" + int h_file_seq ; + +#line 31 "app/batch/ac_bt_0004.pgc" + char h_status [ 2 ] ; +/* exec sql end declare section */ +#line 32 "app/batch/ac_bt_0004.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, lines = 0, errcnt = 0; + long sum_amount = 0; + int file_seq = 0; + char buf_amt[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0004 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0004.pgc" + /* declare cur_ac_bt_0004 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'M' order by key */ +#line 47 "app/batch/ac_bt_0004.pgc" -#line 42 "app/batch/ac_bt_0004.pgc" +#line 47 "app/batch/ac_bt_0004.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0004 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0004 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0004.pgc" +#line 49 "app/batch/ac_bt_0004.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0004"); @@ -149,6 +160,9 @@ static int run_ac_bt_0004(const char *biz_date) if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[ac_bt_0004] tx_begin 경고"); + strncpy(h_status, "C", sizeof(h_status)); + h_status[sizeof(h_status) - 1] = '\0'; + for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_bt_0004", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), @@ -157,7 +171,7 @@ static int run_ac_bt_0004(const char *biz_date) ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0004.pgc" +#line 62 "app/batch/ac_bt_0004.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,25 +183,40 @@ static int run_ac_bt_0004(const char *biz_date) } total++; + file_seq++; + h_file_seq = file_seq; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + snprintf(h_claim_no, sizeof(h_claim_no), "CLM%s", h_key); - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into ac_claim ( claim_no , merch_id , biz_date , amount , file_seq , status ) values ( $1 , $2 , $3 , $4 , $5 , $6 )", + ECPGt_char,(h_claim_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_file_seq),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 81 "app/batch/ac_bt_0004.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT cur_ac_bt_0004"); + tx_log(TX_LOG_ERROR, "[ac_bt_0004] 청구라인 생성 실패 key=%s", h_key); errcnt++; + continue; } + + lines++; + sum_amount += h_amount; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0004", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0004.pgc" +#line 94 "app/batch/ac_bt_0004.pgc" if (errcnt == 0) @@ -195,17 +224,20 @@ static int run_ac_bt_0004(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[ac_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_amount, buf_amt, sizeof(buf_amt)); - printf("========== 매입 배치 결과 [ac_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[ac_bt_0004] 요약 date=%s 대상=%ld 청구건수=%ld 오류=%ld 금액=%ld 최종SEQ=%d", + biz_date, total, lines, errcnt, sum_amount, file_seq); + + printf("========== EDI 매입청구 파일생성 결과 [ac_bt_0004] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 청구 건수 : %ld\n", lines); + printf(" 오류 건수 : %ld\n", errcnt); + printf(" 청구 총액 : %s\n", buf_amt); + printf(" 파일SEQ 최종 : %d\n", file_seq); + printf("==============================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0004.pgc b/app/batch/ac_bt_0004.pgc index 2ee8194..da29b1c 100644 --- a/app/batch/ac_bt_0004.pgc +++ b/app/batch/ac_bt_0004.pgc @@ -1,9 +1,10 @@ /* @tier medium @module ac @transform acquire-batch */ /* - * ac_bt_0004.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=medium] + * ac_bt_0004.pgc - EDI 매입청구 파일생성 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 완료(status='M') 건을 커서로 순회하며 청구파일 SEQ 를 + * 채번하여 ac_claim(status='C') 에 청구 라인으로 적재한다. + * 배치 main 엔트리 포함. * * 실행: ac_bt_0004 [db@host] */ @@ -25,11 +26,15 @@ static int run_ac_bt_0004(const char *biz_date) char h_key[16]; char h_merch_id[16]; long h_amount; - char h_new_status[2]; + char h_claim_no[20]; + int h_file_seq; + char h_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, lines = 0, errcnt = 0; + long sum_amount = 0; + int file_seq = 0; + char buf_amt[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; @@ -38,7 +43,7 @@ static int run_ac_bt_0004(const char *biz_date) SELECT key, merch_id, amount FROM ac_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'M' ORDER BY key; EXEC SQL OPEN cur_ac_bt_0004; @@ -50,6 +55,9 @@ static int run_ac_bt_0004(const char *biz_date) if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[ac_bt_0004] tx_begin 경고"); + strncpy(h_status, "C", sizeof(h_status)); + h_status[sizeof(h_status) - 1] = '\0'; + for (;;) { EXEC SQL FETCH cur_ac_bt_0004 INTO :h_key, :h_merch_id, :h_amount; @@ -62,21 +70,25 @@ static int run_ac_bt_0004(const char *biz_date) } total++; + file_seq++; + h_file_seq = file_seq; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + snprintf(h_claim_no, sizeof(h_claim_no), "CLM%s", h_key); - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL INSERT INTO ac_claim + (claim_no, merch_id, biz_date, amount, file_seq, status) + VALUES + (:h_claim_no, :h_merch_id, :h_biz_date, :h_amount, :h_file_seq, :h_status); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT cur_ac_bt_0004"); + tx_log(TX_LOG_ERROR, "[ac_bt_0004] 청구라인 생성 실패 key=%s", h_key); errcnt++; + continue; } + + lines++; + sum_amount += h_amount; } EXEC SQL CLOSE cur_ac_bt_0004; @@ -86,17 +98,20 @@ static int run_ac_bt_0004(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[ac_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_amount, buf_amt, sizeof(buf_amt)); - printf("========== 매입 배치 결과 [ac_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[ac_bt_0004] 요약 date=%s 대상=%ld 청구건수=%ld 오류=%ld 금액=%ld 최종SEQ=%d", + biz_date, total, lines, errcnt, sum_amount, file_seq); + + printf("========== EDI 매입청구 파일생성 결과 [ac_bt_0004] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 청구 건수 : %ld\n", lines); + printf(" 오류 건수 : %ld\n", errcnt); + printf(" 청구 총액 : %s\n", buf_amt); + printf(" 파일SEQ 최종 : %d\n", file_seq); + printf("==============================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0005.c b/app/batch/ac_bt_0005.c index 91fe7e5..46ea003 100644 --- a/app/batch/ac_bt_0005.c +++ b/app/batch/ac_bt_0005.c @@ -8,10 +8,11 @@ #line 1 "app/batch/ac_bt_0005.pgc" /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0005.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0005.pgc - 대량 매입 적재 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 가적재(ac_stage) 건을 커서로 순회하며 매입원장 + * (ac_ledger, status='R') 으로 적재한다. 수수료는 거래금액의 1%로 + * 임시 산정한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0005 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0005.pgc" +#line 20 "app/batch/ac_bt_0005.pgc" static int run_ac_bt_0005(const char *biz_date) @@ -104,42 +105,51 @@ static int run_ac_bt_0005(const char *biz_date) + + -#line 24 "app/batch/ac_bt_0005.pgc" +#line 25 "app/batch/ac_bt_0005.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0005.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/ac_bt_0005.pgc" - char h_merch_id [ 16 ] ; + char h_stg_key [ 16 ] ; #line 27 "app/batch/ac_bt_0005.pgc" - long h_amount ; + char h_merch_id [ 16 ] ; #line 28 "app/batch/ac_bt_0005.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + char h_card_no [ 20 ] ; + #line 29 "app/batch/ac_bt_0005.pgc" + long h_amount ; + +#line 30 "app/batch/ac_bt_0005.pgc" + long h_fee ; + +#line 31 "app/batch/ac_bt_0005.pgc" + char h_status [ 2 ] ; +/* exec sql end declare section */ +#line 32 "app/batch/ac_bt_0005.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, loaded = 0, errcnt = 0; + long sum_amount = 0; + char buf_amt[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0005 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0005.pgc" + /* declare cur_ac_bt_0005 cursor for select stg_key , merch_id , card_no , amount from ac_stage where biz_date = $1 order by stg_key */ +#line 45 "app/batch/ac_bt_0005.pgc" -#line 42 "app/batch/ac_bt_0005.pgc" +#line 45 "app/batch/ac_bt_0005.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0005 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0005 cursor for select stg_key , merch_id , card_no , amount from ac_stage where biz_date = $1 order by stg_key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0005.pgc" +#line 47 "app/batch/ac_bt_0005.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0005"); @@ -149,15 +159,20 @@ static int run_ac_bt_0005(const char *biz_date) if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[ac_bt_0005] tx_begin 경고"); + strncpy(h_status, AC_ST_RECV, sizeof(h_status)); + h_status[sizeof(h_status) - 1] = '\0'; + for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_bt_0005", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_stg_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0005.pgc" +#line 60 "app/batch/ac_bt_0005.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,25 +184,39 @@ static int run_ac_bt_0005(const char *biz_date) } total++; + h_fee = h_amount / 100; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into ac_ledger ( key , merch_id , card_no , amount , biz_date , status , fee ) values ( $1 , $2 , $3 , $4 , $5 , $6 , $7 )", + ECPGt_char,(h_stg_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 76 "app/batch/ac_bt_0005.pgc" - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT cur_ac_bt_0005"); + tx_log(TX_LOG_ERROR, "[ac_bt_0005] 적재 실패 key=%s", h_stg_key); errcnt++; + continue; } + + loaded++; + sum_amount += h_amount; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0005", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0005.pgc" +#line 89 "app/batch/ac_bt_0005.pgc" if (errcnt == 0) @@ -195,17 +224,19 @@ static int run_ac_bt_0005(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[ac_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_amount, buf_amt, sizeof(buf_amt)); - printf("========== 매입 배치 결과 [ac_bt_0005] ==========\n"); + tx_log(TX_LOG_INFO, + "[ac_bt_0005] 요약 date=%s 대상=%ld 적재=%ld 오류=%ld 금액=%ld", + biz_date, total, loaded, errcnt, sum_amount); + + printf("========== 대량 매입 적재 결과 [ac_bt_0005] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 적재 건수 : %ld\n", loaded); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" 적재 총액 : %s\n", buf_amt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0005.pgc b/app/batch/ac_bt_0005.pgc index d26104a..4a88ab4 100644 --- a/app/batch/ac_bt_0005.pgc +++ b/app/batch/ac_bt_0005.pgc @@ -1,9 +1,10 @@ /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0005.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0005.pgc - 대량 매입 적재 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 가적재(ac_stage) 건을 커서로 순회하며 매입원장 + * (ac_ledger, status='R') 으로 적재한다. 수수료는 거래금액의 1%로 + * 임시 산정한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0005 [db@host] */ @@ -22,24 +23,26 @@ static int run_ac_bt_0005(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; + char h_stg_key[16]; char h_merch_id[16]; + char h_card_no[20]; long h_amount; - char h_new_status[2]; + long h_fee; + char h_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, loaded = 0, errcnt = 0; + long sum_amount = 0; + char buf_amt[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_ac_bt_0005 CURSOR FOR - SELECT key, merch_id, amount - FROM ac_ledger + SELECT stg_key, merch_id, card_no, amount + FROM ac_stage WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + ORDER BY stg_key; EXEC SQL OPEN cur_ac_bt_0005; if (sqlca.sqlcode != TX_SQL_OK) { @@ -50,8 +53,11 @@ static int run_ac_bt_0005(const char *biz_date) if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[ac_bt_0005] tx_begin 경고"); + strncpy(h_status, AC_ST_RECV, sizeof(h_status)); + h_status[sizeof(h_status) - 1] = '\0'; + for (;;) { - EXEC SQL FETCH cur_ac_bt_0005 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_ac_bt_0005 INTO :h_stg_key, :h_merch_id, :h_card_no, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,21 +68,22 @@ static int run_ac_bt_0005(const char *biz_date) } total++; + h_fee = h_amount / 100; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + EXEC SQL INSERT INTO ac_ledger + (key, merch_id, card_no, amount, biz_date, status, fee) + VALUES + (:h_stg_key, :h_merch_id, :h_card_no, :h_amount, :h_biz_date, :h_status, :h_fee); - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT cur_ac_bt_0005"); + tx_log(TX_LOG_ERROR, "[ac_bt_0005] 적재 실패 key=%s", h_stg_key); errcnt++; + continue; } + + loaded++; + sum_amount += h_amount; } EXEC SQL CLOSE cur_ac_bt_0005; @@ -86,17 +93,19 @@ static int run_ac_bt_0005(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[ac_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_amount, buf_amt, sizeof(buf_amt)); - printf("========== 매입 배치 결과 [ac_bt_0005] ==========\n"); + tx_log(TX_LOG_INFO, + "[ac_bt_0005] 요약 date=%s 대상=%ld 적재=%ld 오류=%ld 금액=%ld", + biz_date, total, loaded, errcnt, sum_amount); + + printf("========== 대량 매입 적재 결과 [ac_bt_0005] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 적재 건수 : %ld\n", loaded); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" 적재 총액 : %s\n", buf_amt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0006.c b/app/batch/ac_bt_0006.c index 0dc9f8d..b4e4fc3 100644 --- a/app/batch/ac_bt_0006.c +++ b/app/batch/ac_bt_0006.c @@ -8,10 +8,11 @@ #line 1 "app/batch/ac_bt_0006.pgc" /* @tier hard @module ac @transform acquire-batch */ /* - * ac_bt_0006.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=hard] + * ac_bt_0006.pgc - 가맹점별 매입집계 배치 [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 완료(status='M') 건을 가맹점(merch_id) 별로 집계하여 + * 통계(ac_stat) 에 적재한다. 가맹점별 통계키(corp_cd 컬럼)는 가맹점 + * 번호를 그대로 사용한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0006 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0006.pgc" +#line 20 "app/batch/ac_bt_0006.pgc" static int run_ac_bt_0006(const char *biz_date) @@ -103,43 +104,40 @@ static int run_ac_bt_0006(const char *biz_date) - -#line 24 "app/batch/ac_bt_0006.pgc" - char h_biz_date [ 9 ] ; - #line 25 "app/batch/ac_bt_0006.pgc" - char h_key [ 16 ] ; + char h_biz_date [ 9 ] ; #line 26 "app/batch/ac_bt_0006.pgc" char h_merch_id [ 16 ] ; #line 27 "app/batch/ac_bt_0006.pgc" - long h_amount ; + long h_cnt ; #line 28 "app/batch/ac_bt_0006.pgc" - char h_new_status [ 2 ] ; + long h_sum ; /* exec sql end declare section */ #line 29 "app/batch/ac_bt_0006.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merchants = 0, grand_cnt = 0, errcnt = 0; + long grand_total = 0; + char buf_amt[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0006 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0006.pgc" + /* declare cur_ac_bt_0006 cursor for select merch_id , count ( * ) , sum ( amount ) from ac_ledger where biz_date = $1 and status = 'M' group by merch_id order by merch_id */ +#line 44 "app/batch/ac_bt_0006.pgc" -#line 42 "app/batch/ac_bt_0006.pgc" +#line 44 "app/batch/ac_bt_0006.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0006 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0006 cursor for select merch_id , count ( * ) , sum ( amount ) from ac_ledger where biz_date = $1 and status = 'M' group by merch_id order by merch_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0006.pgc" +#line 46 "app/batch/ac_bt_0006.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0006"); @@ -151,13 +149,13 @@ static int run_ac_bt_0006(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_bt_0006", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0006.pgc" +#line 56 "app/batch/ac_bt_0006.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,26 +166,32 @@ static int run_ac_bt_0006(const char *biz_date) break; } - total++; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into ac_stat ( biz_date , corp_cd , tot_cnt , tot_amt ) values ( $1 , $2 , $3 , $4 )", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 69 "app/batch/ac_bt_0006.pgc" - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT cur_ac_bt_0006"); + tx_log(TX_LOG_ERROR, "[ac_bt_0006] 집계 적재 실패 merch_id=%s", h_merch_id); errcnt++; + continue; } + + merchants++; + grand_cnt += h_cnt; + grand_total += h_sum; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0006", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0006.pgc" +#line 83 "app/batch/ac_bt_0006.pgc" if (errcnt == 0) @@ -195,17 +199,19 @@ static int run_ac_bt_0006(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[ac_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(grand_total, buf_amt, sizeof(buf_amt)); - printf("========== 매입 배치 결과 [ac_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[ac_bt_0006] 요약 date=%s 가맹점수=%ld 총건수=%ld 오류=%ld 총금액=%ld", + biz_date, merchants, grand_cnt, errcnt, grand_total); + + printf("========== 가맹점별 매입집계 결과 [ac_bt_0006] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 가맹점 수 : %ld\n", merchants); + printf(" 총 매입건수 : %ld\n", grand_cnt); + printf(" 오류 건수 : %ld\n", errcnt); + printf(" 총 매입금액 : %s\n", buf_amt); + printf("==========================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0006.pgc b/app/batch/ac_bt_0006.pgc index 521b584..a86ee92 100644 --- a/app/batch/ac_bt_0006.pgc +++ b/app/batch/ac_bt_0006.pgc @@ -1,9 +1,10 @@ /* @tier hard @module ac @transform acquire-batch */ /* - * ac_bt_0006.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=hard] + * ac_bt_0006.pgc - 가맹점별 매입집계 배치 [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 완료(status='M') 건을 가맹점(merch_id) 별로 집계하여 + * 통계(ac_stat) 에 적재한다. 가맹점별 통계키(corp_cd 컬럼)는 가맹점 + * 번호를 그대로 사용한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0006 [db@host] */ @@ -22,24 +23,25 @@ static int run_ac_bt_0006(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_cnt; + long h_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merchants = 0, grand_cnt = 0, errcnt = 0; + long grand_total = 0; + char buf_amt[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_ac_bt_0006 CURSOR FOR - SELECT key, merch_id, amount + SELECT merch_id, count(*), sum(amount) FROM ac_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status = 'M' + GROUP BY merch_id + ORDER BY merch_id; EXEC SQL OPEN cur_ac_bt_0006; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +53,7 @@ static int run_ac_bt_0006(const char *biz_date) tx_log(TX_LOG_WARN, "[ac_bt_0006] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_ac_bt_0006 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_ac_bt_0006 INTO :h_merch_id, :h_cnt, :h_sum; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,22 +63,21 @@ static int run_ac_bt_0006(const char *biz_date) break; } - total++; + EXEC SQL INSERT INTO ac_stat + (biz_date, corp_cd, tot_cnt, tot_amt) + VALUES + (:h_biz_date, :h_merch_id, :h_cnt, :h_sum); - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT cur_ac_bt_0006"); + tx_log(TX_LOG_ERROR, "[ac_bt_0006] 집계 적재 실패 merch_id=%s", h_merch_id); errcnt++; + continue; } + + merchants++; + grand_cnt += h_cnt; + grand_total += h_sum; } EXEC SQL CLOSE cur_ac_bt_0006; @@ -86,17 +87,19 @@ static int run_ac_bt_0006(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[ac_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(grand_total, buf_amt, sizeof(buf_amt)); - printf("========== 매입 배치 결과 [ac_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[ac_bt_0006] 요약 date=%s 가맹점수=%ld 총건수=%ld 오류=%ld 총금액=%ld", + biz_date, merchants, grand_cnt, errcnt, grand_total); + + printf("========== 가맹점별 매입집계 결과 [ac_bt_0006] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 가맹점 수 : %ld\n", merchants); + printf(" 총 매입건수 : %ld\n", grand_cnt); + printf(" 오류 건수 : %ld\n", errcnt); + printf(" 총 매입금액 : %s\n", buf_amt); + printf("==========================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0007.c b/app/batch/ac_bt_0007.c index 3bb8513..e407cbd 100644 --- a/app/batch/ac_bt_0007.c +++ b/app/batch/ac_bt_0007.c @@ -8,10 +8,11 @@ #line 1 "app/batch/ac_bt_0007.pgc" /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0007.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0007.pgc - 카드사별 매입분류 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 전체 매입 건을 커서로 순회하며 카드번호 BIN 앞 2자리로 + * 카드사(corp_cd) 를 판별하여 매입원장에 반영한다. + * ('94'=KB, '95'=SH, '96'=HN, 그 외=ETC). 배치 main 엔트리 포함. * * 실행: ac_bt_0007 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0007.pgc" +#line 20 "app/batch/ac_bt_0007.pgc" static int run_ac_bt_0007(const char *biz_date) @@ -103,40 +104,37 @@ static int run_ac_bt_0007(const char *biz_date) - -#line 24 "app/batch/ac_bt_0007.pgc" +#line 25 "app/batch/ac_bt_0007.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0007.pgc" +#line 26 "app/batch/ac_bt_0007.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/ac_bt_0007.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/ac_bt_0007.pgc" - long h_amount ; + char h_card_no [ 20 ] ; #line 28 "app/batch/ac_bt_0007.pgc" - char h_new_status [ 2 ] ; + char h_corp_cd [ 4 ] ; /* exec sql end declare section */ #line 29 "app/batch/ac_bt_0007.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0; + long cnt_kb = 0, cnt_sh = 0, cnt_hn = 0, cnt_etc = 0; + char bin2[3]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0007 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_ac_bt_0007 cursor for select key , card_no from ac_ledger where biz_date = $1 order by key */ #line 42 "app/batch/ac_bt_0007.pgc" #line 42 "app/batch/ac_bt_0007.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0007 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0007 cursor for select key , card_no from ac_ledger where biz_date = $1 order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/ac_bt_0007.pgc" @@ -153,9 +151,7 @@ static int run_ac_bt_0007(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_bt_0007", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 54 "app/batch/ac_bt_0007.pgc" @@ -170,24 +166,48 @@ static int run_ac_bt_0007(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* 업무규칙: 카드번호 BIN 앞 2자리로 카드사 판별 */ + bin2[0] = h_card_no[0]; + bin2[1] = h_card_no[1]; + bin2[2] = '\0'; - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (strcmp(bin2, "94") == 0) { + strncpy(h_corp_cd, "KB", sizeof(h_corp_cd)); + cnt_kb++; + } else if (strcmp(bin2, "95") == 0) { + strncpy(h_corp_cd, "SH", sizeof(h_corp_cd)); + cnt_sh++; + } else if (strcmp(bin2, "96") == 0) { + strncpy(h_corp_cd, "HN", sizeof(h_corp_cd)); + cnt_hn++; + } else { + strncpy(h_corp_cd, "ETC", sizeof(h_corp_cd)); + cnt_etc++; + } + h_corp_cd[sizeof(h_corp_cd) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set corp_cd = $1 where key = $2 ", + ECPGt_char,(h_corp_cd),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 88 "app/batch/ac_bt_0007.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cur_ac_bt_0007"); + tx_log(TX_LOG_ERROR, "[ac_bt_0007] 분류 반영 실패 key=%s", h_key); + errcnt++; + continue; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[ac_bt_0007] 대상없음 key=%s", h_key); errcnt++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0007", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0007.pgc" +#line 102 "app/batch/ac_bt_0007.pgc" if (errcnt == 0) @@ -196,16 +216,18 @@ static int run_ac_bt_0007(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0007] 요약 date=%s 대상=%ld KB=%ld SH=%ld HN=%ld ETC=%ld 오류=%ld", + biz_date, total, cnt_kb, cnt_sh, cnt_hn, cnt_etc, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0007] ==========\n"); + printf("========== 카드사별 매입분류 결과 [ac_bt_0007] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" KB : %ld\n", cnt_kb); + printf(" SH : %ld\n", cnt_sh); + printf(" HN : %ld\n", cnt_hn); + printf(" ETC : %ld\n", cnt_etc); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("==========================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0007.pgc b/app/batch/ac_bt_0007.pgc index 009b09e..79e10b8 100644 --- a/app/batch/ac_bt_0007.pgc +++ b/app/batch/ac_bt_0007.pgc @@ -1,9 +1,10 @@ /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0007.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0007.pgc - 카드사별 매입분류 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 전체 매입 건을 커서로 순회하며 카드번호 BIN 앞 2자리로 + * 카드사(corp_cd) 를 판별하여 매입원장에 반영한다. + * ('94'=KB, '95'=SH, '96'=HN, 그 외=ETC). 배치 main 엔트리 포함. * * 실행: ac_bt_0007 [db@host] */ @@ -23,22 +24,21 @@ static int run_ac_bt_0007(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_card_no[20]; + char h_corp_cd[4]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0; + long cnt_kb = 0, cnt_sh = 0, cnt_hn = 0, cnt_etc = 0; + char bin2[3]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_ac_bt_0007 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, card_no FROM ac_ledger WHERE biz_date = :h_biz_date - AND status = 'R' ORDER BY key; EXEC SQL OPEN cur_ac_bt_0007; @@ -51,7 +51,7 @@ static int run_ac_bt_0007(const char *biz_date) tx_log(TX_LOG_WARN, "[ac_bt_0007] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_ac_bt_0007 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_ac_bt_0007 INTO :h_key, :h_card_no; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,18 +63,38 @@ static int run_ac_bt_0007(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* 업무규칙: 카드번호 BIN 앞 2자리로 카드사 판별 */ + bin2[0] = h_card_no[0]; + bin2[1] = h_card_no[1]; + bin2[2] = '\0'; - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (strcmp(bin2, "94") == 0) { + strncpy(h_corp_cd, "KB", sizeof(h_corp_cd)); + cnt_kb++; + } else if (strcmp(bin2, "95") == 0) { + strncpy(h_corp_cd, "SH", sizeof(h_corp_cd)); + cnt_sh++; + } else if (strcmp(bin2, "96") == 0) { + strncpy(h_corp_cd, "HN", sizeof(h_corp_cd)); + cnt_hn++; + } else { + strncpy(h_corp_cd, "ETC", sizeof(h_corp_cd)); + cnt_etc++; + } + h_corp_cd[sizeof(h_corp_cd) - 1] = '\0'; + + EXEC SQL UPDATE ac_ledger + SET corp_cd = :h_corp_cd + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cur_ac_bt_0007"); + tx_log(TX_LOG_ERROR, "[ac_bt_0007] 분류 반영 실패 key=%s", h_key); + errcnt++; + continue; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[ac_bt_0007] 대상없음 key=%s", h_key); errcnt++; } } @@ -87,16 +107,18 @@ static int run_ac_bt_0007(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0007] 요약 date=%s 대상=%ld KB=%ld SH=%ld HN=%ld ETC=%ld 오류=%ld", + biz_date, total, cnt_kb, cnt_sh, cnt_hn, cnt_etc, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0007] ==========\n"); + printf("========== 카드사별 매입분류 결과 [ac_bt_0007] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" KB : %ld\n", cnt_kb); + printf(" SH : %ld\n", cnt_sh); + printf(" HN : %ld\n", cnt_hn); + printf(" ETC : %ld\n", cnt_etc); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("==========================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0008.c b/app/batch/ac_bt_0008.c index 7ccaf60..546e527 100644 --- a/app/batch/ac_bt_0008.c +++ b/app/batch/ac_bt_0008.c @@ -8,10 +8,11 @@ #line 1 "app/batch/ac_bt_0008.pgc" /* @tier hard @module ac @transform acquire-batch */ /* - * ac_bt_0008.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=hard] + * ac_bt_0008.pgc - 매입 수수료 조정(재계산) 배치 [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 완료(status='M') 건을 가맹점 수수료율(merchant.fee_rate) + * 과 조인하여 수수료를 재계산(금액 * 수수료율 / 10000)하고, 기존 수수료 + * 와의 증감분을 집계한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0008 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0008.pgc" +#line 20 "app/batch/ac_bt_0008.pgc" static int run_ac_bt_0008(const char *biz_date) @@ -104,42 +105,46 @@ static int run_ac_bt_0008(const char *biz_date) + -#line 24 "app/batch/ac_bt_0008.pgc" +#line 25 "app/batch/ac_bt_0008.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0008.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/ac_bt_0008.pgc" - char h_merch_id [ 16 ] ; + char h_key [ 16 ] ; #line 27 "app/batch/ac_bt_0008.pgc" long h_amount ; #line 28 "app/batch/ac_bt_0008.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_old_fee ; + #line 29 "app/batch/ac_bt_0008.pgc" + long h_fee_rate ; + +#line 30 "app/batch/ac_bt_0008.pgc" + long h_new_fee ; +/* exec sql end declare section */ +#line 31 "app/batch/ac_bt_0008.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, adjusted = 0, errcnt = 0; + long delta_sum = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0008 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0008.pgc" + /* declare cur_ac_bt_0008 cursor for select l . key , l . amount , l . fee , m . fee_rate from ac_ledger l join merchant m on l . merch_id = m . merch_id where l . biz_date = $1 and l . status = 'M' order by l . key */ +#line 45 "app/batch/ac_bt_0008.pgc" -#line 42 "app/batch/ac_bt_0008.pgc" +#line 45 "app/batch/ac_bt_0008.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0008 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0008 cursor for select l . key , l . amount , l . fee , m . fee_rate from ac_ledger l join merchant m on l . merch_id = m . merch_id where l . biz_date = $1 and l . status = 'M' order by l . key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0008.pgc" +#line 47 "app/batch/ac_bt_0008.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0008"); @@ -153,11 +158,13 @@ static int run_ac_bt_0008(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_bt_0008", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_old_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee_rate),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0008.pgc" +#line 57 "app/batch/ac_bt_0008.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +177,35 @@ static int run_ac_bt_0008(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 업무규칙: 신규수수료 = 금액 * 수수료율(bp) / 10000 */ + h_new_fee = h_amount * h_fee_rate / 10000; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set fee = $1 where key = $2 ", + ECPGt_long,&(h_new_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 74 "app/batch/ac_bt_0008.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cur_ac_bt_0008"); + tx_log(TX_LOG_ERROR, "[ac_bt_0008] 수수료 조정 실패 key=%s", h_key); + errcnt++; + continue; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[ac_bt_0008] 대상없음 key=%s", h_key); + errcnt++; + continue; } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + adjusted++; + delta_sum += (h_new_fee - h_old_fee); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0008", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0008.pgc" +#line 92 "app/batch/ac_bt_0008.pgc" if (errcnt == 0) @@ -196,16 +214,16 @@ static int run_ac_bt_0008(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0008] 요약 date=%s 대상=%ld 조정건수=%ld 오류=%ld 증감액=%ld", + biz_date, total, adjusted, errcnt, delta_sum); - printf("========== 매입 배치 결과 [ac_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 매입 수수료 조정 결과 [ac_bt_0008] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 조정 건수 : %ld\n", adjusted); + printf(" 오류 건수 : %ld\n", errcnt); + printf(" 총 수수료 증감 : %ld\n", delta_sum); + printf("=========================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0008.pgc b/app/batch/ac_bt_0008.pgc index ef09cda..1ff87c2 100644 --- a/app/batch/ac_bt_0008.pgc +++ b/app/batch/ac_bt_0008.pgc @@ -1,9 +1,10 @@ /* @tier hard @module ac @transform acquire-batch */ /* - * ac_bt_0008.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=hard] + * ac_bt_0008.pgc - 매입 수수료 조정(재계산) 배치 [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 완료(status='M') 건을 가맹점 수수료율(merchant.fee_rate) + * 과 조인하여 수수료를 재계산(금액 * 수수료율 / 10000)하고, 기존 수수료 + * 와의 증감분을 집계한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0008 [db@host] */ @@ -23,23 +24,25 @@ static int run_ac_bt_0008(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; + long h_old_fee; + long h_fee_rate; + long h_new_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, adjusted = 0, errcnt = 0; + long delta_sum = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_ac_bt_0008 CURSOR FOR - SELECT key, merch_id, amount - FROM ac_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT l.key, l.amount, l.fee, m.fee_rate + FROM ac_ledger l + JOIN merchant m ON l.merch_id = m.merch_id + WHERE l.biz_date = :h_biz_date + AND l.status = 'M' + ORDER BY l.key; EXEC SQL OPEN cur_ac_bt_0008; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +54,7 @@ static int run_ac_bt_0008(const char *biz_date) tx_log(TX_LOG_WARN, "[ac_bt_0008] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_ac_bt_0008 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_ac_bt_0008 INTO :h_key, :h_amount, :h_old_fee, :h_fee_rate; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +66,27 @@ static int run_ac_bt_0008(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 업무규칙: 신규수수료 = 금액 * 수수료율(bp) / 10000 */ + h_new_fee = h_amount * h_fee_rate / 10000; + + EXEC SQL UPDATE ac_ledger + SET fee = :h_new_fee + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cur_ac_bt_0008"); + tx_log(TX_LOG_ERROR, "[ac_bt_0008] 수수료 조정 실패 key=%s", h_key); + errcnt++; + continue; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[ac_bt_0008] 대상없음 key=%s", h_key); + errcnt++; + continue; } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + adjusted++; + delta_sum += (h_new_fee - h_old_fee); } EXEC SQL CLOSE cur_ac_bt_0008; @@ -87,16 +97,16 @@ static int run_ac_bt_0008(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0008] 요약 date=%s 대상=%ld 조정건수=%ld 오류=%ld 증감액=%ld", + biz_date, total, adjusted, errcnt, delta_sum); - printf("========== 매입 배치 결과 [ac_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 매입 수수료 조정 결과 [ac_bt_0008] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 조정 건수 : %ld\n", adjusted); + printf(" 오류 건수 : %ld\n", errcnt); + printf(" 총 수수료 증감 : %ld\n", delta_sum); + printf("=========================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0009.c b/app/batch/ac_bt_0009.c index f122387..a81f1e3 100644 --- a/app/batch/ac_bt_0009.c +++ b/app/batch/ac_bt_0009.c @@ -8,10 +8,11 @@ #line 1 "app/batch/ac_bt_0009.pgc" /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0009.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0009.pgc - 해외매입 환산 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 외화(cur_code<>'KRW') 매입 건을 커서로 순회하며 + * 통화별 당일 기준환율(fx_rate)로 원화 환산하여 매입원장에 반영한다. + * 배치 main 엔트리 포함. * * 실행: ac_bt_0009 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0009.pgc" +#line 20 "app/batch/ac_bt_0009.pgc" static int run_ac_bt_0009(const char *biz_date) @@ -104,42 +105,47 @@ static int run_ac_bt_0009(const char *biz_date) + -#line 24 "app/batch/ac_bt_0009.pgc" +#line 25 "app/batch/ac_bt_0009.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0009.pgc" +#line 26 "app/batch/ac_bt_0009.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/ac_bt_0009.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/ac_bt_0009.pgc" - long h_amount ; + char h_cur_code [ 5 ] ; #line 28 "app/batch/ac_bt_0009.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_fx_amount ; + #line 29 "app/batch/ac_bt_0009.pgc" + long h_base_rate ; + +#line 30 "app/batch/ac_bt_0009.pgc" + long h_krw ; +/* exec sql end declare section */ +#line 31 "app/batch/ac_bt_0009.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, converted = 0, norate = 0, errcnt = 0; + long sum_krw = 0; + char buf_amt[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0009 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0009.pgc" + /* declare cur_ac_bt_0009 cursor for select key , cur_code , fx_amount from ac_ledger where biz_date = $1 and cur_code is not null and cur_code <> 'KRW' order by key */ +#line 46 "app/batch/ac_bt_0009.pgc" -#line 42 "app/batch/ac_bt_0009.pgc" +#line 46 "app/batch/ac_bt_0009.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0009 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0009 cursor for select key , cur_code , fx_amount from ac_ledger where biz_date = $1 and cur_code is not null and cur_code <> 'KRW' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0009.pgc" +#line 48 "app/batch/ac_bt_0009.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0009"); @@ -153,11 +159,11 @@ static int run_ac_bt_0009(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_bt_0009", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_cur_code),(long)5,(long)1,(5)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_fx_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0009.pgc" +#line 58 "app/batch/ac_bt_0009.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +176,56 @@ static int run_ac_bt_0009(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select base_rate from fx_rate where cur_code = $1 and rate_date = $2 ", + ECPGt_char,(h_cur_code),(long)5,(long)1,(5)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_base_rate),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 74 "app/batch/ac_bt_0009.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[ac_bt_0009] 환율없음 key=%s cur=%s", h_key, h_cur_code); + norate++; + continue; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT fx_rate"); + errcnt++; + continue; } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); + /* 업무규칙: 원화환산액 = 외화금액 * 기준환율 / 10000 */ + h_krw = h_fx_amount * h_base_rate / 10000; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set amount = $1 where key = $2 ", + ECPGt_long,&(h_krw),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 92 "app/batch/ac_bt_0009.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cur_ac_bt_0009"); + tx_log(TX_LOG_ERROR, "[ac_bt_0009] 환산 반영 실패 key=%s", h_key); errcnt++; + continue; } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[ac_bt_0009] 대상없음 key=%s", h_key); + errcnt++; + continue; + } + + converted++; + sum_krw += h_krw; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0009", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0009.pgc" +#line 110 "app/batch/ac_bt_0009.pgc" if (errcnt == 0) @@ -195,17 +233,20 @@ static int run_ac_bt_0009(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[ac_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_krw, buf_amt, sizeof(buf_amt)); - printf("========== 매입 배치 결과 [ac_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[ac_bt_0009] 요약 date=%s 대상=%ld 환산=%ld 환율없음=%ld 오류=%ld 금액=%ld", + biz_date, total, converted, norate, errcnt, sum_krw); + + printf("========== 해외매입 환산 결과 [ac_bt_0009] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 환산 건수 : %ld\n", converted); + printf(" 환율없음 : %ld\n", norate); + printf(" 오류 건수 : %ld\n", errcnt); + printf(" 총 환산금액 : %s\n", buf_amt); + printf("=====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0009.pgc b/app/batch/ac_bt_0009.pgc index 7a6ad35..5de3072 100644 --- a/app/batch/ac_bt_0009.pgc +++ b/app/batch/ac_bt_0009.pgc @@ -1,9 +1,10 @@ /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0009.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0009.pgc - 해외매입 환산 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 외화(cur_code<>'KRW') 매입 건을 커서로 순회하며 + * 통화별 당일 기준환율(fx_rate)로 원화 환산하여 매입원장에 반영한다. + * 배치 main 엔트리 포함. * * 실행: ac_bt_0009 [db@host] */ @@ -23,22 +24,25 @@ static int run_ac_bt_0009(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_cur_code[5]; + long h_fx_amount; + long h_base_rate; + long h_krw; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, converted = 0, norate = 0, errcnt = 0; + long sum_krw = 0; + char buf_amt[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_ac_bt_0009 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, cur_code, fx_amount FROM ac_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND cur_code IS NOT NULL + AND cur_code <> 'KRW' ORDER BY key; EXEC SQL OPEN cur_ac_bt_0009; @@ -51,7 +55,7 @@ static int run_ac_bt_0009(const char *biz_date) tx_log(TX_LOG_WARN, "[ac_bt_0009] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_ac_bt_0009 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_ac_bt_0009 INTO :h_key, :h_cur_code, :h_fx_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +67,44 @@ static int run_ac_bt_0009(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + EXEC SQL SELECT base_rate + INTO :h_base_rate + FROM fx_rate + WHERE cur_code = :h_cur_code + AND rate_date = :h_biz_date; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[ac_bt_0009] 환율없음 key=%s cur=%s", h_key, h_cur_code); + norate++; + continue; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT fx_rate"); + errcnt++; + continue; } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); + /* 업무규칙: 원화환산액 = 외화금액 * 기준환율 / 10000 */ + h_krw = h_fx_amount * h_base_rate / 10000; + + EXEC SQL UPDATE ac_ledger + SET amount = :h_krw + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cur_ac_bt_0009"); + tx_log(TX_LOG_ERROR, "[ac_bt_0009] 환산 반영 실패 key=%s", h_key); errcnt++; + continue; } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[ac_bt_0009] 대상없음 key=%s", h_key); + errcnt++; + continue; + } + + converted++; + sum_krw += h_krw; } EXEC SQL CLOSE cur_ac_bt_0009; @@ -86,17 +114,20 @@ static int run_ac_bt_0009(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[ac_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_krw, buf_amt, sizeof(buf_amt)); - printf("========== 매입 배치 결과 [ac_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[ac_bt_0009] 요약 date=%s 대상=%ld 환산=%ld 환율없음=%ld 오류=%ld 금액=%ld", + biz_date, total, converted, norate, errcnt, sum_krw); + + printf("========== 해외매입 환산 결과 [ac_bt_0009] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 환산 건수 : %ld\n", converted); + printf(" 환율없음 : %ld\n", norate); + printf(" 오류 건수 : %ld\n", errcnt); + printf(" 총 환산금액 : %s\n", buf_amt); + printf("=====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0010.c b/app/batch/ac_bt_0010.c index 7dfbc5e..97b8247 100644 --- a/app/batch/ac_bt_0010.c +++ b/app/batch/ac_bt_0010.c @@ -8,10 +8,12 @@ #line 1 "app/batch/ac_bt_0010.pgc" /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0010.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0010.pgc - 할부매입 전개 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에 접수된 할부매입 원거래(acq_type='INST', install_month>=2, + * orig_key IS NULL) 를 회차별로 전개하여 자매입행(자녀행)을 생성한다. + * 각 자녀행은 원거래를 orig_key 로 참조하며, 금액은 회차수로 균등 분할하고 + * 나머지(단수)는 1회차에 합산한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0010 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0010.pgc" +#line 21 "app/batch/ac_bt_0010.pgc" static int run_ac_bt_0010(const char *biz_date) @@ -104,42 +106,72 @@ static int run_ac_bt_0010(const char *biz_date) + + + + + + + -#line 24 "app/batch/ac_bt_0010.pgc" +#line 26 "app/batch/ac_bt_0010.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0010.pgc" +#line 27 "app/batch/ac_bt_0010.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/ac_bt_0010.pgc" +#line 28 "app/batch/ac_bt_0010.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/ac_bt_0010.pgc" +#line 29 "app/batch/ac_bt_0010.pgc" + char h_card_no [ 20 ] ; + +#line 30 "app/batch/ac_bt_0010.pgc" long h_amount ; -#line 28 "app/batch/ac_bt_0010.pgc" - char h_new_status [ 2 ] ; +#line 31 "app/batch/ac_bt_0010.pgc" + int h_install_month ; + +#line 32 "app/batch/ac_bt_0010.pgc" + char h_child_key [ 16 ] ; + +#line 33 "app/batch/ac_bt_0010.pgc" + long h_child_amount ; + +#line 34 "app/batch/ac_bt_0010.pgc" + char h_status [ 2 ] ; + +#line 35 "app/batch/ac_bt_0010.pgc" + char h_acq_type [ 9 ] ; + +#line 36 "app/batch/ac_bt_0010.pgc" + char h_orig_key [ 16 ] ; + +#line 37 "app/batch/ac_bt_0010.pgc" + long h_fee ; /* exec sql end declare section */ -#line 29 "app/batch/ac_bt_0010.pgc" +#line 38 "app/batch/ac_bt_0010.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long parents = 0, children = 0, errcnt = 0; + long monthly, remainder; + int m; + char keybuf[16]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0010 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0010.pgc" + /* declare cur_ac_bt_0010 cursor for select key , merch_id , card_no , amount , install_month from ac_ledger where biz_date = $1 and acq_type = 'INST' and install_month >= 2 and orig_key is null order by key */ +#line 55 "app/batch/ac_bt_0010.pgc" -#line 42 "app/batch/ac_bt_0010.pgc" +#line 55 "app/batch/ac_bt_0010.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0010 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0010 cursor for select key , merch_id , card_no , amount , install_month from ac_ledger where biz_date = $1 and acq_type = 'INST' and install_month >= 2 and orig_key is null order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0010.pgc" +#line 57 "app/batch/ac_bt_0010.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0010"); @@ -155,9 +187,13 @@ static int run_ac_bt_0010(const char *biz_date) ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_install_month),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0010.pgc" +#line 68 "app/batch/ac_bt_0010.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,26 +204,70 @@ static int run_ac_bt_0010(const char *biz_date) break; } - total++; + parents++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (h_install_month < 2) { + tx_log(TX_LOG_WARN, "[ac_bt_0010] 회차수 이상 key=%s month=%d, 건너뜀", + h_key, h_install_month); + continue; } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + monthly = h_amount / h_install_month; + remainder = h_amount - (monthly * h_install_month); + + strncpy(h_orig_key, h_key, sizeof(h_orig_key) - 1); + h_orig_key[sizeof(h_orig_key) - 1] = '\0'; + strncpy(h_acq_type, "INST", sizeof(h_acq_type) - 1); + h_acq_type[sizeof(h_acq_type) - 1] = '\0'; + strncpy(h_status, AC_ST_RECV, sizeof(h_status)); + h_fee = 0; + + for (m = 1; m <= h_install_month; m++) { + snprintf(keybuf, sizeof(keybuf), "%.12s-%02d", h_key, m); + strncpy(h_child_key, keybuf, sizeof(h_child_key) - 1); + h_child_key[sizeof(h_child_key) - 1] = '\0'; + + h_child_amount = monthly; + if (m == 1) + h_child_amount += remainder; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into ac_ledger ( key , merch_id , card_no , amount , biz_date , status , fee , install_month , acq_type , orig_key ) values ( $1 , $2 , $3 , $4 , $5 , $6 , $7 , $8 , $9 , $10 )", + ECPGt_char,(h_child_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_child_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_install_month),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_acq_type),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_orig_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 111 "app/batch/ac_bt_0010.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT ac_bt_0010 child"); + tx_log(TX_LOG_ERROR, "[ac_bt_0010] 자매입행 생성 실패 key=%s m=%d", + h_key, m); + errcnt++; + continue; + } + children++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0010", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0010.pgc" +#line 124 "app/batch/ac_bt_0010.pgc" if (errcnt == 0) @@ -196,16 +276,15 @@ static int run_ac_bt_0010(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0010] 요약 date=%s 원거래=%ld 전개행=%ld 오류=%ld", + biz_date, parents, children, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 할부매입 전개 결과 [ac_bt_0010] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 원거래 건수 : %ld\n", parents); + printf(" 전개 행수 : %ld\n", children); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0010.pgc b/app/batch/ac_bt_0010.pgc index 8a36e4e..32f1f07 100644 --- a/app/batch/ac_bt_0010.pgc +++ b/app/batch/ac_bt_0010.pgc @@ -1,9 +1,11 @@ /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0010.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0010.pgc - 할부매입 전개 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에 접수된 할부매입 원거래(acq_type='INST', install_month>=2, + * orig_key IS NULL) 를 회차별로 전개하여 자매입행(자녀행)을 생성한다. + * 각 자녀행은 원거래를 orig_key 로 참조하며, 금액은 회차수로 균등 분할하고 + * 나머지(단수)는 1회차에 합산한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0010 [db@host] */ @@ -24,21 +26,32 @@ static int run_ac_bt_0010(const char *biz_date) char h_biz_date[9]; char h_key[16]; char h_merch_id[16]; + char h_card_no[20]; long h_amount; - char h_new_status[2]; + int h_install_month; + char h_child_key[16]; + long h_child_amount; + char h_status[2]; + char h_acq_type[9]; + char h_orig_key[16]; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long parents = 0, children = 0, errcnt = 0; + long monthly, remainder; + int m; + char keybuf[16]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_ac_bt_0010 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, merch_id, card_no, amount, install_month FROM ac_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' + WHERE biz_date = :h_biz_date + AND acq_type = 'INST' + AND install_month >= 2 + AND orig_key IS NULL ORDER BY key; EXEC SQL OPEN cur_ac_bt_0010; @@ -51,7 +64,8 @@ static int run_ac_bt_0010(const char *biz_date) tx_log(TX_LOG_WARN, "[ac_bt_0010] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_ac_bt_0010 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_ac_bt_0010 + INTO :h_key, :h_merch_id, :h_card_no, :h_amount, :h_install_month; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,21 +75,49 @@ static int run_ac_bt_0010(const char *biz_date) break; } - total++; + parents++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (h_install_month < 2) { + tx_log(TX_LOG_WARN, "[ac_bt_0010] 회차수 이상 key=%s month=%d, 건너뜀", + h_key, h_install_month); + continue; } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + monthly = h_amount / h_install_month; + remainder = h_amount - (monthly * h_install_month); + + strncpy(h_orig_key, h_key, sizeof(h_orig_key) - 1); + h_orig_key[sizeof(h_orig_key) - 1] = '\0'; + strncpy(h_acq_type, "INST", sizeof(h_acq_type) - 1); + h_acq_type[sizeof(h_acq_type) - 1] = '\0'; + strncpy(h_status, AC_ST_RECV, sizeof(h_status)); + h_fee = 0; + + for (m = 1; m <= h_install_month; m++) { + snprintf(keybuf, sizeof(keybuf), "%.12s-%02d", h_key, m); + strncpy(h_child_key, keybuf, sizeof(h_child_key) - 1); + h_child_key[sizeof(h_child_key) - 1] = '\0'; + + h_child_amount = monthly; + if (m == 1) + h_child_amount += remainder; + + EXEC SQL INSERT INTO ac_ledger + (key, merch_id, card_no, amount, biz_date, status, fee, + install_month, acq_type, orig_key) + VALUES + (:h_child_key, :h_merch_id, :h_card_no, :h_child_amount, + :h_biz_date, :h_status, :h_fee, :h_install_month, + :h_acq_type, :h_orig_key); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT ac_bt_0010 child"); + tx_log(TX_LOG_ERROR, "[ac_bt_0010] 자매입행 생성 실패 key=%s m=%d", + h_key, m); + errcnt++; + continue; + } + children++; } } @@ -87,16 +129,15 @@ static int run_ac_bt_0010(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0010] 요약 date=%s 원거래=%ld 전개행=%ld 오류=%ld", + biz_date, parents, children, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 할부매입 전개 결과 [ac_bt_0010] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 원거래 건수 : %ld\n", parents); + printf(" 전개 행수 : %ld\n", children); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0011.c b/app/batch/ac_bt_0011.c index f08f8ef..b970b88 100644 --- a/app/batch/ac_bt_0011.c +++ b/app/batch/ac_bt_0011.c @@ -8,10 +8,12 @@ #line 1 "app/batch/ac_bt_0011.pgc" /* @tier medium @module ac @transform acquire-batch */ /* - * ac_bt_0011.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=medium] + * ac_bt_0011.pgc - 매입-승인 대사연계 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수(status='R') 매입행을 커서로 순회하며 승인(approval) + * 테이블의 동일 승인번호 금액과 대사한다. 승인정보가 없거나 금액이 + * 상이하면 불일치('U'), 금액이 일치하면 완료('M') 로 상태를 전이하고 + * 불일치 금액합을 집계한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0011 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0011.pgc" +#line 21 "app/batch/ac_bt_0011.pgc" static int run_ac_bt_0011(const char *biz_date) @@ -104,42 +106,47 @@ static int run_ac_bt_0011(const char *biz_date) + -#line 24 "app/batch/ac_bt_0011.pgc" +#line 26 "app/batch/ac_bt_0011.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0011.pgc" +#line 27 "app/batch/ac_bt_0011.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/ac_bt_0011.pgc" - char h_merch_id [ 16 ] ; +#line 28 "app/batch/ac_bt_0011.pgc" + char h_approval_no [ 17 ] ; -#line 27 "app/batch/ac_bt_0011.pgc" +#line 29 "app/batch/ac_bt_0011.pgc" long h_amount ; -#line 28 "app/batch/ac_bt_0011.pgc" +#line 30 "app/batch/ac_bt_0011.pgc" + long h_appr_amount ; + +#line 31 "app/batch/ac_bt_0011.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/ac_bt_0011.pgc" +#line 32 "app/batch/ac_bt_0011.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, matched = 0, unmatch = 0, errcnt = 0; + long diff_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0011 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0011.pgc" + /* declare cur_ac_bt_0011 cursor for select key , approval_no , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ +#line 46 "app/batch/ac_bt_0011.pgc" -#line 42 "app/batch/ac_bt_0011.pgc" +#line 46 "app/batch/ac_bt_0011.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0011 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0011 cursor for select key , approval_no , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0011.pgc" +#line 48 "app/batch/ac_bt_0011.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0011"); @@ -153,11 +160,11 @@ static int run_ac_bt_0011(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_bt_0011", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_approval_no),(long)17,(long)1,(17)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0011.pgc" +#line 58 "app/batch/ac_bt_0011.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,12 +177,29 @@ static int run_ac_bt_0011(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount from approval where approval_no = $1 ", + ECPGt_char,(h_approval_no),(long)17,(long)1,(17)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_appr_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 73 "app/batch/ac_bt_0011.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + /* 승인정보 없음: 전액 불일치로 처리 */ + strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); + diff_sum += labs(h_amount); + unmatch++; + } else if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_bt_0011 approval"); + errcnt++; + continue; + } else if (h_amount == h_appr_amount) { strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; + matched++; } else { strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); + diff_sum += labs(h_amount - h_appr_amount); unmatch++; } @@ -187,7 +211,7 @@ static int run_ac_bt_0011(const char *biz_date) } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0011", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0011.pgc" +#line 100 "app/batch/ac_bt_0011.pgc" if (errcnt == 0) @@ -196,16 +220,17 @@ static int run_ac_bt_0011(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0011] 요약 date=%s 대상=%ld 일치=%ld 불일치=%ld 불일치금액=%ld 오류=%ld", + biz_date, total, matched, unmatch, diff_sum, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 매입-승인 대사연계 결과 [ac_bt_0011] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 대사일치(M) : %ld\n", matched); + printf(" 불일치(U) : %ld\n", unmatch); + printf(" 불일치 금액합 : %ld\n", diff_sum); + printf(" 오류 건수 : %ld\n", errcnt); + printf("============================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0011.pgc b/app/batch/ac_bt_0011.pgc index 57aba2b..87cfdf6 100644 --- a/app/batch/ac_bt_0011.pgc +++ b/app/batch/ac_bt_0011.pgc @@ -1,9 +1,11 @@ /* @tier medium @module ac @transform acquire-batch */ /* - * ac_bt_0011.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=medium] + * ac_bt_0011.pgc - 매입-승인 대사연계 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수(status='R') 매입행을 커서로 순회하며 승인(approval) + * 테이블의 동일 승인번호 금액과 대사한다. 승인정보가 없거나 금액이 + * 상이하면 불일치('U'), 금액이 일치하면 완료('M') 로 상태를 전이하고 + * 불일치 금액합을 집계한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0011 [db@host] */ @@ -23,19 +25,21 @@ static int run_ac_bt_0011(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; + char h_approval_no[17]; long h_amount; + long h_appr_amount; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, matched = 0, unmatch = 0, errcnt = 0; + long diff_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_ac_bt_0011 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, approval_no, amount FROM ac_ledger WHERE biz_date = :h_biz_date AND status = 'R' @@ -51,7 +55,7 @@ static int run_ac_bt_0011(const char *biz_date) tx_log(TX_LOG_WARN, "[ac_bt_0011] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_ac_bt_0011 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_ac_bt_0011 INTO :h_key, :h_approval_no, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,12 +67,26 @@ static int run_ac_bt_0011(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + EXEC SQL SELECT amount + INTO :h_appr_amount + FROM approval + WHERE approval_no = :h_approval_no; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + /* 승인정보 없음: 전액 불일치로 처리 */ + strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); + diff_sum += labs(h_amount); + unmatch++; + } else if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_bt_0011 approval"); + errcnt++; + continue; + } else if (h_amount == h_appr_amount) { strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; + matched++; } else { strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); + diff_sum += labs(h_amount - h_appr_amount); unmatch++; } @@ -87,16 +105,17 @@ static int run_ac_bt_0011(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0011] 요약 date=%s 대상=%ld 일치=%ld 불일치=%ld 불일치금액=%ld 오류=%ld", + biz_date, total, matched, unmatch, diff_sum, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 매입-승인 대사연계 결과 [ac_bt_0011] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 대사일치(M) : %ld\n", matched); + printf(" 불일치(U) : %ld\n", unmatch); + printf(" 불일치 금액합 : %ld\n", diff_sum); + printf(" 오류 건수 : %ld\n", errcnt); + printf("============================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0012.c b/app/batch/ac_bt_0012.c index 70e57f0..ab760ec 100644 --- a/app/batch/ac_bt_0012.c +++ b/app/batch/ac_bt_0012.c @@ -8,10 +8,11 @@ #line 1 "app/batch/ac_bt_0012.pgc" /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0012.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0012.pgc - 매입 원천징수 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에 처리완료(status='M') 된 매입행을 가맹점(merchant) 과 + * 조인하여 가맹점별 원천징수율을 적용, 원천징수액을 계산해 원장에 + * 반영한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0012 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0012.pgc" +#line 20 "app/batch/ac_bt_0012.pgc" static int run_ac_bt_0012(const char *biz_date) @@ -105,41 +106,42 @@ static int run_ac_bt_0012(const char *biz_date) -#line 24 "app/batch/ac_bt_0012.pgc" +#line 25 "app/batch/ac_bt_0012.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0012.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/ac_bt_0012.pgc" - char h_merch_id [ 16 ] ; + char h_key [ 16 ] ; #line 27 "app/batch/ac_bt_0012.pgc" long h_amount ; #line 28 "app/batch/ac_bt_0012.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_withhold_rate ; + #line 29 "app/batch/ac_bt_0012.pgc" + long h_withhold ; +/* exec sql end declare section */ +#line 30 "app/batch/ac_bt_0012.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0; + long withhold_sum = 0; + char wonbuf[64]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0012 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0012.pgc" + /* declare cur_ac_bt_0012 cursor for select l . key , l . amount , m . withhold_rate from ac_ledger l join merchant m on m . merch_id = l . merch_id where l . biz_date = $1 and l . status = 'M' order by l . key */ +#line 45 "app/batch/ac_bt_0012.pgc" -#line 42 "app/batch/ac_bt_0012.pgc" +#line 45 "app/batch/ac_bt_0012.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0012 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0012 cursor for select l . key , l . amount , m . withhold_rate from ac_ledger l join merchant m on m . merch_id = l . merch_id where l . biz_date = $1 and l . status = 'M' order by l . key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0012.pgc" +#line 47 "app/batch/ac_bt_0012.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0012"); @@ -153,11 +155,11 @@ static int run_ac_bt_0012(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_bt_0012", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_withhold_rate),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0012.pgc" +#line 57 "app/batch/ac_bt_0012.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +172,29 @@ static int run_ac_bt_0012(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* withhold_rate 는 만분율(1/10000) 단위 */ + h_withhold = (h_amount * h_withhold_rate) / 10000; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set withhold_amt = $1 where key = $2 ", + ECPGt_long,&(h_withhold),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 74 "app/batch/ac_bt_0012.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_bt_0012 withhold_amt"); + tx_log(TX_LOG_ERROR, "[ac_bt_0012] 원천징수 반영 실패 key=%s", h_key); + errcnt++; + continue; } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + withhold_sum += h_withhold; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0012", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0012.pgc" +#line 86 "app/batch/ac_bt_0012.pgc" if (errcnt == 0) @@ -195,17 +202,18 @@ static int run_ac_bt_0012(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[ac_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(withhold_sum, wonbuf, sizeof(wonbuf)); - printf("========== 매입 배치 결과 [ac_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[ac_bt_0012] 요약 date=%s 대상=%ld 원천징수합계=%ld 오류=%ld", + biz_date, total, withhold_sum, errcnt); + + printf("========== 매입 원천징수 결과 [ac_bt_0012] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 원천징수 합계 : %ld (%s)\n", withhold_sum, wonbuf); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0012.pgc b/app/batch/ac_bt_0012.pgc index 7f50576..bae75d6 100644 --- a/app/batch/ac_bt_0012.pgc +++ b/app/batch/ac_bt_0012.pgc @@ -1,9 +1,10 @@ /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0012.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0012.pgc - 매입 원천징수 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에 처리완료(status='M') 된 매입행을 가맹점(merchant) 과 + * 조인하여 가맹점별 원천징수율을 적용, 원천징수액을 계산해 원장에 + * 반영한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0012 [db@host] */ @@ -23,23 +24,25 @@ static int run_ac_bt_0012(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; + long h_withhold_rate; + long h_withhold; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0; + long withhold_sum = 0; + char wonbuf[64]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_ac_bt_0012 CURSOR FOR - SELECT key, merch_id, amount - FROM ac_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT l.key, l.amount, m.withhold_rate + FROM ac_ledger l + JOIN merchant m ON m.merch_id = l.merch_id + WHERE l.biz_date = :h_biz_date + AND l.status = 'M' + ORDER BY l.key; EXEC SQL OPEN cur_ac_bt_0012; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +54,7 @@ static int run_ac_bt_0012(const char *biz_date) tx_log(TX_LOG_WARN, "[ac_bt_0012] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_ac_bt_0012 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_ac_bt_0012 INTO :h_key, :h_amount, :h_withhold_rate; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +66,21 @@ static int run_ac_bt_0012(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* withhold_rate 는 만분율(1/10000) 단위 */ + h_withhold = (h_amount * h_withhold_rate) / 10000; + + EXEC SQL UPDATE ac_ledger + SET withhold_amt = :h_withhold + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_bt_0012 withhold_amt"); + tx_log(TX_LOG_ERROR, "[ac_bt_0012] 원천징수 반영 실패 key=%s", h_key); + errcnt++; + continue; } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + withhold_sum += h_withhold; } EXEC SQL CLOSE cur_ac_bt_0012; @@ -86,17 +90,18 @@ static int run_ac_bt_0012(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[ac_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(withhold_sum, wonbuf, sizeof(wonbuf)); - printf("========== 매입 배치 결과 [ac_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[ac_bt_0012] 요약 date=%s 대상=%ld 원천징수합계=%ld 오류=%ld", + biz_date, total, withhold_sum, errcnt); + + printf("========== 매입 원천징수 결과 [ac_bt_0012] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 원천징수 합계 : %ld (%s)\n", withhold_sum, wonbuf); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0013.c b/app/batch/ac_bt_0013.c index d3263d0..7086803 100644 --- a/app/batch/ac_bt_0013.c +++ b/app/batch/ac_bt_0013.c @@ -8,10 +8,11 @@ #line 1 "app/batch/ac_bt_0013.pgc" /* @tier medium @module ac @transform acquire-batch */ /* - * ac_bt_0013.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=medium] + * ac_bt_0013.pgc - 매입 통계(카드사별 집계) 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에 처리완료/정산완료(status IN ('M','S')) 된 매입행을 + * 카드사코드(corp_cd) 별로 집계하여 ac_stat 테이블에 적재하고, + * 전체 합계를 산출한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0013 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0013.pgc" +#line 20 "app/batch/ac_bt_0013.pgc" static int run_ac_bt_0013(const char *biz_date) @@ -105,41 +106,41 @@ static int run_ac_bt_0013(const char *biz_date) -#line 24 "app/batch/ac_bt_0013.pgc" +#line 25 "app/batch/ac_bt_0013.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0013.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/ac_bt_0013.pgc" - char h_merch_id [ 16 ] ; + char h_corp_cd [ 5 ] ; #line 27 "app/batch/ac_bt_0013.pgc" - long h_amount ; + long h_cnt ; #line 28 "app/batch/ac_bt_0013.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_amt_sum ; + #line 29 "app/batch/ac_bt_0013.pgc" + long h_fee_sum ; +/* exec sql end declare section */ +#line 30 "app/batch/ac_bt_0013.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long corp_rows = 0, errcnt = 0; + long grand_cnt = 0, grand_amt = 0, grand_fee = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0013 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0013.pgc" + /* declare cur_ac_bt_0013 cursor for select corp_cd , count ( * ) , sum ( amount ) , sum ( fee ) from ac_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by corp_cd order by corp_cd */ +#line 44 "app/batch/ac_bt_0013.pgc" -#line 42 "app/batch/ac_bt_0013.pgc" +#line 44 "app/batch/ac_bt_0013.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0013 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0013 cursor for select corp_cd , count ( * ) , sum ( amount ) , sum ( fee ) from ac_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by corp_cd order by corp_cd", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0013.pgc" +#line 46 "app/batch/ac_bt_0013.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0013"); @@ -149,15 +150,22 @@ static int run_ac_bt_0013(const char *biz_date) if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[ac_bt_0013] tx_begin 경고"); + printf("========== 매입 통계(카드사별) 결과 [ac_bt_0013] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" ---------------------------------------------------------\n"); + printf(" %-6s %10s %16s %14s\n", "카드사", "건수", "매입금액", "수수료"); + for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_bt_0013", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_corp_cd),(long)5,(long)1,(5)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_amt_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0013.pgc" +#line 62 "app/batch/ac_bt_0013.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,26 +176,35 @@ static int run_ac_bt_0013(const char *biz_date) break; } - total++; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into ac_stat ( biz_date , corp_cd , tot_cnt , tot_amt ) values ( $1 , $2 , $3 , $4 )", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_corp_cd),(long)5,(long)1,(5)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amt_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 75 "app/batch/ac_bt_0013.pgc" - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT ac_bt_0013 ac_stat"); + tx_log(TX_LOG_ERROR, "[ac_bt_0013] 통계 적재 실패 corp_cd=%s", h_corp_cd); errcnt++; + continue; } + + printf(" %-6s %10ld %16ld %14ld\n", h_corp_cd, h_cnt, h_amt_sum, h_fee_sum); + + corp_rows++; + grand_cnt += h_cnt; + grand_amt += h_amt_sum; + grand_fee += h_fee_sum; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0013", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0013.pgc" +#line 92 "app/batch/ac_bt_0013.pgc" if (errcnt == 0) @@ -196,16 +213,16 @@ static int run_ac_bt_0013(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0013] 요약 date=%s 카드사수=%ld 총건수=%ld 총금액=%ld 총수수료=%ld 오류=%ld", + biz_date, corp_rows, grand_cnt, grand_amt, grand_fee, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" ---------------------------------------------------------\n"); + printf(" 카드사 수 : %ld\n", corp_rows); + printf(" 총 건수 : %ld\n", grand_cnt); + printf(" 총 매입금액 : %ld\n", grand_amt); + printf(" 총 수수료 : %ld\n", grand_fee); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=============================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0013.pgc b/app/batch/ac_bt_0013.pgc index b0efac8..908d9fc 100644 --- a/app/batch/ac_bt_0013.pgc +++ b/app/batch/ac_bt_0013.pgc @@ -1,9 +1,10 @@ /* @tier medium @module ac @transform acquire-batch */ /* - * ac_bt_0013.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=medium] + * ac_bt_0013.pgc - 매입 통계(카드사별 집계) 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에 처리완료/정산완료(status IN ('M','S')) 된 매입행을 + * 카드사코드(corp_cd) 별로 집계하여 ac_stat 테이블에 적재하고, + * 전체 합계를 산출한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0013 [db@host] */ @@ -22,24 +23,25 @@ static int run_ac_bt_0013(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_corp_cd[5]; + long h_cnt; + long h_amt_sum; + long h_fee_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long corp_rows = 0, errcnt = 0; + long grand_cnt = 0, grand_amt = 0, grand_fee = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_ac_bt_0013 CURSOR FOR - SELECT key, merch_id, amount + SELECT corp_cd, count(*), sum(amount), sum(fee) FROM ac_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status IN ('M', 'S') + GROUP BY corp_cd + ORDER BY corp_cd; EXEC SQL OPEN cur_ac_bt_0013; if (sqlca.sqlcode != TX_SQL_OK) { @@ -50,8 +52,14 @@ static int run_ac_bt_0013(const char *biz_date) if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[ac_bt_0013] tx_begin 경고"); + printf("========== 매입 통계(카드사별) 결과 [ac_bt_0013] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" ---------------------------------------------------------\n"); + printf(" %-6s %10s %16s %14s\n", "카드사", "건수", "매입금액", "수수료"); + for (;;) { - EXEC SQL FETCH cur_ac_bt_0013 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_ac_bt_0013 + INTO :h_corp_cd, :h_cnt, :h_amt_sum, :h_fee_sum; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,22 +69,24 @@ static int run_ac_bt_0013(const char *biz_date) break; } - total++; + EXEC SQL INSERT INTO ac_stat + (biz_date, corp_cd, tot_cnt, tot_amt) + VALUES + (:h_biz_date, :h_corp_cd, :h_cnt, :h_amt_sum); - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT ac_bt_0013 ac_stat"); + tx_log(TX_LOG_ERROR, "[ac_bt_0013] 통계 적재 실패 corp_cd=%s", h_corp_cd); errcnt++; + continue; } + + printf(" %-6s %10ld %16ld %14ld\n", h_corp_cd, h_cnt, h_amt_sum, h_fee_sum); + + corp_rows++; + grand_cnt += h_cnt; + grand_amt += h_amt_sum; + grand_fee += h_fee_sum; } EXEC SQL CLOSE cur_ac_bt_0013; @@ -87,16 +97,16 @@ static int run_ac_bt_0013(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0013] 요약 date=%s 카드사수=%ld 총건수=%ld 총금액=%ld 총수수료=%ld 오류=%ld", + biz_date, corp_rows, grand_cnt, grand_amt, grand_fee, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" ---------------------------------------------------------\n"); + printf(" 카드사 수 : %ld\n", corp_rows); + printf(" 총 건수 : %ld\n", grand_cnt); + printf(" 총 매입금액 : %ld\n", grand_amt); + printf(" 총 수수료 : %ld\n", grand_fee); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=============================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0014.c b/app/batch/ac_bt_0014.c index 0d7bfd4..a97335f 100644 --- a/app/batch/ac_bt_0014.c +++ b/app/batch/ac_bt_0014.c @@ -8,10 +8,12 @@ #line 1 "app/batch/ac_bt_0014.pgc" /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0014.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0014.pgc - 매입 정산연계 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에 처리완료(status='M') 된 매입행을 커서로 순회하며 + * 정산금액(매입금액-수수료) 을 산출, 정산번호를 채번하여 settle + * 테이블에 적재하고 원장 상태를 정산완료('S') 로 전이한다. + * 배치 main 엔트리 포함. * * 실행: ac_bt_0014 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0014.pgc" +#line 21 "app/batch/ac_bt_0014.pgc" static int run_ac_bt_0014(const char *biz_date) @@ -104,42 +106,54 @@ static int run_ac_bt_0014(const char *biz_date) + + + -#line 24 "app/batch/ac_bt_0014.pgc" +#line 26 "app/batch/ac_bt_0014.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0014.pgc" +#line 27 "app/batch/ac_bt_0014.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/ac_bt_0014.pgc" +#line 28 "app/batch/ac_bt_0014.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/ac_bt_0014.pgc" +#line 29 "app/batch/ac_bt_0014.pgc" long h_amount ; -#line 28 "app/batch/ac_bt_0014.pgc" - char h_new_status [ 2 ] ; +#line 30 "app/batch/ac_bt_0014.pgc" + long h_fee ; + +#line 31 "app/batch/ac_bt_0014.pgc" + long h_settle_amt ; + +#line 32 "app/batch/ac_bt_0014.pgc" + char h_settle_no [ 20 ] ; + +#line 33 "app/batch/ac_bt_0014.pgc" + char h_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/ac_bt_0014.pgc" +#line 34 "app/batch/ac_bt_0014.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0, seq = 0; + long settle_sum = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0014 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0014.pgc" + /* declare cur_ac_bt_0014 cursor for select key , merch_id , amount , fee from ac_ledger where biz_date = $1 and status = 'M' order by key */ +#line 47 "app/batch/ac_bt_0014.pgc" -#line 42 "app/batch/ac_bt_0014.pgc" +#line 47 "app/batch/ac_bt_0014.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0014 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0014 cursor for select key , merch_id , amount , fee from ac_ledger where biz_date = $1 and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0014.pgc" +#line 49 "app/batch/ac_bt_0014.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0014"); @@ -156,8 +170,10 @@ static int run_ac_bt_0014(const char *biz_date) ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0014.pgc" +#line 60 "app/batch/ac_bt_0014.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,25 +185,55 @@ static int run_ac_bt_0014(const char *biz_date) } total++; + seq++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + h_settle_amt = h_amount - h_fee; + snprintf(h_settle_no, sizeof(h_settle_no), "ST%s%05ld", h_biz_date, seq); + strncpy(h_status, AC_ST_SETTLED, sizeof(h_status)); - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into settle ( settle_no , merch_id , biz_date , settle_amt , status ) values ( $1 , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_settle_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_settle_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 80 "app/batch/ac_bt_0014.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT ac_bt_0014 settle"); + tx_log(TX_LOG_ERROR, "[ac_bt_0014] 정산 적재 실패 key=%s", h_key); errcnt++; + continue; } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set status = $1 , settle_no = $2 where key = $3 ", + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_settle_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 91 "app/batch/ac_bt_0014.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_bt_0014 ac_ledger"); + tx_log(TX_LOG_ERROR, "[ac_bt_0014] 원장 상태전이 실패 key=%s", h_key); + errcnt++; + continue; + } + + settle_sum += h_settle_amt; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0014", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0014.pgc" +#line 103 "app/batch/ac_bt_0014.pgc" if (errcnt == 0) @@ -196,16 +242,15 @@ static int run_ac_bt_0014(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0014] 요약 date=%s 대상=%ld 정산금액합=%ld 오류=%ld", + biz_date, total, settle_sum, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 매입 정산연계 결과 [ac_bt_0014] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정산 금액 합계 : %ld\n", settle_sum); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0014.pgc b/app/batch/ac_bt_0014.pgc index ff9894f..3c4cdde 100644 --- a/app/batch/ac_bt_0014.pgc +++ b/app/batch/ac_bt_0014.pgc @@ -1,9 +1,11 @@ /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0014.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0014.pgc - 매입 정산연계 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에 처리완료(status='M') 된 매입행을 커서로 순회하며 + * 정산금액(매입금액-수수료) 을 산출, 정산번호를 채번하여 settle + * 테이블에 적재하고 원장 상태를 정산완료('S') 로 전이한다. + * 배치 main 엔트리 포함. * * 실행: ac_bt_0014 [db@host] */ @@ -25,20 +27,23 @@ static int run_ac_bt_0014(const char *biz_date) char h_key[16]; char h_merch_id[16]; long h_amount; - char h_new_status[2]; + long h_fee; + long h_settle_amt; + char h_settle_no[20]; + char h_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0, seq = 0; + long settle_sum = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_ac_bt_0014 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, merch_id, amount, fee FROM ac_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'M' ORDER BY key; EXEC SQL OPEN cur_ac_bt_0014; @@ -51,7 +56,8 @@ static int run_ac_bt_0014(const char *biz_date) tx_log(TX_LOG_WARN, "[ac_bt_0014] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_ac_bt_0014 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_ac_bt_0014 + INTO :h_key, :h_merch_id, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,21 +68,36 @@ static int run_ac_bt_0014(const char *biz_date) } total++; + seq++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + h_settle_amt = h_amount - h_fee; + snprintf(h_settle_no, sizeof(h_settle_no), "ST%s%05ld", h_biz_date, seq); + strncpy(h_status, AC_ST_SETTLED, sizeof(h_status)); - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL INSERT INTO settle + (settle_no, merch_id, biz_date, settle_amt, status) + VALUES + (:h_settle_no, :h_merch_id, :h_biz_date, :h_settle_amt, :h_status); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT ac_bt_0014 settle"); + tx_log(TX_LOG_ERROR, "[ac_bt_0014] 정산 적재 실패 key=%s", h_key); errcnt++; + continue; } + + EXEC SQL UPDATE ac_ledger + SET status = :h_status, settle_no = :h_settle_no + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_bt_0014 ac_ledger"); + tx_log(TX_LOG_ERROR, "[ac_bt_0014] 원장 상태전이 실패 key=%s", h_key); + errcnt++; + continue; + } + + settle_sum += h_settle_amt; } EXEC SQL CLOSE cur_ac_bt_0014; @@ -87,16 +108,15 @@ static int run_ac_bt_0014(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0014] 요약 date=%s 대상=%ld 정산금액합=%ld 오류=%ld", + biz_date, total, settle_sum, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 매입 정산연계 결과 [ac_bt_0014] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정산 금액 합계 : %ld\n", settle_sum); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0015.c b/app/batch/ac_bt_0015.c index 55a392d..e4ba4bc 100644 --- a/app/batch/ac_bt_0015.c +++ b/app/batch/ac_bt_0015.c @@ -8,10 +8,11 @@ #line 1 "app/batch/ac_bt_0015.pgc" /* @tier medium @module ac @transform acquire-batch */ /* - * ac_bt_0015.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=medium] + * ac_bt_0015.pgc - 매입일자 변경(휴일 이월) 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일이 휴일(주말) 인 경우, 해당 일자의 접수(status='R') 건들의 + * biz_date 를 차영업일로 일괄 이월한다. 평일이면 이월 없이 대상 건수만 + * 유지 건으로 집계한다(가드: 평일이면 이월 0건). 배치 main 엔트리 포함. * * 실행: ac_bt_0015 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0015.pgc" +#line 20 "app/batch/ac_bt_0015.pgc" static int run_ac_bt_0015(const char *biz_date) @@ -102,44 +103,50 @@ static int run_ac_bt_0015(const char *biz_date) - - -#line 24 "app/batch/ac_bt_0015.pgc" +#line 25 "app/batch/ac_bt_0015.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0015.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/ac_bt_0015.pgc" - char h_merch_id [ 16 ] ; + char h_new_date [ 9 ] ; #line 27 "app/batch/ac_bt_0015.pgc" - long h_amount ; - -#line 28 "app/batch/ac_bt_0015.pgc" - char h_new_status [ 2 ] ; + char h_key [ 16 ] ; /* exec sql end declare section */ -#line 29 "app/batch/ac_bt_0015.pgc" +#line 28 "app/batch/ac_bt_0015.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, shifted = 0, kept = 0, errcnt = 0; + int is_weekend; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + memset(h_new_date, 0, sizeof(h_new_date)); + + is_weekend = date_is_weekend(biz_date); + + if (is_weekend) { + if (!date_next_business(biz_date, h_new_date)) { + tx_log(TX_LOG_WARN, + "[ac_bt_0015] 차영업일 계산 실패 date=%s, 이월 취소", biz_date); + is_weekend = 0; + } else { + tx_log(TX_LOG_INFO, "[ac_bt_0015] 휴일 감지 date=%s -> 차영업일=%s", + biz_date, h_new_date); + } + } ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0015 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0015.pgc" + /* declare cur_ac_bt_0015 cursor for select key from ac_ledger where biz_date = $1 and status = 'R' order by key */ +#line 55 "app/batch/ac_bt_0015.pgc" -#line 42 "app/batch/ac_bt_0015.pgc" +#line 55 "app/batch/ac_bt_0015.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0015 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0015 cursor for select key from ac_ledger where biz_date = $1 and status = 'R' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0015.pgc" +#line 57 "app/batch/ac_bt_0015.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0015"); @@ -152,12 +159,8 @@ static int run_ac_bt_0015(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_bt_0015", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0015.pgc" +#line 67 "app/batch/ac_bt_0015.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +173,31 @@ static int run_ac_bt_0015(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (!is_weekend) { + kept++; + continue; } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set biz_date = $1 where key = $2 ", + ECPGt_char,(h_new_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 86 "app/batch/ac_bt_0015.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_bt_0015 biz_date"); + tx_log(TX_LOG_ERROR, "[ac_bt_0015] 이월 실패 key=%s", h_key); errcnt++; + continue; } + + shifted++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0015", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0015.pgc" +#line 98 "app/batch/ac_bt_0015.pgc" if (errcnt == 0) @@ -196,16 +206,19 @@ static int run_ac_bt_0015(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0015] 요약 date=%s 휴일여부=%d 대상=%ld 이월=%ld 유지=%ld 오류=%ld", + biz_date, is_weekend, total, shifted, kept, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 매입일자 변경(휴일 이월) 결과 [ac_bt_0015] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 휴일 여부 : %s\n", is_weekend ? "휴일" : "평일"); + if (is_weekend) + printf(" 차영업일 : %s\n", h_new_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 이월 건수 : %ld\n", shifted); + printf(" 유지 건수 : %ld\n", kept); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==================================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0015.pgc b/app/batch/ac_bt_0015.pgc index 84b4a33..6b72764 100644 --- a/app/batch/ac_bt_0015.pgc +++ b/app/batch/ac_bt_0015.pgc @@ -1,9 +1,10 @@ /* @tier medium @module ac @transform acquire-batch */ /* - * ac_bt_0015.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=medium] + * ac_bt_0015.pgc - 매입일자 변경(휴일 이월) 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일이 휴일(주말) 인 경우, 해당 일자의 접수(status='R') 건들의 + * biz_date 를 차영업일로 일괄 이월한다. 평일이면 이월 없이 대상 건수만 + * 유지 건으로 집계한다(가드: 평일이면 이월 0건). 배치 main 엔트리 포함. * * 실행: ac_bt_0015 [db@host] */ @@ -22,20 +23,32 @@ static int run_ac_bt_0015(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; + char h_new_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, shifted = 0, kept = 0, errcnt = 0; + int is_weekend; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + memset(h_new_date, 0, sizeof(h_new_date)); + + is_weekend = date_is_weekend(biz_date); + + if (is_weekend) { + if (!date_next_business(biz_date, h_new_date)) { + tx_log(TX_LOG_WARN, + "[ac_bt_0015] 차영업일 계산 실패 date=%s, 이월 취소", biz_date); + is_weekend = 0; + } else { + tx_log(TX_LOG_INFO, "[ac_bt_0015] 휴일 감지 date=%s -> 차영업일=%s", + biz_date, h_new_date); + } + } EXEC SQL DECLARE cur_ac_bt_0015 CURSOR FOR - SELECT key, merch_id, amount + SELECT key FROM ac_ledger WHERE biz_date = :h_biz_date AND status = 'R' @@ -51,7 +64,7 @@ static int run_ac_bt_0015(const char *biz_date) tx_log(TX_LOG_WARN, "[ac_bt_0015] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_ac_bt_0015 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_ac_bt_0015 INTO :h_key; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +76,23 @@ static int run_ac_bt_0015(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (!is_weekend) { + kept++; + continue; } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL UPDATE ac_ledger + SET biz_date = :h_new_date + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_bt_0015 biz_date"); + tx_log(TX_LOG_ERROR, "[ac_bt_0015] 이월 실패 key=%s", h_key); errcnt++; + continue; } + + shifted++; } EXEC SQL CLOSE cur_ac_bt_0015; @@ -87,16 +103,19 @@ static int run_ac_bt_0015(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0015] 요약 date=%s 휴일여부=%d 대상=%ld 이월=%ld 유지=%ld 오류=%ld", + biz_date, is_weekend, total, shifted, kept, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 매입일자 변경(휴일 이월) 결과 [ac_bt_0015] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 휴일 여부 : %s\n", is_weekend ? "휴일" : "평일"); + if (is_weekend) + printf(" 차영업일 : %s\n", h_new_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 이월 건수 : %ld\n", shifted); + printf(" 유지 건수 : %ld\n", kept); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==================================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0016.c b/app/batch/ac_bt_0016.c index 4de7d1c..87f9d8f 100644 --- a/app/batch/ac_bt_0016.c +++ b/app/batch/ac_bt_0016.c @@ -8,10 +8,11 @@ #line 1 "app/batch/ac_bt_0016.pgc" /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0016.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0016.pgc - 매입 재분류 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에 처리완료(status='M') 된 매입행을 금액 구간에 따라 + * 대형('LG', 100만원 이상) / 중형('MD', 10만원 이상) / 소형('SM', 그 외) + * 으로 재분류하여 acq_type 을 갱신한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0016 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0016.pgc" +#line 20 "app/batch/ac_bt_0016.pgc" static int run_ac_bt_0016(const char *biz_date) @@ -103,40 +104,36 @@ static int run_ac_bt_0016(const char *biz_date) - -#line 24 "app/batch/ac_bt_0016.pgc" +#line 25 "app/batch/ac_bt_0016.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0016.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/ac_bt_0016.pgc" - char h_merch_id [ 16 ] ; + char h_key [ 16 ] ; #line 27 "app/batch/ac_bt_0016.pgc" long h_amount ; #line 28 "app/batch/ac_bt_0016.pgc" - char h_new_status [ 2 ] ; + char h_new_type [ 9 ] ; /* exec sql end declare section */ #line 29 "app/batch/ac_bt_0016.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, lg_cnt = 0, md_cnt = 0, sm_cnt = 0, errcnt = 0; + const char *cls; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0016 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_ac_bt_0016 cursor for select key , amount from ac_ledger where biz_date = $1 and status = 'M' order by key */ #line 42 "app/batch/ac_bt_0016.pgc" #line 42 "app/batch/ac_bt_0016.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0016 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0016 cursor for select key , amount from ac_ledger where biz_date = $1 and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/ac_bt_0016.pgc" @@ -153,8 +150,6 @@ static int run_ac_bt_0016(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_bt_0016", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 54 "app/batch/ac_bt_0016.pgc" @@ -170,24 +165,37 @@ static int run_ac_bt_0016(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; + if (h_amount >= 1000000L) { + cls = "LG"; + lg_cnt++; + } else if (h_amount >= 100000L) { + cls = "MD"; + md_cnt++; } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + cls = "SM"; + sm_cnt++; } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); + strncpy(h_new_type, cls, sizeof(h_new_type) - 1); + h_new_type[sizeof(h_new_type) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set acq_type = $1 where key = $2 ", + ECPGt_char,(h_new_type),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 82 "app/batch/ac_bt_0016.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_bt_0016 acq_type"); + tx_log(TX_LOG_ERROR, "[ac_bt_0016] 재분류 실패 key=%s", h_key); errcnt++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0016", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0016.pgc" +#line 91 "app/batch/ac_bt_0016.pgc" if (errcnt == 0) @@ -196,16 +204,17 @@ static int run_ac_bt_0016(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0016] 요약 date=%s 대상=%ld 대형=%ld 중형=%ld 소형=%ld 오류=%ld", + biz_date, total, lg_cnt, md_cnt, sm_cnt, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 매입 재분류 결과 [ac_bt_0016] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 대형(LG) : %ld\n", lg_cnt); + printf(" 중형(MD) : %ld\n", md_cnt); + printf(" 소형(SM) : %ld\n", sm_cnt); + printf(" 오류 건수 : %ld\n", errcnt); + printf("====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0016.pgc b/app/batch/ac_bt_0016.pgc index 2489854..cf23c36 100644 --- a/app/batch/ac_bt_0016.pgc +++ b/app/batch/ac_bt_0016.pgc @@ -1,9 +1,10 @@ /* @tier easy @module ac @transform acquire-batch */ /* - * ac_bt_0016.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=easy] + * ac_bt_0016.pgc - 매입 재분류 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에 처리완료(status='M') 된 매입행을 금액 구간에 따라 + * 대형('LG', 100만원 이상) / 중형('MD', 10만원 이상) / 소형('SM', 그 외) + * 으로 재분류하여 acq_type 을 갱신한다. 배치 main 엔트리 포함. * * 실행: ac_bt_0016 [db@host] */ @@ -23,22 +24,21 @@ static int run_ac_bt_0016(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; + char h_new_type[9]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, lg_cnt = 0, md_cnt = 0, sm_cnt = 0, errcnt = 0; + const char *cls; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_ac_bt_0016 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount FROM ac_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'M' ORDER BY key; EXEC SQL OPEN cur_ac_bt_0016; @@ -51,7 +51,7 @@ static int run_ac_bt_0016(const char *biz_date) tx_log(TX_LOG_WARN, "[ac_bt_0016] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_ac_bt_0016 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_ac_bt_0016 INTO :h_key, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,18 +63,27 @@ static int run_ac_bt_0016(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; + if (h_amount >= 1000000L) { + cls = "LG"; + lg_cnt++; + } else if (h_amount >= 100000L) { + cls = "MD"; + md_cnt++; } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + cls = "SM"; + sm_cnt++; } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); + strncpy(h_new_type, cls, sizeof(h_new_type) - 1); + h_new_type[sizeof(h_new_type) - 1] = '\0'; + + EXEC SQL UPDATE ac_ledger + SET acq_type = :h_new_type + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_bt_0016 acq_type"); + tx_log(TX_LOG_ERROR, "[ac_bt_0016] 재분류 실패 key=%s", h_key); errcnt++; } } @@ -87,16 +96,17 @@ static int run_ac_bt_0016(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0016] 요약 date=%s 대상=%ld 대형=%ld 중형=%ld 소형=%ld 오류=%ld", + biz_date, total, lg_cnt, md_cnt, sm_cnt, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 매입 재분류 결과 [ac_bt_0016] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 대형(LG) : %ld\n", lg_cnt); + printf(" 중형(MD) : %ld\n", md_cnt); + printf(" 소형(SM) : %ld\n", sm_cnt); + printf(" 오류 건수 : %ld\n", errcnt); + printf("====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0017.c b/app/batch/ac_bt_0017.c index b2624d0..84d66d7 100644 --- a/app/batch/ac_bt_0017.c +++ b/app/batch/ac_bt_0017.c @@ -8,10 +8,15 @@ #line 1 "app/batch/ac_bt_0017.pgc" /* @tier hard @module ac @transform acquire-batch */ /* - * ac_bt_0017.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=hard] + * ac_bt_0017.pgc - 매입 이체연계 배치 [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에 정산완료(status='S') 된 매입행을 가맹점별로 집계하여 + * 순지급액(매입금액-수수료 합계) 을 산출하고 이체지시를 생성한다. + * - 순지급액이 0원 이하이면 이체 대상에서 제외한다. + * - 순지급액이 최소이체금액(AC_BT_0017_MIN_TRANSFER) 미만이면 보류 처리하고 + * 다음 영업일로 넘긴다(이체지시 미생성). + * - 그 외에는 이체번호를 채번하여 settle 테이블에 이체지시('T') 로 적재한다. + * 배치 main 엔트리 포함. * * 실행: ac_bt_0017 [db@host] */ @@ -93,9 +98,17 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/ac_bt_0017.pgc" +#line 24 "app/batch/ac_bt_0017.pgc" +#define AC_BT_0017_MIN_TRANSFER 1000L + +/* + * 이체상태 'T' 는 settle 테이블 전용 로컬 상태코드이며, + * ac_core.h 의 AC_ST_* (ac_ledger 상태) 와는 별개의 값이다. + */ +#define AC_BT_0017_SETTLE_ST_XFER "T" + static int run_ac_bt_0017(const char *biz_date) { /* exec sql begin declare section */ @@ -105,41 +118,42 @@ static int run_ac_bt_0017(const char *biz_date) -#line 24 "app/batch/ac_bt_0017.pgc" +#line 37 "app/batch/ac_bt_0017.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/ac_bt_0017.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/ac_bt_0017.pgc" +#line 38 "app/batch/ac_bt_0017.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/ac_bt_0017.pgc" - long h_amount ; +#line 39 "app/batch/ac_bt_0017.pgc" + long h_net ; -#line 28 "app/batch/ac_bt_0017.pgc" - char h_new_status [ 2 ] ; +#line 40 "app/batch/ac_bt_0017.pgc" + char h_settle_no [ 20 ] ; + +#line 41 "app/batch/ac_bt_0017.pgc" + char h_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/ac_bt_0017.pgc" +#line 42 "app/batch/ac_bt_0017.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merchants = 0, transferred = 0, held = 0, skipped = 0, errcnt = 0; + long seq = 0; + long grand_net = 0, held_sum = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_ac_bt_0017 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/ac_bt_0017.pgc" + /* declare cur_ac_bt_0017 cursor for select merch_id , sum ( amount - fee ) as net from ac_ledger where biz_date = $1 and status = 'S' group by merch_id order by merch_id */ +#line 57 "app/batch/ac_bt_0017.pgc" -#line 42 "app/batch/ac_bt_0017.pgc" +#line 57 "app/batch/ac_bt_0017.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0017 cursor for select key , merch_id , amount from ac_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_bt_0017 cursor for select merch_id , sum ( amount - fee ) as net from ac_ledger where biz_date = $1 and status = 'S' group by merch_id order by merch_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/ac_bt_0017.pgc" +#line 59 "app/batch/ac_bt_0017.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_ac_bt_0017"); @@ -149,15 +163,17 @@ static int run_ac_bt_0017(const char *biz_date) if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[ac_bt_0017] tx_begin 경고"); + printf("========== 매입 이체연계 결과 [ac_bt_0017] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" ------------------------------------------------------\n"); + for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_bt_0017", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_net),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/ac_bt_0017.pgc" +#line 73 "app/batch/ac_bt_0017.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,26 +184,56 @@ static int run_ac_bt_0017(const char *biz_date) break; } - total++; + merchants++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (h_net <= 0) { + tx_log(TX_LOG_WARN, "[ac_bt_0017] 순지급액 이하 제외 merch_id=%s net=%ld", + h_merch_id, h_net); + skipped++; + continue; } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0017] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (h_net < AC_BT_0017_MIN_TRANSFER) { + tx_log(TX_LOG_INFO, "[ac_bt_0017] 최소이체금액 미만 보류 merch_id=%s net=%ld", + h_merch_id, h_net); + held++; + held_sum += h_net; + continue; + } + + seq++; + snprintf(h_settle_no, sizeof(h_settle_no), "TR%s%05ld", h_biz_date, seq); + strncpy(h_status, AC_BT_0017_SETTLE_ST_XFER, sizeof(h_status)); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into settle ( settle_no , merch_id , biz_date , settle_amt , status ) values ( $1 , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_settle_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_net),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 107 "app/batch/ac_bt_0017.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT ac_bt_0017 settle"); + tx_log(TX_LOG_ERROR, "[ac_bt_0017] 이체지시 생성 실패 merch_id=%s", h_merch_id); errcnt++; + continue; } + + printf(" %-16s 이체번호=%-17s 이체금액=%ld\n", h_merch_id, h_settle_no, h_net); + + transferred++; + grand_net += h_net; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_bt_0017", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/ac_bt_0017.pgc" +#line 122 "app/batch/ac_bt_0017.pgc" if (errcnt == 0) @@ -196,16 +242,18 @@ static int run_ac_bt_0017(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0017] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0017] 요약 date=%s 가맹점=%ld 이체=%ld 이체총액=%ld 보류=%ld 보류액=%ld 제외=%ld 오류=%ld", + biz_date, merchants, transferred, grand_net, held, held_sum, skipped, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0017] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" ------------------------------------------------------\n"); + printf(" 대상 가맹점수 : %ld\n", merchants); + printf(" 이체 건수 : %ld\n", transferred); + printf(" 이체 총액 : %ld\n", grand_net); + printf(" 보류 건수 : %ld\n", held); + printf(" 보류 금액합 : %ld\n", held_sum); + printf(" 제외 건수 : %ld\n", skipped); + printf(" 오류 건수 : %ld\n", errcnt); + printf("========================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/ac_bt_0017.pgc b/app/batch/ac_bt_0017.pgc index 8faac49..dc79f8b 100644 --- a/app/batch/ac_bt_0017.pgc +++ b/app/batch/ac_bt_0017.pgc @@ -1,9 +1,14 @@ /* @tier hard @module ac @transform acquire-batch */ /* - * ac_bt_0017.pgc - 매입 배치 (커서 순회 + 상태전이) [tier=hard] + * ac_bt_0017.pgc - 매입 이체연계 배치 [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에 정산완료(status='S') 된 매입행을 가맹점별로 집계하여 + * 순지급액(매입금액-수수료 합계) 을 산출하고 이체지시를 생성한다. + * - 순지급액이 0원 이하이면 이체 대상에서 제외한다. + * - 순지급액이 최소이체금액(AC_BT_0017_MIN_TRANSFER) 미만이면 보류 처리하고 + * 다음 영업일로 넘긴다(이체지시 미생성). + * - 그 외에는 이체번호를 채번하여 settle 테이블에 이체지시('T') 로 적재한다. + * 배치 main 엔트리 포함. * * 실행: ac_bt_0017 [db@host] */ @@ -18,28 +23,38 @@ EXEC SQL INCLUDE sqlca; +#define AC_BT_0017_MIN_TRANSFER 1000L + +/* + * 이체상태 'T' 는 settle 테이블 전용 로컬 상태코드이며, + * ac_core.h 의 AC_ST_* (ac_ledger 상태) 와는 별개의 값이다. + */ +#define AC_BT_0017_SETTLE_ST_XFER "T" + static int run_ac_bt_0017(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_net; + char h_settle_no[20]; + char h_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merchants = 0, transferred = 0, held = 0, skipped = 0, errcnt = 0; + long seq = 0; + long grand_net = 0, held_sum = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_ac_bt_0017 CURSOR FOR - SELECT key, merch_id, amount + SELECT merch_id, sum(amount - fee) AS net FROM ac_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status = 'S' + GROUP BY merch_id + ORDER BY merch_id; EXEC SQL OPEN cur_ac_bt_0017; if (sqlca.sqlcode != TX_SQL_OK) { @@ -50,8 +65,12 @@ static int run_ac_bt_0017(const char *biz_date) if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[ac_bt_0017] tx_begin 경고"); + printf("========== 매입 이체연계 결과 [ac_bt_0017] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" ------------------------------------------------------\n"); + for (;;) { - EXEC SQL FETCH cur_ac_bt_0017 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_ac_bt_0017 INTO :h_merch_id, :h_net; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,22 +80,43 @@ static int run_ac_bt_0017(const char *biz_date) break; } - total++; + merchants++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (h_net <= 0) { + tx_log(TX_LOG_WARN, "[ac_bt_0017] 순지급액 이하 제외 merch_id=%s net=%ld", + h_merch_id, h_net); + skipped++; + continue; } - rc = ac_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ac_bt_0017] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (h_net < AC_BT_0017_MIN_TRANSFER) { + tx_log(TX_LOG_INFO, "[ac_bt_0017] 최소이체금액 미만 보류 merch_id=%s net=%ld", + h_merch_id, h_net); + held++; + held_sum += h_net; + continue; + } + + seq++; + snprintf(h_settle_no, sizeof(h_settle_no), "TR%s%05ld", h_biz_date, seq); + strncpy(h_status, AC_BT_0017_SETTLE_ST_XFER, sizeof(h_status)); + + EXEC SQL INSERT INTO settle + (settle_no, merch_id, biz_date, settle_amt, status) + VALUES + (:h_settle_no, :h_merch_id, :h_biz_date, :h_net, :h_status); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT ac_bt_0017 settle"); + tx_log(TX_LOG_ERROR, "[ac_bt_0017] 이체지시 생성 실패 merch_id=%s", h_merch_id); errcnt++; + continue; } + + printf(" %-16s 이체번호=%-17s 이체금액=%ld\n", h_merch_id, h_settle_no, h_net); + + transferred++; + grand_net += h_net; } EXEC SQL CLOSE cur_ac_bt_0017; @@ -87,16 +127,18 @@ static int run_ac_bt_0017(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[ac_bt_0017] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[ac_bt_0017] 요약 date=%s 가맹점=%ld 이체=%ld 이체총액=%ld 보류=%ld 보류액=%ld 제외=%ld 오류=%ld", + biz_date, merchants, transferred, grand_net, held, held_sum, skipped, errcnt); - printf("========== 매입 배치 결과 [ac_bt_0017] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" ------------------------------------------------------\n"); + printf(" 대상 가맹점수 : %ld\n", merchants); + printf(" 이체 건수 : %ld\n", transferred); + printf(" 이체 총액 : %ld\n", grand_net); + printf(" 보류 건수 : %ld\n", held); + printf(" 보류 금액합 : %ld\n", held_sum); + printf(" 제외 건수 : %ld\n", skipped); + printf(" 오류 건수 : %ld\n", errcnt); + printf("========================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/au_bt_0002.c b/app/batch/au_bt_0002.c index b7c1fd4..9ed7aeb 100644 --- a/app/batch/au_bt_0002.c +++ b/app/batch/au_bt_0002.c @@ -8,12 +8,9 @@ #line 1 "app/batch/au_bt_0002.pgc" /* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0002.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=medium] + * au_bt_0002.pgc - 승인원장 정산반영 배치 (승인완료 M -> 정산 S) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0002 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0002 [db@host] */ #include #include @@ -93,7 +90,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0002.pgc" +#line 16 "app/batch/au_bt_0002.pgc" static int run_au_bt_0002(const char *biz_date) @@ -101,112 +98,72 @@ static int run_au_bt_0002(const char *biz_date) /* exec sql begin declare section */ - - - + -#line 24 "app/batch/au_bt_0002.pgc" +#line 21 "app/batch/au_bt_0002.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/au_bt_0002.pgc" +#line 22 "app/batch/au_bt_0002.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/au_bt_0002.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0002.pgc" - long h_amount ; - -#line 28 "app/batch/au_bt_0002.pgc" - char h_new_status [ 2 ] ; +#line 23 "app/batch/au_bt_0002.pgc" + long h_amount , h_fee ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0002.pgc" +#line 24 "app/batch/au_bt_0002.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, settled = 0, errcnt = 0, sum = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0002 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0002.pgc" + /* declare cur_settle cursor for select key , amount , fee from au_ledger where biz_date = $1 and status = 'M' order by key */ +#line 33 "app/batch/au_bt_0002.pgc" -#line 42 "app/batch/au_bt_0002.pgc" +#line 33 "app/batch/au_bt_0002.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0002 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_settle cursor for select key , amount , fee from au_ledger where biz_date = $1 and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0002.pgc" +#line 34 "app/batch/au_bt_0002.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0002"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0002] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_settle"); return TX_EDB; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[au_bt_0002] tx_begin 경고"); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0002", ECPGt_EOIT, + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_settle", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0002.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0002"); - errcnt++; - break; - } +#line 39 "app/batch/au_bt_0002.pgc" + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_settle"); errcnt++; break; } total++; + sum += h_amount - h_fee; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set status = 'S' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 45 "app/batch/au_bt_0002.pgc" - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; } + else settled++; } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_settle", ECPGt_EOIT, ECPGt_EORT);} +#line 49 "app/batch/au_bt_0002.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0002", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0002.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + if (errcnt == 0) tx_commit(); else tx_abort(); + printf("===== 승인 정산반영 [au_bt_0002] =====\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상(M) : %ld\n", total); + printf(" 정산(S) : %ld\n", settled); + printf(" 순정산액 : %ld\n", sum); + printf(" 오류 : %ld\n", errcnt); + tx_log(TX_LOG_INFO, "[au_bt_0002] 정산 date=%s 대상=%ld 정산=%ld", biz_date, total, settled); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -217,31 +174,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0002] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0002] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0002(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0002] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0002.pgc b/app/batch/au_bt_0002.pgc index 55882eb..b447ae9 100644 --- a/app/batch/au_bt_0002.pgc +++ b/app/batch/au_bt_0002.pgc @@ -1,11 +1,8 @@ /* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0002.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=medium] + * au_bt_0002.pgc - 승인원장 정산반영 배치 (승인완료 M -> 정산 S) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0002 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0002 [db@host] */ #include #include @@ -23,81 +20,42 @@ static int run_au_bt_0002(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_amount, h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, settled = 0, errcnt = 0, sum = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_au_bt_0002 CURSOR FOR - SELECT key, merch_id, amount - FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' + EXEC SQL DECLARE cur_settle CURSOR FOR + SELECT key, amount, fee FROM au_ledger + WHERE biz_date = :h_biz_date AND status = 'M' ORDER BY key; + EXEC SQL OPEN cur_settle; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_settle"); return TX_EDB; } - EXEC SQL OPEN cur_au_bt_0002; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0002"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0002] tx_begin 경고"); - + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[au_bt_0002] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_au_bt_0002 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0002"); - errcnt++; - break; - } - + EXEC SQL FETCH cur_settle INTO :h_key, :h_amount, :h_fee; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_settle"); errcnt++; break; } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + sum += h_amount - h_fee; + EXEC SQL UPDATE au_ledger SET status = 'S', upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; } + else settled++; } + EXEC SQL CLOSE cur_settle; + if (errcnt == 0) tx_commit(); else tx_abort(); - EXEC SQL CLOSE cur_au_bt_0002; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - + printf("===== 승인 정산반영 [au_bt_0002] =====\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상(M) : %ld\n", total); + printf(" 정산(S) : %ld\n", settled); + printf(" 순정산액 : %ld\n", sum); + printf(" 오류 : %ld\n", errcnt); + tx_log(TX_LOG_INFO, "[au_bt_0002] 정산 date=%s 대상=%ld 정산=%ld", biz_date, total, settled); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -108,31 +66,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0002] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0002] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0002(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0002] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0003.c b/app/batch/au_bt_0003.c index 60d9efa..10022e1 100644 --- a/app/batch/au_bt_0003.c +++ b/app/batch/au_bt_0003.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/au_bt_0003.pgc" -/* @tier easy @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0003.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0003.pgc - 승인 일별 통계집계 배치 (상태별 건수/금액) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0003 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0003 [db@host] */ #include #include @@ -93,121 +90,56 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0003.pgc" +#line 16 "app/batch/au_bt_0003.pgc" static int run_au_bt_0003(const char *biz_date) { /* exec sql begin declare section */ - - - - + + -#line 24 "app/batch/au_bt_0003.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/au_bt_0003.pgc" + char h_biz_date [ 9 ] , h_st [ 2 ] ; -#line 25 "app/batch/au_bt_0003.pgc" - char h_key [ 16 ] ; +#line 22 "app/batch/au_bt_0003.pgc" + int h_cnt ; -#line 26 "app/batch/au_bt_0003.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0003.pgc" - long h_amount ; - -#line 28 "app/batch/au_bt_0003.pgc" - char h_new_status [ 2 ] ; +#line 23 "app/batch/au_bt_0003.pgc" + long h_sum ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0003.pgc" +#line 24 "app/batch/au_bt_0003.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const char *codes = "RMUS"; + const char *names[] = { "접수", "승인", "거절", "정산" }; + int i; + long grand = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0003 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0003.pgc" - -#line 42 "app/batch/au_bt_0003.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0003 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + printf("===== 승인 일별통계 [au_bt_0003] date=%s =====\n", biz_date); + for (i = 0; codes[i]; i++) { + h_st[0] = codes[i]; h_st[1] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from au_ledger where biz_date = $1 and status = $2 ", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0003.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0003"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0003] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0003", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_st),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0003.pgc" +#line 38 "app/batch/au_bt_0003.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0003"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("통계"); return TX_EDB; } + printf(" %s(%c) : %6d 건 %15ld 원\n", names[i], codes[i], h_cnt, h_sum); + grand += h_sum; + tx_log(TX_LOG_INFO, "[au_bt_0003] %c cnt=%d sum=%ld", codes[i], h_cnt, h_sum); } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0003", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0003.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf(" 합계금액 : %ld 원\n", grand); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +149,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0003] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0003] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0003(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0003] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0003.pgc b/app/batch/au_bt_0003.pgc index c4d823d..be2f94d 100644 --- a/app/batch/au_bt_0003.pgc +++ b/app/batch/au_bt_0003.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0003.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0003.pgc - 승인 일별 통계집계 배치 (상태별 건수/금액) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0003 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0003 [db@host] */ #include #include @@ -21,84 +18,31 @@ EXEC SQL INCLUDE sqlca; static int run_au_bt_0003(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_st[2]; + int h_cnt; + long h_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const char *codes = "RMUS"; + const char *names[] = { "접수", "승인", "거절", "정산" }; + int i; + long grand = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_au_bt_0003 CURSOR FOR - SELECT key, merch_id, amount - FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_au_bt_0003; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0003"); - return TX_EDB; + printf("===== 승인 일별통계 [au_bt_0003] date=%s =====\n", biz_date); + for (i = 0; codes[i]; i++) { + h_st[0] = codes[i]; h_st[1] = '\0'; + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_cnt, :h_sum + FROM au_ledger WHERE biz_date = :h_biz_date AND status = :h_st; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("통계"); return TX_EDB; } + printf(" %s(%c) : %6d 건 %15ld 원\n", names[i], codes[i], h_cnt, h_sum); + grand += h_sum; + tx_log(TX_LOG_INFO, "[au_bt_0003] %c cnt=%d sum=%ld", codes[i], h_cnt, h_sum); } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0003] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_au_bt_0003 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0003"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_au_bt_0003; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf(" 합계금액 : %ld 원\n", grand); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +52,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0003] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0003] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0003(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0003] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0004.c b/app/batch/au_bt_0004.c index 3ad3882..9395366 100644 --- a/app/batch/au_bt_0004.c +++ b/app/batch/au_bt_0004.c @@ -8,12 +8,9 @@ #line 1 "app/batch/au_bt_0004.pgc" /* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0004.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=medium] + * au_bt_0004.pgc - 승인 실패건 재처리 배치 (거절 U -> 재접수 R) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0004 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0004 [db@host] */ #include #include @@ -93,120 +90,69 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0004.pgc" +#line 16 "app/batch/au_bt_0004.pgc" static int run_au_bt_0004(const char *biz_date) { /* exec sql begin declare section */ - - - - + -#line 24 "app/batch/au_bt_0004.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/au_bt_0004.pgc" + char h_biz_date [ 9 ] , h_key [ 16 ] ; -#line 25 "app/batch/au_bt_0004.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/au_bt_0004.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0004.pgc" +#line 22 "app/batch/au_bt_0004.pgc" long h_amount ; - -#line 28 "app/batch/au_bt_0004.pgc" - char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0004.pgc" +#line 23 "app/batch/au_bt_0004.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, requeued = 0, skipped = 0, errcnt = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0004 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0004.pgc" + /* declare cur_retry cursor for select key , amount from au_ledger where biz_date = $1 and status = 'U' order by key */ +#line 32 "app/batch/au_bt_0004.pgc" -#line 42 "app/batch/au_bt_0004.pgc" +#line 32 "app/batch/au_bt_0004.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0004 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_retry cursor for select key , amount from au_ledger where biz_date = $1 and status = 'U' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0004.pgc" +#line 33 "app/batch/au_bt_0004.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0004"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0004] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_retry"); return TX_EDB; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[au_bt_0004] tx_begin 경고"); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0004", ECPGt_EOIT, + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_retry", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0004.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0004"); - errcnt++; - break; - } +#line 38 "app/batch/au_bt_0004.pgc" + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; break; } total++; + if (!amount_is_valid(h_amount)) { skipped++; continue; } /* 금액오류는 재처리 제외 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set status = 'R' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 44 "app/batch/au_bt_0004.pgc" - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK) errcnt++; else requeued++; } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_retry", ECPGt_EOIT, ECPGt_EORT);} +#line 47 "app/batch/au_bt_0004.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0004", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0004.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + if (errcnt == 0) tx_commit(); else tx_abort(); + printf("===== 승인 재처리 [au_bt_0004] =====\n"); + printf(" 대상(U)=%ld 재접수=%ld 제외=%ld 오류=%ld\n", total, requeued, skipped, errcnt); + tx_log(TX_LOG_INFO, "[au_bt_0004] 재처리 date=%s 재접수=%ld", biz_date, requeued); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -217,31 +163,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0004] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0004] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0004(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0004] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0004.pgc b/app/batch/au_bt_0004.pgc index 3f7aeee..6a09000 100644 --- a/app/batch/au_bt_0004.pgc +++ b/app/batch/au_bt_0004.pgc @@ -1,11 +1,8 @@ /* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0004.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=medium] + * au_bt_0004.pgc - 승인 실패건 재처리 배치 (거절 U -> 재접수 R) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0004 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0004 [db@host] */ #include #include @@ -21,83 +18,38 @@ EXEC SQL INCLUDE sqlca; static int run_au_bt_0004(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; + char h_biz_date[9], h_key[16]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, requeued = 0, skipped = 0, errcnt = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_au_bt_0004 CURSOR FOR - SELECT key, merch_id, amount - FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' + EXEC SQL DECLARE cur_retry CURSOR FOR + SELECT key, amount FROM au_ledger + WHERE biz_date = :h_biz_date AND status = 'U' ORDER BY key; + EXEC SQL OPEN cur_retry; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_retry"); return TX_EDB; } - EXEC SQL OPEN cur_au_bt_0004; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0004"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0004] tx_begin 경고"); - + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[au_bt_0004] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_au_bt_0004 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0004"); - errcnt++; - break; - } - + EXEC SQL FETCH cur_retry INTO :h_key, :h_amount; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; break; } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (!amount_is_valid(h_amount)) { skipped++; continue; } /* 금액오류는 재처리 제외 */ + EXEC SQL UPDATE au_ledger SET status = 'R', upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) errcnt++; else requeued++; } + EXEC SQL CLOSE cur_retry; + if (errcnt == 0) tx_commit(); else tx_abort(); - EXEC SQL CLOSE cur_au_bt_0004; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - + printf("===== 승인 재처리 [au_bt_0004] =====\n"); + printf(" 대상(U)=%ld 재접수=%ld 제외=%ld 오류=%ld\n", total, requeued, skipped, errcnt); + tx_log(TX_LOG_INFO, "[au_bt_0004] 재처리 date=%s 재접수=%ld", biz_date, requeued); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -108,31 +60,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0004] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0004] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0004(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0004] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0005.c b/app/batch/au_bt_0005.c index 5ae0ae7..ed9476c 100644 --- a/app/batch/au_bt_0005.c +++ b/app/batch/au_bt_0005.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/au_bt_0005.pgc" -/* @tier easy @module au @transform authorization-batch */ +/* @tier hard @module au @transform authorization-batch */ /* - * au_bt_0005.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0005.pgc - 이상거래 탐지 배치 (카드별 임계 초과 플래그) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0005 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0005 [db@host] */ #include #include @@ -93,121 +90,70 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0005.pgc" +#line 16 "app/batch/au_bt_0005.pgc" static int run_au_bt_0005(const char *biz_date) { /* exec sql begin declare section */ - - - - + + -#line 24 "app/batch/au_bt_0005.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/au_bt_0005.pgc" + char h_biz_date [ 9 ] , h_card [ 20 ] ; -#line 25 "app/batch/au_bt_0005.pgc" - char h_key [ 16 ] ; +#line 22 "app/batch/au_bt_0005.pgc" + int h_cnt ; -#line 26 "app/batch/au_bt_0005.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0005.pgc" - long h_amount ; - -#line 28 "app/batch/au_bt_0005.pgc" - char h_new_status [ 2 ] ; +#line 23 "app/batch/au_bt_0005.pgc" + long h_sum ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0005.pgc" +#line 24 "app/batch/au_bt_0005.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long cards = 0, flagged = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0005 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0005.pgc" + /* declare cur_fraud cursor for select card_no , count ( * ) , sum ( amount ) from au_ledger where biz_date = $1 group by card_no having count ( * ) >= 10 or sum ( amount ) >= 30000000 order by sum ( amount ) desc */ +#line 36 "app/batch/au_bt_0005.pgc" -#line 42 "app/batch/au_bt_0005.pgc" +#line 36 "app/batch/au_bt_0005.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0005 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_fraud cursor for select card_no , count ( * ) , sum ( amount ) from au_ledger where biz_date = $1 group by card_no having count ( * ) >= 10 or sum ( amount ) >= 30000000 order by sum ( amount ) desc", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0005.pgc" +#line 37 "app/batch/au_bt_0005.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0005"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0005] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_fraud"); return TX_EDB; } + printf("===== 이상거래 탐지 [au_bt_0005] date=%s =====\n", biz_date); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0005", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_fraud", ECPGt_EOIT, + ECPGt_char,(h_card),(long)20,(long)1,(20)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0005.pgc" +#line 42 "app/batch/au_bt_0005.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0005"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + cards++; + flagged++; + printf(" [의심] card=%s 건수=%d 합계=%ld\n", h_card, h_cnt, h_sum); + tx_log(TX_LOG_WARN, "[au_bt_0005] 이상거래 card=%s cnt=%d sum=%ld", h_card, h_cnt, h_sum); } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0005", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0005.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_fraud", ECPGt_EOIT, ECPGt_EORT);} +#line 50 "app/batch/au_bt_0005.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf(" 탐지 카드수 : %ld\n", flagged); + (void)cards; + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +163,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0005] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0005] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0005(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0005] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0005.pgc b/app/batch/au_bt_0005.pgc index 53937c8..979c445 100644 --- a/app/batch/au_bt_0005.pgc +++ b/app/batch/au_bt_0005.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module au @transform authorization-batch */ +/* @tier hard @module au @transform authorization-batch */ /* - * au_bt_0005.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0005.pgc - 이상거래 탐지 배치 (카드별 임계 초과 플래그) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0005 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0005 [db@host] */ #include #include @@ -21,84 +18,40 @@ EXEC SQL INCLUDE sqlca; static int run_au_bt_0005(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_card[20]; + int h_cnt; + long h_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long cards = 0, flagged = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_au_bt_0005 CURSOR FOR - SELECT key, merch_id, amount + EXEC SQL DECLARE cur_fraud CURSOR FOR + SELECT card_no, count(*), sum(amount) FROM au_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_au_bt_0005; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0005"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0005] tx_begin 경고"); + GROUP BY card_no + HAVING count(*) >= 10 OR sum(amount) >= 30000000 + ORDER BY sum(amount) DESC; + EXEC SQL OPEN cur_fraud; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_fraud"); return TX_EDB; } + printf("===== 이상거래 탐지 [au_bt_0005] date=%s =====\n", biz_date); for (;;) { - EXEC SQL FETCH cur_au_bt_0005 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0005"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL FETCH cur_fraud INTO :h_card, :h_cnt, :h_sum; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + cards++; + flagged++; + printf(" [의심] card=%s 건수=%d 합계=%ld\n", h_card, h_cnt, h_sum); + tx_log(TX_LOG_WARN, "[au_bt_0005] 이상거래 card=%s cnt=%d sum=%ld", h_card, h_cnt, h_sum); } + EXEC SQL CLOSE cur_fraud; - EXEC SQL CLOSE cur_au_bt_0005; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf(" 탐지 카드수 : %ld\n", flagged); + (void)cards; + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +61,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0005] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0005] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0005(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0005] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0006.c b/app/batch/au_bt_0006.c index b0ab237..f8393fb 100644 --- a/app/batch/au_bt_0006.c +++ b/app/batch/au_bt_0006.c @@ -8,12 +8,9 @@ #line 1 "app/batch/au_bt_0006.pgc" /* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0006.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=medium] + * au_bt_0006.pgc - 한도 복원 배치 (취소건 한도 원복) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0006 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0006 [db@host] */ #include #include @@ -93,120 +90,72 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0006.pgc" +#line 16 "app/batch/au_bt_0006.pgc" static int run_au_bt_0006(const char *biz_date) { /* exec sql begin declare section */ - - - - + -#line 24 "app/batch/au_bt_0006.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/au_bt_0006.pgc" + char h_biz_date [ 9 ] , h_key [ 16 ] , h_merch [ 16 ] ; -#line 25 "app/batch/au_bt_0006.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/au_bt_0006.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0006.pgc" +#line 22 "app/batch/au_bt_0006.pgc" long h_amount ; - -#line 28 "app/batch/au_bt_0006.pgc" - char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0006.pgc" +#line 23 "app/batch/au_bt_0006.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, restored = 0, errcnt = 0, sum = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0006 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0006.pgc" + /* declare cur_restore cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'U' and amount > 0 order by merch_id , key */ +#line 32 "app/batch/au_bt_0006.pgc" -#line 42 "app/batch/au_bt_0006.pgc" +#line 32 "app/batch/au_bt_0006.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0006 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_restore cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'U' and amount > 0 order by merch_id , key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0006.pgc" +#line 33 "app/batch/au_bt_0006.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0006"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0006] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_restore"); return TX_EDB; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[au_bt_0006] tx_begin 경고"); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0006", ECPGt_EOIT, + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_restore", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0006.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0006"); - errcnt++; - break; - } +#line 38 "app/batch/au_bt_0006.pgc" + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; break; } total++; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set fee = 0 , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 42 "app/batch/au_bt_0006.pgc" - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; continue; } + restored++; + sum += h_amount; } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_restore", ECPGt_EOIT, ECPGt_EORT);} +#line 47 "app/batch/au_bt_0006.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0006", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0006.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + if (errcnt == 0) tx_commit(); else tx_abort(); + printf("===== 한도복원 [au_bt_0006] 대상=%ld 복원=%ld 복원액=%ld =====\n", + total, restored, sum); + tx_log(TX_LOG_INFO, "[au_bt_0006] 한도복원 date=%s 복원=%ld", biz_date, restored); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -217,31 +166,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0006] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0006] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0006(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0006] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0006.pgc b/app/batch/au_bt_0006.pgc index ab854a6..9c9c3a8 100644 --- a/app/batch/au_bt_0006.pgc +++ b/app/batch/au_bt_0006.pgc @@ -1,11 +1,8 @@ /* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0006.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=medium] + * au_bt_0006.pgc - 한도 복원 배치 (취소건 한도 원복) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0006 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0006 [db@host] */ #include #include @@ -21,83 +18,38 @@ EXEC SQL INCLUDE sqlca; static int run_au_bt_0006(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; + char h_biz_date[9], h_key[16], h_merch[16]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, restored = 0, errcnt = 0, sum = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_au_bt_0006 CURSOR FOR - SELECT key, merch_id, amount - FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_au_bt_0006; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0006"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0006] tx_begin 경고"); + EXEC SQL DECLARE cur_restore CURSOR FOR + SELECT key, merch_id, amount FROM au_ledger + WHERE biz_date = :h_biz_date AND status = 'U' AND amount > 0 + ORDER BY merch_id, key; + EXEC SQL OPEN cur_restore; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_restore"); return TX_EDB; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[au_bt_0006] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_au_bt_0006 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0006"); - errcnt++; - break; - } - + EXEC SQL FETCH cur_restore INTO :h_key, :h_merch, :h_amount; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; break; } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL UPDATE au_ledger SET fee = 0, upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; continue; } + restored++; + sum += h_amount; } + EXEC SQL CLOSE cur_restore; + if (errcnt == 0) tx_commit(); else tx_abort(); - EXEC SQL CLOSE cur_au_bt_0006; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - + printf("===== 한도복원 [au_bt_0006] 대상=%ld 복원=%ld 복원액=%ld =====\n", + total, restored, sum); + tx_log(TX_LOG_INFO, "[au_bt_0006] 한도복원 date=%s 복원=%ld", biz_date, restored); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -108,31 +60,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0006] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0006] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0006(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0006] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0007.c b/app/batch/au_bt_0007.c index 8c08933..052ee47 100644 --- a/app/batch/au_bt_0007.c +++ b/app/batch/au_bt_0007.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/au_bt_0007.pgc" -/* @tier easy @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0007.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0007.pgc - 가맹점별 승인금액 집계 배치 (매출 리포트) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0007 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0007 [db@host] */ #include #include @@ -93,121 +90,71 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0007.pgc" +#line 16 "app/batch/au_bt_0007.pgc" static int run_au_bt_0007(const char *biz_date) { /* exec sql begin declare section */ - - - - - + + + -#line 24 "app/batch/au_bt_0007.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/au_bt_0007.pgc" + char h_biz_date [ 9 ] , h_merch [ 16 ] ; -#line 25 "app/batch/au_bt_0007.pgc" - char h_key [ 16 ] ; +#line 22 "app/batch/au_bt_0007.pgc" + int h_cnt ; -#line 26 "app/batch/au_bt_0007.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0007.pgc" - long h_amount ; - -#line 28 "app/batch/au_bt_0007.pgc" - char h_new_status [ 2 ] ; +#line 23 "app/batch/au_bt_0007.pgc" + long h_sum , h_fee ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0007.pgc" +#line 24 "app/batch/au_bt_0007.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merchants = 0, grand = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0007 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0007.pgc" + /* declare cur_msum cursor for select merch_id , count ( * ) , sum ( amount ) , sum ( fee ) from au_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by merch_id order by sum ( amount ) desc */ +#line 35 "app/batch/au_bt_0007.pgc" -#line 42 "app/batch/au_bt_0007.pgc" +#line 35 "app/batch/au_bt_0007.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0007 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_msum cursor for select merch_id , count ( * ) , sum ( amount ) , sum ( fee ) from au_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by merch_id order by sum ( amount ) desc", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0007.pgc" +#line 36 "app/batch/au_bt_0007.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0007"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0007] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_msum"); return TX_EDB; } + printf("===== 가맹점별 승인집계 [au_bt_0007] date=%s =====\n", biz_date); + printf(" %-15s %8s %15s %12s\n", "가맹점", "건수", "승인액", "수수료"); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0007", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_msum", ECPGt_EOIT, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0007.pgc" +#line 42 "app/batch/au_bt_0007.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0007"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + merchants++; + grand += h_sum; + printf(" %-15s %8d %15ld %12ld\n", h_merch, h_cnt, h_sum, h_fee); } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_msum", ECPGt_EOIT, ECPGt_EORT);} +#line 49 "app/batch/au_bt_0007.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0007", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0007.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf(" 가맹점수=%ld 총승인액=%ld\n", merchants, grand); + tx_log(TX_LOG_INFO, "[au_bt_0007] 가맹점집계 date=%s merch=%ld", biz_date, merchants); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +164,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0007] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0007] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0007(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0007] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0007.pgc b/app/batch/au_bt_0007.pgc index 232e769..0312e19 100644 --- a/app/batch/au_bt_0007.pgc +++ b/app/batch/au_bt_0007.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0007.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0007.pgc - 가맹점별 승인금액 집계 배치 (매출 리포트) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0007 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0007 [db@host] */ #include #include @@ -21,84 +18,38 @@ EXEC SQL INCLUDE sqlca; static int run_au_bt_0007(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_merch[16]; + int h_cnt; + long h_sum, h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merchants = 0, grand = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_au_bt_0007 CURSOR FOR - SELECT key, merch_id, amount + EXEC SQL DECLARE cur_msum CURSOR FOR + SELECT merch_id, count(*), sum(amount), sum(fee) FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_au_bt_0007; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0007"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0007] tx_begin 경고"); + WHERE biz_date = :h_biz_date AND status IN ('M','S') + GROUP BY merch_id + ORDER BY sum(amount) DESC; + EXEC SQL OPEN cur_msum; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_msum"); return TX_EDB; } + printf("===== 가맹점별 승인집계 [au_bt_0007] date=%s =====\n", biz_date); + printf(" %-15s %8s %15s %12s\n", "가맹점", "건수", "승인액", "수수료"); for (;;) { - EXEC SQL FETCH cur_au_bt_0007 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0007"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL FETCH cur_msum INTO :h_merch, :h_cnt, :h_sum, :h_fee; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + merchants++; + grand += h_sum; + printf(" %-15s %8d %15ld %12ld\n", h_merch, h_cnt, h_sum, h_fee); } - - EXEC SQL CLOSE cur_au_bt_0007; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + EXEC SQL CLOSE cur_msum; + printf(" 가맹점수=%ld 총승인액=%ld\n", merchants, grand); + tx_log(TX_LOG_INFO, "[au_bt_0007] 가맹점집계 date=%s merch=%ld", biz_date, merchants); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +59,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0007] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0007] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0007(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0007] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0008.c b/app/batch/au_bt_0008.c index e890ae7..98556e3 100644 --- a/app/batch/au_bt_0008.c +++ b/app/batch/au_bt_0008.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/au_bt_0008.pgc" -/* @tier hard @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0008.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=hard] + * au_bt_0008.pgc - 미승인 접수건 정리 배치 (장기 R -> 실효 U) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0008 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0008 [db@host] */ #include #include @@ -93,121 +90,44 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0008.pgc" +#line 16 "app/batch/au_bt_0008.pgc" static int run_au_bt_0008(const char *biz_date) { /* exec sql begin declare section */ - - - - + -#line 24 "app/batch/au_bt_0008.pgc" +#line 21 "app/batch/au_bt_0008.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/au_bt_0008.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/au_bt_0008.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0008.pgc" - long h_amount ; - -#line 28 "app/batch/au_bt_0008.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/au_bt_0008.pgc" + int h_aff ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0008.pgc" +#line 23 "app/batch/au_bt_0008.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0008 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0008.pgc" - -#line 42 "app/batch/au_bt_0008.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0008 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[au_bt_0008] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set status = 'U' , upd_ts = now ( ) where status = 'R' and biz_date < $1 ", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0008.pgc" +#line 30 "app/batch/au_bt_0008.pgc" if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0008"); + TX_DBIO_LOG_ERR("au_bt_0008 정리"); + tx_abort(); return TX_EDB; } + h_aff = (int)sqlca.sqlerrd[2]; + tx_commit(); - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0008] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0008", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0008.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0008"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0008", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0008.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 미승인 정리 [au_bt_0008] 기준일<%s 실효처리=%d 건 =====\n", + biz_date, h_aff); + tx_log(TX_LOG_INFO, "[au_bt_0008] 미승인 실효 %d건", h_aff); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +137,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0008] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0008] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0008(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0008] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0008.pgc b/app/batch/au_bt_0008.pgc index 4a1e8e9..7bfda00 100644 --- a/app/batch/au_bt_0008.pgc +++ b/app/batch/au_bt_0008.pgc @@ -1,11 +1,8 @@ -/* @tier hard @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0008.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=hard] + * au_bt_0008.pgc - 미승인 접수건 정리 배치 (장기 R -> 실효 U) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0008 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0008 [db@host] */ #include #include @@ -22,83 +19,27 @@ static int run_au_bt_0008(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + int h_aff; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - EXEC SQL DECLARE cur_au_bt_0008 CURSOR FOR - SELECT key, merch_id, amount - FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_au_bt_0008; + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[au_bt_0008] tx_begin 경고"); + EXEC SQL UPDATE au_ledger + SET status = 'U', upd_ts = now() + WHERE status = 'R' AND biz_date < :h_biz_date; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0008"); + TX_DBIO_LOG_ERR("au_bt_0008 정리"); + tx_abort(); return TX_EDB; } + h_aff = (int)sqlca.sqlerrd[2]; + tx_commit(); - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0008] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_au_bt_0008 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0008"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_au_bt_0008; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 미승인 정리 [au_bt_0008] 기준일<%s 실효처리=%d 건 =====\n", + biz_date, h_aff); + tx_log(TX_LOG_INFO, "[au_bt_0008] 미승인 실효 %d건", h_aff); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +49,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0008] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0008] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0008(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0008] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0009.c b/app/batch/au_bt_0009.c index 8b44fce..bcfa7e2 100644 --- a/app/batch/au_bt_0009.c +++ b/app/batch/au_bt_0009.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/au_bt_0009.pgc" -/* @tier easy @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0009.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0009.pgc - 승인 수수료 재정산 배치 (요율표 일괄 UPDATE) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0009 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0009 [db@host] */ #include #include @@ -93,120 +90,77 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0009.pgc" +#line 16 "app/batch/au_bt_0009.pgc" static int run_au_bt_0009(const char *biz_date) { /* exec sql begin declare section */ - - - - - + + -#line 24 "app/batch/au_bt_0009.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/au_bt_0009.pgc" + char h_biz_date [ 9 ] , h_key [ 16 ] , h_merch [ 16 ] ; -#line 25 "app/batch/au_bt_0009.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/au_bt_0009.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0009.pgc" - long h_amount ; - -#line 28 "app/batch/au_bt_0009.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/au_bt_0009.pgc" + long h_amount , h_fee ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0009.pgc" +#line 23 "app/batch/au_bt_0009.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0, feesum = 0; + int rate; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0009 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0009.pgc" + /* declare cur_fee cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status in ( 'M' , 'S' ) order by key */ +#line 33 "app/batch/au_bt_0009.pgc" -#line 42 "app/batch/au_bt_0009.pgc" +#line 33 "app/batch/au_bt_0009.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0009 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_fee cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status in ( 'M' , 'S' ) order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0009.pgc" +#line 34 "app/batch/au_bt_0009.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0009"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0009] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_fee"); return TX_EDB; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[au_bt_0009] tx_begin 경고"); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0009", ECPGt_EOIT, + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_fee", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0009.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0009"); - errcnt++; - break; - } +#line 39 "app/batch/au_bt_0009.pgc" + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; break; } total++; + /* 금액구간별 차등요율 (basis point) */ + rate = (h_amount >= 1000000L) ? 80 : (h_amount >= 100000L) ? 120 : 150; + h_fee = h_amount * rate / 10000; + feesum += h_fee; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set fee = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 47 "app/batch/au_bt_0009.pgc" - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK) errcnt++; } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_fee", ECPGt_EOIT, ECPGt_EORT);} +#line 50 "app/batch/au_bt_0009.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0009", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0009.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + if (errcnt == 0) tx_commit(); else tx_abort(); + printf("===== 수수료 재정산 [au_bt_0009] 대상=%ld 수수료합=%ld 오류=%ld =====\n", + total, feesum, errcnt); + tx_log(TX_LOG_INFO, "[au_bt_0009] 수수료 재정산 date=%s feesum=%ld", biz_date, feesum); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -217,31 +171,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0009] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0009] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0009(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0009] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0009.pgc b/app/batch/au_bt_0009.pgc index cf77c64..01ceca3 100644 --- a/app/batch/au_bt_0009.pgc +++ b/app/batch/au_bt_0009.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0009.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0009.pgc - 승인 수수료 재정산 배치 (요율표 일괄 UPDATE) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0009 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0009 [db@host] */ #include #include @@ -21,83 +18,41 @@ EXEC SQL INCLUDE sqlca; static int run_au_bt_0009(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_key[16], h_merch[16]; + long h_amount, h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0, feesum = 0; + int rate; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_au_bt_0009 CURSOR FOR - SELECT key, merch_id, amount - FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' + EXEC SQL DECLARE cur_fee CURSOR FOR + SELECT key, merch_id, amount FROM au_ledger + WHERE biz_date = :h_biz_date AND status IN ('M','S') ORDER BY key; + EXEC SQL OPEN cur_fee; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_fee"); return TX_EDB; } - EXEC SQL OPEN cur_au_bt_0009; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0009"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0009] tx_begin 경고"); - + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[au_bt_0009] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_au_bt_0009 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0009"); - errcnt++; - break; - } - + EXEC SQL FETCH cur_fee INTO :h_key, :h_merch, :h_amount; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; break; } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 금액구간별 차등요율 (basis point) */ + rate = (h_amount >= 1000000L) ? 80 : (h_amount >= 100000L) ? 120 : 150; + h_fee = h_amount * rate / 10000; + feesum += h_fee; + EXEC SQL UPDATE au_ledger SET fee = :h_fee, upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) errcnt++; } + EXEC SQL CLOSE cur_fee; + if (errcnt == 0) tx_commit(); else tx_abort(); - EXEC SQL CLOSE cur_au_bt_0009; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - + printf("===== 수수료 재정산 [au_bt_0009] 대상=%ld 수수료합=%ld 오류=%ld =====\n", + total, feesum, errcnt); + tx_log(TX_LOG_INFO, "[au_bt_0009] 수수료 재정산 date=%s feesum=%ld", biz_date, feesum); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -108,31 +63,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0009] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0009] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0009(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0009] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0010.c b/app/batch/au_bt_0010.c index 5d65823..e9e1be9 100644 --- a/app/batch/au_bt_0010.c +++ b/app/batch/au_bt_0010.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/au_bt_0010.pgc" -/* @tier easy @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0010.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0010.pgc - 무이자할부 만기정산 배치 (할부건 정산전이) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0010 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0010 [db@host] */ #include #include @@ -93,120 +90,67 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0010.pgc" +#line 16 "app/batch/au_bt_0010.pgc" static int run_au_bt_0010(const char *biz_date) { /* exec sql begin declare section */ - - - - + -#line 24 "app/batch/au_bt_0010.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/au_bt_0010.pgc" + char h_biz_date [ 9 ] , h_key [ 16 ] ; -#line 25 "app/batch/au_bt_0010.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/au_bt_0010.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0010.pgc" +#line 22 "app/batch/au_bt_0010.pgc" long h_amount ; - -#line 28 "app/batch/au_bt_0010.pgc" - char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0010.pgc" +#line 23 "app/batch/au_bt_0010.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, matured = 0, errcnt = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0010 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0010.pgc" + /* declare cur_inst cursor for select key , amount from au_ledger where biz_date = $1 and card_no = 'INSTALLMENT' and status = 'M' order by key */ +#line 32 "app/batch/au_bt_0010.pgc" -#line 42 "app/batch/au_bt_0010.pgc" +#line 32 "app/batch/au_bt_0010.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0010 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_inst cursor for select key , amount from au_ledger where biz_date = $1 and card_no = 'INSTALLMENT' and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0010.pgc" +#line 33 "app/batch/au_bt_0010.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0010"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0010] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_inst"); return TX_EDB; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[au_bt_0010] tx_begin 경고"); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0010", ECPGt_EOIT, + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_inst", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0010.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0010"); - errcnt++; - break; - } +#line 38 "app/batch/au_bt_0010.pgc" + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; break; } total++; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set status = 'S' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 42 "app/batch/au_bt_0010.pgc" - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK) errcnt++; else matured++; } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_inst", ECPGt_EOIT, ECPGt_EORT);} +#line 45 "app/batch/au_bt_0010.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0010", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0010.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + if (errcnt == 0) tx_commit(); else tx_abort(); + printf("===== 무이자할부 만기정산 [au_bt_0010] 대상=%ld 정산=%ld =====\n", total, matured); + tx_log(TX_LOG_INFO, "[au_bt_0010] 할부만기 date=%s 정산=%ld", biz_date, matured); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -217,31 +161,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0010] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0010] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0010(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0010] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0010.pgc b/app/batch/au_bt_0010.pgc index 301ce5f..44f0627 100644 --- a/app/batch/au_bt_0010.pgc +++ b/app/batch/au_bt_0010.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0010.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0010.pgc - 무이자할부 만기정산 배치 (할부건 정산전이) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0010 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0010 [db@host] */ #include #include @@ -21,83 +18,35 @@ EXEC SQL INCLUDE sqlca; static int run_au_bt_0010(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; + char h_biz_date[9], h_key[16]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, matured = 0, errcnt = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_au_bt_0010 CURSOR FOR - SELECT key, merch_id, amount - FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' + EXEC SQL DECLARE cur_inst CURSOR FOR + SELECT key, amount FROM au_ledger + WHERE biz_date = :h_biz_date AND card_no = 'INSTALLMENT' AND status = 'M' ORDER BY key; + EXEC SQL OPEN cur_inst; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_inst"); return TX_EDB; } - EXEC SQL OPEN cur_au_bt_0010; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0010"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0010] tx_begin 경고"); - + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[au_bt_0010] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_au_bt_0010 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0010"); - errcnt++; - break; - } - + EXEC SQL FETCH cur_inst INTO :h_key, :h_amount; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; break; } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL UPDATE au_ledger SET status = 'S', upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) errcnt++; else matured++; } + EXEC SQL CLOSE cur_inst; + if (errcnt == 0) tx_commit(); else tx_abort(); - EXEC SQL CLOSE cur_au_bt_0010; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - + printf("===== 무이자할부 만기정산 [au_bt_0010] 대상=%ld 정산=%ld =====\n", total, matured); + tx_log(TX_LOG_INFO, "[au_bt_0010] 할부만기 date=%s 정산=%ld", biz_date, matured); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -108,31 +57,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0010] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0010] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0010(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0010] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0011.c b/app/batch/au_bt_0011.c index 3d27052..a807fa8 100644 --- a/app/batch/au_bt_0011.c +++ b/app/batch/au_bt_0011.c @@ -8,12 +8,9 @@ #line 1 "app/batch/au_bt_0011.pgc" /* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0011.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=medium] + * au_bt_0011.pgc - 가맹점 한도초과 리포트 배치 (임계 초과 추출) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0011 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0011 [db@host] */ #include #include @@ -93,121 +90,69 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0011.pgc" +#line 16 "app/batch/au_bt_0011.pgc" static int run_au_bt_0011(const char *biz_date) { /* exec sql begin declare section */ - - - + -#line 24 "app/batch/au_bt_0011.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/au_bt_0011.pgc" + char h_biz_date [ 9 ] , h_merch [ 16 ] ; -#line 25 "app/batch/au_bt_0011.pgc" - char h_key [ 16 ] ; +#line 22 "app/batch/au_bt_0011.pgc" + long h_sum ; -#line 26 "app/batch/au_bt_0011.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0011.pgc" - long h_amount ; - -#line 28 "app/batch/au_bt_0011.pgc" - char h_new_status [ 2 ] ; +#line 23 "app/batch/au_bt_0011.pgc" + long h_limit ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0011.pgc" +#line 24 "app/batch/au_bt_0011.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long over = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; + h_limit = 100000000L; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0011 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0011.pgc" + ECPGset_var( 0, &( h_limit ), __LINE__);\ + ECPGset_var( 1, ( h_biz_date ), __LINE__);\ + /* declare cur_over cursor for select merch_id , sum ( amount ) from au_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by merch_id having sum ( amount ) > $2 order by sum ( amount ) desc */ +#line 36 "app/batch/au_bt_0011.pgc" -#line 42 "app/batch/au_bt_0011.pgc" +#line 36 "app/batch/au_bt_0011.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0011 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_over cursor for select merch_id , sum ( amount ) from au_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by merch_id having sum ( amount ) > $2 order by sum ( amount ) desc", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_limit),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0011.pgc" +#line 37 "app/batch/au_bt_0011.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0011"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0011] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_over"); return TX_EDB; } + printf("===== 가맹점 한도초과 리포트 [au_bt_0011] (한도 %ld) =====\n", h_limit); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0011", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_over", ECPGt_EOIT, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0011.pgc" +#line 42 "app/batch/au_bt_0011.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0011"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + over++; + printf(" [초과] merch=%s 승인액=%ld 초과=%ld\n", h_merch, h_sum, h_sum - h_limit); + tx_log(TX_LOG_WARN, "[au_bt_0011] 한도초과 merch=%s sum=%ld", h_merch, h_sum); } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_over", ECPGt_EOIT, ECPGt_EORT);} +#line 49 "app/batch/au_bt_0011.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0011", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0011.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf(" 초과 가맹점수=%ld\n", over); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +162,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0011] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0011] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0011(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0011] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0011.pgc b/app/batch/au_bt_0011.pgc index 80bda60..d090863 100644 --- a/app/batch/au_bt_0011.pgc +++ b/app/batch/au_bt_0011.pgc @@ -1,11 +1,8 @@ /* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0011.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=medium] + * au_bt_0011.pgc - 가맹점 한도초과 리포트 배치 (임계 초과 추출) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0011 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0011 [db@host] */ #include #include @@ -21,84 +18,37 @@ EXEC SQL INCLUDE sqlca; static int run_au_bt_0011(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_merch[16]; + long h_sum; + long h_limit; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long over = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; + h_limit = 100000000L; - EXEC SQL DECLARE cur_au_bt_0011 CURSOR FOR - SELECT key, merch_id, amount - FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_au_bt_0011; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0011"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0011] tx_begin 경고"); + EXEC SQL DECLARE cur_over CURSOR FOR + SELECT merch_id, sum(amount) FROM au_ledger + WHERE biz_date = :h_biz_date AND status IN ('M','S') + GROUP BY merch_id + HAVING sum(amount) > :h_limit + ORDER BY sum(amount) DESC; + EXEC SQL OPEN cur_over; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_over"); return TX_EDB; } + printf("===== 가맹점 한도초과 리포트 [au_bt_0011] (한도 %ld) =====\n", h_limit); for (;;) { - EXEC SQL FETCH cur_au_bt_0011 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0011"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL FETCH cur_over INTO :h_merch, :h_sum; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + over++; + printf(" [초과] merch=%s 승인액=%ld 초과=%ld\n", h_merch, h_sum, h_sum - h_limit); + tx_log(TX_LOG_WARN, "[au_bt_0011] 한도초과 merch=%s sum=%ld", h_merch, h_sum); } - - EXEC SQL CLOSE cur_au_bt_0011; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + EXEC SQL CLOSE cur_over; + printf(" 초과 가맹점수=%ld\n", over); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +58,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0011] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0011] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0011(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0011] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0012.c b/app/batch/au_bt_0012.c index d6ea300..0980491 100644 --- a/app/batch/au_bt_0012.c +++ b/app/batch/au_bt_0012.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/au_bt_0012.pgc" -/* @tier easy @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0012.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0012.pgc - 카드사별 승인집계 배치 (BIN 구간 그룹) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0012 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0012 [db@host] */ #include #include @@ -93,121 +90,67 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0012.pgc" +#line 16 "app/batch/au_bt_0012.pgc" static int run_au_bt_0012(const char *biz_date) { /* exec sql begin declare section */ - - - - + + -#line 24 "app/batch/au_bt_0012.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/au_bt_0012.pgc" + char h_biz_date [ 9 ] , h_bin [ 8 ] ; -#line 25 "app/batch/au_bt_0012.pgc" - char h_key [ 16 ] ; +#line 22 "app/batch/au_bt_0012.pgc" + int h_cnt ; -#line 26 "app/batch/au_bt_0012.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0012.pgc" - long h_amount ; - -#line 28 "app/batch/au_bt_0012.pgc" - char h_new_status [ 2 ] ; +#line 23 "app/batch/au_bt_0012.pgc" + long h_sum ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0012.pgc" +#line 24 "app/batch/au_bt_0012.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long bins = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0012 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0012.pgc" + /* declare cur_bin cursor for select substring ( card_no from 1 for 6 ) as bin , count ( * ) , sum ( amount ) from au_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by substring ( card_no from 1 for 6 ) order by count ( * ) desc */ +#line 35 "app/batch/au_bt_0012.pgc" -#line 42 "app/batch/au_bt_0012.pgc" +#line 35 "app/batch/au_bt_0012.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0012 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_bin cursor for select substring ( card_no from 1 for 6 ) as bin , count ( * ) , sum ( amount ) from au_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by substring ( card_no from 1 for 6 ) order by count ( * ) desc", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0012.pgc" +#line 36 "app/batch/au_bt_0012.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0012"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0012] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_bin"); return TX_EDB; } + printf("===== 카드사별(BIN) 승인집계 [au_bt_0012] date=%s =====\n", biz_date); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0012", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_bin", ECPGt_EOIT, + ECPGt_char,(h_bin),(long)8,(long)1,(8)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0012.pgc" +#line 41 "app/batch/au_bt_0012.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0012"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + bins++; + printf(" BIN %-6s : %6d 건 %15ld 원\n", h_bin, h_cnt, h_sum); } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_bin", ECPGt_EOIT, ECPGt_EORT);} +#line 47 "app/batch/au_bt_0012.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0012", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0012.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf(" BIN 구간수=%ld\n", bins); + tx_log(TX_LOG_INFO, "[au_bt_0012] BIN집계 date=%s bins=%ld", biz_date, bins); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +160,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0012] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0012] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0012(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0012] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0012.pgc b/app/batch/au_bt_0012.pgc index 7fc41ae..9e378d7 100644 --- a/app/batch/au_bt_0012.pgc +++ b/app/batch/au_bt_0012.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0012.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0012.pgc - 카드사별 승인집계 배치 (BIN 구간 그룹) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0012 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0012 [db@host] */ #include #include @@ -21,84 +18,36 @@ EXEC SQL INCLUDE sqlca; static int run_au_bt_0012(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_bin[8]; + int h_cnt; + long h_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long bins = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_au_bt_0012 CURSOR FOR - SELECT key, merch_id, amount + EXEC SQL DECLARE cur_bin CURSOR FOR + SELECT substring(card_no from 1 for 6) AS bin, count(*), sum(amount) FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_au_bt_0012; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0012"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0012] tx_begin 경고"); + WHERE biz_date = :h_biz_date AND status IN ('M','S') + GROUP BY substring(card_no from 1 for 6) + ORDER BY count(*) DESC; + EXEC SQL OPEN cur_bin; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_bin"); return TX_EDB; } + printf("===== 카드사별(BIN) 승인집계 [au_bt_0012] date=%s =====\n", biz_date); for (;;) { - EXEC SQL FETCH cur_au_bt_0012 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0012"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL FETCH cur_bin INTO :h_bin, :h_cnt, :h_sum; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + bins++; + printf(" BIN %-6s : %6d 건 %15ld 원\n", h_bin, h_cnt, h_sum); } - - EXEC SQL CLOSE cur_au_bt_0012; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + EXEC SQL CLOSE cur_bin; + printf(" BIN 구간수=%ld\n", bins); + tx_log(TX_LOG_INFO, "[au_bt_0012] BIN집계 date=%s bins=%ld", biz_date, bins); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +57,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0012] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0012] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0012(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0012] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0013.c b/app/batch/au_bt_0013.c index 0006da4..d075178 100644 --- a/app/batch/au_bt_0013.c +++ b/app/batch/au_bt_0013.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/au_bt_0013.pgc" -/* @tier medium @module au @transform authorization-batch */ +/* @tier hard @module au @transform authorization-batch */ /* - * au_bt_0013.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=medium] + * au_bt_0013.pgc - 승인 재시도 배치 (거절건 매입사 재중계 tx_call) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0013 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0013 [db@host] */ #include #include @@ -93,121 +90,85 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0013.pgc" +#line 16 "app/batch/au_bt_0013.pgc" static int run_au_bt_0013(const char *biz_date) { /* exec sql begin declare section */ - - - - + -#line 24 "app/batch/au_bt_0013.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/au_bt_0013.pgc" + char h_biz_date [ 9 ] , h_key [ 16 ] ; -#line 25 "app/batch/au_bt_0013.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/au_bt_0013.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0013.pgc" +#line 22 "app/batch/au_bt_0013.pgc" long h_amount ; - -#line 28 "app/batch/au_bt_0013.pgc" - char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0013.pgc" +#line 23 "app/batch/au_bt_0013.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + TXBUF *req, *resp; + long total = 0, ok = 0, fail = 0; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0013 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0013.pgc" + /* declare cur_resend cursor for select key , amount from au_ledger where biz_date = $1 and status = 'U' order by key */ +#line 34 "app/batch/au_bt_0013.pgc" -#line 42 "app/batch/au_bt_0013.pgc" +#line 34 "app/batch/au_bt_0013.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0013 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_resend cursor for select key , amount from au_ledger where biz_date = $1 and status = 'U' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0013.pgc" +#line 35 "app/batch/au_bt_0013.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0013"); - return TX_EDB; - } + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_resend"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0013] tx_begin 경고"); + req = tx_buf_alloc(); + resp = tx_buf_alloc(); + if (!req || !resp) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_resend", ECPGt_EOIT, ECPGt_EORT);} +#line 40 "app/batch/au_bt_0013.pgc" + return TX_ENOMEM; } for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0013", ECPGt_EOIT, + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_resend", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0013.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0013"); - errcnt++; - break; - } +#line 43 "app/batch/au_bt_0013.pgc" + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; total++; + tx_buf_reset(req); + tx_buf_reset(resp); + tx_buf_sets(req, "KEY", h_key); + tx_buf_setlong(req, "AMOUNT", h_amount); + rc = tx_call("AU_ISSUER", req, resp); + if (rc == TX_OK) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set status = 'M' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 53 "app/batch/au_bt_0013.pgc" - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; + if (sqlca.sqlcode == TX_SQL_OK) { tx_commit(); ok++; } } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + fail++; } } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0013", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0013.pgc" + tx_buf_free(req); + tx_buf_free(resp); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_resend", ECPGt_EOIT, ECPGt_EORT);} +#line 61 "app/batch/au_bt_0013.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 승인 재시도 [au_bt_0013] 대상=%ld 성공=%ld 실패=%ld =====\n", total, ok, fail); + tx_log(TX_LOG_INFO, "[au_bt_0013] 재중계 date=%s ok=%ld fail=%ld", biz_date, ok, fail); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +178,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0013] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0013] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0013(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0013] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0013.pgc b/app/batch/au_bt_0013.pgc index 34c40c0..9c876c1 100644 --- a/app/batch/au_bt_0013.pgc +++ b/app/batch/au_bt_0013.pgc @@ -1,11 +1,8 @@ -/* @tier medium @module au @transform authorization-batch */ +/* @tier hard @module au @transform authorization-batch */ /* - * au_bt_0013.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=medium] + * au_bt_0013.pgc - 승인 재시도 배치 (거절건 매입사 재중계 tx_call) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0013 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0013 [db@host] */ #include #include @@ -21,84 +18,51 @@ EXEC SQL INCLUDE sqlca; static int run_au_bt_0013(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; + char h_biz_date[9], h_key[16]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + TXBUF *req, *resp; + long total = 0, ok = 0, fail = 0; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_au_bt_0013 CURSOR FOR - SELECT key, merch_id, amount - FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' + EXEC SQL DECLARE cur_resend CURSOR FOR + SELECT key, amount FROM au_ledger + WHERE biz_date = :h_biz_date AND status = 'U' ORDER BY key; + EXEC SQL OPEN cur_resend; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_resend"); return TX_EDB; } - EXEC SQL OPEN cur_au_bt_0013; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0013"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0013] tx_begin 경고"); + req = tx_buf_alloc(); + resp = tx_buf_alloc(); + if (!req || !resp) { EXEC SQL CLOSE cur_resend; return TX_ENOMEM; } for (;;) { - EXEC SQL FETCH cur_au_bt_0013 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0013"); - errcnt++; - break; - } - + EXEC SQL FETCH cur_resend INTO :h_key, :h_amount; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; + tx_buf_reset(req); + tx_buf_reset(resp); + tx_buf_sets(req, "KEY", h_key); + tx_buf_setlong(req, "AMOUNT", h_amount); + rc = tx_call("AU_ISSUER", req, resp); + if (rc == TX_OK) { + EXEC SQL UPDATE au_ledger SET status = 'M', upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_OK) { tx_commit(); ok++; } } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + fail++; } } + tx_buf_free(req); + tx_buf_free(resp); + EXEC SQL CLOSE cur_resend; - EXEC SQL CLOSE cur_au_bt_0013; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 승인 재시도 [au_bt_0013] 대상=%ld 성공=%ld 실패=%ld =====\n", total, ok, fail); + tx_log(TX_LOG_INFO, "[au_bt_0013] 재중계 date=%s ok=%ld fail=%ld", biz_date, ok, fail); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +72,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0013] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0013] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0013(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0013] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0014.c b/app/batch/au_bt_0014.c index 9633630..a492171 100644 --- a/app/batch/au_bt_0014.c +++ b/app/batch/au_bt_0014.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/au_bt_0014.pgc" -/* @tier easy @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0014.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0014.pgc - 승인 일마감 배치 (당일 마감/집계 확정) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0014 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0014 [db@host] */ #include #include @@ -93,121 +90,56 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0014.pgc" +#line 16 "app/batch/au_bt_0014.pgc" static int run_au_bt_0014(const char *biz_date) { /* exec sql begin declare section */ - - - + -#line 24 "app/batch/au_bt_0014.pgc" +#line 21 "app/batch/au_bt_0014.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/au_bt_0014.pgc" - char h_key [ 16 ] ; +#line 22 "app/batch/au_bt_0014.pgc" + int h_recv , h_appr , h_settle ; -#line 26 "app/batch/au_bt_0014.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0014.pgc" - long h_amount ; - -#line 28 "app/batch/au_bt_0014.pgc" - char h_new_status [ 2 ] ; +#line 23 "app/batch/au_bt_0014.pgc" + long h_total ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0014.pgc" +#line 24 "app/batch/au_bt_0014.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0014 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0014.pgc" - -#line 42 "app/batch/au_bt_0014.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0014 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) filter ( where status = 'R' ) , count ( * ) filter ( where status = 'M' ) , count ( * ) filter ( where status = 'S' ) , coalesce ( sum ( amount ) filter ( where status in ( 'M' , 'S' ) ) , 0 ) from au_ledger where biz_date = $1 ", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0014.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0014"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0014] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0014", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_recv),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_int,&(h_appr),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_int,&(h_settle),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_total),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0014.pgc" +#line 34 "app/batch/au_bt_0014.pgc" + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("au_bt_0014 마감집계"); return TX_EDB; } - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0014"); - errcnt++; - break; - } + if (h_recv > 0) + tx_log(TX_LOG_WARN, "[au_bt_0014] 미처리 접수건 %d 존재 - 마감 확인필요", h_recv); - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0014", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0014.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 승인 일마감 [au_bt_0014] date=%s =====\n", biz_date); + printf(" 미처리(R) : %d\n", h_recv); + printf(" 승인(M) : %d\n", h_appr); + printf(" 정산(S) : %d\n", h_settle); + printf(" 승인총액 : %ld\n", h_total); + printf(" 마감상태 : %s\n", (h_recv == 0) ? "정상마감" : "보류"); + tx_log(TX_LOG_INFO, "[au_bt_0014] 일마감 date=%s total=%ld", biz_date, h_total); + return (h_recv == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -217,31 +149,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0014] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0014] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0014(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0014] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0014.pgc b/app/batch/au_bt_0014.pgc index 13cc4c0..348653b 100644 --- a/app/batch/au_bt_0014.pgc +++ b/app/batch/au_bt_0014.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0014.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0014.pgc - 승인 일마감 배치 (당일 마감/집계 확정) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0014 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0014 [db@host] */ #include #include @@ -22,83 +19,32 @@ static int run_au_bt_0014(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + int h_recv, h_appr, h_settle; + long h_total; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + EXEC SQL SELECT + count(*) FILTER (WHERE status = 'R'), + count(*) FILTER (WHERE status = 'M'), + count(*) FILTER (WHERE status = 'S'), + coalesce(sum(amount) FILTER (WHERE status IN ('M','S')), 0) + INTO :h_recv, :h_appr, :h_settle, :h_total + FROM au_ledger WHERE biz_date = :h_biz_date; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("au_bt_0014 마감집계"); return TX_EDB; } - EXEC SQL DECLARE cur_au_bt_0014 CURSOR FOR - SELECT key, merch_id, amount - FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + if (h_recv > 0) + tx_log(TX_LOG_WARN, "[au_bt_0014] 미처리 접수건 %d 존재 - 마감 확인필요", h_recv); - EXEC SQL OPEN cur_au_bt_0014; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0014"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0014] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_au_bt_0014 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0014"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_au_bt_0014; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 승인 일마감 [au_bt_0014] date=%s =====\n", biz_date); + printf(" 미처리(R) : %d\n", h_recv); + printf(" 승인(M) : %d\n", h_appr); + printf(" 정산(S) : %d\n", h_settle); + printf(" 승인총액 : %ld\n", h_total); + printf(" 마감상태 : %s\n", (h_recv == 0) ? "정상마감" : "보류"); + tx_log(TX_LOG_INFO, "[au_bt_0014] 일마감 date=%s total=%ld", biz_date, h_total); + return (h_recv == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -108,31 +54,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0014] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0014] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0014(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0014] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0015.c b/app/batch/au_bt_0015.c index 910b359..fc62653 100644 --- a/app/batch/au_bt_0015.c +++ b/app/batch/au_bt_0015.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/au_bt_0015.pgc" -/* @tier medium @module au @transform authorization-batch */ +/* @tier hard @module au @transform authorization-batch */ /* - * au_bt_0015.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=medium] + * au_bt_0015.pgc - 승인 대사 배치 (원장 vs 정산 합계 검증) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0015 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0015 [db@host] */ #include #include @@ -93,121 +90,62 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0015.pgc" +#line 16 "app/batch/au_bt_0015.pgc" static int run_au_bt_0015(const char *biz_date) { /* exec sql begin declare section */ - - - - + + -#line 24 "app/batch/au_bt_0015.pgc" +#line 21 "app/batch/au_bt_0015.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/au_bt_0015.pgc" - char h_key [ 16 ] ; +#line 22 "app/batch/au_bt_0015.pgc" + long h_appr_sum , h_settle_sum ; -#line 26 "app/batch/au_bt_0015.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0015.pgc" - long h_amount ; - -#line 28 "app/batch/au_bt_0015.pgc" - char h_new_status [ 2 ] ; +#line 23 "app/batch/au_bt_0015.pgc" + int h_appr_cnt , h_settle_cnt ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0015.pgc" +#line 24 "app/batch/au_bt_0015.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long diff; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0015 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0015.pgc" - -#line 42 "app/batch/au_bt_0015.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0015 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) from au_ledger where biz_date = $1 and status = 'M'", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0015.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0015"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0015] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0015", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_appr_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_int,&(h_appr_cnt),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0015.pgc" +#line 32 "app/batch/au_bt_0015.pgc" + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("승인합"); return TX_EDB; } - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0015"); - errcnt++; - break; - } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) from au_ledger where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settle_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_settle_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 37 "app/batch/au_bt_0015.pgc" - total++; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("정산합"); return TX_EDB; } - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0015", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0015.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + diff = h_appr_sum - h_settle_sum; + printf("===== 승인-정산 대사 [au_bt_0015] date=%s =====\n", biz_date); + printf(" 승인(M) : %d 건 %ld 원\n", h_appr_cnt, h_appr_sum); + printf(" 정산(S) : %d 건 %ld 원\n", h_settle_cnt, h_settle_sum); + printf(" 차이 : %ld 원\n", diff); + printf(" 대사결과: %s\n", (h_appr_cnt == 0) ? "정산완료(승인잔여없음)" : "미정산 잔존"); + tx_log(TX_LOG_INFO, "[au_bt_0015] 대사 date=%s diff=%ld", biz_date, diff); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +155,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0015] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0015] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0015(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0015] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0015.pgc b/app/batch/au_bt_0015.pgc index c9f530c..a6055c1 100644 --- a/app/batch/au_bt_0015.pgc +++ b/app/batch/au_bt_0015.pgc @@ -1,11 +1,8 @@ -/* @tier medium @module au @transform authorization-batch */ +/* @tier hard @module au @transform authorization-batch */ /* - * au_bt_0015.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=medium] + * au_bt_0015.pgc - 승인 대사 배치 (원장 vs 정산 합계 검증) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0015 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0015 [db@host] */ #include #include @@ -22,83 +19,32 @@ static int run_au_bt_0015(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_appr_sum, h_settle_sum; + int h_appr_cnt, h_settle_cnt; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long diff; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_au_bt_0015 CURSOR FOR - SELECT key, merch_id, amount - FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + EXEC SQL SELECT coalesce(sum(amount), 0), count(*) + INTO :h_appr_sum, :h_appr_cnt + FROM au_ledger WHERE biz_date = :h_biz_date AND status = 'M'; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("승인합"); return TX_EDB; } - EXEC SQL OPEN cur_au_bt_0015; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0015"); - return TX_EDB; - } + EXEC SQL SELECT coalesce(sum(amount), 0), count(*) + INTO :h_settle_sum, :h_settle_cnt + FROM au_ledger WHERE biz_date = :h_biz_date AND status = 'S'; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("정산합"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0015] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_au_bt_0015 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0015"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_au_bt_0015; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + diff = h_appr_sum - h_settle_sum; + printf("===== 승인-정산 대사 [au_bt_0015] date=%s =====\n", biz_date); + printf(" 승인(M) : %d 건 %ld 원\n", h_appr_cnt, h_appr_sum); + printf(" 정산(S) : %d 건 %ld 원\n", h_settle_cnt, h_settle_sum); + printf(" 차이 : %ld 원\n", diff); + printf(" 대사결과: %s\n", (h_appr_cnt == 0) ? "정산완료(승인잔여없음)" : "미정산 잔존"); + tx_log(TX_LOG_INFO, "[au_bt_0015] 대사 date=%s diff=%ld", biz_date, diff); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +54,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0015] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0015] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0015(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0015] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0016.c b/app/batch/au_bt_0016.c index 5166d71..f11f2cf 100644 --- a/app/batch/au_bt_0016.c +++ b/app/batch/au_bt_0016.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/au_bt_0016.pgc" -/* @tier easy @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0016.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0016.pgc - 가맹점별 승인율 산출 배치 (성공/전체 비율) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0016 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0016 [db@host] */ #include #include @@ -93,121 +90,66 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0016.pgc" +#line 16 "app/batch/au_bt_0016.pgc" static int run_au_bt_0016(const char *biz_date) { /* exec sql begin declare section */ - - - - - + + -#line 24 "app/batch/au_bt_0016.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/au_bt_0016.pgc" + char h_biz_date [ 9 ] , h_merch [ 16 ] ; -#line 25 "app/batch/au_bt_0016.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/au_bt_0016.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0016.pgc" - long h_amount ; - -#line 28 "app/batch/au_bt_0016.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/au_bt_0016.pgc" + int h_ok , h_all ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0016.pgc" +#line 23 "app/batch/au_bt_0016.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merchants = 0; + int rate; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0016 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0016.pgc" + /* declare cur_rate cursor for select merch_id , count ( * ) filter ( where status in ( 'M' , 'S' ) ) , count ( * ) from au_ledger where biz_date = $1 group by merch_id having count ( * ) >= 1 order by merch_id */ +#line 37 "app/batch/au_bt_0016.pgc" -#line 42 "app/batch/au_bt_0016.pgc" +#line 37 "app/batch/au_bt_0016.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0016 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rate cursor for select merch_id , count ( * ) filter ( where status in ( 'M' , 'S' ) ) , count ( * ) from au_ledger where biz_date = $1 group by merch_id having count ( * ) >= 1 order by merch_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0016.pgc" +#line 38 "app/batch/au_bt_0016.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0016"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0016] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rate"); return TX_EDB; } + printf("===== 가맹점별 승인율 [au_bt_0016] date=%s =====\n", biz_date); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0016", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rate", ECPGt_EOIT, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_int,&(h_ok),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_int,&(h_all),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0016.pgc" +#line 43 "app/batch/au_bt_0016.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0016"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + merchants++; + rate = (h_all > 0) ? h_ok * 100 / h_all : 0; + printf(" %-15s : %3d%% (%d/%d)\n", h_merch, rate, h_ok, h_all); + if (rate < 50) + tx_log(TX_LOG_WARN, "[au_bt_0016] 저승인율 merch=%s rate=%d%%", h_merch, rate); } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rate", ECPGt_EOIT, ECPGt_EORT);} +#line 52 "app/batch/au_bt_0016.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0016", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0016.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf(" 집계 가맹점수=%ld\n", merchants); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +159,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0016] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0016] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0016(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0016] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0016.pgc b/app/batch/au_bt_0016.pgc index 2b400be..cd7ab94 100644 --- a/app/batch/au_bt_0016.pgc +++ b/app/batch/au_bt_0016.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0016.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=easy] + * au_bt_0016.pgc - 가맹점별 승인율 산출 배치 (성공/전체 비율) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0016 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0016 [db@host] */ #include #include @@ -21,84 +18,40 @@ EXEC SQL INCLUDE sqlca; static int run_au_bt_0016(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_merch[16]; + int h_ok, h_all; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merchants = 0; + int rate; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_au_bt_0016 CURSOR FOR - SELECT key, merch_id, amount - FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_au_bt_0016; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0016"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0016] tx_begin 경고"); + EXEC SQL DECLARE cur_rate CURSOR FOR + SELECT merch_id, + count(*) FILTER (WHERE status IN ('M','S')), + count(*) + FROM au_ledger WHERE biz_date = :h_biz_date + GROUP BY merch_id + HAVING count(*) >= 1 + ORDER BY merch_id; + EXEC SQL OPEN cur_rate; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rate"); return TX_EDB; } + printf("===== 가맹점별 승인율 [au_bt_0016] date=%s =====\n", biz_date); for (;;) { - EXEC SQL FETCH cur_au_bt_0016 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0016"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL FETCH cur_rate INTO :h_merch, :h_ok, :h_all; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + merchants++; + rate = (h_all > 0) ? h_ok * 100 / h_all : 0; + printf(" %-15s : %3d%% (%d/%d)\n", h_merch, rate, h_ok, h_all); + if (rate < 50) + tx_log(TX_LOG_WARN, "[au_bt_0016] 저승인율 merch=%s rate=%d%%", h_merch, rate); } - - EXEC SQL CLOSE cur_au_bt_0016; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + EXEC SQL CLOSE cur_rate; + printf(" 집계 가맹점수=%ld\n", merchants); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +61,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0016] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0016] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0016(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0016] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0017.c b/app/batch/au_bt_0017.c index feb9e77..c36f4cb 100644 --- a/app/batch/au_bt_0017.c +++ b/app/batch/au_bt_0017.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/au_bt_0017.pgc" -/* @tier hard @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0017.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=hard] + * au_bt_0017.pgc - 승인원장 아카이브 배치 (정산완료 노후건 삭제) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0017 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0017 [db@host] */ #include #include @@ -93,121 +90,53 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/au_bt_0017.pgc" +#line 16 "app/batch/au_bt_0017.pgc" static int run_au_bt_0017(const char *biz_date) { /* exec sql begin declare section */ - - - - + -#line 24 "app/batch/au_bt_0017.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/au_bt_0017.pgc" + char h_cutoff [ 9 ] ; -#line 25 "app/batch/au_bt_0017.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/au_bt_0017.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/au_bt_0017.pgc" - long h_amount ; - -#line 28 "app/batch/au_bt_0017.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/au_bt_0017.pgc" + int h_cnt , h_del ; /* exec sql end declare section */ -#line 29 "app/batch/au_bt_0017.pgc" +#line 23 "app/batch/au_bt_0017.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_cutoff, biz_date, 8); h_cutoff[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from au_ledger where status = 'S' and biz_date < $1 ", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 28 "app/batch/au_bt_0017.pgc" - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_au_bt_0017 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/au_bt_0017.pgc" + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("au_bt_0017 집계"); return TX_EDB; } -#line 42 "app/batch/au_bt_0017.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_au_bt_0017 cursor for select key , merch_id , amount from au_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[au_bt_0017] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "delete from au_ledger where status = 'S' and biz_date < $1 ", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/au_bt_0017.pgc" +#line 33 "app/batch/au_bt_0017.pgc" if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0017"); + TX_DBIO_LOG_ERR("au_bt_0017 삭제"); + tx_abort(); return TX_EDB; } + h_del = (int)sqlca.sqlerrd[2]; + tx_commit(); - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0017] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_au_bt_0017", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/au_bt_0017.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0017"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0017] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_au_bt_0017", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/au_bt_0017.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0017] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0017] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 승인원장 아카이브 [au_bt_0017] 기준일<%s =====\n", biz_date); + printf(" 대상=%d 삭제=%d\n", h_cnt, h_del); + tx_log(TX_LOG_INFO, "[au_bt_0017] 아카이브 삭제=%d건", h_del); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +146,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0017] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0017] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0017(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0017] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/au_bt_0017.pgc b/app/batch/au_bt_0017.pgc index 91fd30a..1944f7e 100644 --- a/app/batch/au_bt_0017.pgc +++ b/app/batch/au_bt_0017.pgc @@ -1,11 +1,8 @@ -/* @tier hard @module au @transform authorization-batch */ +/* @tier medium @module au @transform authorization-batch */ /* - * au_bt_0017.pgc - 승인한도 배치 (커서 순회 + 상태전이) [tier=hard] + * au_bt_0017.pgc - 승인원장 아카이브 배치 (정산완료 노후건 삭제) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: au_bt_0017 [db@host] + * 배치 main 엔트리 포함. 실행: au_bt_0017 [db@host] */ #include #include @@ -21,84 +18,31 @@ EXEC SQL INCLUDE sqlca; static int run_au_bt_0017(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_cutoff[9]; + int h_cnt, h_del; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_cutoff, biz_date, 8); h_cutoff[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + EXEC SQL SELECT count(*) INTO :h_cnt + FROM au_ledger WHERE status = 'S' AND biz_date < :h_cutoff; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("au_bt_0017 집계"); return TX_EDB; } - EXEC SQL DECLARE cur_au_bt_0017 CURSOR FOR - SELECT key, merch_id, amount - FROM au_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_au_bt_0017; + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[au_bt_0017] tx_begin 경고"); + EXEC SQL DELETE FROM au_ledger + WHERE status = 'S' AND biz_date < :h_cutoff; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_au_bt_0017"); + TX_DBIO_LOG_ERR("au_bt_0017 삭제"); + tx_abort(); return TX_EDB; } + h_del = (int)sqlca.sqlerrd[2]; + tx_commit(); - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[au_bt_0017] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_au_bt_0017 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_au_bt_0017"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, AU_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, AU_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = au_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[au_bt_0017] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_au_bt_0017; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[au_bt_0017] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 승인한도 배치 결과 [au_bt_0017] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 승인원장 아카이브 [au_bt_0017] 기준일<%s =====\n", biz_date); + printf(" 대상=%d 삭제=%d\n", h_cnt, h_del); + tx_log(TX_LOG_INFO, "[au_bt_0017] 아카이브 삭제=%d건", h_del); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +52,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[au_bt_0017] 배치 시작 date=%s", biz_date); - rc = au_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[au_bt_0017] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_au_bt_0017(biz_date); - au_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[au_bt_0017] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/cl_bt_0002.c b/app/batch/cl_bt_0002.c index 24ea845..72a3bdf 100644 --- a/app/batch/cl_bt_0002.c +++ b/app/batch/cl_bt_0002.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cl_bt_0002.pgc" /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0002.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0002.pgc - 일마감 배치 (미마감 점검 + 정산완료 전이) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 미마감(접수 'R') 잔량을 먼저 점검하고, 처리완료('M')/ + * 불일치('U') 건을 커서로 순회하며 정산완료('S')로 전이한다. + * 미마감 건이 남아 있으면 마감을 보류(경고)한다. 배치 main 엔트리 포함. * * 실행: cl_bt_0002 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0002.pgc" +#line 20 "app/batch/cl_bt_0002.pgc" static int run_cl_bt_0002(const char *biz_date) @@ -102,44 +103,48 @@ static int run_cl_bt_0002(const char *biz_date) - - -#line 24 "app/batch/cl_bt_0002.pgc" +#line 25 "app/batch/cl_bt_0002.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cl_bt_0002.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/cl_bt_0002.pgc" - char h_merch_id [ 16 ] ; + char h_key [ 16 ] ; #line 27 "app/batch/cl_bt_0002.pgc" long h_amount ; - -#line 28 "app/batch/cl_bt_0002.pgc" - char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/cl_bt_0002.pgc" +#line 28 "app/batch/cl_bt_0002.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long recv = 0, settled = 0, errcnt = 0, sum_settle = 0; + char won[32]; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 1) 미마감(접수 'R') 잔량 점검 */ + recv = cl_rec_count(biz_date, CL_ST_RECV); + if (recv < 0) { + tx_log(TX_LOG_ERROR, "[cl_bt_0002] 미마감 건수 조회 실패"); + return TX_EDB; + } + if (recv > 0) + tx_log(TX_LOG_WARN, + "[cl_bt_0002] 미마감(R) %ld건 잔존 - 일마감 보류 권고", recv); + + /* 2) 처리완료('M')/불일치('U') → 정산완료('S') 전이 */ ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0002 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cl_bt_0002.pgc" + /* declare cur_cl_bt_0002 cursor for select key , amount from cl_ledger where biz_date = $1 and status in ( 'M' , 'U' ) order by key */ +#line 53 "app/batch/cl_bt_0002.pgc" -#line 42 "app/batch/cl_bt_0002.pgc" +#line 53 "app/batch/cl_bt_0002.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0002 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0002 cursor for select key , amount from cl_ledger where biz_date = $1 and status in ( 'M' , 'U' ) order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cl_bt_0002.pgc" +#line 55 "app/batch/cl_bt_0002.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0002"); @@ -153,11 +158,9 @@ static int run_cl_bt_0002(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0002", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cl_bt_0002.pgc" +#line 65 "app/batch/cl_bt_0002.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,26 +171,18 @@ static int run_cl_bt_0002(const char *biz_date) break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); + rc = cl_rec_update_status(h_key, CL_ST_SETTLED); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[cl_bt_0002] 정산전이 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + settled++; + sum_settle += h_amount; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0002", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0002.pgc" +#line 85 "app/batch/cl_bt_0002.pgc" if (errcnt == 0) @@ -195,17 +190,21 @@ static int run_cl_bt_0002(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[cl_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_settle, won, sizeof(won)); - printf("========== 마감 배치 결과 [cl_bt_0002] ==========\n"); + tx_log(TX_LOG_INFO, + "[cl_bt_0002] 요약 date=%s 미마감=%ld 정산=%ld 정산액=%ld 오류=%ld", + biz_date, recv, settled, sum_settle, errcnt); + + printf("========== 일마감 배치 결과 [cl_bt_0002] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 미마감(R) : %ld\n", recv); + printf(" 정산완료(S) : %ld\n", settled); + printf(" 정산액 합계 : %s\n", won); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + if (recv > 0) + printf(" ** 경고: 미마감 잔존으로 일마감 보류 권고 **\n"); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0002.pgc b/app/batch/cl_bt_0002.pgc index 5474878..8b3f12a 100644 --- a/app/batch/cl_bt_0002.pgc +++ b/app/batch/cl_bt_0002.pgc @@ -1,9 +1,10 @@ /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0002.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0002.pgc - 일마감 배치 (미마감 점검 + 정산완료 전이) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 미마감(접수 'R') 잔량을 먼저 점검하고, 처리완료('M')/ + * 불일치('U') 건을 커서로 순회하며 정산완료('S')로 전이한다. + * 미마감 건이 남아 있으면 마감을 보류(경고)한다. 배치 main 엔트리 포함. * * 실행: cl_bt_0002 [db@host] */ @@ -23,22 +24,32 @@ static int run_cl_bt_0002(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long recv = 0, settled = 0, errcnt = 0, sum_settle = 0; + char won[32]; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 1) 미마감(접수 'R') 잔량 점검 */ + recv = cl_rec_count(biz_date, CL_ST_RECV); + if (recv < 0) { + tx_log(TX_LOG_ERROR, "[cl_bt_0002] 미마감 건수 조회 실패"); + return TX_EDB; + } + if (recv > 0) + tx_log(TX_LOG_WARN, + "[cl_bt_0002] 미마감(R) %ld건 잔존 - 일마감 보류 권고", recv); + + /* 2) 처리완료('M')/불일치('U') → 정산완료('S') 전이 */ EXEC SQL DECLARE cur_cl_bt_0002 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount FROM cl_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status IN ('M', 'U') ORDER BY key; EXEC SQL OPEN cur_cl_bt_0002; @@ -51,7 +62,7 @@ static int run_cl_bt_0002(const char *biz_date) tx_log(TX_LOG_WARN, "[cl_bt_0002] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_cl_bt_0002 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cl_bt_0002 INTO :h_key, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,22 +72,14 @@ static int run_cl_bt_0002(const char *biz_date) break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); + rc = cl_rec_update_status(h_key, CL_ST_SETTLED); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[cl_bt_0002] 정산전이 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + settled++; + sum_settle += h_amount; } EXEC SQL CLOSE cur_cl_bt_0002; @@ -86,17 +89,21 @@ static int run_cl_bt_0002(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[cl_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_settle, won, sizeof(won)); - printf("========== 마감 배치 결과 [cl_bt_0002] ==========\n"); + tx_log(TX_LOG_INFO, + "[cl_bt_0002] 요약 date=%s 미마감=%ld 정산=%ld 정산액=%ld 오류=%ld", + biz_date, recv, settled, sum_settle, errcnt); + + printf("========== 일마감 배치 결과 [cl_bt_0002] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 미마감(R) : %ld\n", recv); + printf(" 정산완료(S) : %ld\n", settled); + printf(" 정산액 합계 : %s\n", won); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + if (recv > 0) + printf(" ** 경고: 미마감 잔존으로 일마감 보류 권고 **\n"); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0003.c b/app/batch/cl_bt_0003.c index 5a04bb8..24dbbb5 100644 --- a/app/batch/cl_bt_0003.c +++ b/app/batch/cl_bt_0003.c @@ -8,12 +8,13 @@ #line 1 "app/batch/cl_bt_0003.pgc" /* @tier medium @module cl @transform closing-batch */ /* - * cl_bt_0003.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=medium] + * cl_bt_0003.pgc - 월마감 배치 (해당 월 정산완료 일괄 전이) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 인자 YYYYMM 으로부터 'YYYYMM%' LIKE 패턴을 구성하여 해당 월의 + * 처리완료('M')/불일치('U') 건을 커서로 순회하며 정산완료('S')로 전이한다. + * 월 마감건수와 월 정산액 합계를 리포트한다. 배치 main 엔트리 포함. * - * 실행: cl_bt_0003 [db@host] + * 실행: cl_bt_0003 [db@host] */ #include #include @@ -93,51 +94,44 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0003.pgc" +#line 20 "app/batch/cl_bt_0003.pgc" -static int run_cl_bt_0003(const char *biz_date) +static int run_cl_bt_0003(const char *yyyymm) { /* exec sql begin declare section */ - - -#line 24 "app/batch/cl_bt_0003.pgc" - char h_biz_date [ 9 ] ; - #line 25 "app/batch/cl_bt_0003.pgc" - char h_key [ 16 ] ; + char h_pat [ 10 ] ; #line 26 "app/batch/cl_bt_0003.pgc" - char h_merch_id [ 16 ] ; + char h_key [ 16 ] ; #line 27 "app/batch/cl_bt_0003.pgc" long h_amount ; - -#line 28 "app/batch/cl_bt_0003.pgc" - char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/cl_bt_0003.pgc" +#line 28 "app/batch/cl_bt_0003.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, settled = 0, errcnt = 0, sum_settle = 0; + char won[32]; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 'YYYYMM%' LIKE 패턴 구성 */ + snprintf(h_pat, sizeof(h_pat), "%.6s%%", yyyymm); - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0003 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ + ECPGset_var( 0, ( h_pat ), __LINE__);\ + /* declare cur_cl_bt_0003 cursor for select key , amount from cl_ledger where biz_date like $1 and status in ( 'M' , 'U' ) order by biz_date , key */ #line 42 "app/batch/cl_bt_0003.pgc" #line 42 "app/batch/cl_bt_0003.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0003 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0003 cursor for select key , amount from cl_ledger where biz_date like $1 and status in ( 'M' , 'U' ) order by biz_date , key", + ECPGt_char,(h_pat),(long)10,(long)1,(10)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/cl_bt_0003.pgc" @@ -153,8 +147,6 @@ static int run_cl_bt_0003(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0003", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 54 "app/batch/cl_bt_0003.pgc" @@ -169,25 +161,18 @@ static int run_cl_bt_0003(const char *biz_date) } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); + rc = cl_rec_update_status(h_key, CL_ST_SETTLED); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[cl_bt_0003] 월마감 전이 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + settled++; + sum_settle += h_amount; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0003", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0003.pgc" +#line 75 "app/batch/cl_bt_0003.pgc" if (errcnt == 0) @@ -195,42 +180,56 @@ static int run_cl_bt_0003(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[cl_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_settle, won, sizeof(won)); - printf("========== 마감 배치 결과 [cl_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); + tx_log(TX_LOG_INFO, + "[cl_bt_0003] 요약 월=%s 대상=%ld 마감=%ld 정산액=%ld 오류=%ld", + yyyymm, total, settled, sum_settle, errcnt); + + printf("========== 월마감 배치 결과 [cl_bt_0003] ==========\n"); + printf(" 대상 월 : %.6s\n", yyyymm); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 월 마감(S) : %ld\n", settled); + printf(" 월 정산액 : %s\n", won); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } +static int yyyymm_is_valid(const char *s) +{ + int i, mm; + if (!s || strlen(s) != 6) + return 0; + for (i = 0; i < 6; i++) + if (s[i] < '0' || s[i] > '9') + return 0; + mm = (s[4] - '0') * 10 + (s[5] - '0'); + return (mm >= 1 && mm <= 12); +} + int main(int argc, char **argv) { - const char *biz_date; + const char *yyyymm; const char *target; int rc; tx_set_loglevel(TX_LOG_INFO); if (argc < 2) { - fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); + fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } - biz_date = argv[1]; - target = (argc >= 3) ? argv[2] : NULL; + yyyymm = argv[1]; + target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + if (!yyyymm_is_valid(yyyymm)) { + fprintf(stderr, "오류: 유효하지 않은 마감월 '%s'\n", yyyymm); return 2; } - tx_log(TX_LOG_INFO, "[cl_bt_0003] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cl_bt_0003] 배치 시작 월=%s", yyyymm); rc = cl_dbio_connect(target); if (rc != TX_OK) { @@ -238,7 +237,7 @@ int main(int argc, char **argv) return 1; } - rc = run_cl_bt_0003(biz_date); + rc = run_cl_bt_0003(yyyymm); cl_dbio_disconnect(); diff --git a/app/batch/cl_bt_0003.pgc b/app/batch/cl_bt_0003.pgc index cfa5aa2..761bf80 100644 --- a/app/batch/cl_bt_0003.pgc +++ b/app/batch/cl_bt_0003.pgc @@ -1,11 +1,12 @@ /* @tier medium @module cl @transform closing-batch */ /* - * cl_bt_0003.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=medium] + * cl_bt_0003.pgc - 월마감 배치 (해당 월 정산완료 일괄 전이) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 인자 YYYYMM 으로부터 'YYYYMM%' LIKE 패턴을 구성하여 해당 월의 + * 처리완료('M')/불일치('U') 건을 커서로 순회하며 정산완료('S')로 전이한다. + * 월 마감건수와 월 정산액 합계를 리포트한다. 배치 main 엔트리 포함. * - * 실행: cl_bt_0003 [db@host] + * 실행: cl_bt_0003 [db@host] */ #include #include @@ -18,28 +19,27 @@ EXEC SQL INCLUDE sqlca; -static int run_cl_bt_0003(const char *biz_date) +static int run_cl_bt_0003(const char *yyyymm) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; + char h_pat[10]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, settled = 0, errcnt = 0, sum_settle = 0; + char won[32]; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 'YYYYMM%' LIKE 패턴 구성 */ + snprintf(h_pat, sizeof(h_pat), "%.6s%%", yyyymm); EXEC SQL DECLARE cur_cl_bt_0003 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount FROM cl_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + WHERE biz_date LIKE :h_pat + AND status IN ('M', 'U') + ORDER BY biz_date, key; EXEC SQL OPEN cur_cl_bt_0003; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +51,7 @@ static int run_cl_bt_0003(const char *biz_date) tx_log(TX_LOG_WARN, "[cl_bt_0003] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_cl_bt_0003 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cl_bt_0003 INTO :h_key, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,21 +62,14 @@ static int run_cl_bt_0003(const char *biz_date) } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); + rc = cl_rec_update_status(h_key, CL_ST_SETTLED); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[cl_bt_0003] 월마감 전이 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + settled++; + sum_settle += h_amount; } EXEC SQL CLOSE cur_cl_bt_0003; @@ -86,42 +79,56 @@ static int run_cl_bt_0003(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[cl_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_settle, won, sizeof(won)); - printf("========== 마감 배치 결과 [cl_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); + tx_log(TX_LOG_INFO, + "[cl_bt_0003] 요약 월=%s 대상=%ld 마감=%ld 정산액=%ld 오류=%ld", + yyyymm, total, settled, sum_settle, errcnt); + + printf("========== 월마감 배치 결과 [cl_bt_0003] ==========\n"); + printf(" 대상 월 : %.6s\n", yyyymm); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 월 마감(S) : %ld\n", settled); + printf(" 월 정산액 : %s\n", won); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } +static int yyyymm_is_valid(const char *s) +{ + int i, mm; + if (!s || strlen(s) != 6) + return 0; + for (i = 0; i < 6; i++) + if (s[i] < '0' || s[i] > '9') + return 0; + mm = (s[4] - '0') * 10 + (s[5] - '0'); + return (mm >= 1 && mm <= 12); +} + int main(int argc, char **argv) { - const char *biz_date; + const char *yyyymm; const char *target; int rc; tx_set_loglevel(TX_LOG_INFO); if (argc < 2) { - fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); + fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } - biz_date = argv[1]; - target = (argc >= 3) ? argv[2] : NULL; + yyyymm = argv[1]; + target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + if (!yyyymm_is_valid(yyyymm)) { + fprintf(stderr, "오류: 유효하지 않은 마감월 '%s'\n", yyyymm); return 2; } - tx_log(TX_LOG_INFO, "[cl_bt_0003] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cl_bt_0003] 배치 시작 월=%s", yyyymm); rc = cl_dbio_connect(target); if (rc != TX_OK) { @@ -129,7 +136,7 @@ int main(int argc, char **argv) return 1; } - rc = run_cl_bt_0003(biz_date); + rc = run_cl_bt_0003(yyyymm); cl_dbio_disconnect(); diff --git a/app/batch/cl_bt_0004.c b/app/batch/cl_bt_0004.c index 80b6ea6..d99de2c 100644 --- a/app/batch/cl_bt_0004.c +++ b/app/batch/cl_bt_0004.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cl_bt_0004.pgc" /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0004.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0004.pgc - 마감 대사 배치 (집계 교차확인) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 전체 건수/금액합계/수수료합계를 직접 SELECT 하고, + * 표준 DBIO(cl_rec_count/cl_rec_sum)로 재집계하여 교차확인한다. + * 건수 또는 금액이 불일치하면 대사 실패(TX_FAIL)로 리포트한다. * * 실행: cl_bt_0004 [db@host] */ @@ -93,121 +94,98 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0004.pgc" +#line 20 "app/batch/cl_bt_0004.pgc" static int run_cl_bt_0004(const char *biz_date) { /* exec sql begin declare section */ - - + -#line 24 "app/batch/cl_bt_0004.pgc" +#line 25 "app/batch/cl_bt_0004.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cl_bt_0004.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/cl_bt_0004.pgc" - char h_merch_id [ 16 ] ; + int h_cnt ; #line 27 "app/batch/cl_bt_0004.pgc" - long h_amount ; + long h_sum_amt ; #line 28 "app/batch/cl_bt_0004.pgc" - char h_new_status [ 2 ] ; + long h_sum_fee ; /* exec sql end declare section */ #line 29 "app/batch/cl_bt_0004.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long xcount, xsum; + int diff = 0; + char won_amt[32], won_fee[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0004 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cl_bt_0004.pgc" - -#line 42 "app/batch/cl_bt_0004.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0004 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", + /* 1) 직접 집계 (건수/금액/수수료) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from cl_ledger where biz_date = $1 ", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cl_bt_0004.pgc" + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 42 "app/batch/cl_bt_0004.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0004"); + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 집계 cl_bt_0004"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0004] tx_begin 경고"); + /* 2) 표준 DBIO 교차확인 (전체 상태 합산) */ + xcount = cl_rec_count(biz_date, CL_ST_RECV) + + cl_rec_count(biz_date, CL_ST_DONE) + + cl_rec_count(biz_date, CL_ST_FAIL) + + cl_rec_count(biz_date, CL_ST_SETTLED); - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0004", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cl_bt_0004.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cl_bt_0004"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (cl_rec_sum(biz_date, &xsum) != TX_OK) { + tx_log(TX_LOG_ERROR, "[cl_bt_0004] cl_rec_sum 실패"); + return TX_EDB; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0004", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0004.pgc" + if ((long)h_cnt != xcount) { + tx_log(TX_LOG_ERROR, "[cl_bt_0004] 건수 불일치 direct=%d cross=%ld", + h_cnt, xcount); + diff++; + } + if (h_sum_amt != xsum) { + tx_log(TX_LOG_ERROR, "[cl_bt_0004] 금액 불일치 direct=%ld cross=%ld", + h_sum_amt, xsum); + diff++; + } - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + amount_format_won(h_sum_amt, won_amt, sizeof(won_amt)); + amount_format_won(h_sum_fee, won_fee, sizeof(won_fee)); tx_log(TX_LOG_INFO, - "[cl_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cl_bt_0004] 요약 date=%s 건수=%d Σamount=%ld Σfee=%ld 불일치=%d", + biz_date, h_cnt, h_sum_amt, h_sum_fee, diff); - printf("========== 마감 배치 결과 [cl_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 마감 대사 배치 결과 [cl_bt_0004] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 직접 건수 : %d\n", h_cnt); + printf(" 교차 건수 : %ld\n", xcount); + printf(" 금액 합계 : %s\n", won_amt); + printf(" 교차 금액 : %ld\n", xsum); + printf(" 수수료 합계 : %s\n", won_fee); + printf(" 불일치 항목수 : %d\n", diff); + printf(" 대사 결과 : %s\n", (diff == 0) ? "정상" : "불일치(FAIL)"); + printf("===================================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (diff == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) diff --git a/app/batch/cl_bt_0004.pgc b/app/batch/cl_bt_0004.pgc index 92166d4..10e185e 100644 --- a/app/batch/cl_bt_0004.pgc +++ b/app/batch/cl_bt_0004.pgc @@ -1,9 +1,10 @@ /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0004.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0004.pgc - 마감 대사 배치 (집계 교차확인) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 전체 건수/금액합계/수수료합계를 직접 SELECT 하고, + * 표준 DBIO(cl_rec_count/cl_rec_sum)로 재집계하여 교차확인한다. + * 건수 또는 금액이 불일치하면 대사 실패(TX_FAIL)로 리포트한다. * * 실행: cl_bt_0004 [db@host] */ @@ -22,83 +23,70 @@ static int run_cl_bt_0004(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + int h_cnt; + long h_sum_amt; + long h_sum_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long xcount, xsum; + int diff = 0; + char won_amt[32], won_fee[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - EXEC SQL DECLARE cur_cl_bt_0004 CURSOR FOR - SELECT key, merch_id, amount + /* 1) 직접 집계 (건수/금액/수수료) */ + EXEC SQL SELECT count(*), coalesce(sum(amount), 0), coalesce(sum(fee), 0) + INTO :h_cnt, :h_sum_amt, :h_sum_fee FROM cl_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + WHERE biz_date = :h_biz_date; - EXEC SQL OPEN cur_cl_bt_0004; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0004"); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 집계 cl_bt_0004"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0004] tx_begin 경고"); + /* 2) 표준 DBIO 교차확인 (전체 상태 합산) */ + xcount = cl_rec_count(biz_date, CL_ST_RECV) + + cl_rec_count(biz_date, CL_ST_DONE) + + cl_rec_count(biz_date, CL_ST_FAIL) + + cl_rec_count(biz_date, CL_ST_SETTLED); - for (;;) { - EXEC SQL FETCH cur_cl_bt_0004 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cl_bt_0004"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (cl_rec_sum(biz_date, &xsum) != TX_OK) { + tx_log(TX_LOG_ERROR, "[cl_bt_0004] cl_rec_sum 실패"); + return TX_EDB; } - EXEC SQL CLOSE cur_cl_bt_0004; + if ((long)h_cnt != xcount) { + tx_log(TX_LOG_ERROR, "[cl_bt_0004] 건수 불일치 direct=%d cross=%ld", + h_cnt, xcount); + diff++; + } + if (h_sum_amt != xsum) { + tx_log(TX_LOG_ERROR, "[cl_bt_0004] 금액 불일치 direct=%ld cross=%ld", + h_sum_amt, xsum); + diff++; + } - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + amount_format_won(h_sum_amt, won_amt, sizeof(won_amt)); + amount_format_won(h_sum_fee, won_fee, sizeof(won_fee)); tx_log(TX_LOG_INFO, - "[cl_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cl_bt_0004] 요약 date=%s 건수=%d Σamount=%ld Σfee=%ld 불일치=%d", + biz_date, h_cnt, h_sum_amt, h_sum_fee, diff); - printf("========== 마감 배치 결과 [cl_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 마감 대사 배치 결과 [cl_bt_0004] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 직접 건수 : %d\n", h_cnt); + printf(" 교차 건수 : %ld\n", xcount); + printf(" 금액 합계 : %s\n", won_amt); + printf(" 교차 금액 : %ld\n", xsum); + printf(" 수수료 합계 : %s\n", won_fee); + printf(" 불일치 항목수 : %d\n", diff); + printf(" 대사 결과 : %s\n", (diff == 0) ? "정상" : "불일치(FAIL)"); + printf("===================================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (diff == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) diff --git a/app/batch/cl_bt_0005.c b/app/batch/cl_bt_0005.c index 4396d22..ebd068e 100644 --- a/app/batch/cl_bt_0005.c +++ b/app/batch/cl_bt_0005.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cl_bt_0005.pgc" /* @tier medium @module cl @transform closing-batch */ /* - * cl_bt_0005.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=medium] + * cl_bt_0005.pgc - 마감후 집계 배치 (가맹점별 마감집계) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 정산완료('S') 건을 가맹점(merch_id)별로 GROUP BY 집계하여 + * 커서로 순회하며 가맹점별 건수/금액합계를 리포트한다. + * 전체 가맹점 수와 총 정산액을 함께 출력한다. 배치 main 엔트리 포함. * * 실행: cl_bt_0005 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0005.pgc" +#line 20 "app/batch/cl_bt_0005.pgc" static int run_cl_bt_0005(const char *biz_date) @@ -101,63 +102,62 @@ static int run_cl_bt_0005(const char *biz_date) /* exec sql begin declare section */ - - + -#line 24 "app/batch/cl_bt_0005.pgc" - char h_biz_date [ 9 ] ; - #line 25 "app/batch/cl_bt_0005.pgc" - char h_key [ 16 ] ; + char h_biz_date [ 9 ] ; #line 26 "app/batch/cl_bt_0005.pgc" char h_merch_id [ 16 ] ; #line 27 "app/batch/cl_bt_0005.pgc" - long h_amount ; + int h_cnt ; #line 28 "app/batch/cl_bt_0005.pgc" - char h_new_status [ 2 ] ; + long h_sum ; /* exec sql end declare section */ #line 29 "app/batch/cl_bt_0005.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merchants = 0, grand_cnt = 0, grand_sum = 0, errcnt = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0005 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cl_bt_0005.pgc" + /* declare cur_cl_bt_0005 cursor for select merch_id , count ( * ) , coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date = $1 and status = 'S' group by merch_id order by merch_id */ +#line 43 "app/batch/cl_bt_0005.pgc" -#line 42 "app/batch/cl_bt_0005.pgc" +#line 43 "app/batch/cl_bt_0005.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0005 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0005 cursor for select merch_id , count ( * ) , coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date = $1 and status = 'S' group by merch_id order by merch_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cl_bt_0005.pgc" +#line 45 "app/batch/cl_bt_0005.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0005"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0005] tx_begin 경고"); + printf("========== 마감후 집계 배치 [cl_bt_0005] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" ---------------------------------------------\n"); + printf(" %-14s %8s %18s\n", "가맹점", "건수", "정산액"); + printf(" ---------------------------------------------\n"); for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0005", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cl_bt_0005.pgc" +#line 58 "app/batch/cl_bt_0005.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,44 +168,30 @@ static int run_cl_bt_0005(const char *biz_date) break; } - total++; + amount_format_won(h_sum, won, sizeof(won)); + printf(" %-14s %8d %18s\n", h_merch_id, h_cnt, won); - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + merchants++; + grand_cnt += h_cnt; + grand_sum += h_sum; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0005", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0005.pgc" +#line 76 "app/batch/cl_bt_0005.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + amount_format_won(grand_sum, won, sizeof(won)); tx_log(TX_LOG_INFO, - "[cl_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cl_bt_0005] 요약 date=%s 가맹점=%ld 건수=%ld 정산액=%ld 오류=%ld", + biz_date, merchants, grand_cnt, grand_sum, errcnt); - printf("========== 마감 배치 결과 [cl_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" ---------------------------------------------\n"); + printf(" 가맹점 수 : %ld\n", merchants); + printf(" 총 마감건수 : %ld\n", grand_cnt); + printf(" 총 정산액 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0005.pgc b/app/batch/cl_bt_0005.pgc index 53e9eea..344546b 100644 --- a/app/batch/cl_bt_0005.pgc +++ b/app/batch/cl_bt_0005.pgc @@ -1,9 +1,10 @@ /* @tier medium @module cl @transform closing-batch */ /* - * cl_bt_0005.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=medium] + * cl_bt_0005.pgc - 마감후 집계 배치 (가맹점별 마감집계) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 정산완료('S') 건을 가맹점(merch_id)별로 GROUP BY 집계하여 + * 커서로 순회하며 가맹점별 건수/금액합계를 리포트한다. + * 전체 가맹점 수와 총 정산액을 함께 출력한다. 배치 main 엔트리 포함. * * 실행: cl_bt_0005 [db@host] */ @@ -22,24 +23,24 @@ static int run_cl_bt_0005(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + int h_cnt; + long h_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merchants = 0, grand_cnt = 0, grand_sum = 0, errcnt = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_cl_bt_0005 CURSOR FOR - SELECT key, merch_id, amount + SELECT merch_id, count(*), coalesce(sum(amount), 0) FROM cl_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status = 'S' + GROUP BY merch_id + ORDER BY merch_id; EXEC SQL OPEN cur_cl_bt_0005; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,11 +48,14 @@ static int run_cl_bt_0005(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0005] tx_begin 경고"); + printf("========== 마감후 집계 배치 [cl_bt_0005] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" ---------------------------------------------\n"); + printf(" %-14s %8s %18s\n", "가맹점", "건수", "정산액"); + printf(" ---------------------------------------------\n"); for (;;) { - EXEC SQL FETCH cur_cl_bt_0005 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cl_bt_0005 INTO :h_merch_id, :h_cnt, :h_sum; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,42 +65,28 @@ static int run_cl_bt_0005(const char *biz_date) break; } - total++; + amount_format_won(h_sum, won, sizeof(won)); + printf(" %-14s %8d %18s\n", h_merch_id, h_cnt, won); - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + merchants++; + grand_cnt += h_cnt; + grand_sum += h_sum; } EXEC SQL CLOSE cur_cl_bt_0005; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + amount_format_won(grand_sum, won, sizeof(won)); tx_log(TX_LOG_INFO, - "[cl_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cl_bt_0005] 요약 date=%s 가맹점=%ld 건수=%ld 정산액=%ld 오류=%ld", + biz_date, merchants, grand_cnt, grand_sum, errcnt); - printf("========== 마감 배치 결과 [cl_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" ---------------------------------------------\n"); + printf(" 가맹점 수 : %ld\n", merchants); + printf(" 총 마감건수 : %ld\n", grand_cnt); + printf(" 총 정산액 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0006.c b/app/batch/cl_bt_0006.c index 214f074..96782c3 100644 --- a/app/batch/cl_bt_0006.c +++ b/app/batch/cl_bt_0006.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cl_bt_0006.pgc" /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0006.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0006.pgc - 마감 취소 배치 (정산완료 재개방) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 정산완료('S') 건을 커서로 순회하며 처리완료('M')로 + * 되돌려 재개방한다. 취소 건수와 재개방 금액 합계를 리포트한다. + * 배치 main 엔트리 포함. * * 실행: cl_bt_0006 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0006.pgc" +#line 20 "app/batch/cl_bt_0006.pgc" static int run_cl_bt_0006(const char *biz_date) @@ -102,41 +103,34 @@ static int run_cl_bt_0006(const char *biz_date) - - -#line 24 "app/batch/cl_bt_0006.pgc" +#line 25 "app/batch/cl_bt_0006.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cl_bt_0006.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/cl_bt_0006.pgc" - char h_merch_id [ 16 ] ; + char h_key [ 16 ] ; #line 27 "app/batch/cl_bt_0006.pgc" long h_amount ; - -#line 28 "app/batch/cl_bt_0006.pgc" - char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/cl_bt_0006.pgc" +#line 28 "app/batch/cl_bt_0006.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, cancelled = 0, errcnt = 0, sum_amt = 0; + char won[32]; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0006 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_cl_bt_0006 cursor for select key , amount from cl_ledger where biz_date = $1 and status = 'S' order by key */ #line 42 "app/batch/cl_bt_0006.pgc" #line 42 "app/batch/cl_bt_0006.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0006 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0006 cursor for select key , amount from cl_ledger where biz_date = $1 and status = 'S' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/cl_bt_0006.pgc" @@ -153,8 +147,6 @@ static int run_cl_bt_0006(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0006", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 54 "app/batch/cl_bt_0006.pgc" @@ -169,25 +161,19 @@ static int run_cl_bt_0006(const char *biz_date) } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); + /* 정산완료 → 처리완료 로 되돌려 재개방 */ + rc = cl_rec_update_status(h_key, CL_ST_DONE); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[cl_bt_0006] 마감취소 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + cancelled++; + sum_amt += h_amount; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0006", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0006.pgc" +#line 76 "app/batch/cl_bt_0006.pgc" if (errcnt == 0) @@ -195,17 +181,19 @@ static int run_cl_bt_0006(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[cl_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_amt, won, sizeof(won)); - printf("========== 마감 배치 결과 [cl_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[cl_bt_0006] 요약 date=%s 대상=%ld 취소=%ld 재개방액=%ld 오류=%ld", + biz_date, total, cancelled, sum_amt, errcnt); + + printf("========== 마감 취소 배치 결과 [cl_bt_0006] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 취소 대상(S) : %ld\n", total); + printf(" 취소/재개방 : %ld\n", cancelled); + printf(" 재개방 금액 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0006.pgc b/app/batch/cl_bt_0006.pgc index dfa170c..0c7cb56 100644 --- a/app/batch/cl_bt_0006.pgc +++ b/app/batch/cl_bt_0006.pgc @@ -1,9 +1,10 @@ /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0006.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0006.pgc - 마감 취소 배치 (정산완료 재개방) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 정산완료('S') 건을 커서로 순회하며 처리완료('M')로 + * 되돌려 재개방한다. 취소 건수와 재개방 금액 합계를 리포트한다. + * 배치 main 엔트리 포함. * * 실행: cl_bt_0006 [db@host] */ @@ -23,22 +24,21 @@ static int run_cl_bt_0006(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, cancelled = 0, errcnt = 0, sum_amt = 0; + char won[32]; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_cl_bt_0006 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount FROM cl_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'S' ORDER BY key; EXEC SQL OPEN cur_cl_bt_0006; @@ -51,7 +51,7 @@ static int run_cl_bt_0006(const char *biz_date) tx_log(TX_LOG_WARN, "[cl_bt_0006] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_cl_bt_0006 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cl_bt_0006 INTO :h_key, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,21 +62,15 @@ static int run_cl_bt_0006(const char *biz_date) } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); + /* 정산완료 → 처리완료 로 되돌려 재개방 */ + rc = cl_rec_update_status(h_key, CL_ST_DONE); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[cl_bt_0006] 마감취소 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + cancelled++; + sum_amt += h_amount; } EXEC SQL CLOSE cur_cl_bt_0006; @@ -86,17 +80,19 @@ static int run_cl_bt_0006(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[cl_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_amt, won, sizeof(won)); - printf("========== 마감 배치 결과 [cl_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[cl_bt_0006] 요약 date=%s 대상=%ld 취소=%ld 재개방액=%ld 오류=%ld", + biz_date, total, cancelled, sum_amt, errcnt); + + printf("========== 마감 취소 배치 결과 [cl_bt_0006] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 취소 대상(S) : %ld\n", total); + printf(" 취소/재개방 : %ld\n", cancelled); + printf(" 재개방 금액 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0007.c b/app/batch/cl_bt_0007.c index 9ea2fa8..64ded57 100644 --- a/app/batch/cl_bt_0007.c +++ b/app/batch/cl_bt_0007.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cl_bt_0007.pgc" /* @tier medium @module cl @transform closing-batch */ /* - * cl_bt_0007.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=medium] + * cl_bt_0007.pgc - 재마감 배치 (취소 후 잔여건 재마감) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 마감 취소 이후 남아 있는 처리완료('M')/불일치('U') 건을 커서로 순회하여 + * 정산완료('S')로 재마감한다. 재마감 건수와 처리 후 잔여(M/U) 건수를 + * 재확인하여 리포트한다. 배치 main 엔트리 포함. * * 실행: cl_bt_0007 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0007.pgc" +#line 20 "app/batch/cl_bt_0007.pgc" static int run_cl_bt_0007(const char *biz_date) @@ -102,44 +103,38 @@ static int run_cl_bt_0007(const char *biz_date) - - -#line 24 "app/batch/cl_bt_0007.pgc" +#line 25 "app/batch/cl_bt_0007.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cl_bt_0007.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/cl_bt_0007.pgc" - char h_merch_id [ 16 ] ; + char h_key [ 16 ] ; #line 27 "app/batch/cl_bt_0007.pgc" long h_amount ; - -#line 28 "app/batch/cl_bt_0007.pgc" - char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/cl_bt_0007.pgc" +#line 28 "app/batch/cl_bt_0007.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, reclose = 0, errcnt = 0, sum_amt = 0; + long remain_m, remain_u; + char won[32]; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0007 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cl_bt_0007.pgc" + /* declare cur_cl_bt_0007 cursor for select key , amount from cl_ledger where biz_date = $1 and status in ( 'M' , 'U' ) order by key */ +#line 43 "app/batch/cl_bt_0007.pgc" -#line 42 "app/batch/cl_bt_0007.pgc" +#line 43 "app/batch/cl_bt_0007.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0007 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0007 cursor for select key , amount from cl_ledger where biz_date = $1 and status in ( 'M' , 'U' ) order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cl_bt_0007.pgc" +#line 45 "app/batch/cl_bt_0007.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0007"); @@ -153,11 +148,9 @@ static int run_cl_bt_0007(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0007", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cl_bt_0007.pgc" +#line 55 "app/batch/cl_bt_0007.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,25 +162,18 @@ static int run_cl_bt_0007(const char *biz_date) } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); + rc = cl_rec_update_status(h_key, CL_ST_SETTLED); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[cl_bt_0007] 재마감 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + reclose++; + sum_amt += h_amount; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0007", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0007.pgc" +#line 76 "app/batch/cl_bt_0007.pgc" if (errcnt == 0) @@ -195,17 +181,25 @@ static int run_cl_bt_0007(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[cl_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + /* 처리 후 잔여 확인 */ + remain_m = cl_rec_count(biz_date, CL_ST_DONE); + remain_u = cl_rec_count(biz_date, CL_ST_FAIL); - printf("========== 마감 배치 결과 [cl_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + amount_format_won(sum_amt, won, sizeof(won)); + + tx_log(TX_LOG_INFO, + "[cl_bt_0007] 요약 date=%s 대상=%ld 재마감=%ld 잔여M=%ld 잔여U=%ld 오류=%ld", + biz_date, total, reclose, remain_m, remain_u, errcnt); + + printf("========== 재마감 배치 결과 [cl_bt_0007] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 재마감 대상 : %ld\n", total); + printf(" 재마감(S) : %ld\n", reclose); + printf(" 재마감 금액 : %s\n", won); + printf(" 잔여 처리(M) : %ld\n", remain_m); + printf(" 잔여 불일치(U): %ld\n", remain_u); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0007.pgc b/app/batch/cl_bt_0007.pgc index c5e49da..0b0f603 100644 --- a/app/batch/cl_bt_0007.pgc +++ b/app/batch/cl_bt_0007.pgc @@ -1,9 +1,10 @@ /* @tier medium @module cl @transform closing-batch */ /* - * cl_bt_0007.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=medium] + * cl_bt_0007.pgc - 재마감 배치 (취소 후 잔여건 재마감) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 마감 취소 이후 남아 있는 처리완료('M')/불일치('U') 건을 커서로 순회하여 + * 정산완료('S')로 재마감한다. 재마감 건수와 처리 후 잔여(M/U) 건수를 + * 재확인하여 리포트한다. 배치 main 엔트리 포함. * * 실행: cl_bt_0007 [db@host] */ @@ -23,22 +24,22 @@ static int run_cl_bt_0007(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, reclose = 0, errcnt = 0, sum_amt = 0; + long remain_m, remain_u; + char won[32]; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_cl_bt_0007 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount FROM cl_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status IN ('M', 'U') ORDER BY key; EXEC SQL OPEN cur_cl_bt_0007; @@ -51,7 +52,7 @@ static int run_cl_bt_0007(const char *biz_date) tx_log(TX_LOG_WARN, "[cl_bt_0007] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_cl_bt_0007 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cl_bt_0007 INTO :h_key, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,21 +63,14 @@ static int run_cl_bt_0007(const char *biz_date) } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); + rc = cl_rec_update_status(h_key, CL_ST_SETTLED); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[cl_bt_0007] 재마감 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + reclose++; + sum_amt += h_amount; } EXEC SQL CLOSE cur_cl_bt_0007; @@ -86,17 +80,25 @@ static int run_cl_bt_0007(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[cl_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + /* 처리 후 잔여 확인 */ + remain_m = cl_rec_count(biz_date, CL_ST_DONE); + remain_u = cl_rec_count(biz_date, CL_ST_FAIL); - printf("========== 마감 배치 결과 [cl_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + amount_format_won(sum_amt, won, sizeof(won)); + + tx_log(TX_LOG_INFO, + "[cl_bt_0007] 요약 date=%s 대상=%ld 재마감=%ld 잔여M=%ld 잔여U=%ld 오류=%ld", + biz_date, total, reclose, remain_m, remain_u, errcnt); + + printf("========== 재마감 배치 결과 [cl_bt_0007] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 재마감 대상 : %ld\n", total); + printf(" 재마감(S) : %ld\n", reclose); + printf(" 재마감 금액 : %s\n", won); + printf(" 잔여 처리(M) : %ld\n", remain_m); + printf(" 잔여 불일치(U): %ld\n", remain_u); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0008.c b/app/batch/cl_bt_0008.c index 2e41a18..cd4b111 100644 --- a/app/batch/cl_bt_0008.c +++ b/app/batch/cl_bt_0008.c @@ -8,10 +8,12 @@ #line 1 "app/batch/cl_bt_0008.pgc" /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0008.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0008.pgc - 마감 잔액검증 배치 (순액/이상건 검증) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 정산완료('S') 건에 대해 Σamount 와 Σfee 를 집계하고 + * 순액(= Σamount - Σfee)을 산출한다. 이어 커서로 순회하며 금액 이상건 + * (amount_is_valid 실패)을 스캔·계수한다. 순액 음수 또는 이상건 존재 시 + * 검증 실패(TX_FAIL)로 리포트한다. 배치 main 엔트리 포함. * * 실행: cl_bt_0008 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0008.pgc" +#line 21 "app/batch/cl_bt_0008.pgc" static int run_cl_bt_0008(const char *biz_date) @@ -105,109 +107,123 @@ static int run_cl_bt_0008(const char *biz_date) -#line 24 "app/batch/cl_bt_0008.pgc" +#line 26 "app/batch/cl_bt_0008.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cl_bt_0008.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/cl_bt_0008.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/cl_bt_0008.pgc" - long h_amount ; + long h_sum_amt ; #line 28 "app/batch/cl_bt_0008.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_sum_fee ; + #line 29 "app/batch/cl_bt_0008.pgc" + char h_key [ 16 ] ; + +#line 30 "app/batch/cl_bt_0008.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 31 "app/batch/cl_bt_0008.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long scanned = 0, invalid = 0; + long net; + int fail = 0; + char won_amt[32], won_fee[32], won_net[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 1) 정산완료 건 Σamount / Σfee */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from cl_ledger where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_sum_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 46 "app/batch/cl_bt_0008.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 잔액 cl_bt_0008"); + return TX_EDB; + } + + net = h_sum_amt - h_sum_fee; + if (net < 0) { + tx_log(TX_LOG_ERROR, "[cl_bt_0008] 순액 음수 net=%ld", net); + fail++; + } + + /* 2) 금액 이상건 스캔 */ ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0008 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cl_bt_0008.pgc" + /* declare cur_cl_bt_0008 cursor for select key , amount from cl_ledger where biz_date = $1 and status = 'S' order by key */ +#line 65 "app/batch/cl_bt_0008.pgc" -#line 42 "app/batch/cl_bt_0008.pgc" +#line 65 "app/batch/cl_bt_0008.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0008 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0008 cursor for select key , amount from cl_ledger where biz_date = $1 and status = 'S' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cl_bt_0008.pgc" +#line 67 "app/batch/cl_bt_0008.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0008"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0008] tx_begin 경고"); - for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0008", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cl_bt_0008.pgc" +#line 74 "app/batch/cl_bt_0008.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_cl_bt_0008"); - errcnt++; + fail++; break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + scanned++; + if (!amount_is_valid(h_amount)) { + tx_log(TX_LOG_WARN, "[cl_bt_0008] 금액 이상건 key=%s amount=%ld", + h_key, h_amount); + invalid++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0008", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0008.pgc" +#line 92 "app/batch/cl_bt_0008.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + if (invalid > 0) + fail++; + + amount_format_won(h_sum_amt, won_amt, sizeof(won_amt)); + amount_format_won(h_sum_fee, won_fee, sizeof(won_fee)); + amount_format_won(net, won_net, sizeof(won_net)); tx_log(TX_LOG_INFO, - "[cl_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cl_bt_0008] 요약 date=%s Σamt=%ld Σfee=%ld net=%ld 스캔=%ld 이상=%ld", + biz_date, h_sum_amt, h_sum_fee, net, scanned, invalid); - printf("========== 마감 배치 결과 [cl_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 마감 잔액검증 배치 [cl_bt_0008] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" Σamount(S) : %s\n", won_amt); + printf(" Σfee(S) : %s\n", won_fee); + printf(" 순액 : %s\n", won_net); + printf(" 스캔 건수 : %ld\n", scanned); + printf(" 이상 건수 : %ld\n", invalid); + printf(" 검증 결과 : %s\n", (fail == 0) ? "정상" : "실패(FAIL)"); + printf("==================================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (fail == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) diff --git a/app/batch/cl_bt_0008.pgc b/app/batch/cl_bt_0008.pgc index 0792886..ad99712 100644 --- a/app/batch/cl_bt_0008.pgc +++ b/app/batch/cl_bt_0008.pgc @@ -1,9 +1,11 @@ /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0008.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0008.pgc - 마감 잔액검증 배치 (순액/이상건 검증) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 정산완료('S') 건에 대해 Σamount 와 Σfee 를 집계하고 + * 순액(= Σamount - Σfee)을 산출한다. 이어 커서로 순회하며 금액 이상건 + * (amount_is_valid 실패)을 스캔·계수한다. 순액 음수 또는 이상건 존재 시 + * 검증 실패(TX_FAIL)로 리포트한다. 배치 main 엔트리 포함. * * 실행: cl_bt_0008 [db@host] */ @@ -22,23 +24,44 @@ static int run_cl_bt_0008(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; + long h_sum_amt; + long h_sum_fee; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long scanned = 0, invalid = 0; + long net; + int fail = 0; + char won_amt[32], won_fee[32], won_net[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - EXEC SQL DECLARE cur_cl_bt_0008 CURSOR FOR - SELECT key, merch_id, amount + /* 1) 정산완료 건 Σamount / Σfee */ + EXEC SQL SELECT coalesce(sum(amount), 0), coalesce(sum(fee), 0) + INTO :h_sum_amt, :h_sum_fee FROM cl_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'S'; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 잔액 cl_bt_0008"); + return TX_EDB; + } + + net = h_sum_amt - h_sum_fee; + if (net < 0) { + tx_log(TX_LOG_ERROR, "[cl_bt_0008] 순액 음수 net=%ld", net); + fail++; + } + + /* 2) 금액 이상건 스캔 */ + EXEC SQL DECLARE cur_cl_bt_0008 CURSOR FOR + SELECT key, amount + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'S' ORDER BY key; EXEC SQL OPEN cur_cl_bt_0008; @@ -47,58 +70,49 @@ static int run_cl_bt_0008(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0008] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_cl_bt_0008 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cl_bt_0008 INTO :h_key, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_cl_bt_0008"); - errcnt++; + fail++; break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + scanned++; + if (!amount_is_valid(h_amount)) { + tx_log(TX_LOG_WARN, "[cl_bt_0008] 금액 이상건 key=%s amount=%ld", + h_key, h_amount); + invalid++; } } EXEC SQL CLOSE cur_cl_bt_0008; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + if (invalid > 0) + fail++; + + amount_format_won(h_sum_amt, won_amt, sizeof(won_amt)); + amount_format_won(h_sum_fee, won_fee, sizeof(won_fee)); + amount_format_won(net, won_net, sizeof(won_net)); tx_log(TX_LOG_INFO, - "[cl_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cl_bt_0008] 요약 date=%s Σamt=%ld Σfee=%ld net=%ld 스캔=%ld 이상=%ld", + biz_date, h_sum_amt, h_sum_fee, net, scanned, invalid); - printf("========== 마감 배치 결과 [cl_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 마감 잔액검증 배치 [cl_bt_0008] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" Σamount(S) : %s\n", won_amt); + printf(" Σfee(S) : %s\n", won_fee); + printf(" 순액 : %s\n", won_net); + printf(" 스캔 건수 : %ld\n", scanned); + printf(" 이상 건수 : %ld\n", invalid); + printf(" 검증 결과 : %s\n", (fail == 0) ? "정상" : "실패(FAIL)"); + printf("==================================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (fail == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) diff --git a/app/batch/cl_bt_0009.c b/app/batch/cl_bt_0009.c index fd7fe95..001f525 100644 --- a/app/batch/cl_bt_0009.c +++ b/app/batch/cl_bt_0009.c @@ -8,10 +8,16 @@ #line 1 "app/batch/cl_bt_0009.pgc" /* @tier hard @module cl @transform closing-batch */ /* - * cl_bt_0009.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=hard] + * cl_bt_0009.pgc - 마감 스냅샷 배치 (요약행 생성) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 정산완료('S') 건을 집계(count/Σamount/Σfee)한 뒤, + * 스냅샷 요약행을 cl_ledger 에 INSERT 한다. + * key = + "SS" (예: 20260717 -> 20260717SS) + * merch_id = "*SNAP" + * status = 'S' + * amount = Σamount, fee = 마감건수(count) + * 동일 스냅샷 키가 이미 있으면 재생성을 위해 선삭제한다. + * 배치 main 엔트리 포함. * * 실행: cl_bt_0009 [db@host] */ @@ -93,119 +99,155 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0009.pgc" +#line 25 "app/batch/cl_bt_0009.pgc" static int run_cl_bt_0009(const char *biz_date) { /* exec sql begin declare section */ + + + + + -#line 24 "app/batch/cl_bt_0009.pgc" +#line 30 "app/batch/cl_bt_0009.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cl_bt_0009.pgc" - char h_key [ 16 ] ; +#line 31 "app/batch/cl_bt_0009.pgc" + int h_cnt ; -#line 26 "app/batch/cl_bt_0009.pgc" +#line 32 "app/batch/cl_bt_0009.pgc" + long h_sum_amt ; + +#line 33 "app/batch/cl_bt_0009.pgc" + long h_sum_fee ; + +#line 34 "app/batch/cl_bt_0009.pgc" + char h_snap_key [ 16 ] ; + +#line 35 "app/batch/cl_bt_0009.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/cl_bt_0009.pgc" +#line 36 "app/batch/cl_bt_0009.pgc" + char h_card_no [ 20 ] ; + +#line 37 "app/batch/cl_bt_0009.pgc" + char h_status [ 2 ] ; + +#line 38 "app/batch/cl_bt_0009.pgc" long h_amount ; -#line 28 "app/batch/cl_bt_0009.pgc" - char h_new_status [ 2 ] ; +#line 39 "app/batch/cl_bt_0009.pgc" + long h_fee ; /* exec sql end declare section */ -#line 29 "app/batch/cl_bt_0009.pgc" +#line 40 "app/batch/cl_bt_0009.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + int errcnt = 0; + long affected = 0; + char won_amt[32], won_fee[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0009 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cl_bt_0009.pgc" - -#line 42 "app/batch/cl_bt_0009.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0009 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", + /* 1) 마감 집계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from cl_ledger where biz_date = $1 and status = 'S'", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cl_bt_0009.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0009"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0009] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0009", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_sum_amt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sum_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 54 "app/batch/cl_bt_0009.pgc" - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cl_bt_0009"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 집계 cl_bt_0009"); + return TX_EDB; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0009", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0009.pgc" + /* 스냅샷 요약행 필드 구성 */ + snprintf(h_snap_key, sizeof(h_snap_key), "%.8sSS", biz_date); + strncpy(h_merch_id, "*SNAP", sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_card_no, "0000000000000000", sizeof(h_card_no) - 1); + h_card_no[sizeof(h_card_no) - 1] = '\0'; + strncpy(h_status, CL_ST_SETTLED, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + h_amount = h_sum_amt; + h_fee = (long)h_cnt; + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[cl_bt_0009] tx_begin 경고"); + + /* 2) 기존 동일 스냅샷 선삭제 (재생성) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "delete from cl_ledger where key = $1 ", + ECPGt_char,(h_snap_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 76 "app/batch/cl_bt_0009.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("DELETE 스냅샷 cl_bt_0009"); + errcnt++; + } + + /* 3) 스냅샷 요약행 INSERT */ + if (errcnt == 0) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into cl_ledger ( key , merch_id , card_no , amount , biz_date , status , fee ) values ( $1 , $2 , $3 , $4 , $5 , $6 , $7 )", + ECPGt_char,(h_snap_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 88 "app/batch/cl_bt_0009.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT 스냅샷 cl_bt_0009"); + errcnt++; + } else { + affected = sqlca.sqlerrd[2]; + } + } if (errcnt == 0) tx_commit(); else tx_abort(); - tx_log(TX_LOG_INFO, - "[cl_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(h_sum_amt, won_amt, sizeof(won_amt)); + amount_format_won(h_sum_fee, won_fee, sizeof(won_fee)); - printf("========== 마감 배치 결과 [cl_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[cl_bt_0009] 요약 date=%s snap=%s 건수=%d Σamt=%ld Σfee=%ld ins=%ld 오류=%d", + biz_date, h_snap_key, h_cnt, h_sum_amt, h_sum_fee, affected, errcnt); + + printf("========== 마감 스냅샷 배치 [cl_bt_0009] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 스냅샷 키 : %s\n", h_snap_key); + printf(" 마감 건수 : %d\n", h_cnt); + printf(" Σamount : %s\n", won_amt); + printf(" Σfee : %s\n", won_fee); + printf(" 생성 행수 : %ld\n", affected); + printf(" 오류 건수 : %d\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0009.pgc b/app/batch/cl_bt_0009.pgc index 090e143..6121c87 100644 --- a/app/batch/cl_bt_0009.pgc +++ b/app/batch/cl_bt_0009.pgc @@ -1,9 +1,15 @@ /* @tier hard @module cl @transform closing-batch */ /* - * cl_bt_0009.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=hard] + * cl_bt_0009.pgc - 마감 스냅샷 배치 (요약행 생성) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 정산완료('S') 건을 집계(count/Σamount/Σfee)한 뒤, + * 스냅샷 요약행을 cl_ledger 에 INSERT 한다. + * key = + "SS" (예: 20260717 -> 20260717SS) + * merch_id = "*SNAP" + * status = 'S' + * amount = Σamount, fee = 마감건수(count) + * 동일 스냅샷 키가 이미 있으면 재생성을 위해 선삭제한다. + * 배치 main 엔트리 포함. * * 실행: cl_bt_0009 [db@host] */ @@ -22,81 +28,94 @@ static int run_cl_bt_0009(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; + int h_cnt; + long h_sum_amt; + long h_sum_fee; + char h_snap_key[16]; char h_merch_id[16]; + char h_card_no[20]; + char h_status[2]; long h_amount; - char h_new_status[2]; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + int errcnt = 0; + long affected = 0; + char won_amt[32], won_fee[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - EXEC SQL DECLARE cur_cl_bt_0009 CURSOR FOR - SELECT key, merch_id, amount + /* 1) 마감 집계 */ + EXEC SQL SELECT count(*), coalesce(sum(amount), 0), coalesce(sum(fee), 0) + INTO :h_cnt, :h_sum_amt, :h_sum_fee FROM cl_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status = 'S'; - EXEC SQL OPEN cur_cl_bt_0009; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0009"); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 집계 cl_bt_0009"); return TX_EDB; } + /* 스냅샷 요약행 필드 구성 */ + snprintf(h_snap_key, sizeof(h_snap_key), "%.8sSS", biz_date); + strncpy(h_merch_id, "*SNAP", sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_card_no, "0000000000000000", sizeof(h_card_no) - 1); + h_card_no[sizeof(h_card_no) - 1] = '\0'; + strncpy(h_status, CL_ST_SETTLED, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + h_amount = h_sum_amt; + h_fee = (long)h_cnt; + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[cl_bt_0009] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_cl_bt_0009 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cl_bt_0009"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 2) 기존 동일 스냅샷 선삭제 (재생성) */ + EXEC SQL DELETE FROM cl_ledger WHERE key = :h_snap_key; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("DELETE 스냅샷 cl_bt_0009"); + errcnt++; } - EXEC SQL CLOSE cur_cl_bt_0009; + /* 3) 스냅샷 요약행 INSERT */ + if (errcnt == 0) { + EXEC SQL INSERT INTO cl_ledger + (key, merch_id, card_no, amount, biz_date, status, fee) + VALUES + (:h_snap_key, :h_merch_id, :h_card_no, :h_amount, + :h_biz_date, :h_status, :h_fee); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT 스냅샷 cl_bt_0009"); + errcnt++; + } else { + affected = sqlca.sqlerrd[2]; + } + } if (errcnt == 0) tx_commit(); else tx_abort(); - tx_log(TX_LOG_INFO, - "[cl_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(h_sum_amt, won_amt, sizeof(won_amt)); + amount_format_won(h_sum_fee, won_fee, sizeof(won_fee)); - printf("========== 마감 배치 결과 [cl_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[cl_bt_0009] 요약 date=%s snap=%s 건수=%d Σamt=%ld Σfee=%ld ins=%ld 오류=%d", + biz_date, h_snap_key, h_cnt, h_sum_amt, h_sum_fee, affected, errcnt); + + printf("========== 마감 스냅샷 배치 [cl_bt_0009] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 스냅샷 키 : %s\n", h_snap_key); + printf(" 마감 건수 : %d\n", h_cnt); + printf(" Σamount : %s\n", won_amt); + printf(" Σfee : %s\n", won_fee); + printf(" 생성 행수 : %ld\n", affected); + printf(" 오류 건수 : %d\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0010.c b/app/batch/cl_bt_0010.c index db55cde..51e8f7e 100644 --- a/app/batch/cl_bt_0010.c +++ b/app/batch/cl_bt_0010.c @@ -8,10 +8,12 @@ #line 1 "app/batch/cl_bt_0010.pgc" /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0010.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0010.pgc - 마감 순번 채번 배치 (정산건 순번 부여) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 정산완료('S') 건을 key 순으로 커서 순회하며 로컬 순번 + * 카운터를 증가시켜 각 건에 마감 순번을 채번한다. 채번 시 각 건의 + * upd_ts 를 now() 로 갱신(UPDATE)하여 채번 처리 시각을 반영한다. + * 순번 자체는 로컬 계산/로그로 남긴다. 배치 main 엔트리 포함. * * 실행: cl_bt_0010 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0010.pgc" +#line 21 "app/batch/cl_bt_0010.pgc" static int run_cl_bt_0010(const char *biz_date) @@ -101,45 +103,32 @@ static int run_cl_bt_0010(const char *biz_date) /* exec sql begin declare section */ - - - -#line 24 "app/batch/cl_bt_0010.pgc" +#line 26 "app/batch/cl_bt_0010.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cl_bt_0010.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/cl_bt_0010.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/cl_bt_0010.pgc" - long h_amount ; - -#line 28 "app/batch/cl_bt_0010.pgc" - char h_new_status [ 2 ] ; + char h_key [ 16 ] ; /* exec sql end declare section */ -#line 29 "app/batch/cl_bt_0010.pgc" +#line 28 "app/batch/cl_bt_0010.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long seq = 0, numbered = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0010 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cl_bt_0010.pgc" + /* declare cur_cl_bt_0010 cursor for select key from cl_ledger where biz_date = $1 and status = 'S' order by key */ +#line 40 "app/batch/cl_bt_0010.pgc" -#line 42 "app/batch/cl_bt_0010.pgc" +#line 40 "app/batch/cl_bt_0010.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0010 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0010 cursor for select key from cl_ledger where biz_date = $1 and status = 'S' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cl_bt_0010.pgc" +#line 42 "app/batch/cl_bt_0010.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0010"); @@ -152,12 +141,8 @@ static int run_cl_bt_0010(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0010", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cl_bt_0010.pgc" +#line 52 "app/batch/cl_bt_0010.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,26 +153,28 @@ static int run_cl_bt_0010(const char *biz_date) break; } - total++; + seq++; /* 로컬 순번 채번 */ - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* upd_ts 갱신으로 채번 처리 반영 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cl_ledger set upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 67 "app/batch/cl_bt_0010.pgc" - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE 채번 cl_bt_0010"); errcnt++; + continue; } + if (sqlca.sqlerrd[2] > 0) + numbered++; + + tx_log(TX_LOG_DEBUG, "[cl_bt_0010] 채번 seq=%ld key=%s", seq, h_key); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0010", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0010.pgc" +#line 80 "app/batch/cl_bt_0010.pgc" if (errcnt == 0) @@ -196,16 +183,16 @@ static int run_cl_bt_0010(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[cl_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cl_bt_0010] 요약 date=%s 채번=%ld 갱신=%ld 오류=%ld 최종순번=%ld", + biz_date, seq, numbered, errcnt, seq); - printf("========== 마감 배치 결과 [cl_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 마감 순번 채번 배치 [cl_bt_0010] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 채번 건수 : %ld\n", seq); + printf(" upd_ts 갱신 : %ld\n", numbered); + printf(" 최종 순번 : %ld\n", seq); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0010.pgc b/app/batch/cl_bt_0010.pgc index 9fddbaf..83a8f6d 100644 --- a/app/batch/cl_bt_0010.pgc +++ b/app/batch/cl_bt_0010.pgc @@ -1,9 +1,11 @@ /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0010.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0010.pgc - 마감 순번 채번 배치 (정산건 순번 부여) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 정산완료('S') 건을 key 순으로 커서 순회하며 로컬 순번 + * 카운터를 증가시켜 각 건에 마감 순번을 채번한다. 채번 시 각 건의 + * upd_ts 를 now() 로 갱신(UPDATE)하여 채번 처리 시각을 반영한다. + * 순번 자체는 로컬 계산/로그로 남긴다. 배치 main 엔트리 포함. * * 실행: cl_bt_0010 [db@host] */ @@ -23,22 +25,18 @@ static int run_cl_bt_0010(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long seq = 0, numbered = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_cl_bt_0010 CURSOR FOR - SELECT key, merch_id, amount + SELECT key FROM cl_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'S' ORDER BY key; EXEC SQL OPEN cur_cl_bt_0010; @@ -51,7 +49,7 @@ static int run_cl_bt_0010(const char *biz_date) tx_log(TX_LOG_WARN, "[cl_bt_0010] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_cl_bt_0010 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cl_bt_0010 INTO :h_key; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,22 +59,22 @@ static int run_cl_bt_0010(const char *biz_date) break; } - total++; + seq++; /* 로컬 순번 채번 */ - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* upd_ts 갱신으로 채번 처리 반영 */ + EXEC SQL UPDATE cl_ledger + SET upd_ts = now() + WHERE key = :h_key; - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE 채번 cl_bt_0010"); errcnt++; + continue; } + if (sqlca.sqlerrd[2] > 0) + numbered++; + + tx_log(TX_LOG_DEBUG, "[cl_bt_0010] 채번 seq=%ld key=%s", seq, h_key); } EXEC SQL CLOSE cur_cl_bt_0010; @@ -87,16 +85,16 @@ static int run_cl_bt_0010(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[cl_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cl_bt_0010] 요약 date=%s 채번=%ld 갱신=%ld 오류=%ld 최종순번=%ld", + biz_date, seq, numbered, errcnt, seq); - printf("========== 마감 배치 결과 [cl_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 마감 순번 채번 배치 [cl_bt_0010] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 채번 건수 : %ld\n", seq); + printf(" upd_ts 갱신 : %ld\n", numbered); + printf(" 최종 순번 : %ld\n", seq); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0011.c b/app/batch/cl_bt_0011.c index 286f0f4..0efd73c 100644 --- a/app/batch/cl_bt_0011.c +++ b/app/batch/cl_bt_0011.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cl_bt_0011.pgc" /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0011.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0011.pgc - 마감 리포트 배치 (상태별 집계표) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 상태별(R/M/U/S) 건수와 금액합계를 개별 SELECT 로 조회하여 + * 리포트표 형태로 출력한다. 전체 합계와 마감률(S/전체)을 함께 표시한다. + * 배치 main 엔트리 포함. * * 실행: cl_bt_0011 [db@host] */ @@ -93,121 +94,99 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0011.pgc" +#line 20 "app/batch/cl_bt_0011.pgc" -static int run_cl_bt_0011(const char *biz_date) +static int query_status_0011(const char *biz_date, const char *status, + long *out_cnt, long *out_sum) { /* exec sql begin declare section */ - - + -#line 24 "app/batch/cl_bt_0011.pgc" +#line 26 "app/batch/cl_bt_0011.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cl_bt_0011.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/cl_bt_0011.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/cl_bt_0011.pgc" - long h_amount ; + char h_status [ 2 ] ; #line 28 "app/batch/cl_bt_0011.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + int h_cnt ; + #line 29 "app/batch/cl_bt_0011.pgc" + long h_sum ; +/* exec sql end declare section */ +#line 30 "app/batch/cl_bt_0011.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, status, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0011 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cl_bt_0011.pgc" - -#line 42 "app/batch/cl_bt_0011.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0011 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date = $1 and status = $2 ", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cl_bt_0011.pgc" + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 41 "app/batch/cl_bt_0011.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0011"); + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 상태집계 cl_bt_0011"); return TX_EDB; } + *out_cnt = (long)h_cnt; + *out_sum = h_sum; + return TX_OK; +} - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0011] tx_begin 경고"); +static int run_cl_bt_0011(const char *biz_date) +{ + static const char *codes[4] = { "R", "M", "U", "S" }; + static const char *labels[4] = { "접수(R) ", "완료(M) ", + "불일치(U)", "정산(S) " }; + long cnt[4], sum[4]; + long tot_cnt = 0, tot_sum = 0; + int i, pct = 0; + char won[32]; - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0011", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cl_bt_0011.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cl_bt_0011"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + for (i = 0; i < 4; i++) { + if (query_status_0011(biz_date, codes[i], &cnt[i], &sum[i]) != TX_OK) + return TX_EDB; + tot_cnt += cnt[i]; + tot_sum += sum[i]; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0011", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0011.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + if (tot_cnt > 0) + pct = (int)((cnt[3] * 100) / tot_cnt); /* 마감률 = S / 전체 */ tx_log(TX_LOG_INFO, - "[cl_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cl_bt_0011] 요약 date=%s R=%ld M=%ld U=%ld S=%ld 전체=%ld 마감률=%d%%", + biz_date, cnt[0], cnt[1], cnt[2], cnt[3], tot_cnt, pct); - printf("========== 마감 배치 결과 [cl_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 마감 리포트 배치 [cl_bt_0011] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" ---------------------------------------------\n"); + printf(" %-10s %8s %18s\n", "상태", "건수", "금액합계"); + printf(" ---------------------------------------------\n"); + for (i = 0; i < 4; i++) { + amount_format_won(sum[i], won, sizeof(won)); + printf(" %-10s %8ld %18s\n", labels[i], cnt[i], won); + } + amount_format_won(tot_sum, won, sizeof(won)); + printf(" ---------------------------------------------\n"); + printf(" %-10s %8ld %18s\n", "합계", tot_cnt, won); + printf(" 마감률(S/전체) : %d%%\n", pct); + printf("=================================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/cl_bt_0011.pgc b/app/batch/cl_bt_0011.pgc index a0dd04a..b440cb2 100644 --- a/app/batch/cl_bt_0011.pgc +++ b/app/batch/cl_bt_0011.pgc @@ -1,9 +1,10 @@ /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0011.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0011.pgc - 마감 리포트 배치 (상태별 집계표) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 상태별(R/M/U/S) 건수와 금액합계를 개별 SELECT 로 조회하여 + * 리포트표 형태로 출력한다. 전체 합계와 마감률(S/전체)을 함께 표시한다. + * 배치 main 엔트리 포함. * * 실행: cl_bt_0011 [db@host] */ @@ -18,87 +19,76 @@ EXEC SQL INCLUDE sqlca; -static int run_cl_bt_0011(const char *biz_date) +static int query_status_0011(const char *biz_date, const char *status, + long *out_cnt, long *out_sum) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_status[2]; + int h_cnt; + long h_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, status, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; - EXEC SQL DECLARE cur_cl_bt_0011 CURSOR FOR - SELECT key, merch_id, amount + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_cnt, :h_sum FROM cl_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status = :h_status; - EXEC SQL OPEN cur_cl_bt_0011; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0011"); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 상태집계 cl_bt_0011"); return TX_EDB; } + *out_cnt = (long)h_cnt; + *out_sum = h_sum; + return TX_OK; +} - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0011] tx_begin 경고"); +static int run_cl_bt_0011(const char *biz_date) +{ + static const char *codes[4] = { "R", "M", "U", "S" }; + static const char *labels[4] = { "접수(R) ", "완료(M) ", + "불일치(U)", "정산(S) " }; + long cnt[4], sum[4]; + long tot_cnt = 0, tot_sum = 0; + int i, pct = 0; + char won[32]; - for (;;) { - EXEC SQL FETCH cur_cl_bt_0011 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cl_bt_0011"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + for (i = 0; i < 4; i++) { + if (query_status_0011(biz_date, codes[i], &cnt[i], &sum[i]) != TX_OK) + return TX_EDB; + tot_cnt += cnt[i]; + tot_sum += sum[i]; } - EXEC SQL CLOSE cur_cl_bt_0011; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + if (tot_cnt > 0) + pct = (int)((cnt[3] * 100) / tot_cnt); /* 마감률 = S / 전체 */ tx_log(TX_LOG_INFO, - "[cl_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cl_bt_0011] 요약 date=%s R=%ld M=%ld U=%ld S=%ld 전체=%ld 마감률=%d%%", + biz_date, cnt[0], cnt[1], cnt[2], cnt[3], tot_cnt, pct); - printf("========== 마감 배치 결과 [cl_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 마감 리포트 배치 [cl_bt_0011] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" ---------------------------------------------\n"); + printf(" %-10s %8s %18s\n", "상태", "건수", "금액합계"); + printf(" ---------------------------------------------\n"); + for (i = 0; i < 4; i++) { + amount_format_won(sum[i], won, sizeof(won)); + printf(" %-10s %8ld %18s\n", labels[i], cnt[i], won); + } + amount_format_won(tot_sum, won, sizeof(won)); + printf(" ---------------------------------------------\n"); + printf(" %-10s %8ld %18s\n", "합계", tot_cnt, won); + printf(" 마감률(S/전체) : %d%%\n", pct); + printf("=================================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/cl_bt_0012.c b/app/batch/cl_bt_0012.c index f364986..0877eb9 100644 --- a/app/batch/cl_bt_0012.c +++ b/app/batch/cl_bt_0012.c @@ -8,10 +8,12 @@ #line 1 "app/batch/cl_bt_0012.pgc" /* @tier medium @module cl @transform closing-batch */ /* - * cl_bt_0012.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=medium] + * cl_bt_0012.pgc - 마감 알림 배치 (임계 초과 알림) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 미마감(접수 'R') 및 불일치('U') 건수를 조회하여 알림 + * 임계치와 비교한다. 임계 초과 시 알림 대상(R/U) 상위건을 커서로 순회 + * 하며 목록으로 출력한다. 알림 발생 건수를 리포트한다. + * 배치 main 엔트리 포함. * * 실행: cl_bt_0012 [db@host] */ @@ -93,9 +95,12 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0012.pgc" +#line 21 "app/batch/cl_bt_0012.pgc" +#define CL_ALERT_THRESHOLD 0 /* 미마감/불일치 임계치(초과 시 알림) */ +#define CL_ALERT_TOPN 20 /* 알림 목록 상위 건수 */ + static int run_cl_bt_0012(const char *biz_date) { /* exec sql begin declare section */ @@ -105,59 +110,92 @@ static int run_cl_bt_0012(const char *biz_date) -#line 24 "app/batch/cl_bt_0012.pgc" +#line 29 "app/batch/cl_bt_0012.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cl_bt_0012.pgc" +#line 30 "app/batch/cl_bt_0012.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/cl_bt_0012.pgc" +#line 31 "app/batch/cl_bt_0012.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/cl_bt_0012.pgc" - long h_amount ; +#line 32 "app/batch/cl_bt_0012.pgc" + char h_status [ 2 ] ; -#line 28 "app/batch/cl_bt_0012.pgc" - char h_new_status [ 2 ] ; +#line 33 "app/batch/cl_bt_0012.pgc" + long h_amount ; /* exec sql end declare section */ -#line 29 "app/batch/cl_bt_0012.pgc" +#line 34 "app/batch/cl_bt_0012.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long recv, unmatch, target_cnt; + long listed = 0, errcnt = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + recv = cl_rec_count(biz_date, CL_ST_RECV); + unmatch = cl_rec_count(biz_date, CL_ST_FAIL); + if (recv < 0 || unmatch < 0) { + tx_log(TX_LOG_ERROR, "[cl_bt_0012] 알림대상 건수 조회 실패"); + return TX_EDB; + } + target_cnt = recv + unmatch; + + tx_log(TX_LOG_INFO, + "[cl_bt_0012] 알림대상 R=%ld U=%ld 합=%ld 임계=%d", + recv, unmatch, target_cnt, CL_ALERT_THRESHOLD); + + printf("========== 마감 알림 배치 [cl_bt_0012] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 미마감(R) : %ld\n", recv); + printf(" 불일치(U) : %ld\n", unmatch); + printf(" 알림 임계치 : %d\n", CL_ALERT_THRESHOLD); + + if (target_cnt <= CL_ALERT_THRESHOLD) { + printf(" ** 임계 이내 - 알림 없음 **\n"); + printf("===============================================\n"); + tx_log(TX_LOG_INFO, "[cl_bt_0012] 임계 이내 - 알림 없음"); + return TX_OK; + } + + printf(" ** 임계 초과 - 알림 대상 목록 (상위 %d) **\n", CL_ALERT_TOPN); + printf(" ---------------------------------------------\n"); + printf(" %-14s %-4s %14s\n", "가맹점", "상태", "금액"); + printf(" ---------------------------------------------\n"); + ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0012 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cl_bt_0012.pgc" + /* declare cur_cl_bt_0012 cursor for select key , merch_id , status , amount from cl_ledger where biz_date = $1 and status in ( 'R' , 'U' ) order by status , key */ +#line 78 "app/batch/cl_bt_0012.pgc" -#line 42 "app/batch/cl_bt_0012.pgc" +#line 78 "app/batch/cl_bt_0012.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0012 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0012 cursor for select key , merch_id , status , amount from cl_ledger where biz_date = $1 and status in ( 'R' , 'U' ) order by status , key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cl_bt_0012.pgc" +#line 80 "app/batch/cl_bt_0012.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0012"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0012] tx_begin 경고"); - for (;;) { + if (listed >= CL_ALERT_TOPN) + break; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0012", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cl_bt_0012.pgc" +#line 90 "app/batch/cl_bt_0012.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,44 +206,24 @@ static int run_cl_bt_0012(const char *biz_date) break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + amount_format_won(h_amount, won, sizeof(won)); + printf(" %-14s %-4s %14s key=%s\n", h_merch_id, h_status, won, h_key); + listed++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0012", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0012.pgc" +#line 105 "app/batch/cl_bt_0012.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + tx_log(TX_LOG_WARN, + "[cl_bt_0012] 요약 date=%s 알림대상=%ld 출력=%ld 오류=%ld", + biz_date, target_cnt, listed, errcnt); - tx_log(TX_LOG_INFO, - "[cl_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 마감 배치 결과 [cl_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" ---------------------------------------------\n"); + printf(" 알림 대상 수 : %ld\n", target_cnt); + printf(" 출력 건수 : %ld\n", listed); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0012.pgc b/app/batch/cl_bt_0012.pgc index 883d566..d623a3c 100644 --- a/app/batch/cl_bt_0012.pgc +++ b/app/batch/cl_bt_0012.pgc @@ -1,9 +1,11 @@ /* @tier medium @module cl @transform closing-batch */ /* - * cl_bt_0012.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=medium] + * cl_bt_0012.pgc - 마감 알림 배치 (임계 초과 알림) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 미마감(접수 'R') 및 불일치('U') 건수를 조회하여 알림 + * 임계치와 비교한다. 임계 초과 시 알림 대상(R/U) 상위건을 커서로 순회 + * 하며 목록으로 출력한다. 알림 발생 건수를 리포트한다. + * 배치 main 엔트리 포함. * * 실행: cl_bt_0012 [db@host] */ @@ -18,28 +20,62 @@ EXEC SQL INCLUDE sqlca; +#define CL_ALERT_THRESHOLD 0 /* 미마감/불일치 임계치(초과 시 알림) */ +#define CL_ALERT_TOPN 20 /* 알림 목록 상위 건수 */ + static int run_cl_bt_0012(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; char h_merch_id[16]; + char h_status[2]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long recv, unmatch, target_cnt; + long listed = 0, errcnt = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + recv = cl_rec_count(biz_date, CL_ST_RECV); + unmatch = cl_rec_count(biz_date, CL_ST_FAIL); + if (recv < 0 || unmatch < 0) { + tx_log(TX_LOG_ERROR, "[cl_bt_0012] 알림대상 건수 조회 실패"); + return TX_EDB; + } + target_cnt = recv + unmatch; + + tx_log(TX_LOG_INFO, + "[cl_bt_0012] 알림대상 R=%ld U=%ld 합=%ld 임계=%d", + recv, unmatch, target_cnt, CL_ALERT_THRESHOLD); + + printf("========== 마감 알림 배치 [cl_bt_0012] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 미마감(R) : %ld\n", recv); + printf(" 불일치(U) : %ld\n", unmatch); + printf(" 알림 임계치 : %d\n", CL_ALERT_THRESHOLD); + + if (target_cnt <= CL_ALERT_THRESHOLD) { + printf(" ** 임계 이내 - 알림 없음 **\n"); + printf("===============================================\n"); + tx_log(TX_LOG_INFO, "[cl_bt_0012] 임계 이내 - 알림 없음"); + return TX_OK; + } + + printf(" ** 임계 초과 - 알림 대상 목록 (상위 %d) **\n", CL_ALERT_TOPN); + printf(" ---------------------------------------------\n"); + printf(" %-14s %-4s %14s\n", "가맹점", "상태", "금액"); + printf(" ---------------------------------------------\n"); + EXEC SQL DECLARE cur_cl_bt_0012 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, merch_id, status, amount FROM cl_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status IN ('R', 'U') + ORDER BY status, key; EXEC SQL OPEN cur_cl_bt_0012; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,11 +83,11 @@ static int run_cl_bt_0012(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0012] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_cl_bt_0012 INTO :h_key, :h_merch_id, :h_amount; + if (listed >= CL_ALERT_TOPN) + break; + + EXEC SQL FETCH cur_cl_bt_0012 INTO :h_key, :h_merch_id, :h_status, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,42 +97,22 @@ static int run_cl_bt_0012(const char *biz_date) break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + amount_format_won(h_amount, won, sizeof(won)); + printf(" %-14s %-4s %14s key=%s\n", h_merch_id, h_status, won, h_key); + listed++; } EXEC SQL CLOSE cur_cl_bt_0012; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + tx_log(TX_LOG_WARN, + "[cl_bt_0012] 요약 date=%s 알림대상=%ld 출력=%ld 오류=%ld", + biz_date, target_cnt, listed, errcnt); - tx_log(TX_LOG_INFO, - "[cl_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 마감 배치 결과 [cl_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" ---------------------------------------------\n"); + printf(" 알림 대상 수 : %ld\n", target_cnt); + printf(" 출력 건수 : %ld\n", listed); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0013.c b/app/batch/cl_bt_0013.c index e1bcef8..bb11e3d 100644 --- a/app/batch/cl_bt_0013.c +++ b/app/batch/cl_bt_0013.c @@ -8,10 +8,12 @@ #line 1 "app/batch/cl_bt_0013.pgc" /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0013.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0013.pgc - 미마감 점검 배치 (미결 목록 검출) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에서 아직 마감되지 않은 미결건(접수 'R' / 불일치 'U')을 + * 커서로 순회하며 목록과 합계를 출력한다. 미마감 건이 하나라도 존재하면 + * 점검 실패(TX_FAIL)로 종료하여 후속 마감을 차단한다. + * 배치 main 엔트리 포함. * * 실행: cl_bt_0013 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0013.pgc" +#line 21 "app/batch/cl_bt_0013.pgc" static int run_cl_bt_0013(const char *biz_date) @@ -105,49 +107,52 @@ static int run_cl_bt_0013(const char *biz_date) -#line 24 "app/batch/cl_bt_0013.pgc" +#line 26 "app/batch/cl_bt_0013.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cl_bt_0013.pgc" +#line 27 "app/batch/cl_bt_0013.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/cl_bt_0013.pgc" +#line 28 "app/batch/cl_bt_0013.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/cl_bt_0013.pgc" - long h_amount ; - -#line 28 "app/batch/cl_bt_0013.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ #line 29 "app/batch/cl_bt_0013.pgc" + char h_status [ 2 ] ; + +#line 30 "app/batch/cl_bt_0013.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 31 "app/batch/cl_bt_0013.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long pending = 0, errcnt = 0, sum_amt = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0013 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cl_bt_0013.pgc" + /* declare cur_cl_bt_0013 cursor for select key , merch_id , status , amount from cl_ledger where biz_date = $1 and status in ( 'R' , 'U' ) order by status , key */ +#line 44 "app/batch/cl_bt_0013.pgc" -#line 42 "app/batch/cl_bt_0013.pgc" +#line 44 "app/batch/cl_bt_0013.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0013 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0013 cursor for select key , merch_id , status , amount from cl_ledger where biz_date = $1 and status in ( 'R' , 'U' ) order by status , key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cl_bt_0013.pgc" +#line 46 "app/batch/cl_bt_0013.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0013"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0013] tx_begin 경고"); + printf("========== 미마감 점검 배치 [cl_bt_0013] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" ---------------------------------------------\n"); + printf(" %-14s %-14s %-4s %14s\n", "처리키", "가맹점", "상태", "금액"); + printf(" ---------------------------------------------\n"); for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0013", ECPGt_EOIT, @@ -155,9 +160,11 @@ static int run_cl_bt_0013(const char *biz_date) ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cl_bt_0013.pgc" +#line 59 "app/batch/cl_bt_0013.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,46 +175,33 @@ static int run_cl_bt_0013(const char *biz_date) break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + amount_format_won(h_amount, won, sizeof(won)); + printf(" %-14s %-14s %-4s %14s\n", h_key, h_merch_id, h_status, won); + pending++; + sum_amt += h_amount; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0013", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0013.pgc" +#line 75 "app/batch/cl_bt_0013.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + amount_format_won(sum_amt, won, sizeof(won)); tx_log(TX_LOG_INFO, - "[cl_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cl_bt_0013] 요약 date=%s 미마감=%ld 미마감액=%ld 오류=%ld", + biz_date, pending, sum_amt, errcnt); - printf("========== 마감 배치 결과 [cl_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" ---------------------------------------------\n"); + printf(" 미마감 건수 : %ld\n", pending); + printf(" 미마감 금액 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf(" 점검 결과 : %s\n", + (errcnt == 0 && pending == 0) ? "정상(마감가능)" : "미마감 존재(FAIL)"); + printf("=================================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + if (errcnt > 0) + return TX_FAIL; + return (pending == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) diff --git a/app/batch/cl_bt_0013.pgc b/app/batch/cl_bt_0013.pgc index 97c31e0..57832f4 100644 --- a/app/batch/cl_bt_0013.pgc +++ b/app/batch/cl_bt_0013.pgc @@ -1,9 +1,11 @@ /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0013.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0013.pgc - 미마감 점검 배치 (미결 목록 검출) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에서 아직 마감되지 않은 미결건(접수 'R' / 불일치 'U')을 + * 커서로 순회하며 목록과 합계를 출력한다. 미마감 건이 하나라도 존재하면 + * 점검 실패(TX_FAIL)로 종료하여 후속 마감을 차단한다. + * 배치 main 엔트리 포함. * * 실행: cl_bt_0013 [db@host] */ @@ -24,22 +26,22 @@ static int run_cl_bt_0013(const char *biz_date) char h_biz_date[9]; char h_key[16]; char h_merch_id[16]; + char h_status[2]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long pending = 0, errcnt = 0, sum_amt = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_cl_bt_0013 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, merch_id, status, amount FROM cl_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status IN ('R', 'U') + ORDER BY status, key; EXEC SQL OPEN cur_cl_bt_0013; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,11 +49,14 @@ static int run_cl_bt_0013(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0013] tx_begin 경고"); + printf("========== 미마감 점검 배치 [cl_bt_0013] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" ---------------------------------------------\n"); + printf(" %-14s %-14s %-4s %14s\n", "처리키", "가맹점", "상태", "금액"); + printf(" ---------------------------------------------\n"); for (;;) { - EXEC SQL FETCH cur_cl_bt_0013 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cl_bt_0013 INTO :h_key, :h_merch_id, :h_status, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,44 +66,31 @@ static int run_cl_bt_0013(const char *biz_date) break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + amount_format_won(h_amount, won, sizeof(won)); + printf(" %-14s %-14s %-4s %14s\n", h_key, h_merch_id, h_status, won); + pending++; + sum_amt += h_amount; } EXEC SQL CLOSE cur_cl_bt_0013; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + amount_format_won(sum_amt, won, sizeof(won)); tx_log(TX_LOG_INFO, - "[cl_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cl_bt_0013] 요약 date=%s 미마감=%ld 미마감액=%ld 오류=%ld", + biz_date, pending, sum_amt, errcnt); - printf("========== 마감 배치 결과 [cl_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" ---------------------------------------------\n"); + printf(" 미마감 건수 : %ld\n", pending); + printf(" 미마감 금액 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf(" 점검 결과 : %s\n", + (errcnt == 0 && pending == 0) ? "정상(마감가능)" : "미마감 존재(FAIL)"); + printf("=================================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + if (errcnt > 0) + return TX_FAIL; + return (pending == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) diff --git a/app/batch/cl_bt_0014.c b/app/batch/cl_bt_0014.c index 06069bd..7592fae 100644 --- a/app/batch/cl_bt_0014.c +++ b/app/batch/cl_bt_0014.c @@ -8,12 +8,14 @@ #line 1 "app/batch/cl_bt_0014.pgc" /* @tier medium @module cl @transform closing-batch */ /* - * cl_bt_0014.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=medium] + * cl_bt_0014.pgc - 분기마감 배치 (기간 일괄 정산) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 인자로 받은 두 영업일 D1, D2 사이(biz_date BETWEEN :d1 AND :d2)의 + * 처리완료('M')/불일치('U') 건을 커서로 순회하여 정산완료('S')로 일괄 + * 분기마감한다. 분기 마감건수와 정산액 합계를 리포트한다. + * 인자 2개(D1 D2)를 필수로 요구한다. 배치 main 엔트리 포함. * - * 실행: cl_bt_0014 [db@host] + * 실행: cl_bt_0014 [db@host] */ #include #include @@ -93,53 +95,55 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0014.pgc" +#line 21 "app/batch/cl_bt_0014.pgc" -static int run_cl_bt_0014(const char *biz_date) +static int run_cl_bt_0014(const char *d1, const char *d2) { /* exec sql begin declare section */ - -#line 24 "app/batch/cl_bt_0014.pgc" - char h_biz_date [ 9 ] ; - -#line 25 "app/batch/cl_bt_0014.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/cl_bt_0014.pgc" - char h_merch_id [ 16 ] ; + char h_d1 [ 9 ] ; #line 27 "app/batch/cl_bt_0014.pgc" - long h_amount ; + char h_d2 [ 9 ] ; #line 28 "app/batch/cl_bt_0014.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + char h_key [ 16 ] ; + #line 29 "app/batch/cl_bt_0014.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 30 "app/batch/cl_bt_0014.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, settled = 0, errcnt = 0, sum_amt = 0; + char won[32]; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_d1, d1, sizeof(h_d1) - 1); + h_d1[sizeof(h_d1) - 1] = '\0'; + strncpy(h_d2, d2, sizeof(h_d2) - 1); + h_d2[sizeof(h_d2) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0014 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cl_bt_0014.pgc" + ECPGset_var( 0, ( h_d2 ), __LINE__);\ + ECPGset_var( 1, ( h_d1 ), __LINE__);\ + /* declare cur_cl_bt_0014 cursor for select key , amount from cl_ledger where biz_date between $1 and $2 and status in ( 'M' , 'U' ) order by biz_date , key */ +#line 46 "app/batch/cl_bt_0014.pgc" -#line 42 "app/batch/cl_bt_0014.pgc" +#line 46 "app/batch/cl_bt_0014.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0014 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0014 cursor for select key , amount from cl_ledger where biz_date between $1 and $2 and status in ( 'M' , 'U' ) order by biz_date , key", + ECPGt_char,(h_d1),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_d2),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cl_bt_0014.pgc" +#line 48 "app/batch/cl_bt_0014.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0014"); @@ -153,11 +157,9 @@ static int run_cl_bt_0014(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0014", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cl_bt_0014.pgc" +#line 58 "app/batch/cl_bt_0014.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,25 +171,18 @@ static int run_cl_bt_0014(const char *biz_date) } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); + rc = cl_rec_update_status(h_key, CL_ST_SETTLED); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[cl_bt_0014] 분기마감 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + settled++; + sum_amt += h_amount; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0014", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0014.pgc" +#line 79 "app/batch/cl_bt_0014.pgc" if (errcnt == 0) @@ -195,42 +190,56 @@ static int run_cl_bt_0014(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[cl_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_amt, won, sizeof(won)); - printf("========== 마감 배치 결과 [cl_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[cl_bt_0014] 요약 기간=%s~%s 대상=%ld 마감=%ld 정산액=%ld 오류=%ld", + d1, d2, total, settled, sum_amt, errcnt); + + printf("========== 분기마감 배치 결과 [cl_bt_0014] ==========\n"); + printf(" 마감 기간 : %s ~ %s\n", d1, d2); + printf(" 대상 건수 : %ld\n", total); + printf(" 분기마감(S) : %ld\n", settled); + printf(" 정산액 합계 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) { - const char *biz_date; + const char *d1; + const char *d2; const char *target; int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { - fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); + if (argc < 3) { + fprintf(stderr, + "사용법: %s [db@host]\n", argv[0]); return 2; } - biz_date = argv[1]; - target = (argc >= 3) ? argv[2] : NULL; + d1 = argv[1]; + d2 = argv[2]; + target = (argc >= 4) ? argv[3] : NULL; - if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + if (!date_is_valid(d1)) { + fprintf(stderr, "오류: 유효하지 않은 시작영업일 '%s'\n", d1); + return 2; + } + if (!date_is_valid(d2)) { + fprintf(stderr, "오류: 유효하지 않은 종료영업일 '%s'\n", d2); + return 2; + } + if (strcmp(d1, d2) > 0) { + fprintf(stderr, "오류: 시작영업일이 종료영업일보다 큽니다 '%s' > '%s'\n", + d1, d2); return 2; } - tx_log(TX_LOG_INFO, "[cl_bt_0014] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cl_bt_0014] 배치 시작 기간=%s~%s", d1, d2); rc = cl_dbio_connect(target); if (rc != TX_OK) { @@ -238,7 +247,7 @@ int main(int argc, char **argv) return 1; } - rc = run_cl_bt_0014(biz_date); + rc = run_cl_bt_0014(d1, d2); cl_dbio_disconnect(); diff --git a/app/batch/cl_bt_0014.pgc b/app/batch/cl_bt_0014.pgc index 05caf58..6f43256 100644 --- a/app/batch/cl_bt_0014.pgc +++ b/app/batch/cl_bt_0014.pgc @@ -1,11 +1,13 @@ /* @tier medium @module cl @transform closing-batch */ /* - * cl_bt_0014.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=medium] + * cl_bt_0014.pgc - 분기마감 배치 (기간 일괄 정산) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 인자로 받은 두 영업일 D1, D2 사이(biz_date BETWEEN :d1 AND :d2)의 + * 처리완료('M')/불일치('U') 건을 커서로 순회하여 정산완료('S')로 일괄 + * 분기마감한다. 분기 마감건수와 정산액 합계를 리포트한다. + * 인자 2개(D1 D2)를 필수로 요구한다. 배치 main 엔트리 포함. * - * 실행: cl_bt_0014 [db@host] + * 실행: cl_bt_0014 [db@host] */ #include #include @@ -18,28 +20,30 @@ EXEC SQL INCLUDE sqlca; -static int run_cl_bt_0014(const char *biz_date) +static int run_cl_bt_0014(const char *d1, const char *d2) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; + char h_d1[9]; + char h_d2[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, settled = 0, errcnt = 0, sum_amt = 0; + char won[32]; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_d1, d1, sizeof(h_d1) - 1); + h_d1[sizeof(h_d1) - 1] = '\0'; + strncpy(h_d2, d2, sizeof(h_d2) - 1); + h_d2[sizeof(h_d2) - 1] = '\0'; EXEC SQL DECLARE cur_cl_bt_0014 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount FROM cl_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + WHERE biz_date BETWEEN :h_d1 AND :h_d2 + AND status IN ('M', 'U') + ORDER BY biz_date, key; EXEC SQL OPEN cur_cl_bt_0014; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +55,7 @@ static int run_cl_bt_0014(const char *biz_date) tx_log(TX_LOG_WARN, "[cl_bt_0014] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_cl_bt_0014 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cl_bt_0014 INTO :h_key, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,21 +66,14 @@ static int run_cl_bt_0014(const char *biz_date) } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); + rc = cl_rec_update_status(h_key, CL_ST_SETTLED); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[cl_bt_0014] 분기마감 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + settled++; + sum_amt += h_amount; } EXEC SQL CLOSE cur_cl_bt_0014; @@ -86,42 +83,56 @@ static int run_cl_bt_0014(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[cl_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_amt, won, sizeof(won)); - printf("========== 마감 배치 결과 [cl_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[cl_bt_0014] 요약 기간=%s~%s 대상=%ld 마감=%ld 정산액=%ld 오류=%ld", + d1, d2, total, settled, sum_amt, errcnt); + + printf("========== 분기마감 배치 결과 [cl_bt_0014] ==========\n"); + printf(" 마감 기간 : %s ~ %s\n", d1, d2); + printf(" 대상 건수 : %ld\n", total); + printf(" 분기마감(S) : %ld\n", settled); + printf(" 정산액 합계 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) { - const char *biz_date; + const char *d1; + const char *d2; const char *target; int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { - fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); + if (argc < 3) { + fprintf(stderr, + "사용법: %s [db@host]\n", argv[0]); return 2; } - biz_date = argv[1]; - target = (argc >= 3) ? argv[2] : NULL; + d1 = argv[1]; + d2 = argv[2]; + target = (argc >= 4) ? argv[3] : NULL; - if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + if (!date_is_valid(d1)) { + fprintf(stderr, "오류: 유효하지 않은 시작영업일 '%s'\n", d1); + return 2; + } + if (!date_is_valid(d2)) { + fprintf(stderr, "오류: 유효하지 않은 종료영업일 '%s'\n", d2); + return 2; + } + if (strcmp(d1, d2) > 0) { + fprintf(stderr, "오류: 시작영업일이 종료영업일보다 큽니다 '%s' > '%s'\n", + d1, d2); return 2; } - tx_log(TX_LOG_INFO, "[cl_bt_0014] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cl_bt_0014] 배치 시작 기간=%s~%s", d1, d2); rc = cl_dbio_connect(target); if (rc != TX_OK) { @@ -129,7 +140,7 @@ int main(int argc, char **argv) return 1; } - rc = run_cl_bt_0014(biz_date); + rc = run_cl_bt_0014(d1, d2); cl_dbio_disconnect(); diff --git a/app/batch/cl_bt_0015.c b/app/batch/cl_bt_0015.c index 24f6dfd..c8a5f54 100644 --- a/app/batch/cl_bt_0015.c +++ b/app/batch/cl_bt_0015.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cl_bt_0015.pgc" /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0015.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0015.pgc - 마감 통계 배치 (상태별 통계표) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 상태별(R/M/U/S) 건수와 금액합계를 개별 SELECT 로 조회하고, + * 각 상태의 평균 거래금액(정수 나눗셈 = 합계/건수)을 계산하여 통계표로 + * 출력한다. 전체 평균도 함께 표시한다. 배치 main 엔트리 포함. * * 실행: cl_bt_0015 [db@host] */ @@ -93,121 +94,100 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0015.pgc" +#line 20 "app/batch/cl_bt_0015.pgc" -static int run_cl_bt_0015(const char *biz_date) +static int query_stat_0015(const char *biz_date, const char *status, + long *out_cnt, long *out_sum) { /* exec sql begin declare section */ - - + -#line 24 "app/batch/cl_bt_0015.pgc" +#line 26 "app/batch/cl_bt_0015.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cl_bt_0015.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/cl_bt_0015.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/cl_bt_0015.pgc" - long h_amount ; + char h_status [ 2 ] ; #line 28 "app/batch/cl_bt_0015.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + int h_cnt ; + #line 29 "app/batch/cl_bt_0015.pgc" + long h_sum ; +/* exec sql end declare section */ +#line 30 "app/batch/cl_bt_0015.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, status, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0015 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cl_bt_0015.pgc" - -#line 42 "app/batch/cl_bt_0015.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0015 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date = $1 and status = $2 ", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cl_bt_0015.pgc" + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 41 "app/batch/cl_bt_0015.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0015"); + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 통계 cl_bt_0015"); return TX_EDB; } + *out_cnt = (long)h_cnt; + *out_sum = h_sum; + return TX_OK; +} - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0015] tx_begin 경고"); +static int run_cl_bt_0015(const char *biz_date) +{ + static const char *codes[4] = { "R", "M", "U", "S" }; + static const char *labels[4] = { "접수(R) ", "완료(M) ", + "불일치(U)", "정산(S) " }; + long cnt[4], sum[4], avg; + long tot_cnt = 0, tot_sum = 0, tot_avg = 0; + int i; + char won_sum[32], won_avg[32]; - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0015", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cl_bt_0015.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cl_bt_0015"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + for (i = 0; i < 4; i++) { + if (query_stat_0015(biz_date, codes[i], &cnt[i], &sum[i]) != TX_OK) + return TX_EDB; + tot_cnt += cnt[i]; + tot_sum += sum[i]; } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0015", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0015.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + if (tot_cnt > 0) + tot_avg = tot_sum / tot_cnt; /* 전체 평균(정수 나눗셈) */ tx_log(TX_LOG_INFO, - "[cl_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cl_bt_0015] 요약 date=%s 전체건수=%ld 총액=%ld 전체평균=%ld", + biz_date, tot_cnt, tot_sum, tot_avg); - printf("========== 마감 배치 결과 [cl_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 마감 통계 배치 [cl_bt_0015] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" -----------------------------------------------------\n"); + printf(" %-10s %8s %16s %14s\n", "상태", "건수", "금액합계", "평균"); + printf(" -----------------------------------------------------\n"); + for (i = 0; i < 4; i++) { + avg = (cnt[i] > 0) ? (sum[i] / cnt[i]) : 0; /* 상태별 평균 */ + amount_format_won(sum[i], won_sum, sizeof(won_sum)); + amount_format_won(avg, won_avg, sizeof(won_avg)); + printf(" %-10s %8ld %16s %14s\n", labels[i], cnt[i], won_sum, won_avg); + } + amount_format_won(tot_sum, won_sum, sizeof(won_sum)); + amount_format_won(tot_avg, won_avg, sizeof(won_avg)); + printf(" -----------------------------------------------------\n"); + printf(" %-10s %8ld %16s %14s\n", "합계", tot_cnt, won_sum, won_avg); + printf("===============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/cl_bt_0015.pgc b/app/batch/cl_bt_0015.pgc index fa73e1a..9ff682b 100644 --- a/app/batch/cl_bt_0015.pgc +++ b/app/batch/cl_bt_0015.pgc @@ -1,9 +1,10 @@ /* @tier easy @module cl @transform closing-batch */ /* - * cl_bt_0015.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=easy] + * cl_bt_0015.pgc - 마감 통계 배치 (상태별 통계표) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 상태별(R/M/U/S) 건수와 금액합계를 개별 SELECT 로 조회하고, + * 각 상태의 평균 거래금액(정수 나눗셈 = 합계/건수)을 계산하여 통계표로 + * 출력한다. 전체 평균도 함께 표시한다. 배치 main 엔트리 포함. * * 실행: cl_bt_0015 [db@host] */ @@ -18,87 +19,77 @@ EXEC SQL INCLUDE sqlca; -static int run_cl_bt_0015(const char *biz_date) +static int query_stat_0015(const char *biz_date, const char *status, + long *out_cnt, long *out_sum) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_status[2]; + int h_cnt; + long h_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, status, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; - EXEC SQL DECLARE cur_cl_bt_0015 CURSOR FOR - SELECT key, merch_id, amount + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_cnt, :h_sum FROM cl_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status = :h_status; - EXEC SQL OPEN cur_cl_bt_0015; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0015"); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 통계 cl_bt_0015"); return TX_EDB; } + *out_cnt = (long)h_cnt; + *out_sum = h_sum; + return TX_OK; +} - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0015] tx_begin 경고"); +static int run_cl_bt_0015(const char *biz_date) +{ + static const char *codes[4] = { "R", "M", "U", "S" }; + static const char *labels[4] = { "접수(R) ", "완료(M) ", + "불일치(U)", "정산(S) " }; + long cnt[4], sum[4], avg; + long tot_cnt = 0, tot_sum = 0, tot_avg = 0; + int i; + char won_sum[32], won_avg[32]; - for (;;) { - EXEC SQL FETCH cur_cl_bt_0015 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cl_bt_0015"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + for (i = 0; i < 4; i++) { + if (query_stat_0015(biz_date, codes[i], &cnt[i], &sum[i]) != TX_OK) + return TX_EDB; + tot_cnt += cnt[i]; + tot_sum += sum[i]; } - - EXEC SQL CLOSE cur_cl_bt_0015; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + if (tot_cnt > 0) + tot_avg = tot_sum / tot_cnt; /* 전체 평균(정수 나눗셈) */ tx_log(TX_LOG_INFO, - "[cl_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cl_bt_0015] 요약 date=%s 전체건수=%ld 총액=%ld 전체평균=%ld", + biz_date, tot_cnt, tot_sum, tot_avg); - printf("========== 마감 배치 결과 [cl_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 마감 통계 배치 [cl_bt_0015] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" -----------------------------------------------------\n"); + printf(" %-10s %8s %16s %14s\n", "상태", "건수", "금액합계", "평균"); + printf(" -----------------------------------------------------\n"); + for (i = 0; i < 4; i++) { + avg = (cnt[i] > 0) ? (sum[i] / cnt[i]) : 0; /* 상태별 평균 */ + amount_format_won(sum[i], won_sum, sizeof(won_sum)); + amount_format_won(avg, won_avg, sizeof(won_avg)); + printf(" %-10s %8ld %16s %14s\n", labels[i], cnt[i], won_sum, won_avg); + } + amount_format_won(tot_sum, won_sum, sizeof(won_sum)); + amount_format_won(tot_avg, won_avg, sizeof(won_avg)); + printf(" -----------------------------------------------------\n"); + printf(" %-10s %8ld %16s %14s\n", "합계", tot_cnt, won_sum, won_avg); + printf("===============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/cl_bt_0016.c b/app/batch/cl_bt_0016.c index 0487828..a821d12 100644 --- a/app/batch/cl_bt_0016.c +++ b/app/batch/cl_bt_0016.c @@ -8,10 +8,14 @@ #line 1 "app/batch/cl_bt_0016.pgc" /* @tier medium @module cl @transform closing-batch */ /* - * cl_bt_0016.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=medium] + * cl_bt_0016.pgc - 마감 배치반영 배치 (종합 일일마감) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 종합 일일마감을 2단계로 수행한다. + * 1) 접수('R') 건을 커서 순회하며 금액 유효성으로 처리완료('M')/ + * 불일치('U') 로 반영한다. + * 2) 반영이 끝난 전체 처리완료('M') 건을 정산완료('S')로 일괄 마감 + * (단일 UPDATE)하고 정산액을 집계한다. + * 반영 건수와 마감 건수/정산액을 리포트한다. 배치 main 엔트리 포함. * * 실행: cl_bt_0016 [db@host] */ @@ -93,7 +97,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cl_bt_0016.pgc" +#line 23 "app/batch/cl_bt_0016.pgc" static int run_cl_bt_0016(const char *biz_date) @@ -103,61 +107,64 @@ static int run_cl_bt_0016(const char *biz_date) - -#line 24 "app/batch/cl_bt_0016.pgc" +#line 28 "app/batch/cl_bt_0016.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cl_bt_0016.pgc" +#line 29 "app/batch/cl_bt_0016.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/cl_bt_0016.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/cl_bt_0016.pgc" +#line 30 "app/batch/cl_bt_0016.pgc" long h_amount ; -#line 28 "app/batch/cl_bt_0016.pgc" - char h_new_status [ 2 ] ; +#line 31 "app/batch/cl_bt_0016.pgc" + long h_sum_settle ; /* exec sql end declare section */ -#line 29 "app/batch/cl_bt_0016.pgc" +#line 32 "app/batch/cl_bt_0016.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long recv0, applied_m = 0, applied_u = 0, closed = 0, errcnt = 0; + char won[32]; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cl_bt_0016 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cl_bt_0016.pgc" - -#line 42 "app/batch/cl_bt_0016.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0016 cursor for select key , merch_id , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cl_bt_0016.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0016"); + recv0 = cl_rec_count(biz_date, CL_ST_RECV); + if (recv0 < 0) { + tx_log(TX_LOG_ERROR, "[cl_bt_0016] 접수 건수 조회 실패"); return TX_EDB; } if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[cl_bt_0016] tx_begin 경고"); + /* 1단계: 접수('R') → 처리완료('M')/불일치('U') 반영 */ + ECPGset_var( 0, ( h_biz_date ), __LINE__);\ + /* declare cur_cl_bt_0016 cursor for select key , amount from cl_ledger where biz_date = $1 and status = 'R' order by key */ +#line 56 "app/batch/cl_bt_0016.pgc" + +#line 56 "app/batch/cl_bt_0016.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_bt_0016 cursor for select key , amount from cl_ledger where biz_date = $1 and status = 'R' order by key", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 58 "app/batch/cl_bt_0016.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0016"); + tx_abort(); + return TX_EDB; + } + for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_bt_0016", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cl_bt_0016.pgc" +#line 66 "app/batch/cl_bt_0016.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,44 +175,73 @@ static int run_cl_bt_0016(const char *biz_date) break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; + rc = cl_rec_update_status(h_key, CL_ST_DONE); + if (rc == TX_OK) applied_m++; + else { errcnt++; tx_log(TX_LOG_ERROR, "[cl_bt_0016] M반영 실패 key=%s", h_key); } } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + rc = cl_rec_update_status(h_key, CL_ST_FAIL); + if (rc == TX_OK) applied_u++; + else { errcnt++; tx_log(TX_LOG_ERROR, "[cl_bt_0016] U반영 실패 key=%s", h_key); } } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_bt_0016", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cl_bt_0016.pgc" +#line 87 "app/batch/cl_bt_0016.pgc" + /* 2단계: 전체 처리완료('M') → 정산완료('S') 일괄 마감 */ + if (errcnt == 0) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cl_ledger set status = 'S' , upd_ts = now ( ) where biz_date = $1 and status = 'M'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 94 "app/batch/cl_bt_0016.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE 일괄마감 cl_bt_0016"); + errcnt++; + } else { + closed = sqlca.sqlerrd[2]; + } + } + + /* 정산액 집계 */ + h_sum_settle = 0; + if (errcnt == 0) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_sum_settle),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 111 "app/batch/cl_bt_0016.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 정산집계 cl_bt_0016"); + errcnt++; + } + } + if (errcnt == 0) tx_commit(); else tx_abort(); - tx_log(TX_LOG_INFO, - "[cl_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(h_sum_settle, won, sizeof(won)); - printf("========== 마감 배치 결과 [cl_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[cl_bt_0016] 요약 date=%s 접수=%ld 반영M=%ld 반영U=%ld 마감S=%ld 정산액=%ld 오류=%ld", + biz_date, recv0, applied_m, applied_u, closed, h_sum_settle, errcnt); + + printf("========== 마감 배치반영 배치 [cl_bt_0016] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 접수(R) : %ld\n", recv0); + printf(" 반영 완료(M) : %ld\n", applied_m); + printf(" 반영 불일치(U): %ld\n", applied_u); + printf(" 일괄 마감(S) : %ld\n", closed); + printf(" 정산액 합계 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cl_bt_0016.pgc b/app/batch/cl_bt_0016.pgc index 1d7400b..c510d7a 100644 --- a/app/batch/cl_bt_0016.pgc +++ b/app/batch/cl_bt_0016.pgc @@ -1,9 +1,13 @@ /* @tier medium @module cl @transform closing-batch */ /* - * cl_bt_0016.pgc - 마감 배치 (커서 순회 + 상태전이) [tier=medium] + * cl_bt_0016.pgc - 마감 배치반영 배치 (종합 일일마감) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 종합 일일마감을 2단계로 수행한다. + * 1) 접수('R') 건을 커서 순회하며 금액 유효성으로 처리완료('M')/ + * 불일치('U') 로 반영한다. + * 2) 반영이 끝난 전체 처리완료('M') 건을 정산완료('S')로 일괄 마감 + * (단일 UPDATE)하고 정산액을 집계한다. + * 반영 건수와 마감 건수/정산액을 리포트한다. 배치 main 엔트리 포함. * * 실행: cl_bt_0016 [db@host] */ @@ -23,19 +27,29 @@ static int run_cl_bt_0016(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; + long h_sum_settle; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long recv0, applied_m = 0, applied_u = 0, closed = 0, errcnt = 0; + char won[32]; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + recv0 = cl_rec_count(biz_date, CL_ST_RECV); + if (recv0 < 0) { + tx_log(TX_LOG_ERROR, "[cl_bt_0016] 접수 건수 조회 실패"); + return TX_EDB; + } + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[cl_bt_0016] tx_begin 경고"); + + /* 1단계: 접수('R') → 처리완료('M')/불일치('U') 반영 */ EXEC SQL DECLARE cur_cl_bt_0016 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount FROM cl_ledger WHERE biz_date = :h_biz_date AND status = 'R' @@ -44,14 +58,12 @@ static int run_cl_bt_0016(const char *biz_date) EXEC SQL OPEN cur_cl_bt_0016; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cl_bt_0016"); + tx_abort(); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cl_bt_0016] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_cl_bt_0016 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cl_bt_0016 INTO :h_key, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,42 +73,68 @@ static int run_cl_bt_0016(const char *biz_date) break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CL_ST_DONE, sizeof(h_new_status)); - done++; + rc = cl_rec_update_status(h_key, CL_ST_DONE); + if (rc == TX_OK) applied_m++; + else { errcnt++; tx_log(TX_LOG_ERROR, "[cl_bt_0016] M반영 실패 key=%s", h_key); } } else { - strncpy(h_new_status, CL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cl_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + rc = cl_rec_update_status(h_key, CL_ST_FAIL); + if (rc == TX_OK) applied_u++; + else { errcnt++; tx_log(TX_LOG_ERROR, "[cl_bt_0016] U반영 실패 key=%s", h_key); } } } EXEC SQL CLOSE cur_cl_bt_0016; + /* 2단계: 전체 처리완료('M') → 정산완료('S') 일괄 마감 */ + if (errcnt == 0) { + EXEC SQL UPDATE cl_ledger + SET status = 'S', upd_ts = now() + WHERE biz_date = :h_biz_date + AND status = 'M'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE 일괄마감 cl_bt_0016"); + errcnt++; + } else { + closed = sqlca.sqlerrd[2]; + } + } + + /* 정산액 집계 */ + h_sum_settle = 0; + if (errcnt == 0) { + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_sum_settle + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'S'; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 정산집계 cl_bt_0016"); + errcnt++; + } + } + if (errcnt == 0) tx_commit(); else tx_abort(); - tx_log(TX_LOG_INFO, - "[cl_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(h_sum_settle, won, sizeof(won)); - printf("========== 마감 배치 결과 [cl_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[cl_bt_0016] 요약 date=%s 접수=%ld 반영M=%ld 반영U=%ld 마감S=%ld 정산액=%ld 오류=%ld", + biz_date, recv0, applied_m, applied_u, closed, h_sum_settle, errcnt); + + printf("========== 마감 배치반영 배치 [cl_bt_0016] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 접수(R) : %ld\n", recv0); + printf(" 반영 완료(M) : %ld\n", applied_m); + printf(" 반영 불일치(U): %ld\n", applied_u); + printf(" 일괄 마감(S) : %ld\n", closed); + printf(" 정산액 합계 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/cm_bt_0001.c b/app/batch/cm_bt_0001.c index f150945..b18ae13 100644 --- a/app/batch/cm_bt_0001.c +++ b/app/batch/cm_bt_0001.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cm_bt_0001.pgc" /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0001.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0001.pgc - 공통코드 캐시 정합성 점검 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 공통코드 마스터(cm_code)를 커서로 순회하며 코드값 형식/정렬순번/사용여부 + * 를 검증한다. 형식 위반 건은 경고 로그로 리포트하고, 유효건/위반건을 + * 집계한다. 배치 main 엔트리 포함. * * 실행: cm_bt_0001 [db@host] */ @@ -93,121 +94,117 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0001.pgc" +#line 20 "app/batch/cm_bt_0001.pgc" +/* 코드값 형식 규칙: 공백 아님 + 영숫자 2자리 이상 */ +static int cm_code_format_ok(const char *code) +{ + int i, n = 0; + if (!code || code[0] == '\0') + return 0; + for (i = 0; code[i]; i++) { + unsigned char c = (unsigned char)code[i]; + if (c == ' ') + continue; + if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z'))) + return 0; + n++; + } + return (n >= 2); +} + static int run_cm_bt_0001(const char *biz_date) { /* exec sql begin declare section */ - - + -#line 24 "app/batch/cm_bt_0001.pgc" - char h_biz_date [ 9 ] ; +#line 43 "app/batch/cm_bt_0001.pgc" + char h_grp [ 16 ] ; -#line 25 "app/batch/cm_bt_0001.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/cm_bt_0001.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/cm_bt_0001.pgc" - long h_amount ; - -#line 28 "app/batch/cm_bt_0001.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ -#line 29 "app/batch/cm_bt_0001.pgc" - - - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; - - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0001 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0001.pgc" - -#line 42 "app/batch/cm_bt_0001.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0001 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/cm_bt_0001.pgc" + char h_code [ 16 ] ; + +#line 45 "app/batch/cm_bt_0001.pgc" + char h_use_yn [ 2 ] ; + +#line 46 "app/batch/cm_bt_0001.pgc" + int h_sort ; +/* exec sql end declare section */ +#line 47 "app/batch/cm_bt_0001.pgc" + + + long total = 0, valid = 0, invalid = 0, disabled = 0; + + (void)biz_date; + + /* declare cur_cm_bt_0001 cursor for select grp_cd , code_cd , use_yn , coalesce ( sort_no , 0 ) from cm_code order by grp_cd , sort_no */ +#line 56 "app/batch/cm_bt_0001.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0001 cursor for select grp_cd , code_cd , use_yn , coalesce ( sort_no , 0 ) from cm_code order by grp_cd , sort_no", ECPGt_EOIT, ECPGt_EORT);} +#line 58 "app/batch/cm_bt_0001.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0001"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cm_bt_0001] tx_begin 경고"); - for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0001", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_grp),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_code),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_use_yn),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_sort),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0001.pgc" +#line 66 "app/batch/cm_bt_0001.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0001"); - errcnt++; break; } total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (h_use_yn[0] != 'Y') { + disabled++; + continue; } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0001] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (cm_code_format_ok(h_code)) { + valid++; + } else { + invalid++; + tx_log(TX_LOG_WARN, "[cm_bt_0001] 코드형식 위반 grp=%s code=%s", + h_grp, h_code); } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0001", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0001.pgc" +#line 90 "app/batch/cm_bt_0001.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[cm_bt_0001] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0001] 요약 총=%ld 유효=%ld 위반=%ld 미사용=%ld", + total, valid, invalid, disabled); - printf("========== 공통 배치 결과 [cm_bt_0001] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 공통코드 정합성 점검 [cm_bt_0001] ==========\n"); + printf(" 총 코드수 : %ld\n", total); + printf(" 유효 코드 : %ld\n", valid); + printf(" 형식 위반 : %ld\n", invalid); + printf(" 미사용(N) : %ld\n", disabled); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (invalid == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -230,7 +227,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0001] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0001] 코드점검 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0001.pgc b/app/batch/cm_bt_0001.pgc index 5294241..d6cfe81 100644 --- a/app/batch/cm_bt_0001.pgc +++ b/app/batch/cm_bt_0001.pgc @@ -1,9 +1,10 @@ /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0001.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0001.pgc - 공통코드 캐시 정합성 점검 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 공통코드 마스터(cm_code)를 커서로 순회하며 코드값 형식/정렬순번/사용여부 + * 를 검증한다. 형식 위반 건은 경고 로그로 리포트하고, 유효건/위반건을 + * 집계한다. 배치 main 엔트리 포함. * * 실행: cm_bt_0001 [db@host] */ @@ -18,28 +19,41 @@ EXEC SQL INCLUDE sqlca; +/* 코드값 형식 규칙: 공백 아님 + 영숫자 2자리 이상 */ +static int cm_code_format_ok(const char *code) +{ + int i, n = 0; + if (!code || code[0] == '\0') + return 0; + for (i = 0; code[i]; i++) { + unsigned char c = (unsigned char)code[i]; + if (c == ' ') + continue; + if (!((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z'))) + return 0; + n++; + } + return (n >= 2); +} + static int run_cm_bt_0001(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_grp[16]; + char h_code[16]; + char h_use_yn[2]; + int h_sort; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, valid = 0, invalid = 0, disabled = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + (void)biz_date; EXEC SQL DECLARE cur_cm_bt_0001 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT grp_cd, code_cd, use_yn, COALESCE(sort_no, 0) + FROM cm_code + ORDER BY grp_cd, sort_no; EXEC SQL OPEN cur_cm_bt_0001; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,58 +61,46 @@ static int run_cm_bt_0001(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cm_bt_0001] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_cm_bt_0001 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cm_bt_0001 + INTO :h_grp, :h_code, :h_use_yn, :h_sort; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0001"); - errcnt++; break; } total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (h_use_yn[0] != 'Y') { + disabled++; + continue; } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0001] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (cm_code_format_ok(h_code)) { + valid++; + } else { + invalid++; + tx_log(TX_LOG_WARN, "[cm_bt_0001] 코드형식 위반 grp=%s code=%s", + h_grp, h_code); } } EXEC SQL CLOSE cur_cm_bt_0001; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[cm_bt_0001] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0001] 요약 총=%ld 유효=%ld 위반=%ld 미사용=%ld", + total, valid, invalid, disabled); - printf("========== 공통 배치 결과 [cm_bt_0001] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 공통코드 정합성 점검 [cm_bt_0001] ==========\n"); + printf(" 총 코드수 : %ld\n", total); + printf(" 유효 코드 : %ld\n", valid); + printf(" 형식 위반 : %ld\n", invalid); + printf(" 미사용(N) : %ld\n", disabled); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (invalid == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -121,7 +123,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0001] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0001] 코드점검 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0002.c b/app/batch/cm_bt_0002.c index cfb3b0f..857c725 100644 --- a/app/batch/cm_bt_0002.c +++ b/app/batch/cm_bt_0002.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cm_bt_0002.pgc" /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0002.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0002.pgc - 공통코드 캐시 리로드 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 공통코드 마스터(cm_code)를 그룹코드 순으로 커서 순회하며 그룹별 코드수 + * 를 집계한다. 그룹이 바뀔 때마다 로드 로그를 남기고, 최종적으로 캐시에 + * 적재된 그룹수/코드수를 리포트한다. 배치 main 엔트리 포함. * * 실행: cm_bt_0002 [db@host] */ @@ -93,121 +94,107 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0002.pgc" +#line 20 "app/batch/cm_bt_0002.pgc" +/* 그룹 전환 여부 판정: 직전 그룹키와 다르면 새 그룹 */ +static int cm_group_changed(const char *prev, const char *cur) +{ + if (prev[0] == '\0') + return 1; + return (strcmp(prev, cur) != 0) ? 1 : 0; +} + static int run_cm_bt_0002(const char *biz_date) { /* exec sql begin declare section */ - - -#line 24 "app/batch/cm_bt_0002.pgc" - char h_biz_date [ 9 ] ; +#line 33 "app/batch/cm_bt_0002.pgc" + char h_grp [ 16 ] ; -#line 25 "app/batch/cm_bt_0002.pgc" - char h_key [ 16 ] ; +#line 34 "app/batch/cm_bt_0002.pgc" + char h_code [ 16 ] ; -#line 26 "app/batch/cm_bt_0002.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/cm_bt_0002.pgc" - long h_amount ; - -#line 28 "app/batch/cm_bt_0002.pgc" - char h_new_status [ 2 ] ; +#line 35 "app/batch/cm_bt_0002.pgc" + char h_use_yn [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/cm_bt_0002.pgc" +#line 36 "app/batch/cm_bt_0002.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + char prev_grp[16]; + long groups = 0, codes = 0, active = 0, grp_codes = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + (void)biz_date; + prev_grp[0] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0002 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0002.pgc" - -#line 42 "app/batch/cm_bt_0002.pgc" + /* declare cur_cm_bt_0002 cursor for select grp_cd , code_cd , use_yn from cm_code order by grp_cd , sort_no */ +#line 47 "app/batch/cm_bt_0002.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0002 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0002.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0002 cursor for select grp_cd , code_cd , use_yn from cm_code order by grp_cd , sort_no", ECPGt_EOIT, ECPGt_EORT);} +#line 49 "app/batch/cm_bt_0002.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0002"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cm_bt_0002] tx_begin 경고"); - for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0002", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_grp),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_code),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_use_yn),(long)2,(long)1,(2)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0002.pgc" +#line 57 "app/batch/cm_bt_0002.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0002"); - errcnt++; break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (cm_group_changed(prev_grp, h_grp)) { + if (groups > 0) + tx_log(TX_LOG_INFO, "[cm_bt_0002] 그룹 로드 grp=%s codes=%ld", + prev_grp, grp_codes); + groups++; + grp_codes = 0; + strncpy(prev_grp, h_grp, sizeof(prev_grp) - 1); + prev_grp[sizeof(prev_grp) - 1] = '\0'; } - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + codes++; + grp_codes++; + if (h_use_yn[0] == 'Y') + active++; } + if (groups > 0) + tx_log(TX_LOG_INFO, "[cm_bt_0002] 그룹 로드 grp=%s codes=%ld", + prev_grp, grp_codes); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0002", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0002.pgc" +#line 86 "app/batch/cm_bt_0002.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[cm_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0002] 캐시 리로드 완료 groups=%ld codes=%ld active=%ld", + groups, codes, active); - printf("========== 공통 배치 결과 [cm_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 공통코드 캐시 리로드 [cm_bt_0002] ==========\n"); + printf(" 로드 그룹수 : %ld\n", groups); + printf(" 전체 코드수 : %ld\n", codes); + printf(" 사용중 코드 : %ld\n", active); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (codes > 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -230,7 +217,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0002] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0002] 캐시 리로드 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0002.pgc b/app/batch/cm_bt_0002.pgc index d81509a..923487d 100644 --- a/app/batch/cm_bt_0002.pgc +++ b/app/batch/cm_bt_0002.pgc @@ -1,9 +1,10 @@ /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0002.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0002.pgc - 공통코드 캐시 리로드 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 공통코드 마스터(cm_code)를 그룹코드 순으로 커서 순회하며 그룹별 코드수 + * 를 집계한다. 그룹이 바뀔 때마다 로드 로그를 남기고, 최종적으로 캐시에 + * 적재된 그룹수/코드수를 리포트한다. 배치 main 엔트리 포함. * * 실행: cm_bt_0002 [db@host] */ @@ -18,28 +19,32 @@ EXEC SQL INCLUDE sqlca; +/* 그룹 전환 여부 판정: 직전 그룹키와 다르면 새 그룹 */ +static int cm_group_changed(const char *prev, const char *cur) +{ + if (prev[0] == '\0') + return 1; + return (strcmp(prev, cur) != 0) ? 1 : 0; +} + static int run_cm_bt_0002(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_grp[16]; + char h_code[16]; + char h_use_yn[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + char prev_grp[16]; + long groups = 0, codes = 0, active = 0, grp_codes = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + (void)biz_date; + prev_grp[0] = '\0'; EXEC SQL DECLARE cur_cm_bt_0002 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT grp_cd, code_cd, use_yn + FROM cm_code + ORDER BY grp_cd, sort_no; EXEC SQL OPEN cur_cm_bt_0002; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,58 +52,50 @@ static int run_cm_bt_0002(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cm_bt_0002] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_cm_bt_0002 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cm_bt_0002 + INTO :h_grp, :h_code, :h_use_yn; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0002"); - errcnt++; break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (cm_group_changed(prev_grp, h_grp)) { + if (groups > 0) + tx_log(TX_LOG_INFO, "[cm_bt_0002] 그룹 로드 grp=%s codes=%ld", + prev_grp, grp_codes); + groups++; + grp_codes = 0; + strncpy(prev_grp, h_grp, sizeof(prev_grp) - 1); + prev_grp[sizeof(prev_grp) - 1] = '\0'; } - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + codes++; + grp_codes++; + if (h_use_yn[0] == 'Y') + active++; } + if (groups > 0) + tx_log(TX_LOG_INFO, "[cm_bt_0002] 그룹 로드 grp=%s codes=%ld", + prev_grp, grp_codes); + EXEC SQL CLOSE cur_cm_bt_0002; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[cm_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0002] 캐시 리로드 완료 groups=%ld codes=%ld active=%ld", + groups, codes, active); - printf("========== 공통 배치 결과 [cm_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 공통코드 캐시 리로드 [cm_bt_0002] ==========\n"); + printf(" 로드 그룹수 : %ld\n", groups); + printf(" 전체 코드수 : %ld\n", codes); + printf(" 사용중 코드 : %ld\n", active); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (codes > 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -121,7 +118,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0002] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0002] 캐시 리로드 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0003.c b/app/batch/cm_bt_0003.c index 1205a47..3f0854d 100644 --- a/app/batch/cm_bt_0003.c +++ b/app/batch/cm_bt_0003.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cm_bt_0003.pgc" /* @tier medium @module cm @transform common-batch */ /* - * cm_bt_0003.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=medium] + * cm_bt_0003.pgc - 영업일 캘린더 생성 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 시작 영업일(argv1)부터 연속 31일의 달력을 생성한다. 각 일자를 하루씩 + * 증가시키며 주말/평일 여부를 date_is_weekend 로 판정하고, 결과를 + * cm_calendar 에 raw INSERT 로 적재한다. 영업일/휴일 건수를 리포트한다. * * 실행: cm_bt_0003 [db@host] */ @@ -93,118 +94,110 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0003.pgc" +#line 20 "app/batch/cm_bt_0003.pgc" +#define CM_CAL_DAYS 31 + +/* YYYYMMDD 를 하루 증가시켜 out 에 채운다 (월/년 롤오버 포함) */ +static void cm_date_plus_one(const char *ymd, char *out) +{ + int y, m, d; + int mdays[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + y = (ymd[0]-'0')*1000 + (ymd[1]-'0')*100 + (ymd[2]-'0')*10 + (ymd[3]-'0'); + m = (ymd[4]-'0')*10 + (ymd[5]-'0'); + d = (ymd[6]-'0')*10 + (ymd[7]-'0'); + + if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) + mdays[2] = 29; + + d++; + if (m >= 1 && m <= 12 && d > mdays[m]) { + d = 1; + m++; + if (m > 12) { m = 1; y++; } + } + sprintf(out, "%04d%02d%02d", y, m, d); +} + static int run_cm_bt_0003(const char *biz_date) { /* exec sql begin declare section */ - - - + -#line 24 "app/batch/cm_bt_0003.pgc" - char h_biz_date [ 9 ] ; +#line 49 "app/batch/cm_bt_0003.pgc" + char h_cal_date [ 9 ] ; -#line 25 "app/batch/cm_bt_0003.pgc" - char h_key [ 16 ] ; +#line 50 "app/batch/cm_bt_0003.pgc" + char h_day_type [ 2 ] ; -#line 26 "app/batch/cm_bt_0003.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/cm_bt_0003.pgc" - long h_amount ; - -#line 28 "app/batch/cm_bt_0003.pgc" - char h_new_status [ 2 ] ; +#line 51 "app/batch/cm_bt_0003.pgc" + int h_week_day ; /* exec sql end declare section */ -#line 29 "app/batch/cm_bt_0003.pgc" +#line 52 "app/batch/cm_bt_0003.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + char cur_date[9], next_date[9]; + long biz_cnt = 0, holi_cnt = 0, errcnt = 0; + int i; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0003 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0003.pgc" - -#line 42 "app/batch/cm_bt_0003.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0003 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0003.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0003"); - return TX_EDB; - } + strncpy(cur_date, biz_date, sizeof(cur_date) - 1); + cur_date[sizeof(cur_date) - 1] = '\0'; if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[cm_bt_0003] tx_begin 경고"); - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0003", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0003.pgc" + for (i = 0; i < CM_CAL_DAYS; i++) { + strncpy(h_cal_date, cur_date, sizeof(h_cal_date) - 1); + h_cal_date[sizeof(h_cal_date) - 1] = '\0'; + h_week_day = date_weekday(cur_date); - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0003"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; + if (date_is_weekend(cur_date)) { + strncpy(h_day_type, "H", sizeof(h_day_type)); + holi_cnt++; } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + strncpy(h_day_type, "B", sizeof(h_day_type)); + biz_cnt++; } - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into cm_calendar ( cal_date , day_type , week_day ) values ( $1 , $2 , $3 )", + ECPGt_char,(h_cal_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_day_type),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_week_day),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 78 "app/batch/cm_bt_0003.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT cm_calendar"); errcnt++; } + + cm_date_plus_one(cur_date, next_date); + strncpy(cur_date, next_date, sizeof(cur_date) - 1); + cur_date[sizeof(cur_date) - 1] = '\0'; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0003", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0003.pgc" - - if (errcnt == 0) tx_commit(); else tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0003] 캘린더 생성 시작=%s 일수=%d 영업일=%ld 휴일=%ld 오류=%ld", + biz_date, CM_CAL_DAYS, biz_cnt, holi_cnt, errcnt); - printf("========== 공통 배치 결과 [cm_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 영업일 캘린더 생성 [cm_bt_0003] ==========\n"); + printf(" 시작 영업일 : %s\n", biz_date); + printf(" 생성 일수 : %d\n", CM_CAL_DAYS); + printf(" 영업일 수 : %ld\n", biz_cnt); + printf(" 휴일 수 : %ld\n", holi_cnt); + printf(" 적재 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -230,7 +223,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0003] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0003] 캘린더 생성 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0003.pgc b/app/batch/cm_bt_0003.pgc index 722032c..9f90ae3 100644 --- a/app/batch/cm_bt_0003.pgc +++ b/app/batch/cm_bt_0003.pgc @@ -1,9 +1,10 @@ /* @tier medium @module cm @transform common-batch */ /* - * cm_bt_0003.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=medium] + * cm_bt_0003.pgc - 영업일 캘린더 생성 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 시작 영업일(argv1)부터 연속 31일의 달력을 생성한다. 각 일자를 하루씩 + * 증가시키며 주말/평일 여부를 date_is_weekend 로 판정하고, 결과를 + * cm_calendar 에 raw INSERT 로 적재한다. 영업일/휴일 건수를 리포트한다. * * 실행: cm_bt_0003 [db@host] */ @@ -18,68 +19,73 @@ EXEC SQL INCLUDE sqlca; +#define CM_CAL_DAYS 31 + +/* YYYYMMDD 를 하루 증가시켜 out 에 채운다 (월/년 롤오버 포함) */ +static void cm_date_plus_one(const char *ymd, char *out) +{ + int y, m, d; + int mdays[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + + y = (ymd[0]-'0')*1000 + (ymd[1]-'0')*100 + (ymd[2]-'0')*10 + (ymd[3]-'0'); + m = (ymd[4]-'0')*10 + (ymd[5]-'0'); + d = (ymd[6]-'0')*10 + (ymd[7]-'0'); + + if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) + mdays[2] = 29; + + d++; + if (m >= 1 && m <= 12 && d > mdays[m]) { + d = 1; + m++; + if (m > 12) { m = 1; y++; } + } + sprintf(out, "%04d%02d%02d", y, m, d); +} + static int run_cm_bt_0003(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_cal_date[9]; + char h_day_type[2]; + int h_week_day; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + char cur_date[9], next_date[9]; + long biz_cnt = 0, holi_cnt = 0, errcnt = 0; + int i; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - EXEC SQL DECLARE cur_cm_bt_0003 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_cm_bt_0003; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0003"); - return TX_EDB; - } + strncpy(cur_date, biz_date, sizeof(cur_date) - 1); + cur_date[sizeof(cur_date) - 1] = '\0'; if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[cm_bt_0003] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_cm_bt_0003 INTO :h_key, :h_merch_id, :h_amount; + for (i = 0; i < CM_CAL_DAYS; i++) { + strncpy(h_cal_date, cur_date, sizeof(h_cal_date) - 1); + h_cal_date[sizeof(h_cal_date) - 1] = '\0'; + h_week_day = date_weekday(cur_date); - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0003"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; + if (date_is_weekend(cur_date)) { + strncpy(h_day_type, "H", sizeof(h_day_type)); + holi_cnt++; } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + strncpy(h_day_type, "B", sizeof(h_day_type)); + biz_cnt++; } - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL INSERT INTO cm_calendar (cal_date, day_type, week_day) + VALUES (:h_cal_date, :h_day_type, :h_week_day); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT cm_calendar"); errcnt++; } - } - EXEC SQL CLOSE cur_cm_bt_0003; + cm_date_plus_one(cur_date, next_date); + strncpy(cur_date, next_date, sizeof(cur_date) - 1); + cur_date[sizeof(cur_date) - 1] = '\0'; + } if (errcnt == 0) tx_commit(); @@ -87,15 +93,15 @@ static int run_cm_bt_0003(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0003] 캘린더 생성 시작=%s 일수=%d 영업일=%ld 휴일=%ld 오류=%ld", + biz_date, CM_CAL_DAYS, biz_cnt, holi_cnt, errcnt); - printf("========== 공통 배치 결과 [cm_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 영업일 캘린더 생성 [cm_bt_0003] ==========\n"); + printf(" 시작 영업일 : %s\n", biz_date); + printf(" 생성 일수 : %d\n", CM_CAL_DAYS); + printf(" 영업일 수 : %ld\n", biz_cnt); + printf(" 휴일 수 : %ld\n", holi_cnt); + printf(" 적재 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -121,7 +127,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0003] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0003] 캘린더 생성 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0004.c b/app/batch/cm_bt_0004.c index a590b73..6b2693f 100644 --- a/app/batch/cm_bt_0004.c +++ b/app/batch/cm_bt_0004.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cm_bt_0004.pgc" /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0004.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0004.pgc - 순번(채번) 리셋 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 채번 마스터(cm_seq)를 커서로 순회하며 일 리셋주기('D') 채번키의 당일 + * 순번을 0 으로 raw UPDATE 하여 초기화한다. 리셋된 채번키 수와 건너뛴 + * (월/연 주기) 채번키 수를 리포트한다. 배치 main 엔트리 포함. * * 실행: cm_bt_0004 [db@host] */ @@ -93,53 +94,44 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0004.pgc" +#line 20 "app/batch/cm_bt_0004.pgc" +/* 리셋주기 문자로 일 리셋 대상 여부 판정 ('D'=일단위) */ +static int cm_seq_is_daily(const char *cycle) +{ + return (cycle && (cycle[0] == 'D' || cycle[0] == 'd')) ? 1 : 0; +} + static int run_cm_bt_0004(const char *biz_date) { /* exec sql begin declare section */ - - -#line 24 "app/batch/cm_bt_0004.pgc" - char h_biz_date [ 9 ] ; +#line 31 "app/batch/cm_bt_0004.pgc" + char h_seq_key [ 16 ] ; -#line 25 "app/batch/cm_bt_0004.pgc" - char h_key [ 16 ] ; +#line 32 "app/batch/cm_bt_0004.pgc" + char h_cycle [ 2 ] ; -#line 26 "app/batch/cm_bt_0004.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/cm_bt_0004.pgc" - long h_amount ; - -#line 28 "app/batch/cm_bt_0004.pgc" - char h_new_status [ 2 ] ; +#line 33 "app/batch/cm_bt_0004.pgc" + long h_cur_val ; /* exec sql end declare section */ -#line 29 "app/batch/cm_bt_0004.pgc" +#line 34 "app/batch/cm_bt_0004.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, reset_cnt = 0, skipped = 0, errcnt = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + (void)biz_date; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0004 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0004.pgc" - -#line 42 "app/batch/cm_bt_0004.pgc" + /* declare cur_cm_bt_0004 cursor for select seq_key , reset_cycle , coalesce ( cur_val , 0 ) from cm_seq order by seq_key */ +#line 43 "app/batch/cm_bt_0004.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0004 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0004.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0004 cursor for select seq_key , reset_cycle , coalesce ( cur_val , 0 ) from cm_seq order by seq_key", ECPGt_EOIT, ECPGt_EORT);} +#line 45 "app/batch/cm_bt_0004.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0004"); @@ -151,13 +143,13 @@ static int run_cm_bt_0004(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0004", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_seq_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_cycle),(long)2,(long)1,(2)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_cur_val),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0004.pgc" +#line 55 "app/batch/cm_bt_0004.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +162,29 @@ static int run_cm_bt_0004(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (!cm_seq_is_daily(h_cycle)) { + skipped++; + continue; } - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cm_seq set cur_val = 0 where seq_key = $1 ", + ECPGt_char,(h_seq_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 74 "app/batch/cm_bt_0004.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cm_seq"); errcnt++; + continue; } + reset_cnt++; + tx_log(TX_LOG_DEBUG, "[cm_bt_0004] 순번리셋 key=%s prev=%ld", + h_seq_key, h_cur_val); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0004", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0004.pgc" +#line 86 "app/batch/cm_bt_0004.pgc" if (errcnt == 0) @@ -196,14 +193,13 @@ static int run_cm_bt_0004(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0004] 순번리셋 총=%ld 리셋=%ld 건너뜀=%ld 오류=%ld", + total, reset_cnt, skipped, errcnt); - printf("========== 공통 배치 결과 [cm_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf("========== 순번 리셋 배치 [cm_bt_0004] ==========\n"); + printf(" 전체 채번키 : %ld\n", total); + printf(" 리셋 채번키 : %ld\n", reset_cnt); + printf(" 대상외(월/연): %ld\n", skipped); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); @@ -230,7 +226,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0004] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0004] 순번리셋 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0004.pgc b/app/batch/cm_bt_0004.pgc index 4253f81..c249012 100644 --- a/app/batch/cm_bt_0004.pgc +++ b/app/batch/cm_bt_0004.pgc @@ -1,9 +1,10 @@ /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0004.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0004.pgc - 순번(채번) 리셋 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 채번 마스터(cm_seq)를 커서로 순회하며 일 리셋주기('D') 채번키의 당일 + * 순번을 0 으로 raw UPDATE 하여 초기화한다. 리셋된 채번키 수와 건너뛴 + * (월/연 주기) 채번키 수를 리포트한다. 배치 main 엔트리 포함. * * 실행: cm_bt_0004 [db@host] */ @@ -18,28 +19,28 @@ EXEC SQL INCLUDE sqlca; +/* 리셋주기 문자로 일 리셋 대상 여부 판정 ('D'=일단위) */ +static int cm_seq_is_daily(const char *cycle) +{ + return (cycle && (cycle[0] == 'D' || cycle[0] == 'd')) ? 1 : 0; +} + static int run_cm_bt_0004(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_seq_key[16]; + char h_cycle[2]; + long h_cur_val; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, reset_cnt = 0, skipped = 0, errcnt = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + (void)biz_date; EXEC SQL DECLARE cur_cm_bt_0004 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT seq_key, reset_cycle, COALESCE(cur_val, 0) + FROM cm_seq + ORDER BY seq_key; EXEC SQL OPEN cur_cm_bt_0004; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +52,7 @@ static int run_cm_bt_0004(const char *biz_date) tx_log(TX_LOG_WARN, "[cm_bt_0004] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_cm_bt_0004 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cm_bt_0004 INTO :h_seq_key, :h_cycle, :h_cur_val; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +64,23 @@ static int run_cm_bt_0004(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (!cm_seq_is_daily(h_cycle)) { + skipped++; + continue; } - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL UPDATE cm_seq + SET cur_val = 0 + WHERE seq_key = :h_seq_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cm_seq"); errcnt++; + continue; } + reset_cnt++; + tx_log(TX_LOG_DEBUG, "[cm_bt_0004] 순번리셋 key=%s prev=%ld", + h_seq_key, h_cur_val); } EXEC SQL CLOSE cur_cm_bt_0004; @@ -87,14 +91,13 @@ static int run_cm_bt_0004(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0004] 순번리셋 총=%ld 리셋=%ld 건너뜀=%ld 오류=%ld", + total, reset_cnt, skipped, errcnt); - printf("========== 공통 배치 결과 [cm_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf("========== 순번 리셋 배치 [cm_bt_0004] ==========\n"); + printf(" 전체 채번키 : %ld\n", total); + printf(" 리셋 채번키 : %ld\n", reset_cnt); + printf(" 대상외(월/연): %ld\n", skipped); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); @@ -121,7 +124,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0004] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0004] 순번리셋 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0005.c b/app/batch/cm_bt_0005.c index dfdb903..d8c6ee9 100644 --- a/app/batch/cm_bt_0005.c +++ b/app/batch/cm_bt_0005.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cm_bt_0005.pgc" /* @tier medium @module cm @transform common-batch */ /* - * cm_bt_0005.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=medium] + * cm_bt_0005.pgc - 공통 로그 아카이빙 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정일(argv1)의 공통 로그(cm_log)를 커서로 순회한다. 보존대상(WARN + * 이상) 로그는 아카이브 테이블(cm_log_arch)로 이관 후 원본을 삭제하고, + * 그 외 하위 로그는 즉시 삭제(purge)한다. 이관/삭제 건수를 리포트한다. * * 실행: cm_bt_0005 [db@host] */ @@ -93,53 +94,52 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0005.pgc" +#line 20 "app/batch/cm_bt_0005.pgc" +/* 로그레벨 문자로 아카이브 보존 대상 여부 판정 (W/E = 보존) */ +static int cm_log_keep(const char *level) +{ + if (!level) + return 0; + return (level[0] == 'W' || level[0] == 'E') ? 1 : 0; +} + static int run_cm_bt_0005(const char *biz_date) { /* exec sql begin declare section */ - - -#line 24 "app/batch/cm_bt_0005.pgc" - char h_biz_date [ 9 ] ; +#line 33 "app/batch/cm_bt_0005.pgc" + char h_log_date [ 9 ] ; -#line 25 "app/batch/cm_bt_0005.pgc" - char h_key [ 16 ] ; +#line 34 "app/batch/cm_bt_0005.pgc" + long h_log_id ; -#line 26 "app/batch/cm_bt_0005.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/cm_bt_0005.pgc" - long h_amount ; - -#line 28 "app/batch/cm_bt_0005.pgc" - char h_new_status [ 2 ] ; +#line 35 "app/batch/cm_bt_0005.pgc" + char h_level [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/cm_bt_0005.pgc" +#line 36 "app/batch/cm_bt_0005.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, archived = 0, purged = 0, errcnt = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_log_date, biz_date, sizeof(h_log_date) - 1); + h_log_date[sizeof(h_log_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0005 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0005.pgc" + ECPGset_var( 0, ( h_log_date ), __LINE__);\ + /* declare cur_cm_bt_0005 cursor for select log_id , log_level from cm_log where log_date = $1 order by log_id */ +#line 47 "app/batch/cm_bt_0005.pgc" -#line 42 "app/batch/cm_bt_0005.pgc" +#line 47 "app/batch/cm_bt_0005.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0005 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0005 cursor for select log_id , log_level from cm_log where log_date = $1 order by log_id", + ECPGt_char,(h_log_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0005.pgc" +#line 49 "app/batch/cm_bt_0005.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0005"); @@ -151,13 +151,11 @@ static int run_cm_bt_0005(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0005", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_log_id),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_level),(long)2,(long)1,(2)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0005.pgc" +#line 59 "app/batch/cm_bt_0005.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +168,35 @@ static int run_cm_bt_0005(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; + if (cm_log_keep(h_level)) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into cm_log_arch ( log_id , log_date , log_level ) select log_id , log_date , log_level from cm_log where log_id = $1 ", + ECPGt_long,&(h_log_id),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 75 "app/batch/cm_bt_0005.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT cm_log_arch"); + errcnt++; + continue; + } + archived++; } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + purged++; } - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "delete from cm_log where log_id = $1 ", + ECPGt_long,&(h_log_id),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 86 "app/batch/cm_bt_0005.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("DELETE cm_log"); errcnt++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0005", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0005.pgc" +#line 93 "app/batch/cm_bt_0005.pgc" if (errcnt == 0) @@ -196,14 +205,14 @@ static int run_cm_bt_0005(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0005] 로그아카이빙 date=%s 총=%ld 이관=%ld 삭제=%ld 오류=%ld", + biz_date, total, archived, purged, errcnt); - printf("========== 공통 배치 결과 [cm_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf("========== 공통 로그 아카이빙 [cm_bt_0005] ==========\n"); + printf(" 대상 일자 : %s\n", biz_date); + printf(" 전체 로그 : %ld\n", total); + printf(" 아카이브 : %ld\n", archived); + printf(" 즉시 삭제 : %ld\n", purged); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); @@ -230,7 +239,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0005] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0005] 로그아카이빙 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0005.pgc b/app/batch/cm_bt_0005.pgc index 08b75a2..d364268 100644 --- a/app/batch/cm_bt_0005.pgc +++ b/app/batch/cm_bt_0005.pgc @@ -1,9 +1,10 @@ /* @tier medium @module cm @transform common-batch */ /* - * cm_bt_0005.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=medium] + * cm_bt_0005.pgc - 공통 로그 아카이빙 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정일(argv1)의 공통 로그(cm_log)를 커서로 순회한다. 보존대상(WARN + * 이상) 로그는 아카이브 테이블(cm_log_arch)로 이관 후 원본을 삭제하고, + * 그 외 하위 로그는 즉시 삭제(purge)한다. 이관/삭제 건수를 리포트한다. * * 실행: cm_bt_0005 [db@host] */ @@ -18,28 +19,32 @@ EXEC SQL INCLUDE sqlca; +/* 로그레벨 문자로 아카이브 보존 대상 여부 판정 (W/E = 보존) */ +static int cm_log_keep(const char *level) +{ + if (!level) + return 0; + return (level[0] == 'W' || level[0] == 'E') ? 1 : 0; +} + static int run_cm_bt_0005(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_log_date[9]; + long h_log_id; + char h_level[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, archived = 0, purged = 0, errcnt = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_log_date, biz_date, sizeof(h_log_date) - 1); + h_log_date[sizeof(h_log_date) - 1] = '\0'; EXEC SQL DECLARE cur_cm_bt_0005 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT log_id, log_level + FROM cm_log + WHERE log_date = :h_log_date + ORDER BY log_id; EXEC SQL OPEN cur_cm_bt_0005; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +56,7 @@ static int run_cm_bt_0005(const char *biz_date) tx_log(TX_LOG_WARN, "[cm_bt_0005] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_cm_bt_0005 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cm_bt_0005 INTO :h_log_id, :h_level; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,18 +68,24 @@ static int run_cm_bt_0005(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; + if (cm_log_keep(h_level)) { + EXEC SQL INSERT INTO cm_log_arch (log_id, log_date, log_level) + SELECT log_id, log_date, log_level + FROM cm_log + WHERE log_id = :h_log_id; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT cm_log_arch"); + errcnt++; + continue; + } + archived++; } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + purged++; } - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL DELETE FROM cm_log WHERE log_id = :h_log_id; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("DELETE cm_log"); errcnt++; } } @@ -87,14 +98,14 @@ static int run_cm_bt_0005(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0005] 로그아카이빙 date=%s 총=%ld 이관=%ld 삭제=%ld 오류=%ld", + biz_date, total, archived, purged, errcnt); - printf("========== 공통 배치 결과 [cm_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf("========== 공통 로그 아카이빙 [cm_bt_0005] ==========\n"); + printf(" 대상 일자 : %s\n", biz_date); + printf(" 전체 로그 : %ld\n", total); + printf(" 아카이브 : %ld\n", archived); + printf(" 즉시 삭제 : %ld\n", purged); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); @@ -121,7 +132,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0005] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0005] 로그아카이빙 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0006.c b/app/batch/cm_bt_0006.c index e17f669..200612e 100644 --- a/app/batch/cm_bt_0006.c +++ b/app/batch/cm_bt_0006.c @@ -8,10 +8,12 @@ #line 1 "app/batch/cm_bt_0006.pgc" /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0006.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0006.pgc - 세션 만료 정리 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 활성 세션(cm_session, status='A') 중 만료일자가 기준 영업일(argv1)을 + * 지난 세션을 커서로 순회한다. 만료일이 지난 세션은 만료('E')로 raw + * UPDATE 하고, 30일 이상 경과한 세션은 완전 삭제한다. 정리 세션수를 + * 리포트한다. 배치 main 엔트리 포함. * * 실행: cm_bt_0006 [db@host] */ @@ -93,53 +95,59 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0006.pgc" +#line 21 "app/batch/cm_bt_0006.pgc" +/* YYYYMMDD 두 일자의 경과일 근사치(월 30일 기준) 반환 */ +static long cm_days_between(const char *from_ymd, const char *to_ymd) +{ + long a, b; + a = (from_ymd[0]-'0')*10000000L + (from_ymd[1]-'0')*1000000L + + (from_ymd[2]-'0')*100000L + (from_ymd[3]-'0')*10000L + + (from_ymd[4]-'0')*3000L + (from_ymd[5]-'0')*300L + + (from_ymd[6]-'0')*10L + (from_ymd[7]-'0'); + b = (to_ymd[0]-'0')*10000000L + (to_ymd[1]-'0')*1000000L + + (to_ymd[2]-'0')*100000L + (to_ymd[3]-'0')*10000L + + (to_ymd[4]-'0')*3000L + (to_ymd[5]-'0')*300L + + (to_ymd[6]-'0')*10L + (to_ymd[7]-'0'); + return (b - a); +} + static int run_cm_bt_0006(const char *biz_date) { /* exec sql begin declare section */ - - -#line 24 "app/batch/cm_bt_0006.pgc" +#line 41 "app/batch/cm_bt_0006.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cm_bt_0006.pgc" - char h_key [ 16 ] ; +#line 42 "app/batch/cm_bt_0006.pgc" + char h_sess_id [ 24 ] ; -#line 26 "app/batch/cm_bt_0006.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/cm_bt_0006.pgc" - long h_amount ; - -#line 28 "app/batch/cm_bt_0006.pgc" - char h_new_status [ 2 ] ; +#line 43 "app/batch/cm_bt_0006.pgc" + char h_open_date [ 9 ] ; /* exec sql end declare section */ -#line 29 "app/batch/cm_bt_0006.pgc" +#line 44 "app/batch/cm_bt_0006.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, expired = 0, deleted = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0006 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0006.pgc" + /* declare cur_cm_bt_0006 cursor for select sess_id , open_date from cm_session where status = 'A' and expire_date <= $1 order by sess_id */ +#line 56 "app/batch/cm_bt_0006.pgc" -#line 42 "app/batch/cm_bt_0006.pgc" +#line 56 "app/batch/cm_bt_0006.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0006 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0006 cursor for select sess_id , open_date from cm_session where status = 'A' and expire_date <= $1 order by sess_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0006.pgc" +#line 58 "app/batch/cm_bt_0006.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0006"); @@ -151,13 +159,11 @@ static int run_cm_bt_0006(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0006", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_sess_id),(long)24,(long)1,(24)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_open_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0006.pgc" +#line 68 "app/batch/cm_bt_0006.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +176,35 @@ static int run_cm_bt_0006(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + if (cm_days_between(h_open_date, h_biz_date) >= 30) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "delete from cm_session where sess_id = $1 ", + ECPGt_char,(h_sess_id),(long)24,(long)1,(24)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 81 "app/batch/cm_bt_0006.pgc" - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("DELETE cm_session"); + errcnt++; + continue; + } + deleted++; + } else { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cm_session set status = 'E' where sess_id = $1 ", + ECPGt_char,(h_sess_id),(long)24,(long)1,(24)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 91 "app/batch/cm_bt_0006.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cm_session"); + errcnt++; + continue; + } + expired++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0006", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0006.pgc" +#line 101 "app/batch/cm_bt_0006.pgc" if (errcnt == 0) @@ -196,14 +213,14 @@ static int run_cm_bt_0006(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0006] 세션정리 기준=%s 총=%ld 만료=%ld 삭제=%ld 오류=%ld", + biz_date, total, expired, deleted, errcnt); - printf("========== 공통 배치 결과 [cm_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf("========== 세션 만료 정리 [cm_bt_0006] ==========\n"); + printf(" 기준 영업일 : %s\n", biz_date); + printf(" 정리 세션 : %ld\n", total); + printf(" 만료처리(E) : %ld\n", expired); + printf(" 완전삭제 : %ld\n", deleted); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); @@ -230,7 +247,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0006] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0006] 세션정리 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0006.pgc b/app/batch/cm_bt_0006.pgc index c1c6986..bf38d62 100644 --- a/app/batch/cm_bt_0006.pgc +++ b/app/batch/cm_bt_0006.pgc @@ -1,9 +1,11 @@ /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0006.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0006.pgc - 세션 만료 정리 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 활성 세션(cm_session, status='A') 중 만료일자가 기준 영업일(argv1)을 + * 지난 세션을 커서로 순회한다. 만료일이 지난 세션은 만료('E')로 raw + * UPDATE 하고, 30일 이상 경과한 세션은 완전 삭제한다. 정리 세션수를 + * 리포트한다. 배치 main 엔트리 포함. * * 실행: cm_bt_0006 [db@host] */ @@ -18,28 +20,40 @@ EXEC SQL INCLUDE sqlca; +/* YYYYMMDD 두 일자의 경과일 근사치(월 30일 기준) 반환 */ +static long cm_days_between(const char *from_ymd, const char *to_ymd) +{ + long a, b; + a = (from_ymd[0]-'0')*10000000L + (from_ymd[1]-'0')*1000000L + + (from_ymd[2]-'0')*100000L + (from_ymd[3]-'0')*10000L + + (from_ymd[4]-'0')*3000L + (from_ymd[5]-'0')*300L + + (from_ymd[6]-'0')*10L + (from_ymd[7]-'0'); + b = (to_ymd[0]-'0')*10000000L + (to_ymd[1]-'0')*1000000L + + (to_ymd[2]-'0')*100000L + (to_ymd[3]-'0')*10000L + + (to_ymd[4]-'0')*3000L + (to_ymd[5]-'0')*300L + + (to_ymd[6]-'0')*10L + (to_ymd[7]-'0'); + return (b - a); +} + static int run_cm_bt_0006(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_sess_id[24]; + char h_open_date[9]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, expired = 0, deleted = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_cm_bt_0006 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT sess_id, open_date + FROM cm_session + WHERE status = 'A' + AND expire_date <= :h_biz_date + ORDER BY sess_id; EXEC SQL OPEN cur_cm_bt_0006; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +65,7 @@ static int run_cm_bt_0006(const char *biz_date) tx_log(TX_LOG_WARN, "[cm_bt_0006] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_cm_bt_0006 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cm_bt_0006 INTO :h_sess_id, :h_open_date; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,19 +77,24 @@ static int run_cm_bt_0006(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; + if (cm_days_between(h_open_date, h_biz_date) >= 30) { + EXEC SQL DELETE FROM cm_session WHERE sess_id = :h_sess_id; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("DELETE cm_session"); + errcnt++; + continue; + } + deleted++; } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + EXEC SQL UPDATE cm_session + SET status = 'E' + WHERE sess_id = :h_sess_id; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cm_session"); + errcnt++; + continue; + } + expired++; } } @@ -87,14 +106,14 @@ static int run_cm_bt_0006(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0006] 세션정리 기준=%s 총=%ld 만료=%ld 삭제=%ld 오류=%ld", + biz_date, total, expired, deleted, errcnt); - printf("========== 공통 배치 결과 [cm_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf("========== 세션 만료 정리 [cm_bt_0006] ==========\n"); + printf(" 기준 영업일 : %s\n", biz_date); + printf(" 정리 세션 : %ld\n", total); + printf(" 만료처리(E) : %ld\n", expired); + printf(" 완전삭제 : %ld\n", deleted); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); @@ -121,7 +140,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0006] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0006] 세션정리 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0007.c b/app/batch/cm_bt_0007.c index f626d61..04fc2bb 100644 --- a/app/batch/cm_bt_0007.c +++ b/app/batch/cm_bt_0007.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cm_bt_0007.pgc" /* @tier medium @module cm @transform common-batch */ /* - * cm_bt_0007.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=medium] + * cm_bt_0007.pgc - 공통 파라미터 스냅샷 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 운영 파라미터(cm_param) 전량을 커서로 읽어 키/값을 순서대로 해시에 + * 반영하여 스냅샷 해시와 버전을 산출한다. 파라미터 개수와 산출된 + * 스냅샷 버전을 리포트한다(무결성 비교용). 배치 main 엔트리 포함. * * 실행: cm_bt_0007 [db@host] */ @@ -93,121 +94,91 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0007.pgc" +#line 20 "app/batch/cm_bt_0007.pgc" +/* djb2 계열 해시 누적: 문자열을 기존 해시값에 반영 */ +static unsigned long cm_hash_acc(unsigned long h, const char *s) +{ + int i; + if (h == 0UL) + h = 5381UL; + for (i = 0; s[i]; i++) + h = ((h << 5) + h) + (unsigned char)s[i]; + return h; +} + static int run_cm_bt_0007(const char *biz_date) { /* exec sql begin declare section */ - - - -#line 24 "app/batch/cm_bt_0007.pgc" - char h_biz_date [ 9 ] ; +#line 36 "app/batch/cm_bt_0007.pgc" + char h_param_key [ 32 ] ; -#line 25 "app/batch/cm_bt_0007.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/cm_bt_0007.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/cm_bt_0007.pgc" - long h_amount ; - -#line 28 "app/batch/cm_bt_0007.pgc" - char h_new_status [ 2 ] ; +#line 37 "app/batch/cm_bt_0007.pgc" + char h_param_val [ 64 ] ; /* exec sql end declare section */ -#line 29 "app/batch/cm_bt_0007.pgc" +#line 38 "app/batch/cm_bt_0007.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + unsigned long snap = 0UL; + long params = 0; + long version; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + (void)biz_date; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0007 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0007.pgc" - -#line 42 "app/batch/cm_bt_0007.pgc" + /* declare cur_cm_bt_0007 cursor for select param_key , coalesce ( param_val , '' ) from cm_param order by param_key */ +#line 49 "app/batch/cm_bt_0007.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0007 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0007.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0007 cursor for select param_key , coalesce ( param_val , '' ) from cm_param order by param_key", ECPGt_EOIT, ECPGt_EORT);} +#line 51 "app/batch/cm_bt_0007.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0007"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cm_bt_0007] tx_begin 경고"); - for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0007", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_param_key),(long)32,(long)1,(32)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_param_val),(long)64,(long)1,(64)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0007.pgc" +#line 58 "app/batch/cm_bt_0007.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0007"); - errcnt++; break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + params++; + snap = cm_hash_acc(snap, h_param_key); + snap = cm_hash_acc(snap, h_param_val); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0007", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0007.pgc" +#line 72 "app/batch/cm_bt_0007.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + version = (long)(snap % 1000000UL); tx_log(TX_LOG_INFO, - "[cm_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0007] 파라미터 스냅샷 params=%ld hash=%lu version=%06ld", + params, snap, version); - printf("========== 공통 배치 결과 [cm_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 파라미터 스냅샷 [cm_bt_0007] ==========\n"); + printf(" 파라미터 수 : %ld\n", params); + printf(" 스냅샷 해시 : %lu\n", snap); + printf(" 스냅샷 버전 : %06ld\n", version); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (params > 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -230,7 +201,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0007] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0007] 파라미터 스냅샷 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0007.pgc b/app/batch/cm_bt_0007.pgc index f8235a1..728949c 100644 --- a/app/batch/cm_bt_0007.pgc +++ b/app/batch/cm_bt_0007.pgc @@ -1,9 +1,10 @@ /* @tier medium @module cm @transform common-batch */ /* - * cm_bt_0007.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=medium] + * cm_bt_0007.pgc - 공통 파라미터 스냅샷 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 운영 파라미터(cm_param) 전량을 커서로 읽어 키/값을 순서대로 해시에 + * 반영하여 스냅샷 해시와 버전을 산출한다. 파라미터 개수와 산출된 + * 스냅샷 버전을 리포트한다(무결성 비교용). 배치 main 엔트리 포함. * * 실행: cm_bt_0007 [db@host] */ @@ -18,28 +19,34 @@ EXEC SQL INCLUDE sqlca; +/* djb2 계열 해시 누적: 문자열을 기존 해시값에 반영 */ +static unsigned long cm_hash_acc(unsigned long h, const char *s) +{ + int i; + if (h == 0UL) + h = 5381UL; + for (i = 0; s[i]; i++) + h = ((h << 5) + h) + (unsigned char)s[i]; + return h; +} + static int run_cm_bt_0007(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_param_key[32]; + char h_param_val[64]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + unsigned long snap = 0UL; + long params = 0; + long version; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + (void)biz_date; EXEC SQL DECLARE cur_cm_bt_0007 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT param_key, COALESCE(param_val, '') + FROM cm_param + ORDER BY param_key; EXEC SQL OPEN cur_cm_bt_0007; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,58 +54,36 @@ static int run_cm_bt_0007(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cm_bt_0007] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_cm_bt_0007 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cm_bt_0007 INTO :h_param_key, :h_param_val; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0007"); - errcnt++; break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + params++; + snap = cm_hash_acc(snap, h_param_key); + snap = cm_hash_acc(snap, h_param_val); } EXEC SQL CLOSE cur_cm_bt_0007; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + version = (long)(snap % 1000000UL); tx_log(TX_LOG_INFO, - "[cm_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0007] 파라미터 스냅샷 params=%ld hash=%lu version=%06ld", + params, snap, version); - printf("========== 공통 배치 결과 [cm_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 파라미터 스냅샷 [cm_bt_0007] ==========\n"); + printf(" 파라미터 수 : %ld\n", params); + printf(" 스냅샷 해시 : %lu\n", snap); + printf(" 스냅샷 버전 : %06ld\n", version); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (params > 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -121,7 +106,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0007] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0007] 파라미터 스냅샷 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0008.c b/app/batch/cm_bt_0008.c index 42c73ae..d605fe7 100644 --- a/app/batch/cm_bt_0008.c +++ b/app/batch/cm_bt_0008.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cm_bt_0008.pgc" /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0008.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0008.pgc - 공통 통계 집계 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일(argv1)의 원장(cm_ledger)을 커서로 순회하며 상태코드별 + * 건수/금액을 메모리 버킷에 집계한다. 집계 결과를 일자별 통계 + * 테이블(cm_daily)에 상태별로 raw UPSERT 적재하고 리포트한다. * * 실행: cm_bt_0008 [db@host] */ @@ -93,9 +94,23 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0008.pgc" +#line 20 "app/batch/cm_bt_0008.pgc" +#define CM_ST_BUCKETS 4 + +/* 상태코드 문자를 버킷 인덱스(R=0,M=1,U=2,S=3)로 변환, 미분류는 -1 */ +static int cm_status_idx(const char *st) +{ + switch (st[0]) { + case 'R': return 0; + case 'M': return 1; + case 'U': return 2; + case 'S': return 3; + default: return -1; + } +} + static int run_cm_bt_0008(const char *biz_date) { /* exec sql begin declare section */ @@ -105,106 +120,124 @@ static int run_cm_bt_0008(const char *biz_date) -#line 24 "app/batch/cm_bt_0008.pgc" +#line 39 "app/batch/cm_bt_0008.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cm_bt_0008.pgc" - char h_key [ 16 ] ; +#line 40 "app/batch/cm_bt_0008.pgc" + char h_status [ 2 ] ; -#line 26 "app/batch/cm_bt_0008.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/cm_bt_0008.pgc" +#line 41 "app/batch/cm_bt_0008.pgc" long h_amount ; -#line 28 "app/batch/cm_bt_0008.pgc" - char h_new_status [ 2 ] ; +#line 42 "app/batch/cm_bt_0008.pgc" + long h_cnt ; + +#line 43 "app/batch/cm_bt_0008.pgc" + long h_sum ; /* exec sql end declare section */ -#line 29 "app/batch/cm_bt_0008.pgc" +#line 44 "app/batch/cm_bt_0008.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + static const char *st_code[CM_ST_BUCKETS] = { "R", "M", "U", "S" }; + long cnt[CM_ST_BUCKETS] = { 0, 0, 0, 0 }; + long sum[CM_ST_BUCKETS] = { 0, 0, 0, 0 }; + long total = 0, errcnt = 0; + int idx, i; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0008 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0008.pgc" + /* declare cur_cm_bt_0008 cursor for select status , coalesce ( amount , 0 ) from cm_ledger where biz_date = $1 order by key */ +#line 59 "app/batch/cm_bt_0008.pgc" -#line 42 "app/batch/cm_bt_0008.pgc" +#line 59 "app/batch/cm_bt_0008.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0008 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0008 cursor for select status , coalesce ( amount , 0 ) from cm_ledger where biz_date = $1 order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0008.pgc" +#line 61 "app/batch/cm_bt_0008.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0008"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cm_bt_0008] tx_begin 경고"); - for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0008", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0008.pgc" +#line 68 "app/batch/cm_bt_0008.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0008"); - errcnt++; break; } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + idx = cm_status_idx(h_status); + if (idx >= 0) { + cnt[idx]++; + sum[idx] += h_amount; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0008", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0008.pgc" +#line 85 "app/batch/cm_bt_0008.pgc" + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[cm_bt_0008] tx_begin 경고"); + + for (i = 0; i < CM_ST_BUCKETS; i++) { + strncpy(h_status, st_code[i], sizeof(h_status)); + h_cnt = cnt[i]; + h_sum = sum[i]; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into cm_daily ( biz_date , status , tx_cnt , tx_sum ) values ( $1 , $2 , $3 , $4 ) on conflict ( biz_date , status ) do update set tx_cnt = $5 , tx_sum = $6 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 98 "app/batch/cm_bt_0008.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPSERT cm_daily"); + errcnt++; + } + } + if (errcnt == 0) tx_commit(); else tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0008] 통계집계 date=%s 총=%ld 오류=%ld", biz_date, total, errcnt); - printf("========== 공통 배치 결과 [cm_bt_0008] ==========\n"); + printf("========== 공통 통계 집계 [cm_bt_0008] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 전체 건수 : %ld\n", total); + printf(" 접수(R) : %ld건 / %ld원\n", cnt[0], sum[0]); + printf(" 완료(M) : %ld건 / %ld원\n", cnt[1], sum[1]); + printf(" 불일치(U) : %ld건 / %ld원\n", cnt[2], sum[2]); + printf(" 정산(S) : %ld건 / %ld원\n", cnt[3], sum[3]); + printf(" 적재 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -230,7 +263,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0008] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0008] 통계집계 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0008.pgc b/app/batch/cm_bt_0008.pgc index 864d7ef..4921fc2 100644 --- a/app/batch/cm_bt_0008.pgc +++ b/app/batch/cm_bt_0008.pgc @@ -1,9 +1,10 @@ /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0008.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0008.pgc - 공통 통계 집계 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일(argv1)의 원장(cm_ledger)을 커서로 순회하며 상태코드별 + * 건수/금액을 메모리 버킷에 집계한다. 집계 결과를 일자별 통계 + * 테이블(cm_daily)에 상태별로 raw UPSERT 적재하고 리포트한다. * * 실행: cm_bt_0008 [db@host] */ @@ -18,28 +19,44 @@ EXEC SQL INCLUDE sqlca; +#define CM_ST_BUCKETS 4 + +/* 상태코드 문자를 버킷 인덱스(R=0,M=1,U=2,S=3)로 변환, 미분류는 -1 */ +static int cm_status_idx(const char *st) +{ + switch (st[0]) { + case 'R': return 0; + case 'M': return 1; + case 'U': return 2; + case 'S': return 3; + default: return -1; + } +} + static int run_cm_bt_0008(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; + char h_status[2]; long h_amount; - char h_new_status[2]; + long h_cnt; + long h_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + static const char *st_code[CM_ST_BUCKETS] = { "R", "M", "U", "S" }; + long cnt[CM_ST_BUCKETS] = { 0, 0, 0, 0 }; + long sum[CM_ST_BUCKETS] = { 0, 0, 0, 0 }; + long total = 0, errcnt = 0; + int idx, i; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_cm_bt_0008 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT status, COALESCE(amount, 0) + FROM cm_ledger + WHERE biz_date = :h_biz_date + ORDER BY key; EXEC SQL OPEN cur_cm_bt_0008; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,55 +64,61 @@ static int run_cm_bt_0008(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cm_bt_0008] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_cm_bt_0008 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cm_bt_0008 INTO :h_status, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0008"); - errcnt++; break; } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + idx = cm_status_idx(h_status); + if (idx >= 0) { + cnt[idx]++; + sum[idx] += h_amount; } } EXEC SQL CLOSE cur_cm_bt_0008; + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[cm_bt_0008] tx_begin 경고"); + + for (i = 0; i < CM_ST_BUCKETS; i++) { + strncpy(h_status, st_code[i], sizeof(h_status)); + h_cnt = cnt[i]; + h_sum = sum[i]; + + EXEC SQL INSERT INTO cm_daily (biz_date, status, tx_cnt, tx_sum) + VALUES (:h_biz_date, :h_status, :h_cnt, :h_sum) + ON CONFLICT (biz_date, status) + DO UPDATE SET tx_cnt = :h_cnt, tx_sum = :h_sum; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPSERT cm_daily"); + errcnt++; + } + } + if (errcnt == 0) tx_commit(); else tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0008] 통계집계 date=%s 총=%ld 오류=%ld", biz_date, total, errcnt); - printf("========== 공통 배치 결과 [cm_bt_0008] ==========\n"); + printf("========== 공통 통계 집계 [cm_bt_0008] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 전체 건수 : %ld\n", total); + printf(" 접수(R) : %ld건 / %ld원\n", cnt[0], sum[0]); + printf(" 완료(M) : %ld건 / %ld원\n", cnt[1], sum[1]); + printf(" 불일치(U) : %ld건 / %ld원\n", cnt[2], sum[2]); + printf(" 정산(S) : %ld건 / %ld원\n", cnt[3], sum[3]); + printf(" 적재 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -121,7 +144,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0008] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0008] 통계집계 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0009.c b/app/batch/cm_bt_0009.c index 688e5cd..4478031 100644 --- a/app/batch/cm_bt_0009.c +++ b/app/batch/cm_bt_0009.c @@ -6,12 +6,13 @@ /* End of automatic include section */ #line 1 "app/batch/cm_bt_0009.pgc" -/* @tier hard @module cm @transform common-batch */ +/* @tier medium @module cm @transform common-batch */ /* - * cm_bt_0009.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=hard] + * cm_bt_0009.pgc - 환율 일괄 적재 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정일(argv1)의 기준 환율 세트를 통화 배열을 순회하며 통화별로 + * cm_rate 에 raw INSERT(반복)한다. 유효하지 않은 환율은 건너뛰고, + * 적재된 통화 수를 리포트한다. 배치 main 엔트리 포함. * * 실행: cm_bt_0009 [db@host] */ @@ -93,118 +94,101 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0009.pgc" +#line 20 "app/batch/cm_bt_0009.pgc" +typedef struct { + const char *ccy; /* 통화코드 */ + long rate_x100; /* 원/통화 환율 x100 (소수2자리) */ +} cm_rate_seed_t; + +/* 환율(원/통화 x100) 유효성: 양수이며 상한 이내 */ +static int cm_rate_is_valid(long rate_x100) +{ + return (rate_x100 > 0 && rate_x100 < 100000000L) ? 1 : 0; +} + static int run_cm_bt_0009(const char *biz_date) { /* exec sql begin declare section */ - - -#line 24 "app/batch/cm_bt_0009.pgc" +#line 36 "app/batch/cm_bt_0009.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cm_bt_0009.pgc" - char h_key [ 16 ] ; +#line 37 "app/batch/cm_bt_0009.pgc" + char h_ccy [ 4 ] ; -#line 26 "app/batch/cm_bt_0009.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/cm_bt_0009.pgc" - long h_amount ; - -#line 28 "app/batch/cm_bt_0009.pgc" - char h_new_status [ 2 ] ; +#line 38 "app/batch/cm_bt_0009.pgc" + long h_rate ; /* exec sql end declare section */ -#line 29 "app/batch/cm_bt_0009.pgc" +#line 39 "app/batch/cm_bt_0009.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + static const cm_rate_seed_t seeds[] = { + { "USD", 138550L }, + { "EUR", 150200L }, + { "JPY", 920L }, + { "CNY", 19180L }, + { "GBP", 176300L }, + { "HKD", 17720L }, + }; + int nseed = (int)(sizeof(seeds) / sizeof(seeds[0])); + long loaded = 0, skipped = 0, errcnt = 0; + int i; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0009 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0009.pgc" - -#line 42 "app/batch/cm_bt_0009.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0009 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0009.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0009"); - return TX_EDB; - } - if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[cm_bt_0009] tx_begin 경고"); - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0009", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + for (i = 0; i < nseed; i++) { + if (!cm_rate_is_valid(seeds[i].rate_x100)) { + skipped++; + continue; + } + strncpy(h_ccy, seeds[i].ccy, sizeof(h_ccy) - 1); + h_ccy[sizeof(h_ccy) - 1] = '\0'; + h_rate = seeds[i].rate_x100; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into cm_rate ( biz_date , ccy_cd , rate_x100 ) values ( $1 , $2 , $3 )", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_ccy),(long)4,(long)1,(4)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0009.pgc" + ECPGt_long,&(h_rate),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 69 "app/batch/cm_bt_0009.pgc" - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0009"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); + TX_DBIO_LOG_ERR("INSERT cm_rate"); errcnt++; + continue; } + loaded++; + tx_log(TX_LOG_DEBUG, "[cm_bt_0009] 환율적재 ccy=%s rate=%ld.%02ld", + h_ccy, h_rate / 100, h_rate % 100); } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0009", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0009.pgc" - - if (errcnt == 0) tx_commit(); else tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0009] 환율적재 date=%s 적재=%ld 스킵=%ld 오류=%ld", + biz_date, loaded, skipped, errcnt); - printf("========== 공통 배치 결과 [cm_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 환율 일괄 적재 [cm_bt_0009] ==========\n"); + printf(" 기준 일자 : %s\n", biz_date); + printf(" 대상 통화 : %d\n", nseed); + printf(" 적재 통화 : %ld\n", loaded); + printf(" 스킵(무효) : %ld\n", skipped); + printf(" 적재 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -230,7 +214,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0009] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0009] 환율적재 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0009.pgc b/app/batch/cm_bt_0009.pgc index 3cdbdae..b4d8c93 100644 --- a/app/batch/cm_bt_0009.pgc +++ b/app/batch/cm_bt_0009.pgc @@ -1,9 +1,10 @@ -/* @tier hard @module cm @transform common-batch */ +/* @tier medium @module cm @transform common-batch */ /* - * cm_bt_0009.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=hard] + * cm_bt_0009.pgc - 환율 일괄 적재 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정일(argv1)의 기준 환율 세트를 통화 배열을 순회하며 통화별로 + * cm_rate 에 raw INSERT(반복)한다. 유효하지 않은 환율은 건너뛰고, + * 적재된 통화 수를 리포트한다. 배치 main 엔트리 포함. * * 실행: cm_bt_0009 [db@host] */ @@ -18,84 +19,80 @@ EXEC SQL INCLUDE sqlca; +typedef struct { + const char *ccy; /* 통화코드 */ + long rate_x100; /* 원/통화 환율 x100 (소수2자리) */ +} cm_rate_seed_t; + +/* 환율(원/통화 x100) 유효성: 양수이며 상한 이내 */ +static int cm_rate_is_valid(long rate_x100) +{ + return (rate_x100 > 0 && rate_x100 < 100000000L) ? 1 : 0; +} + static int run_cm_bt_0009(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_ccy[4]; + long h_rate; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + static const cm_rate_seed_t seeds[] = { + { "USD", 138550L }, + { "EUR", 150200L }, + { "JPY", 920L }, + { "CNY", 19180L }, + { "GBP", 176300L }, + { "HKD", 17720L }, + }; + int nseed = (int)(sizeof(seeds) / sizeof(seeds[0])); + long loaded = 0, skipped = 0, errcnt = 0; + int i; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - EXEC SQL DECLARE cur_cm_bt_0009 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_cm_bt_0009; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0009"); - return TX_EDB; - } - if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[cm_bt_0009] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_cm_bt_0009 INTO :h_key, :h_merch_id, :h_amount; + for (i = 0; i < nseed; i++) { + if (!cm_rate_is_valid(seeds[i].rate_x100)) { + skipped++; + continue; + } + strncpy(h_ccy, seeds[i].ccy, sizeof(h_ccy) - 1); + h_ccy[sizeof(h_ccy) - 1] = '\0'; + h_rate = seeds[i].rate_x100; + + EXEC SQL INSERT INTO cm_rate (biz_date, ccy_cd, rate_x100) + VALUES (:h_biz_date, :h_ccy, :h_rate); - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0009"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); + TX_DBIO_LOG_ERR("INSERT cm_rate"); errcnt++; + continue; } + loaded++; + tx_log(TX_LOG_DEBUG, "[cm_bt_0009] 환율적재 ccy=%s rate=%ld.%02ld", + h_ccy, h_rate / 100, h_rate % 100); } - EXEC SQL CLOSE cur_cm_bt_0009; - if (errcnt == 0) tx_commit(); else tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0009] 환율적재 date=%s 적재=%ld 스킵=%ld 오류=%ld", + biz_date, loaded, skipped, errcnt); - printf("========== 공통 배치 결과 [cm_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 환율 일괄 적재 [cm_bt_0009] ==========\n"); + printf(" 기준 일자 : %s\n", biz_date); + printf(" 대상 통화 : %d\n", nseed); + printf(" 적재 통화 : %ld\n", loaded); + printf(" 스킵(무효) : %ld\n", skipped); + printf(" 적재 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -121,7 +118,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0009] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0009] 환율적재 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0010.c b/app/batch/cm_bt_0010.c index 719747d..1f92018 100644 --- a/app/batch/cm_bt_0010.c +++ b/app/batch/cm_bt_0010.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cm_bt_0010.pgc" /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0010.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0010.pgc - 공통 코드 매핑 정합성 검증 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 코드 매핑 테이블(cm_code_map)을 커서로 순회하며 각 매핑의 원천코드 + * (src_cd)와 대상코드(dst_cd)가 공통코드 마스터(cm_code)에 실제 존재 + * 하는지 조회하여 고아(orphan) 매핑을 집계한다. 배치 main 엔트리 포함. * * 실행: cm_bt_0010 [db@host] */ @@ -93,10 +94,10 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0010.pgc" +#line 20 "app/batch/cm_bt_0010.pgc" -static int run_cm_bt_0010(const char *biz_date) +static int run_cm_bt_0010(void) { /* exec sql begin declare section */ @@ -104,110 +105,123 @@ static int run_cm_bt_0010(const char *biz_date) + -#line 24 "app/batch/cm_bt_0010.pgc" - char h_biz_date [ 9 ] ; - #line 25 "app/batch/cm_bt_0010.pgc" - char h_key [ 16 ] ; + char h_map_id [ 16 ] ; #line 26 "app/batch/cm_bt_0010.pgc" - char h_merch_id [ 16 ] ; + char h_src_grp [ 16 ] ; #line 27 "app/batch/cm_bt_0010.pgc" - long h_amount ; + char h_src_cd [ 16 ] ; #line 28 "app/batch/cm_bt_0010.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + char h_dst_grp [ 16 ] ; + #line 29 "app/batch/cm_bt_0010.pgc" + char h_dst_cd [ 16 ] ; + +#line 30 "app/batch/cm_bt_0010.pgc" + int h_cnt ; +/* exec sql end declare section */ +#line 31 "app/batch/cm_bt_0010.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, valid = 0, orphan_src = 0, orphan_dst = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0010 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0010.pgc" - -#line 42 "app/batch/cm_bt_0010.pgc" + /* declare cur_cm_bt_0010 cursor for select map_id , src_grp , src_cd , dst_grp , dst_cd from cm_code_map order by map_id */ +#line 38 "app/batch/cm_bt_0010.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0010 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0010.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0010 cursor for select map_id , src_grp , src_cd , dst_grp , dst_cd from cm_code_map order by map_id", ECPGt_EOIT, ECPGt_EORT);} +#line 40 "app/batch/cm_bt_0010.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0010"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cm_bt_0010] tx_begin 경고"); - for (;;) { + int ok = 1; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0010", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_map_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_src_grp),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_src_cd),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_dst_grp),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_dst_cd),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0010.pgc" +#line 50 "app/batch/cm_bt_0010.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0010"); - errcnt++; break; } total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + h_cnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cm_code where grp_cd = $1 and code_cd = $2 ", + ECPGt_char,(h_src_grp),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_src_cd),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 64 "app/batch/cm_bt_0010.pgc" + + if (h_cnt == 0) { + orphan_src++; + ok = 0; + tx_log(TX_LOG_WARN, "[cm_bt_0010] 원천코드 없음 map=%s src=%s/%s", + h_map_id, h_src_grp, h_src_cd); } - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + h_cnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cm_code where grp_cd = $1 and code_cd = $2 ", + ECPGt_char,(h_dst_grp),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_dst_cd),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 75 "app/batch/cm_bt_0010.pgc" + + if (h_cnt == 0) { + orphan_dst++; + ok = 0; + tx_log(TX_LOG_WARN, "[cm_bt_0010] 대상코드 없음 map=%s dst=%s/%s", + h_map_id, h_dst_grp, h_dst_cd); } + + if (ok) + valid++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0010", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0010.pgc" +#line 87 "app/batch/cm_bt_0010.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[cm_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0010] 매핑검증 총=%ld 정합=%ld 원천고아=%ld 대상고아=%ld", + total, valid, orphan_src, orphan_dst); - printf("========== 공통 배치 결과 [cm_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 코드 매핑 정합성 검증 [cm_bt_0010] ==========\n"); + printf(" 총 매핑수 : %ld\n", total); + printf(" 정합 매핑 : %ld\n", valid); + printf(" 원천 고아 : %ld\n", orphan_src); + printf(" 대상 고아 : %ld\n", orphan_dst); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (orphan_src == 0 && orphan_dst == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -230,7 +244,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0010] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0010] 매핑검증 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { @@ -238,7 +252,7 @@ int main(int argc, char **argv) return 1; } - rc = run_cm_bt_0010(biz_date); + rc = run_cm_bt_0010(); cm_dbio_disconnect(); diff --git a/app/batch/cm_bt_0010.pgc b/app/batch/cm_bt_0010.pgc index c5877ab..c6fbc3b 100644 --- a/app/batch/cm_bt_0010.pgc +++ b/app/batch/cm_bt_0010.pgc @@ -1,9 +1,10 @@ /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0010.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0010.pgc - 공통 코드 매핑 정합성 검증 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 코드 매핑 테이블(cm_code_map)을 커서로 순회하며 각 매핑의 원천코드 + * (src_cd)와 대상코드(dst_cd)가 공통코드 마스터(cm_code)에 실제 존재 + * 하는지 조회하여 고아(orphan) 매핑을 집계한다. 배치 main 엔트리 포함. * * 실행: cm_bt_0010 [db@host] */ @@ -18,28 +19,23 @@ EXEC SQL INCLUDE sqlca; -static int run_cm_bt_0010(const char *biz_date) +static int run_cm_bt_0010(void) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_map_id[16]; + char h_src_grp[16]; + char h_src_cd[16]; + char h_dst_grp[16]; + char h_dst_cd[16]; + int h_cnt; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; - - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + long total = 0, valid = 0, orphan_src = 0, orphan_dst = 0; EXEC SQL DECLARE cur_cm_bt_0010 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT map_id, src_grp, src_cd, dst_grp, dst_cd + FROM cm_code_map + ORDER BY map_id; EXEC SQL OPEN cur_cm_bt_0010; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,58 +43,61 @@ static int run_cm_bt_0010(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cm_bt_0010] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_cm_bt_0010 INTO :h_key, :h_merch_id, :h_amount; + int ok = 1; + + EXEC SQL FETCH cur_cm_bt_0010 + INTO :h_map_id, :h_src_grp, :h_src_cd, :h_dst_grp, :h_dst_cd; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0010"); - errcnt++; break; } total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + h_cnt = 0; + EXEC SQL SELECT COUNT(*) INTO :h_cnt + FROM cm_code + WHERE grp_cd = :h_src_grp AND code_cd = :h_src_cd; + if (h_cnt == 0) { + orphan_src++; + ok = 0; + tx_log(TX_LOG_WARN, "[cm_bt_0010] 원천코드 없음 map=%s src=%s/%s", + h_map_id, h_src_grp, h_src_cd); } - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + h_cnt = 0; + EXEC SQL SELECT COUNT(*) INTO :h_cnt + FROM cm_code + WHERE grp_cd = :h_dst_grp AND code_cd = :h_dst_cd; + if (h_cnt == 0) { + orphan_dst++; + ok = 0; + tx_log(TX_LOG_WARN, "[cm_bt_0010] 대상코드 없음 map=%s dst=%s/%s", + h_map_id, h_dst_grp, h_dst_cd); } + + if (ok) + valid++; } EXEC SQL CLOSE cur_cm_bt_0010; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[cm_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0010] 매핑검증 총=%ld 정합=%ld 원천고아=%ld 대상고아=%ld", + total, valid, orphan_src, orphan_dst); - printf("========== 공통 배치 결과 [cm_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 코드 매핑 정합성 검증 [cm_bt_0010] ==========\n"); + printf(" 총 매핑수 : %ld\n", total); + printf(" 정합 매핑 : %ld\n", valid); + printf(" 원천 고아 : %ld\n", orphan_src); + printf(" 대상 고아 : %ld\n", orphan_dst); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (orphan_src == 0 && orphan_dst == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -121,7 +120,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0010] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0010] 매핑검증 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { @@ -129,7 +128,7 @@ int main(int argc, char **argv) return 1; } - rc = run_cm_bt_0010(biz_date); + rc = run_cm_bt_0010(); cm_dbio_disconnect(); diff --git a/app/batch/cm_bt_0011.c b/app/batch/cm_bt_0011.c index bbcb5de..4647899 100644 --- a/app/batch/cm_bt_0011.c +++ b/app/batch/cm_bt_0011.c @@ -6,12 +6,14 @@ /* End of automatic include section */ #line 1 "app/batch/cm_bt_0011.pgc" -/* @tier hard @module cm @transform common-batch */ +/* @tier medium @module cm @transform common-batch */ /* - * cm_bt_0011.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=hard] + * cm_bt_0011.pgc - 공통 배치 컨트롤 갱신 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 배치 스케줄 컨트롤 테이블(cm_batch_ctl)에서 당일 수행대상(sched_yn='Y') + * 배치를 커서로 순회하며, 선행배치 완료여부를 확인한 뒤 상태를 '완료(C)' + * 로 갱신하고 수행일시를 기록한다. 미충족 건은 대기로 남긴다. + * 배치 main 엔트리 포함. * * 실행: cm_bt_0011 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0011.pgc" +#line 21 "app/batch/cm_bt_0011.pgc" static int run_cm_bt_0011(const char *biz_date) @@ -103,43 +105,42 @@ static int run_cm_bt_0011(const char *biz_date) - + -#line 24 "app/batch/cm_bt_0011.pgc" +#line 26 "app/batch/cm_bt_0011.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cm_bt_0011.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/cm_bt_0011.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/cm_bt_0011.pgc" - long h_amount ; + char h_batch_id [ 16 ] ; #line 28 "app/batch/cm_bt_0011.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + char h_prereq [ 16 ] ; + #line 29 "app/batch/cm_bt_0011.pgc" + char h_prereq_st [ 2 ] ; + +#line 30 "app/batch/cm_bt_0011.pgc" + int h_done ; +/* exec sql end declare section */ +#line 31 "app/batch/cm_bt_0011.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, updated = 0, waiting = 0, errcnt = 0; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0011 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0011.pgc" + /* declare cur_cm_bt_0011 cursor for select batch_id , coalesce ( prereq_id , '' ) from cm_batch_ctl where sched_yn = 'Y' and biz_date = $1 order by run_seq */ +#line 43 "app/batch/cm_bt_0011.pgc" -#line 42 "app/batch/cm_bt_0011.pgc" +#line 43 "app/batch/cm_bt_0011.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0011 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0011 cursor for select batch_id , coalesce ( prereq_id , '' ) from cm_batch_ctl where sched_yn = 'Y' and biz_date = $1 order by run_seq", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0011.pgc" +#line 45 "app/batch/cm_bt_0011.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0011"); @@ -150,14 +151,14 @@ static int run_cm_bt_0011(const char *biz_date) tx_log(TX_LOG_WARN, "[cm_bt_0011] tx_begin 경고"); for (;;) { + int ready = 1; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0011", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_batch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_prereq),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0011.pgc" +#line 57 "app/batch/cm_bt_0011.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +171,48 @@ static int run_cm_bt_0011(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 선행배치가 지정된 경우 완료상태인지 확인 */ + if (h_prereq[0] != '\0') { + h_done = 0; + memset(h_prereq_st, 0, sizeof(h_prereq_st)); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cm_batch_ctl where batch_id = $1 and biz_date = $2 and status = 'C'", + ECPGt_char,(h_prereq),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_done),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 77 "app/batch/cm_bt_0011.pgc" + + if (h_done == 0) { + ready = 0; + waiting++; + tx_log(TX_LOG_INFO, "[cm_bt_0011] 선행미완 batch=%s prereq=%s", + h_batch_id, h_prereq); + } } - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (!ready) + continue; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cm_batch_ctl set status = 'C' , run_ts = current_timestamp where batch_id = $1 and biz_date = $2 ", + ECPGt_char,(h_batch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 93 "app/batch/cm_bt_0011.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cm_batch_ctl"); errcnt++; + continue; } + updated++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0011", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0011.pgc" +#line 103 "app/batch/cm_bt_0011.pgc" if (errcnt == 0) @@ -196,14 +221,14 @@ static int run_cm_bt_0011(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0011] 컨트롤갱신 대상=%ld 완료=%ld 대기=%ld 오류=%ld", + total, updated, waiting, errcnt); - printf("========== 공통 배치 결과 [cm_bt_0011] ==========\n"); + printf("========== 배치 컨트롤 갱신 [cm_bt_0011] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 대상 배치 : %ld\n", total); + printf(" 완료 갱신 : %ld\n", updated); + printf(" 선행 대기 : %ld\n", waiting); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); @@ -230,7 +255,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0011] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0011] 컨트롤갱신 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0011.pgc b/app/batch/cm_bt_0011.pgc index 077d73c..cd399ad 100644 --- a/app/batch/cm_bt_0011.pgc +++ b/app/batch/cm_bt_0011.pgc @@ -1,9 +1,11 @@ -/* @tier hard @module cm @transform common-batch */ +/* @tier medium @module cm @transform common-batch */ /* - * cm_bt_0011.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=hard] + * cm_bt_0011.pgc - 공통 배치 컨트롤 갱신 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 배치 스케줄 컨트롤 테이블(cm_batch_ctl)에서 당일 수행대상(sched_yn='Y') + * 배치를 커서로 순회하며, 선행배치 완료여부를 확인한 뒤 상태를 '완료(C)' + * 로 갱신하고 수행일시를 기록한다. 미충족 건은 대기로 남긴다. + * 배치 main 엔트리 포함. * * 실행: cm_bt_0011 [db@host] */ @@ -22,24 +24,23 @@ static int run_cm_bt_0011(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_batch_id[16]; + char h_prereq[16]; + char h_prereq_st[2]; + int h_done; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, updated = 0, waiting = 0, errcnt = 0; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_cm_bt_0011 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT batch_id, COALESCE(prereq_id, '') + FROM cm_batch_ctl + WHERE sched_yn = 'Y' + AND biz_date = :h_biz_date + ORDER BY run_seq; EXEC SQL OPEN cur_cm_bt_0011; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +52,9 @@ static int run_cm_bt_0011(const char *biz_date) tx_log(TX_LOG_WARN, "[cm_bt_0011] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_cm_bt_0011 INTO :h_key, :h_merch_id, :h_amount; + int ready = 1; + + EXEC SQL FETCH cur_cm_bt_0011 INTO :h_batch_id, :h_prereq; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +66,38 @@ static int run_cm_bt_0011(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 선행배치가 지정된 경우 완료상태인지 확인 */ + if (h_prereq[0] != '\0') { + h_done = 0; + memset(h_prereq_st, 0, sizeof(h_prereq_st)); + EXEC SQL SELECT COUNT(*) INTO :h_done + FROM cm_batch_ctl + WHERE batch_id = :h_prereq + AND biz_date = :h_biz_date + AND status = 'C'; + if (h_done == 0) { + ready = 0; + waiting++; + tx_log(TX_LOG_INFO, "[cm_bt_0011] 선행미완 batch=%s prereq=%s", + h_batch_id, h_prereq); + } } - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (!ready) + continue; + + EXEC SQL UPDATE cm_batch_ctl + SET status = 'C', + run_ts = CURRENT_TIMESTAMP + WHERE batch_id = :h_batch_id + AND biz_date = :h_biz_date; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cm_batch_ctl"); errcnt++; + continue; } + updated++; } EXEC SQL CLOSE cur_cm_bt_0011; @@ -87,14 +108,14 @@ static int run_cm_bt_0011(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0011] 컨트롤갱신 대상=%ld 완료=%ld 대기=%ld 오류=%ld", + total, updated, waiting, errcnt); - printf("========== 공통 배치 결과 [cm_bt_0011] ==========\n"); + printf("========== 배치 컨트롤 갱신 [cm_bt_0011] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 대상 배치 : %ld\n", total); + printf(" 완료 갱신 : %ld\n", updated); + printf(" 선행 대기 : %ld\n", waiting); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); @@ -121,7 +142,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0011] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0011] 컨트롤갱신 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0012.c b/app/batch/cm_bt_0012.c index 7e8899a..ce3d009 100644 --- a/app/batch/cm_bt_0012.c +++ b/app/batch/cm_bt_0012.c @@ -8,10 +8,12 @@ #line 1 "app/batch/cm_bt_0012.pgc" /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0012.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0012.pgc - 공통 영업일자 롤오버 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 시스템 공통 파라미터(cm_param)의 현재 영업일(BIZ_DATE)을 조회하여 + * date_next_business 로 다음 영업일을 산출한 뒤 파라미터를 갱신한다. + * 인자로 받은 기준일과 현재 영업일의 일치 여부도 점검한다. + * 배치 main 엔트리 포함. * * 실행: cm_bt_0012 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0012.pgc" +#line 21 "app/batch/cm_bt_0012.pgc" static int run_cm_bt_0012(const char *biz_date) @@ -101,113 +103,75 @@ static int run_cm_bt_0012(const char *biz_date) /* exec sql begin declare section */ - - - -#line 24 "app/batch/cm_bt_0012.pgc" - char h_biz_date [ 9 ] ; - -#line 25 "app/batch/cm_bt_0012.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/cm_bt_0012.pgc" - char h_merch_id [ 16 ] ; + char h_cur_date [ 9 ] ; #line 27 "app/batch/cm_bt_0012.pgc" - long h_amount ; - -#line 28 "app/batch/cm_bt_0012.pgc" - char h_new_status [ 2 ] ; + char h_next_date [ 9 ] ; /* exec sql end declare section */ -#line 29 "app/batch/cm_bt_0012.pgc" +#line 28 "app/batch/cm_bt_0012.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + char next_date[9]; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0012 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0012.pgc" - -#line 42 "app/batch/cm_bt_0012.pgc" + /* 1) 현재 영업일 파라미터 조회 */ + memset(h_cur_date, 0, sizeof(h_cur_date)); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select param_val from cm_param where param_key = 'BIZ_DATE'", ECPGt_EOIT, + ECPGt_char,(h_cur_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 36 "app/batch/cm_bt_0012.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0012 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0012.pgc" - + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_ERROR, "[cm_bt_0012] BIZ_DATE 파라미터 없음"); + return TX_ENOENT; + } if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0012"); + TX_DBIO_LOG_ERR("SELECT cm_param BIZ_DATE"); return TX_EDB; } + /* 2) 기준일 정합성 점검 (경고만) */ + if (strncmp(h_cur_date, biz_date, 8) != 0) + tx_log(TX_LOG_WARN, "[cm_bt_0012] 기준일 불일치 param=%s arg=%s", + h_cur_date, biz_date); + + /* 3) 다음 영업일 산출 */ + memset(next_date, 0, sizeof(next_date)); + if (date_next_business(h_cur_date, next_date) != 0) { + tx_log(TX_LOG_ERROR, "[cm_bt_0012] 다음영업일 계산 실패 cur=%s", h_cur_date); + return TX_FAIL; + } + + memset(h_next_date, 0, sizeof(h_next_date)); + strncpy(h_next_date, next_date, sizeof(h_next_date) - 1); + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[cm_bt_0012] tx_begin 경고"); - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0012", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0012.pgc" + /* 4) 영업일 파라미터 갱신 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cm_param set param_val = $1 , upd_ts = current_timestamp where param_key = 'BIZ_DATE'", + ECPGt_char,(h_next_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 69 "app/batch/cm_bt_0012.pgc" - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0012"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0012", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0012.pgc" - - - if (errcnt == 0) - tx_commit(); - else + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cm_param BIZ_DATE"); tx_abort(); + return TX_EDB; + } + tx_commit(); - tx_log(TX_LOG_INFO, - "[cm_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_log(TX_LOG_INFO, "[cm_bt_0012] 영업일 롤오버 %s -> %s", h_cur_date, next_date); - printf("========== 공통 배치 결과 [cm_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 영업일자 롤오버 [cm_bt_0012] ==========\n"); + printf(" 이전 영업일 : %s\n", h_cur_date); + printf(" 신규 영업일 : %s\n", next_date); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) @@ -230,7 +194,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0012] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0012] 롤오버 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0012.pgc b/app/batch/cm_bt_0012.pgc index 9f2f025..d9aac20 100644 --- a/app/batch/cm_bt_0012.pgc +++ b/app/batch/cm_bt_0012.pgc @@ -1,9 +1,11 @@ /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0012.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0012.pgc - 공통 영업일자 롤오버 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 시스템 공통 파라미터(cm_param)의 현재 영업일(BIZ_DATE)을 조회하여 + * date_next_business 로 다음 영업일을 산출한 뒤 파라미터를 갱신한다. + * 인자로 받은 기준일과 현재 영업일의 일치 여부도 점검한다. + * 배치 main 엔트리 포함. * * 실행: cm_bt_0012 [db@host] */ @@ -21,84 +23,66 @@ EXEC SQL INCLUDE sqlca; static int run_cm_bt_0012(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_cur_date[9]; + char h_next_date[9]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + char next_date[9]; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 1) 현재 영업일 파라미터 조회 */ + memset(h_cur_date, 0, sizeof(h_cur_date)); + EXEC SQL SELECT param_val INTO :h_cur_date + FROM cm_param + WHERE param_key = 'BIZ_DATE'; - EXEC SQL DECLARE cur_cm_bt_0012 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_cm_bt_0012; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_ERROR, "[cm_bt_0012] BIZ_DATE 파라미터 없음"); + return TX_ENOENT; + } if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0012"); + TX_DBIO_LOG_ERR("SELECT cm_param BIZ_DATE"); return TX_EDB; } + /* 2) 기준일 정합성 점검 (경고만) */ + if (strncmp(h_cur_date, biz_date, 8) != 0) + tx_log(TX_LOG_WARN, "[cm_bt_0012] 기준일 불일치 param=%s arg=%s", + h_cur_date, biz_date); + + /* 3) 다음 영업일 산출 */ + memset(next_date, 0, sizeof(next_date)); + if (date_next_business(h_cur_date, next_date) != 0) { + tx_log(TX_LOG_ERROR, "[cm_bt_0012] 다음영업일 계산 실패 cur=%s", h_cur_date); + return TX_FAIL; + } + + memset(h_next_date, 0, sizeof(h_next_date)); + strncpy(h_next_date, next_date, sizeof(h_next_date) - 1); + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[cm_bt_0012] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_cm_bt_0012 INTO :h_key, :h_merch_id, :h_amount; + /* 4) 영업일 파라미터 갱신 */ + EXEC SQL UPDATE cm_param + SET param_val = :h_next_date, + upd_ts = CURRENT_TIMESTAMP + WHERE param_key = 'BIZ_DATE'; - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0012"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_cm_bt_0012; - - if (errcnt == 0) - tx_commit(); - else + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cm_param BIZ_DATE"); tx_abort(); + return TX_EDB; + } + tx_commit(); - tx_log(TX_LOG_INFO, - "[cm_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_log(TX_LOG_INFO, "[cm_bt_0012] 영업일 롤오버 %s -> %s", h_cur_date, next_date); - printf("========== 공통 배치 결과 [cm_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 영업일자 롤오버 [cm_bt_0012] ==========\n"); + printf(" 이전 영업일 : %s\n", h_cur_date); + printf(" 신규 영업일 : %s\n", next_date); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) @@ -121,7 +105,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0012] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0012] 롤오버 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0013.c b/app/batch/cm_bt_0013.c index 8dc5860..0259437 100644 --- a/app/batch/cm_bt_0013.c +++ b/app/batch/cm_bt_0013.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cm_bt_0013.pgc" /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0013.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0013.pgc - 공통 캐시 정합성 점검 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 공통코드 마스터(cm_code)와 코드 매핑(cm_code_map)의 건수를 각각 단건 + * 집계로 조회하여, 매핑이 참조하는 코드 대비 마스터 코드 규모를 대조하고 + * 고아 매핑 가능성을 판정·리포트한다. 배치 main 엔트리 포함. * * 실행: cm_bt_0013 [db@host] */ @@ -93,121 +94,92 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0013.pgc" +#line 20 "app/batch/cm_bt_0013.pgc" -static int run_cm_bt_0013(const char *biz_date) +static int run_cm_bt_0013(void) { /* exec sql begin declare section */ - -#line 24 "app/batch/cm_bt_0013.pgc" - char h_biz_date [ 9 ] ; - #line 25 "app/batch/cm_bt_0013.pgc" - char h_key [ 16 ] ; + long h_code_cnt ; #line 26 "app/batch/cm_bt_0013.pgc" - char h_merch_id [ 16 ] ; + long h_active_cnt ; #line 27 "app/batch/cm_bt_0013.pgc" - long h_amount ; + long h_map_cnt ; #line 28 "app/batch/cm_bt_0013.pgc" - char h_new_status [ 2 ] ; + long h_orphan_cnt ; /* exec sql end declare section */ #line 29 "app/batch/cm_bt_0013.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + h_code_cnt = h_active_cnt = h_map_cnt = h_orphan_cnt = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0013 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0013.pgc" - -#line 42 "app/batch/cm_bt_0013.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0013 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0013.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cm_code", ECPGt_EOIT, + ECPGt_long,&(h_code_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 33 "app/batch/cm_bt_0013.pgc" if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0013"); + TX_DBIO_LOG_ERR("COUNT cm_code"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cm_bt_0013] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0013", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cm_code where use_yn = 'Y'", ECPGt_EOIT, + ECPGt_long,&(h_active_cnt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0013.pgc" +#line 40 "app/batch/cm_bt_0013.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0013"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("COUNT cm_code active"); + return TX_EDB; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0013", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0013.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cm_code_map", ECPGt_EOIT, + ECPGt_long,&(h_map_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 46 "app/batch/cm_bt_0013.pgc" + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("COUNT cm_code_map"); + return TX_EDB; + } - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + /* 매핑의 원천코드가 마스터에 없는 고아 매핑 수 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cm_code_map m where not exists ( select 1 from cm_code c where c . grp_cd = m . src_grp and c . code_cd = m . src_cd )", ECPGt_EOIT, + ECPGt_long,&(h_orphan_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 58 "app/batch/cm_bt_0013.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("COUNT orphan"); + return TX_EDB; + } tx_log(TX_LOG_INFO, - "[cm_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0013] 캐시점검 코드=%ld 활성=%ld 매핑=%ld 고아=%ld", + h_code_cnt, h_active_cnt, h_map_cnt, h_orphan_cnt); - printf("========== 공통 배치 결과 [cm_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + if (h_orphan_cnt > 0) + tx_log(TX_LOG_WARN, "[cm_bt_0013] 고아 매핑 %ld건 발견 - 캐시 재적재 권고", + h_orphan_cnt); + + printf("========== 캐시 정합성 점검 [cm_bt_0013] ==========\n"); + printf(" 전체 코드 : %ld\n", h_code_cnt); + printf(" 활성 코드 : %ld\n", h_active_cnt); + printf(" 코드 매핑 : %ld\n", h_map_cnt); + printf(" 고아 매핑 : %ld\n", h_orphan_cnt); + printf(" 정합 여부 : %s\n", (h_orphan_cnt == 0) ? "정상" : "불일치"); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (h_orphan_cnt == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -230,7 +202,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0013] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0013] 캐시점검 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { @@ -238,7 +210,7 @@ int main(int argc, char **argv) return 1; } - rc = run_cm_bt_0013(biz_date); + rc = run_cm_bt_0013(); cm_dbio_disconnect(); diff --git a/app/batch/cm_bt_0013.pgc b/app/batch/cm_bt_0013.pgc index c4df7d0..0dfdb1d 100644 --- a/app/batch/cm_bt_0013.pgc +++ b/app/batch/cm_bt_0013.pgc @@ -1,9 +1,10 @@ /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0013.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0013.pgc - 공통 캐시 정합성 점검 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 공통코드 마스터(cm_code)와 코드 매핑(cm_code_map)의 건수를 각각 단건 + * 집계로 조회하여, 매핑이 참조하는 코드 대비 마스터 코드 규모를 대조하고 + * 고아 매핑 가능성을 판정·리포트한다. 배치 main 엔트리 포함. * * 실행: cm_bt_0013 [db@host] */ @@ -18,87 +19,65 @@ EXEC SQL INCLUDE sqlca; -static int run_cm_bt_0013(const char *biz_date) +static int run_cm_bt_0013(void) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_code_cnt; + long h_active_cnt; + long h_map_cnt; + long h_orphan_cnt; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + h_code_cnt = h_active_cnt = h_map_cnt = h_orphan_cnt = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - EXEC SQL DECLARE cur_cm_bt_0013 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_cm_bt_0013; + EXEC SQL SELECT COUNT(*) INTO :h_code_cnt FROM cm_code; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0013"); + TX_DBIO_LOG_ERR("COUNT cm_code"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cm_bt_0013] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_cm_bt_0013 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0013"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL SELECT COUNT(*) INTO :h_active_cnt + FROM cm_code WHERE use_yn = 'Y'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("COUNT cm_code active"); + return TX_EDB; } - EXEC SQL CLOSE cur_cm_bt_0013; + EXEC SQL SELECT COUNT(*) INTO :h_map_cnt FROM cm_code_map; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("COUNT cm_code_map"); + return TX_EDB; + } - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + /* 매핑의 원천코드가 마스터에 없는 고아 매핑 수 */ + EXEC SQL SELECT COUNT(*) INTO :h_orphan_cnt + FROM cm_code_map m + WHERE NOT EXISTS ( + SELECT 1 FROM cm_code c + WHERE c.grp_cd = m.src_grp + AND c.code_cd = m.src_cd); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("COUNT orphan"); + return TX_EDB; + } tx_log(TX_LOG_INFO, - "[cm_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0013] 캐시점검 코드=%ld 활성=%ld 매핑=%ld 고아=%ld", + h_code_cnt, h_active_cnt, h_map_cnt, h_orphan_cnt); - printf("========== 공통 배치 결과 [cm_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + if (h_orphan_cnt > 0) + tx_log(TX_LOG_WARN, "[cm_bt_0013] 고아 매핑 %ld건 발견 - 캐시 재적재 권고", + h_orphan_cnt); + + printf("========== 캐시 정합성 점검 [cm_bt_0013] ==========\n"); + printf(" 전체 코드 : %ld\n", h_code_cnt); + printf(" 활성 코드 : %ld\n", h_active_cnt); + printf(" 코드 매핑 : %ld\n", h_map_cnt); + printf(" 고아 매핑 : %ld\n", h_orphan_cnt); + printf(" 정합 여부 : %s\n", (h_orphan_cnt == 0) ? "정상" : "불일치"); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (h_orphan_cnt == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -121,7 +100,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0013] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0013] 캐시점검 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { @@ -129,7 +108,7 @@ int main(int argc, char **argv) return 1; } - rc = run_cm_bt_0013(biz_date); + rc = run_cm_bt_0013(); cm_dbio_disconnect(); diff --git a/app/batch/cm_bt_0014.c b/app/batch/cm_bt_0014.c index d311c3b..de8dd95 100644 --- a/app/batch/cm_bt_0014.c +++ b/app/batch/cm_bt_0014.c @@ -8,10 +8,12 @@ #line 1 "app/batch/cm_bt_0014.pgc" /* @tier medium @module cm @transform common-batch */ /* - * cm_bt_0014.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=medium] + * cm_bt_0014.pgc - 공통 수수료 재계산 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 처리완료(status='M') 거래를 커서로 순회하며 거래금액에 + * 가맹점 수수료율(기본 250bp = 2.5%)을 적용해 수수료를 재계산하고, + * 원장(cm_ledger.fee)을 갱신한다. 재계산 건수/총수수료를 리포트한다. + * 배치 main 엔트리 포함. * * 실행: cm_bt_0014 [db@host] */ @@ -93,9 +95,18 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0014.pgc" +#line 21 "app/batch/cm_bt_0014.pgc" +#define CM_FEE_BP 250L /* 기본 수수료율: 250 basis point = 2.5% */ + +/* 금액 * bp / 10000, 반올림 */ +static long calc_fee(long amount, long bp) +{ + long base = amount * bp; + return (base + 5000L) / 10000L; +} + static int run_cm_bt_0014(const char *biz_date) { /* exec sql begin declare section */ @@ -103,43 +114,39 @@ static int run_cm_bt_0014(const char *biz_date) - -#line 24 "app/batch/cm_bt_0014.pgc" +#line 35 "app/batch/cm_bt_0014.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/cm_bt_0014.pgc" +#line 36 "app/batch/cm_bt_0014.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/cm_bt_0014.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/cm_bt_0014.pgc" +#line 37 "app/batch/cm_bt_0014.pgc" long h_amount ; -#line 28 "app/batch/cm_bt_0014.pgc" - char h_new_status [ 2 ] ; +#line 38 "app/batch/cm_bt_0014.pgc" + long h_new_fee ; /* exec sql end declare section */ -#line 29 "app/batch/cm_bt_0014.pgc" +#line 39 "app/batch/cm_bt_0014.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, recalc = 0, skipped = 0, errcnt = 0; + long sum_fee = 0; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0014 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0014.pgc" + /* declare cur_cm_bt_0014 cursor for select key , amount from cm_ledger where biz_date = $1 and status = 'M' order by key */ +#line 52 "app/batch/cm_bt_0014.pgc" -#line 42 "app/batch/cm_bt_0014.pgc" +#line 52 "app/batch/cm_bt_0014.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0014 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0014 cursor for select key , amount from cm_ledger where biz_date = $1 and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0014.pgc" +#line 54 "app/batch/cm_bt_0014.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0014"); @@ -153,11 +160,9 @@ static int run_cm_bt_0014(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0014", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0014.pgc" +#line 64 "app/batch/cm_bt_0014.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +175,33 @@ static int run_cm_bt_0014(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (!amount_is_valid(h_amount)) { + skipped++; + tx_log(TX_LOG_WARN, "[cm_bt_0014] 금액무효 재계산제외 key=%s", h_key); + continue; } - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); + h_new_fee = calc_fee(h_amount, CM_FEE_BP); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cm_ledger set fee = $1 , upd_ts = current_timestamp where key = $2 ", + ECPGt_long,&(h_new_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 87 "app/batch/cm_bt_0014.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cm_ledger fee"); errcnt++; + continue; } + recalc++; + sum_fee += h_new_fee; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0014", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0014.pgc" +#line 98 "app/batch/cm_bt_0014.pgc" if (errcnt == 0) @@ -196,14 +210,15 @@ static int run_cm_bt_0014(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0014] 수수료재계산 date=%s 대상=%ld 재계산=%ld 총수수료=%ld", + biz_date, total, recalc, sum_fee); - printf("========== 공통 배치 결과 [cm_bt_0014] ==========\n"); + printf("========== 수수료 재계산 [cm_bt_0014] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 대상(M) : %ld\n", total); + printf(" 재계산 건수 : %ld\n", recalc); + printf(" 제외 건수 : %ld\n", skipped); + printf(" 총 수수료 : %ld\n", sum_fee); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); @@ -230,7 +245,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0014] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0014] 수수료재계산 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0014.pgc b/app/batch/cm_bt_0014.pgc index 2c9bf0a..2e33394 100644 --- a/app/batch/cm_bt_0014.pgc +++ b/app/batch/cm_bt_0014.pgc @@ -1,9 +1,11 @@ /* @tier medium @module cm @transform common-batch */ /* - * cm_bt_0014.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=medium] + * cm_bt_0014.pgc - 공통 수수료 재계산 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 처리완료(status='M') 거래를 커서로 순회하며 거래금액에 + * 가맹점 수수료율(기본 250bp = 2.5%)을 적용해 수수료를 재계산하고, + * 원장(cm_ledger.fee)을 갱신한다. 재계산 건수/총수수료를 리포트한다. + * 배치 main 엔트리 포함. * * 실행: cm_bt_0014 [db@host] */ @@ -18,28 +20,36 @@ EXEC SQL INCLUDE sqlca; +#define CM_FEE_BP 250L /* 기본 수수료율: 250 basis point = 2.5% */ + +/* 금액 * bp / 10000, 반올림 */ +static long calc_fee(long amount, long bp) +{ + long base = amount * bp; + return (base + 5000L) / 10000L; +} + static int run_cm_bt_0014(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; + long h_new_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, recalc = 0, skipped = 0, errcnt = 0; + long sum_fee = 0; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_cm_bt_0014 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT key, amount + FROM cm_ledger + WHERE biz_date = :h_biz_date + AND status = 'M' + ORDER BY key; EXEC SQL OPEN cur_cm_bt_0014; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +61,7 @@ static int run_cm_bt_0014(const char *biz_date) tx_log(TX_LOG_WARN, "[cm_bt_0014] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_cm_bt_0014 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_cm_bt_0014 INTO :h_key, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +73,26 @@ static int run_cm_bt_0014(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (!amount_is_valid(h_amount)) { + skipped++; + tx_log(TX_LOG_WARN, "[cm_bt_0014] 금액무효 재계산제외 key=%s", h_key); + continue; } - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); + h_new_fee = calc_fee(h_amount, CM_FEE_BP); + + EXEC SQL UPDATE cm_ledger + SET fee = :h_new_fee, + upd_ts = CURRENT_TIMESTAMP + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE cm_ledger fee"); errcnt++; + continue; } + recalc++; + sum_fee += h_new_fee; } EXEC SQL CLOSE cur_cm_bt_0014; @@ -87,14 +103,15 @@ static int run_cm_bt_0014(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[cm_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0014] 수수료재계산 date=%s 대상=%ld 재계산=%ld 총수수료=%ld", + biz_date, total, recalc, sum_fee); - printf("========== 공통 배치 결과 [cm_bt_0014] ==========\n"); + printf("========== 수수료 재계산 [cm_bt_0014] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 대상(M) : %ld\n", total); + printf(" 재계산 건수 : %ld\n", recalc); + printf(" 제외 건수 : %ld\n", skipped); + printf(" 총 수수료 : %ld\n", sum_fee); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); @@ -121,7 +138,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0014] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0014] 수수료재계산 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0015.c b/app/batch/cm_bt_0015.c index a935904..e02d22d 100644 --- a/app/batch/cm_bt_0015.c +++ b/app/batch/cm_bt_0015.c @@ -8,10 +8,12 @@ #line 1 "app/batch/cm_bt_0015.pgc" /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0015.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0015.pgc - 공통 일일요약 리포트 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 공통 원장(cm_ledger)을 표준 DBIO API 로 상태별(R/M/U/S) + * 건수 집계하고 총거래금액을 합산하여 일일 처리요약을 리포트한다. + * 커서를 사용하지 않고 cm_rec_count / cm_rec_sum 만 호출한다. + * 배치 main 엔트리 포함. * * 실행: cm_bt_0015 [db@host] */ @@ -93,121 +95,53 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0015.pgc" +#line 21 "app/batch/cm_bt_0015.pgc" static int run_cm_bt_0015(const char *biz_date) { - /* exec sql begin declare section */ - - - - - - -#line 24 "app/batch/cm_bt_0015.pgc" - char h_biz_date [ 9 ] ; - -#line 25 "app/batch/cm_bt_0015.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/cm_bt_0015.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/cm_bt_0015.pgc" - long h_amount ; - -#line 28 "app/batch/cm_bt_0015.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ -#line 29 "app/batch/cm_bt_0015.pgc" - - - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long n_recv, n_done, n_fail, n_settled, total; + long sum_amt = 0; + char won[32]; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + n_recv = cm_rec_count(biz_date, CM_ST_RECV); + n_done = cm_rec_count(biz_date, CM_ST_DONE); + n_fail = cm_rec_count(biz_date, CM_ST_FAIL); + n_settled = cm_rec_count(biz_date, CM_ST_SETTLED); - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0015 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0015.pgc" - -#line 42 "app/batch/cm_bt_0015.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0015 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0015.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0015"); + if (n_recv < 0 || n_done < 0 || n_fail < 0 || n_settled < 0) { + tx_log(TX_LOG_ERROR, "[cm_bt_0015] 상태별 건수 집계 실패"); return TX_EDB; } + total = n_recv + n_done + n_fail + n_settled; - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cm_bt_0015] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0015", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0015.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0015"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + rc = cm_rec_sum(biz_date, &sum_amt); + if (rc != TX_OK) { + tx_log(TX_LOG_WARN, "[cm_bt_0015] 금액합계 집계 경고 rc=%d", rc); + sum_amt = 0; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0015", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0015.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + memset(won, 0, sizeof(won)); + amount_format_won(sum_amt, won, sizeof(won)); tx_log(TX_LOG_INFO, - "[cm_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0015] 일일요약 date=%s 총=%ld 완료=%ld 실패=%ld 정산=%ld", + biz_date, total, n_done, n_fail, n_settled); - printf("========== 공통 배치 결과 [cm_bt_0015] ==========\n"); + printf("========== 공통 일일요약 [cm_bt_0015] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 접수(R) : %ld\n", n_recv); + printf(" 완료(M) : %ld\n", n_done); + printf(" 실패(U) : %ld\n", n_fail); + printf(" 정산(S) : %ld\n", n_settled); + printf(" 전체 건수 : %ld\n", total); + printf(" 거래총액 : %s\n", won); + if (total > 0) + printf(" 완료율 : %ld%%\n", (n_done * 100) / total); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) @@ -230,7 +164,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0015] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0015] 일일요약 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0015.pgc b/app/batch/cm_bt_0015.pgc index 207466a..b1baa91 100644 --- a/app/batch/cm_bt_0015.pgc +++ b/app/batch/cm_bt_0015.pgc @@ -1,9 +1,11 @@ /* @tier easy @module cm @transform common-batch */ /* - * cm_bt_0015.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=easy] + * cm_bt_0015.pgc - 공통 일일요약 리포트 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 공통 원장(cm_ledger)을 표준 DBIO API 로 상태별(R/M/U/S) + * 건수 집계하고 총거래금액을 합산하여 일일 처리요약을 리포트한다. + * 커서를 사용하지 않고 cm_rec_count / cm_rec_sum 만 호출한다. + * 배치 main 엔트리 포함. * * 실행: cm_bt_0015 [db@host] */ @@ -20,85 +22,48 @@ EXEC SQL INCLUDE sqlca; static int run_cm_bt_0015(const char *biz_date) { - EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; - EXEC SQL END DECLARE SECTION; - - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long n_recv, n_done, n_fail, n_settled, total; + long sum_amt = 0; + char won[32]; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + n_recv = cm_rec_count(biz_date, CM_ST_RECV); + n_done = cm_rec_count(biz_date, CM_ST_DONE); + n_fail = cm_rec_count(biz_date, CM_ST_FAIL); + n_settled = cm_rec_count(biz_date, CM_ST_SETTLED); - EXEC SQL DECLARE cur_cm_bt_0015 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_cm_bt_0015; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0015"); + if (n_recv < 0 || n_done < 0 || n_fail < 0 || n_settled < 0) { + tx_log(TX_LOG_ERROR, "[cm_bt_0015] 상태별 건수 집계 실패"); return TX_EDB; } + total = n_recv + n_done + n_fail + n_settled; - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[cm_bt_0015] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_cm_bt_0015 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0015"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + rc = cm_rec_sum(biz_date, &sum_amt); + if (rc != TX_OK) { + tx_log(TX_LOG_WARN, "[cm_bt_0015] 금액합계 집계 경고 rc=%d", rc); + sum_amt = 0; } - EXEC SQL CLOSE cur_cm_bt_0015; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + memset(won, 0, sizeof(won)); + amount_format_won(sum_amt, won, sizeof(won)); tx_log(TX_LOG_INFO, - "[cm_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[cm_bt_0015] 일일요약 date=%s 총=%ld 완료=%ld 실패=%ld 정산=%ld", + biz_date, total, n_done, n_fail, n_settled); - printf("========== 공통 배치 결과 [cm_bt_0015] ==========\n"); + printf("========== 공통 일일요약 [cm_bt_0015] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 접수(R) : %ld\n", n_recv); + printf(" 완료(M) : %ld\n", n_done); + printf(" 실패(U) : %ld\n", n_fail); + printf(" 정산(S) : %ld\n", n_settled); + printf(" 전체 건수 : %ld\n", total); + printf(" 거래총액 : %s\n", won); + if (total > 0) + printf(" 완료율 : %ld%%\n", (n_done * 100) / total); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) @@ -121,7 +86,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0015] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0015] 일일요약 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0016.c b/app/batch/cm_bt_0016.c index a1b5536..35386fe 100644 --- a/app/batch/cm_bt_0016.c +++ b/app/batch/cm_bt_0016.c @@ -8,10 +8,11 @@ #line 1 "app/batch/cm_bt_0016.pgc" /* @tier medium @module cm @transform common-batch */ /* - * cm_bt_0016.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=medium] + * cm_bt_0016.pgc - 공통 만료 데이터 정리 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 기준일(argv1)로부터 보존기간(기본 90일) 이전의 정산완료(status='S') + * 원장 데이터와 만료 로그(cm_log)를 삭제하여 데이터를 정리한다. 삭제 전 + * 대상 건수를 집계·리포트한 뒤 실제 삭제를 수행한다. 배치 main 포함. * * 실행: cm_bt_0016 [db@host] */ @@ -93,121 +94,126 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/cm_bt_0016.pgc" +#line 20 "app/batch/cm_bt_0016.pgc" +#define CM_RETAIN_DAYS 90 + +/* YYYYMMDD 를 일련일수(대략)로 환산해 보존한도 경계일 문자열 계산. + * 단순 근사(월 30일 기준)로 경계 영업일 문자열을 만든다. */ +static void calc_cutoff(const char *base, char *out, int outlen) +{ + long y, m, d, total; + char buf[9]; + + memset(buf, 0, sizeof(buf)); + strncpy(buf, base, 8); + y = (buf[0]-'0')*1000 + (buf[1]-'0')*100 + (buf[2]-'0')*10 + (buf[3]-'0'); + m = (buf[4]-'0')*10 + (buf[5]-'0'); + d = (buf[6]-'0')*10 + (buf[7]-'0'); + + total = y * 360L + (m - 1) * 30L + d - CM_RETAIN_DAYS; + y = total / 360L; + total %= 360L; + m = total / 30L + 1; + d = total % 30L + 1; + + snprintf(out, outlen, "%04ld%02ld%02ld", y, m, d); +} + static int run_cm_bt_0016(const char *biz_date) { /* exec sql begin declare section */ - - -#line 24 "app/batch/cm_bt_0016.pgc" - char h_biz_date [ 9 ] ; +#line 49 "app/batch/cm_bt_0016.pgc" + char h_cutoff [ 9 ] ; -#line 25 "app/batch/cm_bt_0016.pgc" - char h_key [ 16 ] ; +#line 50 "app/batch/cm_bt_0016.pgc" + long h_led_cnt ; -#line 26 "app/batch/cm_bt_0016.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/cm_bt_0016.pgc" - long h_amount ; - -#line 28 "app/batch/cm_bt_0016.pgc" - char h_new_status [ 2 ] ; +#line 51 "app/batch/cm_bt_0016.pgc" + long h_log_cnt ; /* exec sql end declare section */ -#line 29 "app/batch/cm_bt_0016.pgc" +#line 52 "app/batch/cm_bt_0016.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + char cutoff[9]; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + memset(cutoff, 0, sizeof(cutoff)); + calc_cutoff(biz_date, cutoff, sizeof(cutoff)); + memset(h_cutoff, 0, sizeof(h_cutoff)); + strncpy(h_cutoff, cutoff, sizeof(h_cutoff) - 1); - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_cm_bt_0016 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/cm_bt_0016.pgc" + tx_log(TX_LOG_INFO, "[cm_bt_0016] 보존한도 경계일 cutoff=%s (%d일 보존)", + cutoff, CM_RETAIN_DAYS); -#line 42 "app/batch/cm_bt_0016.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_bt_0016 cursor for select key , merch_id , amount from cm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/cm_bt_0016.pgc" + /* 삭제대상 사전 집계 */ + h_led_cnt = h_log_cnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cm_ledger where status = 'S' and biz_date < $1 ", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_led_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 68 "app/batch/cm_bt_0016.pgc" if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0016"); + TX_DBIO_LOG_ERR("COUNT cm_ledger expired"); + return TX_EDB; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cm_log where log_date < $1 ", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_log_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 76 "app/batch/cm_bt_0016.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("COUNT cm_log expired"); return TX_EDB; } if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[cm_bt_0016] tx_begin 경고"); - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_bt_0016", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/cm_bt_0016.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "delete from cm_ledger where status = 'S' and biz_date < $1 ", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 86 "app/batch/cm_bt_0016.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0016"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("DELETE cm_ledger"); + tx_abort(); + return TX_EDB; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_bt_0016", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/cm_bt_0016.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "delete from cm_log where log_date < $1 ", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 93 "app/batch/cm_bt_0016.pgc" - - if (errcnt == 0) - tx_commit(); - else + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("DELETE cm_log"); tx_abort(); + return TX_EDB; + } - tx_log(TX_LOG_INFO, - "[cm_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_commit(); - printf("========== 공통 배치 결과 [cm_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + tx_log(TX_LOG_INFO, "[cm_bt_0016] 만료정리 원장=%ld 로그=%ld cutoff=%s", + h_led_cnt, h_log_cnt, cutoff); + + printf("========== 만료 데이터 정리 [cm_bt_0016] ==========\n"); + printf(" 기준 영업일 : %s\n", biz_date); + printf(" 보존 한도일 : %s\n", cutoff); + printf(" 원장 삭제 : %ld\n", h_led_cnt); + printf(" 로그 삭제 : %ld\n", h_log_cnt); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) @@ -230,7 +236,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0016] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0016] 만료정리 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/cm_bt_0016.pgc b/app/batch/cm_bt_0016.pgc index ae19a69..73f27a5 100644 --- a/app/batch/cm_bt_0016.pgc +++ b/app/batch/cm_bt_0016.pgc @@ -1,9 +1,10 @@ /* @tier medium @module cm @transform common-batch */ /* - * cm_bt_0016.pgc - 공통 배치 (커서 순회 + 상태전이) [tier=medium] + * cm_bt_0016.pgc - 공통 만료 데이터 정리 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 기준일(argv1)로부터 보존기간(기본 90일) 이전의 정산완료(status='S') + * 원장 데이터와 만료 로그(cm_log)를 삭제하여 데이터를 정리한다. 삭제 전 + * 대상 건수를 집계·리포트한 뒤 실제 삭제를 수행한다. 배치 main 포함. * * 실행: cm_bt_0016 [db@host] */ @@ -18,87 +19,97 @@ EXEC SQL INCLUDE sqlca; +#define CM_RETAIN_DAYS 90 + +/* YYYYMMDD 를 일련일수(대략)로 환산해 보존한도 경계일 문자열 계산. + * 단순 근사(월 30일 기준)로 경계 영업일 문자열을 만든다. */ +static void calc_cutoff(const char *base, char *out, int outlen) +{ + long y, m, d, total; + char buf[9]; + + memset(buf, 0, sizeof(buf)); + strncpy(buf, base, 8); + y = (buf[0]-'0')*1000 + (buf[1]-'0')*100 + (buf[2]-'0')*10 + (buf[3]-'0'); + m = (buf[4]-'0')*10 + (buf[5]-'0'); + d = (buf[6]-'0')*10 + (buf[7]-'0'); + + total = y * 360L + (m - 1) * 30L + d - CM_RETAIN_DAYS; + y = total / 360L; + total %= 360L; + m = total / 30L + 1; + d = total % 30L + 1; + + snprintf(out, outlen, "%04ld%02ld%02ld", y, m, d); +} + static int run_cm_bt_0016(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_cutoff[9]; + long h_led_cnt; + long h_log_cnt; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + char cutoff[9]; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + memset(cutoff, 0, sizeof(cutoff)); + calc_cutoff(biz_date, cutoff, sizeof(cutoff)); + memset(h_cutoff, 0, sizeof(h_cutoff)); + strncpy(h_cutoff, cutoff, sizeof(h_cutoff) - 1); - EXEC SQL DECLARE cur_cm_bt_0016 CURSOR FOR - SELECT key, merch_id, amount - FROM cm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + tx_log(TX_LOG_INFO, "[cm_bt_0016] 보존한도 경계일 cutoff=%s (%d일 보존)", + cutoff, CM_RETAIN_DAYS); - EXEC SQL OPEN cur_cm_bt_0016; + /* 삭제대상 사전 집계 */ + h_led_cnt = h_log_cnt = 0; + EXEC SQL SELECT COUNT(*) INTO :h_led_cnt + FROM cm_ledger + WHERE status = 'S' AND biz_date < :h_cutoff; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_cm_bt_0016"); + TX_DBIO_LOG_ERR("COUNT cm_ledger expired"); + return TX_EDB; + } + + EXEC SQL SELECT COUNT(*) INTO :h_log_cnt + FROM cm_log + WHERE log_date < :h_cutoff; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("COUNT cm_log expired"); return TX_EDB; } if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[cm_bt_0016] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_cm_bt_0016 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_cm_bt_0016"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, CM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, CM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = cm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[cm_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL DELETE FROM cm_ledger + WHERE status = 'S' AND biz_date < :h_cutoff; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("DELETE cm_ledger"); + tx_abort(); + return TX_EDB; } - EXEC SQL CLOSE cur_cm_bt_0016; - - if (errcnt == 0) - tx_commit(); - else + EXEC SQL DELETE FROM cm_log WHERE log_date < :h_cutoff; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("DELETE cm_log"); tx_abort(); + return TX_EDB; + } - tx_log(TX_LOG_INFO, - "[cm_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_commit(); - printf("========== 공통 배치 결과 [cm_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + tx_log(TX_LOG_INFO, "[cm_bt_0016] 만료정리 원장=%ld 로그=%ld cutoff=%s", + h_led_cnt, h_log_cnt, cutoff); + + printf("========== 만료 데이터 정리 [cm_bt_0016] ==========\n"); + printf(" 기준 영업일 : %s\n", biz_date); + printf(" 보존 한도일 : %s\n", cutoff); + printf(" 원장 삭제 : %ld\n", h_led_cnt); + printf(" 로그 삭제 : %ld\n", h_log_cnt); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) @@ -121,7 +132,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[cm_bt_0016] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[cm_bt_0016] 만료정리 배치 시작 date=%s", biz_date); rc = cm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/lg_bt_0002.c b/app/batch/lg_bt_0002.c index 0b58db2..99050d8 100644 --- a/app/batch/lg_bt_0002.c +++ b/app/batch/lg_bt_0002.c @@ -8,10 +8,10 @@ #line 1 "app/batch/lg_bt_0002.pgc" /* @tier hard @module lg @transform ledger-batch */ /* - * lg_bt_0002.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=hard] + * lg_bt_0002.pgc - 정산원장 반영 배치 (완료건 정산전이 + 정산액 집계) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 처리완료(status='M') 원장을 커서로 순회하며 각 건을 + * 정산완료('S')로 전이하고, 정산액/수수료를 누계하여 요약을 출력한다. * * 실행: lg_bt_0002 [db@host] */ @@ -103,7 +103,6 @@ static int run_lg_bt_0002(const char *biz_date) - #line 24 "app/batch/lg_bt_0002.pgc" char h_biz_date [ 9 ] ; @@ -112,34 +111,33 @@ static int run_lg_bt_0002(const char *biz_date) char h_key [ 16 ] ; #line 26 "app/batch/lg_bt_0002.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/lg_bt_0002.pgc" long h_amount ; -#line 28 "app/batch/lg_bt_0002.pgc" - char h_new_status [ 2 ] ; +#line 27 "app/batch/lg_bt_0002.pgc" + long h_fee ; /* exec sql end declare section */ -#line 29 "app/batch/lg_bt_0002.pgc" +#line 28 "app/batch/lg_bt_0002.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, settled = 0, errcnt = 0; + long sum_settle = 0, sum_fee = 0; + char won[32]; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0002 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/lg_bt_0002.pgc" + /* declare cur_lg_bt_0002 cursor for select key , amount , fee from lg_ledger where biz_date = $1 and status = 'M' order by key */ +#line 43 "app/batch/lg_bt_0002.pgc" -#line 42 "app/batch/lg_bt_0002.pgc" +#line 43 "app/batch/lg_bt_0002.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0002 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0002 cursor for select key , amount , fee from lg_ledger where biz_date = $1 and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/lg_bt_0002.pgc" +#line 45 "app/batch/lg_bt_0002.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0002"); @@ -153,11 +151,11 @@ static int run_lg_bt_0002(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_lg_bt_0002", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/lg_bt_0002.pgc" +#line 55 "app/batch/lg_bt_0002.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +168,19 @@ static int run_lg_bt_0002(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); + rc = lg_rec_update_status(h_key, LG_ST_SETTLED); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[lg_bt_0002] 정산전이 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + settled++; + sum_settle += h_amount; + sum_fee += h_fee; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0002", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0002.pgc" +#line 78 "app/batch/lg_bt_0002.pgc" if (errcnt == 0) @@ -195,17 +188,20 @@ static int run_lg_bt_0002(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[lg_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_settle, won, sizeof(won)); - printf("========== 원장 배치 결과 [lg_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[lg_bt_0002] 요약 date=%s 대상=%ld 정산=%ld 정산액=%ld 오류=%ld", + biz_date, total, settled, sum_settle, errcnt); + + printf("========== 정산원장 반영 결과 [lg_bt_0002] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상(M) 건수 : %ld\n", total); + printf(" 정산(S) 건수 : %ld\n", settled); + printf(" 정산액 합계 : %s\n", won); + printf(" 수수료 합계 : %ld\n", sum_fee); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/lg_bt_0002.pgc b/app/batch/lg_bt_0002.pgc index a9f1e12..5d33981 100644 --- a/app/batch/lg_bt_0002.pgc +++ b/app/batch/lg_bt_0002.pgc @@ -1,9 +1,9 @@ /* @tier hard @module lg @transform ledger-batch */ /* - * lg_bt_0002.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=hard] + * lg_bt_0002.pgc - 정산원장 반영 배치 (완료건 정산전이 + 정산액 집계) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 처리완료(status='M') 원장을 커서로 순회하며 각 건을 + * 정산완료('S')로 전이하고, 정산액/수수료를 누계하여 요약을 출력한다. * * 실행: lg_bt_0002 [db@host] */ @@ -23,22 +23,23 @@ static int run_lg_bt_0002(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, settled = 0, errcnt = 0; + long sum_settle = 0, sum_fee = 0; + char won[32]; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_lg_bt_0002 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount, fee FROM lg_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'M' ORDER BY key; EXEC SQL OPEN cur_lg_bt_0002; @@ -51,7 +52,7 @@ static int run_lg_bt_0002(const char *biz_date) tx_log(TX_LOG_WARN, "[lg_bt_0002] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_lg_bt_0002 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_lg_bt_0002 INTO :h_key, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +64,15 @@ static int run_lg_bt_0002(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); + rc = lg_rec_update_status(h_key, LG_ST_SETTLED); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[lg_bt_0002] 정산전이 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + settled++; + sum_settle += h_amount; + sum_fee += h_fee; } EXEC SQL CLOSE cur_lg_bt_0002; @@ -86,17 +82,20 @@ static int run_lg_bt_0002(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[lg_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_settle, won, sizeof(won)); - printf("========== 원장 배치 결과 [lg_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[lg_bt_0002] 요약 date=%s 대상=%ld 정산=%ld 정산액=%ld 오류=%ld", + biz_date, total, settled, sum_settle, errcnt); + + printf("========== 정산원장 반영 결과 [lg_bt_0002] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상(M) 건수 : %ld\n", total); + printf(" 정산(S) 건수 : %ld\n", settled); + printf(" 정산액 합계 : %s\n", won); + printf(" 수수료 합계 : %ld\n", sum_fee); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/lg_bt_0003.c b/app/batch/lg_bt_0003.c index 0bbaea4..419ed4b 100644 --- a/app/batch/lg_bt_0003.c +++ b/app/batch/lg_bt_0003.c @@ -8,10 +8,11 @@ #line 1 "app/batch/lg_bt_0003.pgc" /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0003.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0003.pgc - 원장 대사(對査) 배치 (차변/대변 균형검증) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장의 건수, 차변합(amount>=0), 대변합(amount<0의 절대값), + * 수수료합을 집계하여 차변합과 대변합의 균형을 검증한다. + * 불일치 시 TX_FAIL 로 종료한다. * * 실행: lg_bt_0003 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/lg_bt_0003.pgc" +#line 20 "app/batch/lg_bt_0003.pgc" static int run_lg_bt_0003(const char *biz_date) @@ -105,109 +106,77 @@ static int run_lg_bt_0003(const char *biz_date) -#line 24 "app/batch/lg_bt_0003.pgc" +#line 25 "app/batch/lg_bt_0003.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/lg_bt_0003.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/lg_bt_0003.pgc" - char h_merch_id [ 16 ] ; + long h_cnt ; #line 27 "app/batch/lg_bt_0003.pgc" - long h_amount ; + long h_debit ; #line 28 "app/batch/lg_bt_0003.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_credit ; + #line 29 "app/batch/lg_bt_0003.pgc" + long h_fee ; +/* exec sql end declare section */ +#line 30 "app/batch/lg_bt_0003.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long diff; + int balanced; + char won_d[32], won_c[32], won_diff[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0003 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/lg_bt_0003.pgc" - -#line 42 "app/batch/lg_bt_0003.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0003 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( case when amount >= 0 then amount else 0 end ) , 0 ) , coalesce ( sum ( case when amount < 0 then - amount else 0 end ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from lg_ledger where biz_date = $1 ", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/lg_bt_0003.pgc" + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_debit),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_credit),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 45 "app/batch/lg_bt_0003.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0003"); + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 대사집계 lg_bt_0003"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[lg_bt_0003] tx_begin 경고"); + diff = h_debit - h_credit; + balanced = (diff == 0); - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_lg_bt_0003", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/lg_bt_0003.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_lg_bt_0003"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0003", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0003.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + amount_format_won(h_debit, won_d, sizeof(won_d)); + amount_format_won(h_credit, won_c, sizeof(won_c)); + amount_format_won(diff, won_diff, sizeof(won_diff)); tx_log(TX_LOG_INFO, - "[lg_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[lg_bt_0003] 대사 date=%s 건수=%ld 차변=%ld 대변=%ld 차이=%ld %s", + biz_date, h_cnt, h_debit, h_credit, diff, + balanced ? "균형" : "불일치"); - printf("========== 원장 배치 결과 [lg_bt_0003] ==========\n"); + printf("========== 원장 대사 결과 [lg_bt_0003] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 대상 건수 : %ld\n", h_cnt); + printf(" 차변 합계 : %s\n", won_d); + printf(" 대변 합계 : %s\n", won_c); + printf(" 수수료 합계 : %ld\n", h_fee); + printf(" 차이(차-대) : %s\n", won_diff); + printf(" 대사 결과 : %s\n", balanced ? "균형 (정상)" : "불일치 (오류)"); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + if (!balanced) { + tx_log(TX_LOG_ERROR, "[lg_bt_0003] 대사 불일치 차이=%ld", diff); + return TX_FAIL; + } + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/lg_bt_0003.pgc b/app/batch/lg_bt_0003.pgc index 82c5be5..8200e13 100644 --- a/app/batch/lg_bt_0003.pgc +++ b/app/batch/lg_bt_0003.pgc @@ -1,9 +1,10 @@ /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0003.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0003.pgc - 원장 대사(對査) 배치 (차변/대변 균형검증) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장의 건수, 차변합(amount>=0), 대변합(amount<0의 절대값), + * 수수료합을 집계하여 차변합과 대변합의 균형을 검증한다. + * 불일치 시 TX_FAIL 로 종료한다. * * 실행: lg_bt_0003 [db@host] */ @@ -22,83 +23,59 @@ static int run_lg_bt_0003(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_cnt; + long h_debit; + long h_credit; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long diff; + int balanced; + char won_d[32], won_c[32], won_diff[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - EXEC SQL DECLARE cur_lg_bt_0003 CURSOR FOR - SELECT key, merch_id, amount + EXEC SQL SELECT count(*), + coalesce(sum(case when amount >= 0 then amount else 0 end), 0), + coalesce(sum(case when amount < 0 then -amount else 0 end), 0), + coalesce(sum(fee), 0) + INTO :h_cnt, :h_debit, :h_credit, :h_fee FROM lg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + WHERE biz_date = :h_biz_date; - EXEC SQL OPEN cur_lg_bt_0003; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0003"); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 대사집계 lg_bt_0003"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[lg_bt_0003] tx_begin 경고"); + diff = h_debit - h_credit; + balanced = (diff == 0); - for (;;) { - EXEC SQL FETCH cur_lg_bt_0003 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_lg_bt_0003"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_lg_bt_0003; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + amount_format_won(h_debit, won_d, sizeof(won_d)); + amount_format_won(h_credit, won_c, sizeof(won_c)); + amount_format_won(diff, won_diff, sizeof(won_diff)); tx_log(TX_LOG_INFO, - "[lg_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[lg_bt_0003] 대사 date=%s 건수=%ld 차변=%ld 대변=%ld 차이=%ld %s", + biz_date, h_cnt, h_debit, h_credit, diff, + balanced ? "균형" : "불일치"); - printf("========== 원장 배치 결과 [lg_bt_0003] ==========\n"); + printf("========== 원장 대사 결과 [lg_bt_0003] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 대상 건수 : %ld\n", h_cnt); + printf(" 차변 합계 : %s\n", won_d); + printf(" 대변 합계 : %s\n", won_c); + printf(" 수수료 합계 : %ld\n", h_fee); + printf(" 차이(차-대) : %s\n", won_diff); + printf(" 대사 결과 : %s\n", balanced ? "균형 (정상)" : "불일치 (오류)"); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + if (!balanced) { + tx_log(TX_LOG_ERROR, "[lg_bt_0003] 대사 불일치 차이=%ld", diff); + return TX_FAIL; + } + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/lg_bt_0004.c b/app/batch/lg_bt_0004.c index 45b431b..65bebad 100644 --- a/app/batch/lg_bt_0004.c +++ b/app/batch/lg_bt_0004.c @@ -8,10 +8,11 @@ #line 1 "app/batch/lg_bt_0004.pgc" /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0004.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0004.pgc - 원장 이월 배치 (미결건 차기영업일 이월) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 미결(status IN 'R','U') 원장을 커서로 순회하며 각 건의 + * biz_date 를 다음 영업일로 UPDATE 하여 이월한다. 다음 영업일은 + * date_next_business 로 산출한다. * * 실행: lg_bt_0004 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/lg_bt_0004.pgc" +#line 20 "app/batch/lg_bt_0004.pgc" static int run_lg_bt_0004(const char *biz_date) @@ -102,44 +103,43 @@ static int run_lg_bt_0004(const char *biz_date) - - -#line 24 "app/batch/lg_bt_0004.pgc" +#line 25 "app/batch/lg_bt_0004.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/lg_bt_0004.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/lg_bt_0004.pgc" - char h_merch_id [ 16 ] ; + char h_next [ 9 ] ; #line 27 "app/batch/lg_bt_0004.pgc" - long h_amount ; - -#line 28 "app/batch/lg_bt_0004.pgc" - char h_new_status [ 2 ] ; + char h_key [ 16 ] ; /* exec sql end declare section */ -#line 29 "app/batch/lg_bt_0004.pgc" +#line 28 "app/batch/lg_bt_0004.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, moved = 0, errcnt = 0; + char next_buf[9]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + if (date_next_business(biz_date, next_buf) != 0) { + tx_log(TX_LOG_ERROR, "[lg_bt_0004] 다음 영업일 산출 실패 date=%s", biz_date); + return TX_EINVAL; + } + strncpy(h_next, next_buf, sizeof(h_next) - 1); + h_next[sizeof(h_next) - 1] = '\0'; + ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0004 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/lg_bt_0004.pgc" + /* declare cur_lg_bt_0004 cursor for select key from lg_ledger where biz_date = $1 and status in ( 'R' , 'U' ) order by key */ +#line 48 "app/batch/lg_bt_0004.pgc" -#line 42 "app/batch/lg_bt_0004.pgc" +#line 48 "app/batch/lg_bt_0004.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0004 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0004 cursor for select key from lg_ledger where biz_date = $1 and status in ( 'R' , 'U' ) order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/lg_bt_0004.pgc" +#line 50 "app/batch/lg_bt_0004.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0004"); @@ -152,12 +152,8 @@ static int run_lg_bt_0004(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_lg_bt_0004", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/lg_bt_0004.pgc" +#line 60 "app/batch/lg_bt_0004.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +166,25 @@ static int run_lg_bt_0004(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set biz_date = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_char,(h_next),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 74 "app/batch/lg_bt_0004.pgc" - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE 이월 lg_bt_0004"); errcnt++; + continue; } + if (sqlca.sqlerrd[2] > 0) + moved++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0004", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0004.pgc" +#line 85 "app/batch/lg_bt_0004.pgc" if (errcnt == 0) @@ -196,14 +193,14 @@ static int run_lg_bt_0004(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[lg_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[lg_bt_0004] 요약 date=%s→%s 대상=%ld 이월=%ld 오류=%ld", + biz_date, next_buf, total, moved, errcnt); - printf("========== 원장 배치 결과 [lg_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf("========== 원장 이월 결과 [lg_bt_0004] ==========\n"); + printf(" 기준 영업일 : %s\n", biz_date); + printf(" 이월 영업일 : %s\n", next_buf); + printf(" 미결 건수 : %ld\n", total); + printf(" 이월 건수 : %ld\n", moved); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); diff --git a/app/batch/lg_bt_0004.pgc b/app/batch/lg_bt_0004.pgc index 2d883fe..f177701 100644 --- a/app/batch/lg_bt_0004.pgc +++ b/app/batch/lg_bt_0004.pgc @@ -1,9 +1,10 @@ /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0004.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0004.pgc - 원장 이월 배치 (미결건 차기영업일 이월) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 미결(status IN 'R','U') 원장을 커서로 순회하며 각 건의 + * biz_date 를 다음 영업일로 UPDATE 하여 이월한다. 다음 영업일은 + * date_next_business 로 산출한다. * * 실행: lg_bt_0004 [db@host] */ @@ -22,23 +23,28 @@ static int run_lg_bt_0004(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; + char h_next[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, moved = 0, errcnt = 0; + char next_buf[9]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + if (date_next_business(biz_date, next_buf) != 0) { + tx_log(TX_LOG_ERROR, "[lg_bt_0004] 다음 영업일 산출 실패 date=%s", biz_date); + return TX_EINVAL; + } + strncpy(h_next, next_buf, sizeof(h_next) - 1); + h_next[sizeof(h_next) - 1] = '\0'; + EXEC SQL DECLARE cur_lg_bt_0004 CURSOR FOR - SELECT key, merch_id, amount + SELECT key FROM lg_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status IN ('R', 'U') ORDER BY key; EXEC SQL OPEN cur_lg_bt_0004; @@ -51,7 +57,7 @@ static int run_lg_bt_0004(const char *biz_date) tx_log(TX_LOG_WARN, "[lg_bt_0004] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_lg_bt_0004 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_lg_bt_0004 INTO :h_key; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +69,17 @@ static int run_lg_bt_0004(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + EXEC SQL UPDATE lg_ledger + SET biz_date = :h_next, upd_ts = now() + WHERE key = :h_key; - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE 이월 lg_bt_0004"); errcnt++; + continue; } + if (sqlca.sqlerrd[2] > 0) + moved++; } EXEC SQL CLOSE cur_lg_bt_0004; @@ -87,14 +90,14 @@ static int run_lg_bt_0004(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[lg_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[lg_bt_0004] 요약 date=%s→%s 대상=%ld 이월=%ld 오류=%ld", + biz_date, next_buf, total, moved, errcnt); - printf("========== 원장 배치 결과 [lg_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf("========== 원장 이월 결과 [lg_bt_0004] ==========\n"); + printf(" 기준 영업일 : %s\n", biz_date); + printf(" 이월 영업일 : %s\n", next_buf); + printf(" 미결 건수 : %ld\n", total); + printf(" 이월 건수 : %ld\n", moved); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); diff --git a/app/batch/lg_bt_0005.c b/app/batch/lg_bt_0005.c index b30e8f7..500cebc 100644 --- a/app/batch/lg_bt_0005.c +++ b/app/batch/lg_bt_0005.c @@ -8,10 +8,11 @@ #line 1 "app/batch/lg_bt_0005.pgc" /* @tier medium @module lg @transform ledger-batch */ /* - * lg_bt_0005.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=medium] + * lg_bt_0005.pgc - 계정별(가맹점별) 집계 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장을 가맹점(merch_id) 기준으로 GROUP BY 집계하여 + * 가맹점별 건수/거래금액 합계 리포트를 출력한다. 커서로 그룹 결과를 + * 순회하며 총계를 함께 산출한다. * * 실행: lg_bt_0005 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/lg_bt_0005.pgc" +#line 20 "app/batch/lg_bt_0005.pgc" static int run_lg_bt_0005(const char *biz_date) @@ -103,40 +104,36 @@ static int run_lg_bt_0005(const char *biz_date) - -#line 24 "app/batch/lg_bt_0005.pgc" - char h_biz_date [ 9 ] ; - #line 25 "app/batch/lg_bt_0005.pgc" - char h_key [ 16 ] ; + char h_biz_date [ 9 ] ; #line 26 "app/batch/lg_bt_0005.pgc" char h_merch_id [ 16 ] ; #line 27 "app/batch/lg_bt_0005.pgc" - long h_amount ; + long h_cnt ; #line 28 "app/batch/lg_bt_0005.pgc" - char h_new_status [ 2 ] ; + long h_sum ; /* exec sql end declare section */ #line 29 "app/batch/lg_bt_0005.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long groups = 0, grand_cnt = 0, grand_sum = 0, errcnt = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0005 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_lg_bt_0005 cursor for select merch_id , count ( * ) , coalesce ( sum ( amount ) , 0 ) from lg_ledger where biz_date = $1 group by merch_id order by merch_id */ #line 42 "app/batch/lg_bt_0005.pgc" #line 42 "app/batch/lg_bt_0005.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0005 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0005 cursor for select merch_id , count ( * ) , coalesce ( sum ( amount ) , 0 ) from lg_ledger where biz_date = $1 group by merch_id order by merch_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/lg_bt_0005.pgc" @@ -146,18 +143,21 @@ static int run_lg_bt_0005(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[lg_bt_0005] tx_begin 경고"); + printf("========== 계정별 집계 결과 [lg_bt_0005] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" --------------------------------------------\n"); + printf(" %-16s %8s %18s\n", "가맹점", "건수", "거래금액"); + printf(" --------------------------------------------\n"); for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_lg_bt_0005", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/lg_bt_0005.pgc" +#line 57 "app/batch/lg_bt_0005.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,44 +168,29 @@ static int run_lg_bt_0005(const char *biz_date) break; } - total++; + groups++; + grand_cnt += h_cnt; + grand_sum += h_sum; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + amount_format_won(h_sum, won, sizeof(won)); + printf(" %-16s %8ld %18s\n", h_merch_id, h_cnt, won); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0005", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0005.pgc" +#line 75 "app/batch/lg_bt_0005.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + amount_format_won(grand_sum, won, sizeof(won)); + printf(" --------------------------------------------\n"); + printf(" 가맹점 수 : %ld\n", groups); + printf(" 총 건수 : %ld\n", grand_cnt); + printf(" 총 거래금액 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); tx_log(TX_LOG_INFO, - "[lg_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 원장 배치 결과 [lg_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + "[lg_bt_0005] 요약 date=%s 가맹점=%ld 건수=%ld 금액=%ld 오류=%ld", + biz_date, groups, grand_cnt, grand_sum, errcnt); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/lg_bt_0005.pgc b/app/batch/lg_bt_0005.pgc index 214cd8b..ae8e94f 100644 --- a/app/batch/lg_bt_0005.pgc +++ b/app/batch/lg_bt_0005.pgc @@ -1,9 +1,10 @@ /* @tier medium @module lg @transform ledger-batch */ /* - * lg_bt_0005.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=medium] + * lg_bt_0005.pgc - 계정별(가맹점별) 집계 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장을 가맹점(merch_id) 기준으로 GROUP BY 집계하여 + * 가맹점별 건수/거래금액 합계 리포트를 출력한다. 커서로 그룹 결과를 + * 순회하며 총계를 함께 산출한다. * * 실행: lg_bt_0005 [db@host] */ @@ -22,24 +23,23 @@ static int run_lg_bt_0005(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_cnt; + long h_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long groups = 0, grand_cnt = 0, grand_sum = 0, errcnt = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_lg_bt_0005 CURSOR FOR - SELECT key, merch_id, amount + SELECT merch_id, count(*), coalesce(sum(amount), 0) FROM lg_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + GROUP BY merch_id + ORDER BY merch_id; EXEC SQL OPEN cur_lg_bt_0005; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,11 +47,14 @@ static int run_lg_bt_0005(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[lg_bt_0005] tx_begin 경고"); + printf("========== 계정별 집계 결과 [lg_bt_0005] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" --------------------------------------------\n"); + printf(" %-16s %8s %18s\n", "가맹점", "건수", "거래금액"); + printf(" --------------------------------------------\n"); for (;;) { - EXEC SQL FETCH cur_lg_bt_0005 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_lg_bt_0005 INTO :h_merch_id, :h_cnt, :h_sum; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,42 +64,27 @@ static int run_lg_bt_0005(const char *biz_date) break; } - total++; + groups++; + grand_cnt += h_cnt; + grand_sum += h_sum; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + amount_format_won(h_sum, won, sizeof(won)); + printf(" %-16s %8ld %18s\n", h_merch_id, h_cnt, won); } EXEC SQL CLOSE cur_lg_bt_0005; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + amount_format_won(grand_sum, won, sizeof(won)); + printf(" --------------------------------------------\n"); + printf(" 가맹점 수 : %ld\n", groups); + printf(" 총 건수 : %ld\n", grand_cnt); + printf(" 총 거래금액 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); tx_log(TX_LOG_INFO, - "[lg_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 원장 배치 결과 [lg_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + "[lg_bt_0005] 요약 date=%s 가맹점=%ld 건수=%ld 금액=%ld 오류=%ld", + biz_date, groups, grand_cnt, grand_sum, errcnt); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/lg_bt_0006.c b/app/batch/lg_bt_0006.c index 14c46aa..826cc2c 100644 --- a/app/batch/lg_bt_0006.c +++ b/app/batch/lg_bt_0006.c @@ -8,10 +8,11 @@ #line 1 "app/batch/lg_bt_0006.pgc" /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0006.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0006.pgc - 대변/차변 전표생성 배치 (상계 전표 생성) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 처리완료(status='M') 원장을 커서로 순회하며 각 건에 대해 + * 반대부호(-amount)의 상계 전표행(key+'V')을 INSERT 하여 대변/차변 + * 전표를 생성한다. 생성 건수와 금액을 요약한다. * * 실행: lg_bt_0006 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/lg_bt_0006.pgc" +#line 20 "app/batch/lg_bt_0006.pgc" static int run_lg_bt_0006(const char *biz_date) @@ -104,42 +105,51 @@ static int run_lg_bt_0006(const char *biz_date) + + -#line 24 "app/batch/lg_bt_0006.pgc" +#line 25 "app/batch/lg_bt_0006.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/lg_bt_0006.pgc" +#line 26 "app/batch/lg_bt_0006.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/lg_bt_0006.pgc" +#line 27 "app/batch/lg_bt_0006.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/lg_bt_0006.pgc" +#line 28 "app/batch/lg_bt_0006.pgc" + char h_card_no [ 20 ] ; + +#line 29 "app/batch/lg_bt_0006.pgc" long h_amount ; -#line 28 "app/batch/lg_bt_0006.pgc" - char h_new_status [ 2 ] ; +#line 30 "app/batch/lg_bt_0006.pgc" + char h_vkey [ 16 ] ; + +#line 31 "app/batch/lg_bt_0006.pgc" + long h_vamount ; /* exec sql end declare section */ -#line 29 "app/batch/lg_bt_0006.pgc" +#line 32 "app/batch/lg_bt_0006.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, created = 0, errcnt = 0; + long sum_offset = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0006 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/lg_bt_0006.pgc" + /* declare cur_lg_bt_0006 cursor for select key , merch_id , card_no , amount from lg_ledger where biz_date = $1 and status = 'M' order by key */ +#line 46 "app/batch/lg_bt_0006.pgc" -#line 42 "app/batch/lg_bt_0006.pgc" +#line 46 "app/batch/lg_bt_0006.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0006 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0006 cursor for select key , merch_id , card_no , amount from lg_ledger where biz_date = $1 and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/lg_bt_0006.pgc" +#line 48 "app/batch/lg_bt_0006.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0006"); @@ -155,9 +165,11 @@ static int run_lg_bt_0006(const char *biz_date) ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/lg_bt_0006.pgc" +#line 59 "app/batch/lg_bt_0006.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +182,35 @@ static int run_lg_bt_0006(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* 상계 전표키 = 원키(최대 14자) + 'V', 금액은 반대부호 */ + snprintf(h_vkey, sizeof(h_vkey), "%.14sV", h_key); + h_vamount = -h_amount; - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into lg_ledger ( key , merch_id , card_no , amount , biz_date , status , fee ) values ( $1 , $2 , $3 , $4 , $5 , 'M' , 0 )", + ECPGt_char,(h_vkey),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_vamount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 78 "app/batch/lg_bt_0006.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT 상계전표 lg_bt_0006"); errcnt++; + continue; } + created++; + sum_offset += h_vamount; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0006", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0006.pgc" +#line 89 "app/batch/lg_bt_0006.pgc" if (errcnt == 0) @@ -195,17 +218,19 @@ static int run_lg_bt_0006(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[lg_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_offset, won, sizeof(won)); - printf("========== 원장 배치 결과 [lg_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[lg_bt_0006] 요약 date=%s 대상=%ld 생성=%ld 상계액=%ld 오류=%ld", + biz_date, total, created, sum_offset, errcnt); + + printf("========== 상계 전표생성 결과 [lg_bt_0006] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상(M) 건수 : %ld\n", total); + printf(" 생성 전표건수 : %ld\n", created); + printf(" 상계 금액합계 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/lg_bt_0006.pgc b/app/batch/lg_bt_0006.pgc index 0264222..d4e7b57 100644 --- a/app/batch/lg_bt_0006.pgc +++ b/app/batch/lg_bt_0006.pgc @@ -1,9 +1,10 @@ /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0006.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0006.pgc - 대변/차변 전표생성 배치 (상계 전표 생성) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 처리완료(status='M') 원장을 커서로 순회하며 각 건에 대해 + * 반대부호(-amount)의 상계 전표행(key+'V')을 INSERT 하여 대변/차변 + * 전표를 생성한다. 생성 건수와 금액을 요약한다. * * 실행: lg_bt_0006 [db@host] */ @@ -24,21 +25,24 @@ static int run_lg_bt_0006(const char *biz_date) char h_biz_date[9]; char h_key[16]; char h_merch_id[16]; + char h_card_no[20]; long h_amount; - char h_new_status[2]; + char h_vkey[16]; + long h_vamount; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, created = 0, errcnt = 0; + long sum_offset = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_lg_bt_0006 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, merch_id, card_no, amount FROM lg_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'M' ORDER BY key; EXEC SQL OPEN cur_lg_bt_0006; @@ -51,7 +55,8 @@ static int run_lg_bt_0006(const char *biz_date) tx_log(TX_LOG_WARN, "[lg_bt_0006] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_lg_bt_0006 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_lg_bt_0006 + INTO :h_key, :h_merch_id, :h_card_no, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +68,22 @@ static int run_lg_bt_0006(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* 상계 전표키 = 원키(최대 14자) + 'V', 금액은 반대부호 */ + snprintf(h_vkey, sizeof(h_vkey), "%.14sV", h_key); + h_vamount = -h_amount; - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL INSERT INTO lg_ledger + (key, merch_id, card_no, amount, biz_date, status, fee) + VALUES + (:h_vkey, :h_merch_id, :h_card_no, :h_vamount, :h_biz_date, 'M', 0); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT 상계전표 lg_bt_0006"); errcnt++; + continue; } + created++; + sum_offset += h_vamount; } EXEC SQL CLOSE cur_lg_bt_0006; @@ -86,17 +93,19 @@ static int run_lg_bt_0006(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[lg_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_offset, won, sizeof(won)); - printf("========== 원장 배치 결과 [lg_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[lg_bt_0006] 요약 date=%s 대상=%ld 생성=%ld 상계액=%ld 오류=%ld", + biz_date, total, created, sum_offset, errcnt); + + printf("========== 상계 전표생성 결과 [lg_bt_0006] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상(M) 건수 : %ld\n", total); + printf(" 생성 전표건수 : %ld\n", created); + printf(" 상계 금액합계 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/lg_bt_0007.c b/app/batch/lg_bt_0007.c index 4aec6cd..273f750 100644 --- a/app/batch/lg_bt_0007.c +++ b/app/batch/lg_bt_0007.c @@ -8,10 +8,10 @@ #line 1 "app/batch/lg_bt_0007.pgc" /* @tier medium @module lg @transform ledger-batch */ /* - * lg_bt_0007.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=medium] + * lg_bt_0007.pgc - 원장 미결(R/U) 추출 배치 * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 미결원장(접수 R / 불일치 U)을 커서로 순회하며 목록을 + * 출력하고 미결 합계를 집계한다. 상태를 변경하지 않는 읽기 전용 추출. * * 실행: lg_bt_0007 [db@host] */ @@ -115,28 +115,28 @@ static int run_lg_bt_0007(const char *biz_date) char h_merch_id [ 16 ] ; #line 27 "app/batch/lg_bt_0007.pgc" - long h_amount ; + char h_status [ 2 ] ; #line 28 "app/batch/lg_bt_0007.pgc" - char h_new_status [ 2 ] ; + long h_amount ; /* exec sql end declare section */ #line 29 "app/batch/lg_bt_0007.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long open_recv = 0, open_fail = 0, open_sum = 0, errcnt = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0007 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_lg_bt_0007 cursor for select key , merch_id , status , amount from lg_ledger where biz_date = $1 and status in ( 'R' , 'U' ) order by status , key */ #line 42 "app/batch/lg_bt_0007.pgc" #line 42 "app/batch/lg_bt_0007.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0007 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0007 cursor for select key , merch_id , status , amount from lg_ledger where biz_date = $1 and status in ( 'R' , 'U' ) order by status , key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/lg_bt_0007.pgc" @@ -146,8 +146,8 @@ static int run_lg_bt_0007(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[lg_bt_0007] tx_begin 경고"); + printf("========== 미결원장 추출 [lg_bt_0007] date=%s ==========\n", biz_date); + printf(" %-14s %-14s %-4s %16s\n", "처리키", "가맹점", "상태", "금액"); for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_lg_bt_0007", ECPGt_EOIT, @@ -155,6 +155,8 @@ static int run_lg_bt_0007(const char *biz_date) ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 54 "app/batch/lg_bt_0007.pgc" @@ -168,44 +170,31 @@ static int run_lg_bt_0007(const char *biz_date) break; } - total++; + amount_format_won(h_amount, won, sizeof(won)); + printf(" %-14s %-14s %-4s %16s\n", h_key, h_merch_id, h_status, won); - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (strcmp(h_status, LG_ST_RECV) == 0) + open_recv++; + else + open_fail++; + open_sum += h_amount; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0007", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0007.pgc" +#line 74 "app/batch/lg_bt_0007.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + amount_format_won(open_sum, won, sizeof(won)); + printf("---------------------------------------------------------\n"); + printf(" 접수(R) : %ld 건\n", open_recv); + printf(" 불일치(U) : %ld 건\n", open_fail); + printf(" 미결 합계 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=========================================================\n"); tx_log(TX_LOG_INFO, - "[lg_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 원장 배치 결과 [lg_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + "[lg_bt_0007] 미결추출 date=%s 접수=%ld 불일치=%ld 합계=%ld", + biz_date, open_recv, open_fail, open_sum); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -230,7 +219,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[lg_bt_0007] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[lg_bt_0007] 미결추출 배치 시작 date=%s", biz_date); rc = lg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/lg_bt_0007.pgc b/app/batch/lg_bt_0007.pgc index 973215c..e417e35 100644 --- a/app/batch/lg_bt_0007.pgc +++ b/app/batch/lg_bt_0007.pgc @@ -1,9 +1,9 @@ /* @tier medium @module lg @transform ledger-batch */ /* - * lg_bt_0007.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=medium] + * lg_bt_0007.pgc - 원장 미결(R/U) 추출 배치 * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 미결원장(접수 R / 불일치 U)을 커서로 순회하며 목록을 + * 출력하고 미결 합계를 집계한다. 상태를 변경하지 않는 읽기 전용 추출. * * 실행: lg_bt_0007 [db@host] */ @@ -24,22 +24,22 @@ static int run_lg_bt_0007(const char *biz_date) char h_biz_date[9]; char h_key[16]; char h_merch_id[16]; + char h_status[2]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long open_recv = 0, open_fail = 0, open_sum = 0, errcnt = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_lg_bt_0007 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, merch_id, status, amount FROM lg_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status IN ('R', 'U') + ORDER BY status, key; EXEC SQL OPEN cur_lg_bt_0007; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,11 +47,11 @@ static int run_lg_bt_0007(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[lg_bt_0007] tx_begin 경고"); + printf("========== 미결원장 추출 [lg_bt_0007] date=%s ==========\n", biz_date); + printf(" %-14s %-14s %-4s %16s\n", "처리키", "가맹점", "상태", "금액"); for (;;) { - EXEC SQL FETCH cur_lg_bt_0007 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_lg_bt_0007 INTO :h_key, :h_merch_id, :h_status, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,42 +61,29 @@ static int run_lg_bt_0007(const char *biz_date) break; } - total++; + amount_format_won(h_amount, won, sizeof(won)); + printf(" %-14s %-14s %-4s %16s\n", h_key, h_merch_id, h_status, won); - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (strcmp(h_status, LG_ST_RECV) == 0) + open_recv++; + else + open_fail++; + open_sum += h_amount; } EXEC SQL CLOSE cur_lg_bt_0007; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + amount_format_won(open_sum, won, sizeof(won)); + printf("---------------------------------------------------------\n"); + printf(" 접수(R) : %ld 건\n", open_recv); + printf(" 불일치(U) : %ld 건\n", open_fail); + printf(" 미결 합계 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=========================================================\n"); tx_log(TX_LOG_INFO, - "[lg_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 원장 배치 결과 [lg_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + "[lg_bt_0007] 미결추출 date=%s 접수=%ld 불일치=%ld 합계=%ld", + biz_date, open_recv, open_fail, open_sum); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -121,7 +108,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[lg_bt_0007] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[lg_bt_0007] 미결추출 배치 시작 date=%s", biz_date); rc = lg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/lg_bt_0008.c b/app/batch/lg_bt_0008.c index 3d9d1e1..39bd138 100644 --- a/app/batch/lg_bt_0008.c +++ b/app/batch/lg_bt_0008.c @@ -8,10 +8,10 @@ #line 1 "app/batch/lg_bt_0008.pgc" /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0008.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0008.pgc - 원장 통계 배치 (상태별 건수/금액 집계) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장의 상태별(R/M/U/S) 건수와 금액 합계를 각각 집계하여 + * 통계표를 출력한다. 순수 읽기 전용 집계 배치. * * 실행: lg_bt_0008 [db@host] */ @@ -96,118 +96,89 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/lg_bt_0008.pgc" -static int run_lg_bt_0008(const char *biz_date) +static int stat_one(const char *biz_date, const char *status, + long *out_cnt, long *out_sum) { /* exec sql begin declare section */ - -#line 24 "app/batch/lg_bt_0008.pgc" +#line 25 "app/batch/lg_bt_0008.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/lg_bt_0008.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/lg_bt_0008.pgc" - char h_merch_id [ 16 ] ; + char h_status [ 2 ] ; #line 27 "app/batch/lg_bt_0008.pgc" - long h_amount ; + long h_cnt ; #line 28 "app/batch/lg_bt_0008.pgc" - char h_new_status [ 2 ] ; + long h_sum ; /* exec sql end declare section */ #line 29 "app/batch/lg_bt_0008.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, status, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0008 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/lg_bt_0008.pgc" - -#line 42 "app/batch/lg_bt_0008.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0008 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from lg_ledger where biz_date = $1 and status = $2 ", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/lg_bt_0008.pgc" + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 40 "app/batch/lg_bt_0008.pgc" + if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0008"); + TX_DBIO_LOG_ERR("stat_one"); return TX_EDB; } + *out_cnt = h_cnt; + *out_sum = h_sum; + return TX_OK; +} - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[lg_bt_0008] tx_begin 경고"); +static int run_lg_bt_0008(const char *biz_date) +{ + struct { const char *code; const char *label; } row[4] = { + { LG_ST_RECV, "접수(R) " }, + { LG_ST_DONE, "완료(M) " }, + { LG_ST_FAIL, "불일치(U)" }, + { LG_ST_SETTLED, "정산(S) " }, + }; + long cnt = 0, sum = 0, tot_cnt = 0, tot_sum = 0; + char won[32]; + int i, rc; - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_lg_bt_0008", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/lg_bt_0008.pgc" + printf("========== 원장 통계 [lg_bt_0008] date=%s ==========\n", biz_date); + printf(" %-10s %10s %18s\n", "상태", "건수", "금액합계"); - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_lg_bt_0008"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + for (i = 0; i < 4; i++) { + rc = stat_one(biz_date, row[i].code, &cnt, &sum); + if (rc != TX_OK) + return rc; + amount_format_won(sum, won, sizeof(won)); + printf(" %-10s %10ld %18s\n", row[i].label, cnt, won); + tot_cnt += cnt; + tot_sum += sum; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0008", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0008.pgc" + amount_format_won(tot_sum, won, sizeof(won)); + printf("------------------------------------------------------\n"); + printf(" %-10s %10ld %18s\n", "합계", tot_cnt, won); + printf("======================================================\n"); - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[lg_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 원장 배치 결과 [lg_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + tx_log(TX_LOG_INFO, "[lg_bt_0008] 통계 date=%s 총건수=%ld 총금액=%ld", + biz_date, tot_cnt, tot_sum); + return TX_OK; } int main(int argc, char **argv) @@ -230,7 +201,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[lg_bt_0008] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[lg_bt_0008] 통계 배치 시작 date=%s", biz_date); rc = lg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/lg_bt_0008.pgc b/app/batch/lg_bt_0008.pgc index b5361a5..bcabcc2 100644 --- a/app/batch/lg_bt_0008.pgc +++ b/app/batch/lg_bt_0008.pgc @@ -1,9 +1,9 @@ /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0008.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0008.pgc - 원장 통계 배치 (상태별 건수/금액 집계) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장의 상태별(R/M/U/S) 건수와 금액 합계를 각각 집계하여 + * 통계표를 출력한다. 순수 읽기 전용 집계 배치. * * 실행: lg_bt_0008 [db@host] */ @@ -18,87 +18,69 @@ EXEC SQL INCLUDE sqlca; -static int run_lg_bt_0008(const char *biz_date) +static int stat_one(const char *biz_date, const char *status, + long *out_cnt, long *out_sum) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_status[2]; + long h_cnt; + long h_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, status, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; - EXEC SQL DECLARE cur_lg_bt_0008 CURSOR FOR - SELECT key, merch_id, amount + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_cnt, :h_sum FROM lg_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status = :h_status; - EXEC SQL OPEN cur_lg_bt_0008; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0008"); + TX_DBIO_LOG_ERR("stat_one"); return TX_EDB; } + *out_cnt = h_cnt; + *out_sum = h_sum; + return TX_OK; +} - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[lg_bt_0008] tx_begin 경고"); +static int run_lg_bt_0008(const char *biz_date) +{ + struct { const char *code; const char *label; } row[4] = { + { LG_ST_RECV, "접수(R) " }, + { LG_ST_DONE, "완료(M) " }, + { LG_ST_FAIL, "불일치(U)" }, + { LG_ST_SETTLED, "정산(S) " }, + }; + long cnt = 0, sum = 0, tot_cnt = 0, tot_sum = 0; + char won[32]; + int i, rc; - for (;;) { - EXEC SQL FETCH cur_lg_bt_0008 INTO :h_key, :h_merch_id, :h_amount; + printf("========== 원장 통계 [lg_bt_0008] date=%s ==========\n", biz_date); + printf(" %-10s %10s %18s\n", "상태", "건수", "금액합계"); - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_lg_bt_0008"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + for (i = 0; i < 4; i++) { + rc = stat_one(biz_date, row[i].code, &cnt, &sum); + if (rc != TX_OK) + return rc; + amount_format_won(sum, won, sizeof(won)); + printf(" %-10s %10ld %18s\n", row[i].label, cnt, won); + tot_cnt += cnt; + tot_sum += sum; } - EXEC SQL CLOSE cur_lg_bt_0008; + amount_format_won(tot_sum, won, sizeof(won)); + printf("------------------------------------------------------\n"); + printf(" %-10s %10ld %18s\n", "합계", tot_cnt, won); + printf("======================================================\n"); - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[lg_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 원장 배치 결과 [lg_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + tx_log(TX_LOG_INFO, "[lg_bt_0008] 통계 date=%s 총건수=%ld 총금액=%ld", + biz_date, tot_cnt, tot_sum); + return TX_OK; } int main(int argc, char **argv) @@ -121,7 +103,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[lg_bt_0008] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[lg_bt_0008] 통계 배치 시작 date=%s", biz_date); rc = lg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/lg_bt_0009.c b/app/batch/lg_bt_0009.c index 34a4e97..08ef1ac 100644 --- a/app/batch/lg_bt_0009.c +++ b/app/batch/lg_bt_0009.c @@ -8,10 +8,10 @@ #line 1 "app/batch/lg_bt_0009.pgc" /* @tier medium @module lg @transform ledger-batch */ /* - * lg_bt_0009.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=medium] + * lg_bt_0009.pgc - 원장 잔액(수수료) 재계산 배치 * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 전건을 커서로 순회하며 수수료(fee)를 amount/100 규칙으로 + * 재산정하여 UPDATE 한다. 재계산된 순액 합계를 집계한다. * * 실행: lg_bt_0009 [db@host] */ @@ -112,34 +112,33 @@ static int run_lg_bt_0009(const char *biz_date) char h_key [ 16 ] ; #line 26 "app/batch/lg_bt_0009.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/lg_bt_0009.pgc" long h_amount ; +#line 27 "app/batch/lg_bt_0009.pgc" + long h_old_fee ; + #line 28 "app/batch/lg_bt_0009.pgc" - char h_new_status [ 2 ] ; + long h_new_fee ; /* exec sql end declare section */ #line 29 "app/batch/lg_bt_0009.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, changed = 0, net_sum = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0009 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/lg_bt_0009.pgc" + /* declare cur_lg_bt_0009 cursor for select key , amount , fee from lg_ledger where biz_date = $1 order by key */ +#line 40 "app/batch/lg_bt_0009.pgc" -#line 42 "app/batch/lg_bt_0009.pgc" +#line 40 "app/batch/lg_bt_0009.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0009 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0009 cursor for select key , amount , fee from lg_ledger where biz_date = $1 order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/lg_bt_0009.pgc" +#line 42 "app/batch/lg_bt_0009.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0009"); @@ -153,11 +152,11 @@ static int run_lg_bt_0009(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_lg_bt_0009", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_old_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/lg_bt_0009.pgc" +#line 52 "app/batch/lg_bt_0009.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,25 +168,30 @@ static int run_lg_bt_0009(const char *biz_date) } total++; + h_new_fee = h_amount / 100; + net_sum += (h_amount - h_new_fee); - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + if (h_new_fee == h_old_fee) + continue; - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set fee = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_long,&(h_new_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 71 "app/batch/lg_bt_0009.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE fee"); errcnt++; + continue; } + changed++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0009", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0009.pgc" +#line 81 "app/batch/lg_bt_0009.pgc" if (errcnt == 0) @@ -195,17 +199,17 @@ static int run_lg_bt_0009(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[lg_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 원장 배치 결과 [lg_bt_0009] ==========\n"); + printf("========== 원장 잔액 재계산 [lg_bt_0009] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 수수료 갱신 : %ld\n", changed); + printf(" 순액 합계 : %ld\n", net_sum); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("===================================================\n"); + + tx_log(TX_LOG_INFO, + "[lg_bt_0009] 재계산 date=%s 대상=%ld 갱신=%ld 순액=%ld", + biz_date, total, changed, net_sum); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -230,7 +234,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[lg_bt_0009] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[lg_bt_0009] 재계산 배치 시작 date=%s", biz_date); rc = lg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/lg_bt_0009.pgc b/app/batch/lg_bt_0009.pgc index 70f7aec..ac4e929 100644 --- a/app/batch/lg_bt_0009.pgc +++ b/app/batch/lg_bt_0009.pgc @@ -1,9 +1,9 @@ /* @tier medium @module lg @transform ledger-batch */ /* - * lg_bt_0009.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=medium] + * lg_bt_0009.pgc - 원장 잔액(수수료) 재계산 배치 * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 전건을 커서로 순회하며 수수료(fee)를 amount/100 규칙으로 + * 재산정하여 UPDATE 한다. 재계산된 순액 합계를 집계한다. * * 실행: lg_bt_0009 [db@host] */ @@ -23,22 +23,20 @@ static int run_lg_bt_0009(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; + long h_old_fee; + long h_new_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, changed = 0, net_sum = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_lg_bt_0009 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount, fee FROM lg_ledger WHERE biz_date = :h_biz_date - AND status = 'R' ORDER BY key; EXEC SQL OPEN cur_lg_bt_0009; @@ -51,7 +49,7 @@ static int run_lg_bt_0009(const char *biz_date) tx_log(TX_LOG_WARN, "[lg_bt_0009] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_lg_bt_0009 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_lg_bt_0009 INTO :h_key, :h_amount, :h_old_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,21 +60,22 @@ static int run_lg_bt_0009(const char *biz_date) } total++; + h_new_fee = h_amount / 100; + net_sum += (h_amount - h_new_fee); - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + if (h_new_fee == h_old_fee) + continue; - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL UPDATE lg_ledger + SET fee = :h_new_fee, upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE fee"); errcnt++; + continue; } + changed++; } EXEC SQL CLOSE cur_lg_bt_0009; @@ -86,17 +85,17 @@ static int run_lg_bt_0009(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[lg_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 원장 배치 결과 [lg_bt_0009] ==========\n"); + printf("========== 원장 잔액 재계산 [lg_bt_0009] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 수수료 갱신 : %ld\n", changed); + printf(" 순액 합계 : %ld\n", net_sum); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("===================================================\n"); + + tx_log(TX_LOG_INFO, + "[lg_bt_0009] 재계산 date=%s 대상=%ld 갱신=%ld 순액=%ld", + biz_date, total, changed, net_sum); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -121,7 +120,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[lg_bt_0009] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[lg_bt_0009] 재계산 배치 시작 date=%s", biz_date); rc = lg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/lg_bt_0010.c b/app/batch/lg_bt_0010.c index 08a0a56..d6f6ce0 100644 --- a/app/batch/lg_bt_0010.c +++ b/app/batch/lg_bt_0010.c @@ -8,10 +8,10 @@ #line 1 "app/batch/lg_bt_0010.pgc" /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0010.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0010.pgc - 원장 정정 배치 (불일치 재판정) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 불일치(U) 건을 커서로 순회하며 업무규칙(금액 유효성)으로 + * 재판정한다. 유효하면 완료(M)로 정정하고, 여전히 무효면 잔여로 남긴다. * * 실행: lg_bt_0010 [db@host] */ @@ -102,8 +102,6 @@ static int run_lg_bt_0010(const char *biz_date) - - #line 24 "app/batch/lg_bt_0010.pgc" char h_biz_date [ 9 ] ; @@ -112,34 +110,28 @@ static int run_lg_bt_0010(const char *biz_date) char h_key [ 16 ] ; #line 26 "app/batch/lg_bt_0010.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/lg_bt_0010.pgc" long h_amount ; - -#line 28 "app/batch/lg_bt_0010.pgc" - char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/lg_bt_0010.pgc" +#line 27 "app/batch/lg_bt_0010.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, fixed = 0, remain = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0010 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/lg_bt_0010.pgc" + /* declare cur_lg_bt_0010 cursor for select key , amount from lg_ledger where biz_date = $1 and status = 'U' order by key */ +#line 40 "app/batch/lg_bt_0010.pgc" -#line 42 "app/batch/lg_bt_0010.pgc" +#line 40 "app/batch/lg_bt_0010.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0010 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0010 cursor for select key , amount from lg_ledger where biz_date = $1 and status = 'U' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/lg_bt_0010.pgc" +#line 42 "app/batch/lg_bt_0010.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0010"); @@ -153,11 +145,9 @@ static int run_lg_bt_0010(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_lg_bt_0010", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/lg_bt_0010.pgc" +#line 52 "app/batch/lg_bt_0010.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +160,23 @@ static int run_lg_bt_0010(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (!amount_is_valid(h_amount)) { + remain++; + continue; } - rc = lg_rec_update_status(h_key, h_new_status); + rc = lg_rec_update_status(h_key, LG_ST_DONE); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[lg_bt_0010] 정정 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + fixed++; + tx_log(TX_LOG_DEBUG, "[lg_bt_0010] 정정 U->M key=%s", h_key); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0010", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0010.pgc" +#line 79 "app/batch/lg_bt_0010.pgc" if (errcnt == 0) @@ -195,17 +184,17 @@ static int run_lg_bt_0010(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[lg_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 원장 배치 결과 [lg_bt_0010] ==========\n"); + printf("========== 원장 정정 배치 [lg_bt_0010] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 불일치 대상 : %ld\n", total); + printf(" 정정(U->M) : %ld\n", fixed); + printf(" 잔여(U) : %ld\n", remain); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("=================================================\n"); + + tx_log(TX_LOG_INFO, + "[lg_bt_0010] 정정 date=%s 대상=%ld 정정=%ld 잔여=%ld", + biz_date, total, fixed, remain); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -230,7 +219,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[lg_bt_0010] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[lg_bt_0010] 정정 배치 시작 date=%s", biz_date); rc = lg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/lg_bt_0010.pgc b/app/batch/lg_bt_0010.pgc index 945a40d..b585064 100644 --- a/app/batch/lg_bt_0010.pgc +++ b/app/batch/lg_bt_0010.pgc @@ -1,9 +1,9 @@ /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0010.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0010.pgc - 원장 정정 배치 (불일치 재판정) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 불일치(U) 건을 커서로 순회하며 업무규칙(금액 유효성)으로 + * 재판정한다. 유효하면 완료(M)로 정정하고, 여전히 무효면 잔여로 남긴다. * * 실행: lg_bt_0010 [db@host] */ @@ -23,22 +23,20 @@ static int run_lg_bt_0010(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, fixed = 0, remain = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_lg_bt_0010 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount FROM lg_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'U' ORDER BY key; EXEC SQL OPEN cur_lg_bt_0010; @@ -51,7 +49,7 @@ static int run_lg_bt_0010(const char *biz_date) tx_log(TX_LOG_WARN, "[lg_bt_0010] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_lg_bt_0010 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_lg_bt_0010 INTO :h_key, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +61,19 @@ static int run_lg_bt_0010(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (!amount_is_valid(h_amount)) { + remain++; + continue; } - rc = lg_rec_update_status(h_key, h_new_status); + rc = lg_rec_update_status(h_key, LG_ST_DONE); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[lg_bt_0010] 정정 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + fixed++; + tx_log(TX_LOG_DEBUG, "[lg_bt_0010] 정정 U->M key=%s", h_key); } EXEC SQL CLOSE cur_lg_bt_0010; @@ -86,17 +83,17 @@ static int run_lg_bt_0010(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[lg_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 원장 배치 결과 [lg_bt_0010] ==========\n"); + printf("========== 원장 정정 배치 [lg_bt_0010] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 불일치 대상 : %ld\n", total); + printf(" 정정(U->M) : %ld\n", fixed); + printf(" 잔여(U) : %ld\n", remain); printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("=================================================\n"); + + tx_log(TX_LOG_INFO, + "[lg_bt_0010] 정정 date=%s 대상=%ld 정정=%ld 잔여=%ld", + biz_date, total, fixed, remain); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -121,7 +118,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[lg_bt_0010] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[lg_bt_0010] 정정 배치 시작 date=%s", biz_date); rc = lg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/lg_bt_0011.c b/app/batch/lg_bt_0011.c index df6e6d8..b08e43d 100644 --- a/app/batch/lg_bt_0011.c +++ b/app/batch/lg_bt_0011.c @@ -8,10 +8,10 @@ #line 1 "app/batch/lg_bt_0011.pgc" /* @tier hard @module lg @transform ledger-batch */ /* - * lg_bt_0011.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=hard] + * lg_bt_0011.pgc - 원장 마감연계 배치 (완료건 일괄 정산전이) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 완료(M) 건을 정산(S)으로 일괄 UPDATE 하여 마감에 연계한다. + * 연계 전 정산대상 합계를 검증하고, 연계 후 실제 반영 건수를 대조한다. * * 실행: lg_bt_0011 [db@host] */ @@ -103,111 +103,87 @@ static int run_lg_bt_0011(const char *biz_date) - #line 24 "app/batch/lg_bt_0011.pgc" char h_biz_date [ 9 ] ; #line 25 "app/batch/lg_bt_0011.pgc" - char h_key [ 16 ] ; + long h_pre_cnt ; #line 26 "app/batch/lg_bt_0011.pgc" - char h_merch_id [ 16 ] ; + long h_pre_sum ; #line 27 "app/batch/lg_bt_0011.pgc" - long h_amount ; - -#line 28 "app/batch/lg_bt_0011.pgc" - char h_new_status [ 2 ] ; + long h_affected ; /* exec sql end declare section */ -#line 29 "app/batch/lg_bt_0011.pgc" +#line 28 "app/batch/lg_bt_0011.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0011 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/lg_bt_0011.pgc" - -#line 42 "app/batch/lg_bt_0011.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0011 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", + /* 1) 연계 대상(완료건) 사전 집계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from lg_ledger where biz_date = $1 and status = 'M'", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/lg_bt_0011.pgc" + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_pre_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_pre_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 40 "app/batch/lg_bt_0011.pgc" + if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0011"); + TX_DBIO_LOG_ERR("사전집계"); return TX_EDB; } + if (h_pre_cnt == 0) { + tx_log(TX_LOG_WARN, "[lg_bt_0011] 연계대상 없음 date=%s", biz_date); + printf("[lg_bt_0011] 마감연계 대상 없음 (date=%s)\n", biz_date); + return TX_OK; + } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[lg_bt_0011] tx_begin 경고"); - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_lg_bt_0011", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/lg_bt_0011.pgc" + /* 2) 완료(M) -> 정산(S) 일괄 전이 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set status = 'S' , upd_ts = now ( ) where biz_date = $1 and status = 'M'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 60 "app/batch/lg_bt_0011.pgc" - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_lg_bt_0011"); - errcnt++; - break; - } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("마감연계 UPDATE"); + tx_abort(); + return TX_EDB; + } + h_affected = sqlca.sqlerrd[2]; - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 3) 대조: 사전 건수 == 실제 반영 건수 */ + if (h_affected != h_pre_cnt) { + tx_log(TX_LOG_ERROR, "[lg_bt_0011] 건수 불일치 사전=%ld 반영=%ld", + (long)h_pre_cnt, (long)h_affected); + tx_abort(); + return TX_FAIL; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0011", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0011.pgc" + tx_commit(); - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[lg_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 원장 배치 결과 [lg_bt_0011] ==========\n"); + amount_format_won(h_pre_sum, won, sizeof(won)); + printf("========== 원장 마감연계 [lg_bt_0011] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" 연계 건수 : %ld\n", (long)h_affected); + printf(" 정산 합계 : %s\n", won); + printf("================================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + tx_log(TX_LOG_INFO, "[lg_bt_0011] 마감연계 date=%s 건수=%ld 합계=%ld", + biz_date, (long)h_affected, (long)h_pre_sum); + + return TX_OK; } int main(int argc, char **argv) @@ -230,7 +206,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[lg_bt_0011] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[lg_bt_0011] 마감연계 배치 시작 date=%s", biz_date); rc = lg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/lg_bt_0011.pgc b/app/batch/lg_bt_0011.pgc index aff3d0e..c74e4b0 100644 --- a/app/batch/lg_bt_0011.pgc +++ b/app/batch/lg_bt_0011.pgc @@ -1,9 +1,9 @@ /* @tier hard @module lg @transform ledger-batch */ /* - * lg_bt_0011.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=hard] + * lg_bt_0011.pgc - 원장 마감연계 배치 (완료건 일괄 정산전이) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 완료(M) 건을 정산(S)으로 일괄 UPDATE 하여 마감에 연계한다. + * 연계 전 정산대상 합계를 검증하고, 연계 후 실제 반영 건수를 대조한다. * * 실행: lg_bt_0011 [db@host] */ @@ -22,83 +22,71 @@ static int run_lg_bt_0011(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_pre_cnt; + long h_pre_sum; + long h_affected; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - EXEC SQL DECLARE cur_lg_bt_0011 CURSOR FOR - SELECT key, merch_id, amount + /* 1) 연계 대상(완료건) 사전 집계 */ + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_pre_cnt, :h_pre_sum FROM lg_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status = 'M'; - EXEC SQL OPEN cur_lg_bt_0011; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0011"); + TX_DBIO_LOG_ERR("사전집계"); return TX_EDB; } + if (h_pre_cnt == 0) { + tx_log(TX_LOG_WARN, "[lg_bt_0011] 연계대상 없음 date=%s", biz_date); + printf("[lg_bt_0011] 마감연계 대상 없음 (date=%s)\n", biz_date); + return TX_OK; + } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[lg_bt_0011] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_lg_bt_0011 INTO :h_key, :h_merch_id, :h_amount; + /* 2) 완료(M) -> 정산(S) 일괄 전이 */ + EXEC SQL UPDATE lg_ledger + SET status = 'S', upd_ts = now() + WHERE biz_date = :h_biz_date + AND status = 'M'; - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_lg_bt_0011"); - errcnt++; - break; - } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("마감연계 UPDATE"); + tx_abort(); + return TX_EDB; + } + h_affected = sqlca.sqlerrd[2]; - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 3) 대조: 사전 건수 == 실제 반영 건수 */ + if (h_affected != h_pre_cnt) { + tx_log(TX_LOG_ERROR, "[lg_bt_0011] 건수 불일치 사전=%ld 반영=%ld", + (long)h_pre_cnt, (long)h_affected); + tx_abort(); + return TX_FAIL; } - EXEC SQL CLOSE cur_lg_bt_0011; + tx_commit(); - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[lg_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 원장 배치 결과 [lg_bt_0011] ==========\n"); + amount_format_won(h_pre_sum, won, sizeof(won)); + printf("========== 원장 마감연계 [lg_bt_0011] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf(" 연계 건수 : %ld\n", (long)h_affected); + printf(" 정산 합계 : %s\n", won); + printf("================================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + tx_log(TX_LOG_INFO, "[lg_bt_0011] 마감연계 date=%s 건수=%ld 합계=%ld", + biz_date, (long)h_affected, (long)h_pre_sum); + + return TX_OK; } int main(int argc, char **argv) @@ -121,7 +109,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[lg_bt_0011] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[lg_bt_0011] 마감연계 배치 시작 date=%s", biz_date); rc = lg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/lg_bt_0012.c b/app/batch/lg_bt_0012.c index 04b89a3..4eac28d 100644 --- a/app/batch/lg_bt_0012.c +++ b/app/batch/lg_bt_0012.c @@ -8,12 +8,12 @@ #line 1 "app/batch/lg_bt_0012.pgc" /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0012.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0012.pgc - diff 검증 배치 (두 영업일 대비) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 인자로 받은 두 영업일 D1, D2 각각의 건수/거래금액 합계를 SELECT 하여 + * 두 일자 간 차이를 리포트한다. 인자 3개(프로그램명 포함)를 요구한다. * - * 실행: lg_bt_0012 [db@host] + * 실행: lg_bt_0012 [db@host] */ #include #include @@ -96,141 +96,118 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/lg_bt_0012.pgc" -static int run_lg_bt_0012(const char *biz_date) +static int run_lg_bt_0012(const char *d1, const char *d2) { /* exec sql begin declare section */ - - #line 24 "app/batch/lg_bt_0012.pgc" - char h_biz_date [ 9 ] ; + char h_date [ 9 ] ; #line 25 "app/batch/lg_bt_0012.pgc" - char h_key [ 16 ] ; + long h_cnt ; #line 26 "app/batch/lg_bt_0012.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/lg_bt_0012.pgc" - long h_amount ; - -#line 28 "app/batch/lg_bt_0012.pgc" - char h_new_status [ 2 ] ; + long h_sum ; /* exec sql end declare section */ -#line 29 "app/batch/lg_bt_0012.pgc" +#line 27 "app/batch/lg_bt_0012.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long cnt1 = 0, cnt2 = 0, sum1 = 0, sum2 = 0; + long dcnt, dsum; + char won1[32], won2[32], wond[32]; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* D1 집계 */ + strncpy(h_date, d1, sizeof(h_date) - 1); + h_date[sizeof(h_date) - 1] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from lg_ledger where biz_date = $1 ", + ECPGt_char,(h_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 39 "app/batch/lg_bt_0012.pgc" - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0012 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/lg_bt_0012.pgc" - -#line 42 "app/batch/lg_bt_0012.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0012 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/lg_bt_0012.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0012"); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT D1 lg_bt_0012"); return TX_EDB; } + cnt1 = h_cnt; + sum1 = h_sum; - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[lg_bt_0012] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_lg_bt_0012", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + /* D2 집계 */ + strncpy(h_date, d2, sizeof(h_date) - 1); + h_date[sizeof(h_date) - 1] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from lg_ledger where biz_date = $1 ", + ECPGt_char,(h_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/lg_bt_0012.pgc" +#line 53 "app/batch/lg_bt_0012.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_lg_bt_0012"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT D2 lg_bt_0012"); + return TX_EDB; } + cnt2 = h_cnt; + sum2 = h_sum; - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0012", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0012.pgc" + dcnt = cnt2 - cnt1; + dsum = sum2 - sum1; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + amount_format_won(sum1, won1, sizeof(won1)); + amount_format_won(sum2, won2, sizeof(won2)); + amount_format_won(dsum, wond, sizeof(wond)); tx_log(TX_LOG_INFO, - "[lg_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[lg_bt_0012] diff D1=%s(%ld/%ld) D2=%s(%ld/%ld) Δ건수=%ld Δ금액=%ld", + d1, cnt1, sum1, d2, cnt2, sum2, dcnt, dsum); - printf("========== 원장 배치 결과 [lg_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 원장 diff 검증 결과 [lg_bt_0012] ==========\n"); + printf(" 기준일 D1 : %s 건수=%ld 금액=%s\n", d1, cnt1, won1); + printf(" 비교일 D2 : %s 건수=%ld 금액=%s\n", d2, cnt2, won2); + printf(" ----------------------------------------------\n"); + printf(" 건수 차이(D2-D1) : %ld\n", dcnt); + printf(" 금액 차이(D2-D1) : %s\n", wond); + printf(" 판정 : %s\n", + (dcnt == 0 && dsum == 0) ? "동일" : "차이 있음"); + printf("==================================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) { - const char *biz_date; + const char *d1; + const char *d2; const char *target; int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { - fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); + if (argc < 3) { + fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } - biz_date = argv[1]; - target = (argc >= 3) ? argv[2] : NULL; + d1 = argv[1]; + d2 = argv[2]; + target = (argc >= 4) ? argv[3] : NULL; - if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + if (!date_is_valid(d1)) { + fprintf(stderr, "오류: 유효하지 않은 영업일 D1 '%s'\n", d1); + return 2; + } + if (!date_is_valid(d2)) { + fprintf(stderr, "오류: 유효하지 않은 영업일 D2 '%s'\n", d2); return 2; } - tx_log(TX_LOG_INFO, "[lg_bt_0012] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[lg_bt_0012] 배치 시작 D1=%s D2=%s", d1, d2); rc = lg_dbio_connect(target); if (rc != TX_OK) { @@ -238,7 +215,7 @@ int main(int argc, char **argv) return 1; } - rc = run_lg_bt_0012(biz_date); + rc = run_lg_bt_0012(d1, d2); lg_dbio_disconnect(); diff --git a/app/batch/lg_bt_0012.pgc b/app/batch/lg_bt_0012.pgc index 3dde87b..545beef 100644 --- a/app/batch/lg_bt_0012.pgc +++ b/app/batch/lg_bt_0012.pgc @@ -1,11 +1,11 @@ /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0012.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0012.pgc - diff 검증 배치 (두 영업일 대비) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 인자로 받은 두 영업일 D1, D2 각각의 건수/거래금액 합계를 SELECT 하여 + * 두 일자 간 차이를 리포트한다. 인자 3개(프로그램명 포함)를 요구한다. * - * 실행: lg_bt_0012 [db@host] + * 실행: lg_bt_0012 [db@host] */ #include #include @@ -18,110 +18,97 @@ EXEC SQL INCLUDE sqlca; -static int run_lg_bt_0012(const char *biz_date) +static int run_lg_bt_0012(const char *d1, const char *d2) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_date[9]; + long h_cnt; + long h_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long cnt1 = 0, cnt2 = 0, sum1 = 0, sum2 = 0; + long dcnt, dsum; + char won1[32], won2[32], wond[32]; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - EXEC SQL DECLARE cur_lg_bt_0012 CURSOR FOR - SELECT key, merch_id, amount + /* D1 집계 */ + strncpy(h_date, d1, sizeof(h_date) - 1); + h_date[sizeof(h_date) - 1] = '\0'; + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_cnt, :h_sum FROM lg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_lg_bt_0012; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0012"); + WHERE biz_date = :h_date; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT D1 lg_bt_0012"); return TX_EDB; } + cnt1 = h_cnt; + sum1 = h_sum; - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[lg_bt_0012] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_lg_bt_0012 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_lg_bt_0012"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* D2 집계 */ + strncpy(h_date, d2, sizeof(h_date) - 1); + h_date[sizeof(h_date) - 1] = '\0'; + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_cnt, :h_sum + FROM lg_ledger + WHERE biz_date = :h_date; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT D2 lg_bt_0012"); + return TX_EDB; } + cnt2 = h_cnt; + sum2 = h_sum; - EXEC SQL CLOSE cur_lg_bt_0012; + dcnt = cnt2 - cnt1; + dsum = sum2 - sum1; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + amount_format_won(sum1, won1, sizeof(won1)); + amount_format_won(sum2, won2, sizeof(won2)); + amount_format_won(dsum, wond, sizeof(wond)); tx_log(TX_LOG_INFO, - "[lg_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[lg_bt_0012] diff D1=%s(%ld/%ld) D2=%s(%ld/%ld) Δ건수=%ld Δ금액=%ld", + d1, cnt1, sum1, d2, cnt2, sum2, dcnt, dsum); - printf("========== 원장 배치 결과 [lg_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 원장 diff 검증 결과 [lg_bt_0012] ==========\n"); + printf(" 기준일 D1 : %s 건수=%ld 금액=%s\n", d1, cnt1, won1); + printf(" 비교일 D2 : %s 건수=%ld 금액=%s\n", d2, cnt2, won2); + printf(" ----------------------------------------------\n"); + printf(" 건수 차이(D2-D1) : %ld\n", dcnt); + printf(" 금액 차이(D2-D1) : %s\n", wond); + printf(" 판정 : %s\n", + (dcnt == 0 && dsum == 0) ? "동일" : "차이 있음"); + printf("==================================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) { - const char *biz_date; + const char *d1; + const char *d2; const char *target; int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { - fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); + if (argc < 3) { + fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } - biz_date = argv[1]; - target = (argc >= 3) ? argv[2] : NULL; + d1 = argv[1]; + d2 = argv[2]; + target = (argc >= 4) ? argv[3] : NULL; - if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + if (!date_is_valid(d1)) { + fprintf(stderr, "오류: 유효하지 않은 영업일 D1 '%s'\n", d1); + return 2; + } + if (!date_is_valid(d2)) { + fprintf(stderr, "오류: 유효하지 않은 영업일 D2 '%s'\n", d2); return 2; } - tx_log(TX_LOG_INFO, "[lg_bt_0012] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[lg_bt_0012] 배치 시작 D1=%s D2=%s", d1, d2); rc = lg_dbio_connect(target); if (rc != TX_OK) { @@ -129,7 +116,7 @@ int main(int argc, char **argv) return 1; } - rc = run_lg_bt_0012(biz_date); + rc = run_lg_bt_0012(d1, d2); lg_dbio_disconnect(); diff --git a/app/batch/lg_bt_0013.c b/app/batch/lg_bt_0013.c index 2f86c06..c098ac0 100644 --- a/app/batch/lg_bt_0013.c +++ b/app/batch/lg_bt_0013.c @@ -8,10 +8,12 @@ #line 1 "app/batch/lg_bt_0013.pgc" /* @tier hard @module lg @transform ledger-batch */ /* - * lg_bt_0013.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=hard] + * lg_bt_0013.pgc - 전기(轉記) 배치 (접수건 원장전기) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수(status='R') 원장을 커서로 순회하며 업무규칙으로 + * 판정하여 유효건은 완료('M')로 전기하고, 무효건은 불일치('U')로 보류한다. + * 표준 API 대신 직접 EXEC SQL UPDATE 로 status 와 fee(=amount/100)를 + * 함께 갱신한다. * * 실행: lg_bt_0013 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/lg_bt_0013.pgc" +#line 21 "app/batch/lg_bt_0013.pgc" static int run_lg_bt_0013(const char *biz_date) @@ -105,41 +107,42 @@ static int run_lg_bt_0013(const char *biz_date) -#line 24 "app/batch/lg_bt_0013.pgc" +#line 26 "app/batch/lg_bt_0013.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/lg_bt_0013.pgc" +#line 27 "app/batch/lg_bt_0013.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/lg_bt_0013.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/lg_bt_0013.pgc" +#line 28 "app/batch/lg_bt_0013.pgc" long h_amount ; -#line 28 "app/batch/lg_bt_0013.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ #line 29 "app/batch/lg_bt_0013.pgc" + char h_status [ 2 ] ; + +#line 30 "app/batch/lg_bt_0013.pgc" + long h_fee ; +/* exec sql end declare section */ +#line 31 "app/batch/lg_bt_0013.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, posted = 0, held = 0, errcnt = 0; + long sum_fee = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0013 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/lg_bt_0013.pgc" + /* declare cur_lg_bt_0013 cursor for select key , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ +#line 45 "app/batch/lg_bt_0013.pgc" -#line 42 "app/batch/lg_bt_0013.pgc" +#line 45 "app/batch/lg_bt_0013.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0013 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0013 cursor for select key , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/lg_bt_0013.pgc" +#line 47 "app/batch/lg_bt_0013.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0013"); @@ -153,11 +156,9 @@ static int run_lg_bt_0013(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_lg_bt_0013", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/lg_bt_0013.pgc" +#line 57 "app/batch/lg_bt_0013.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +171,42 @@ static int run_lg_bt_0013(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ + /* 전기 판정: 유효 → 완료(M)+수수료 산정, 무효 → 보류(U)+수수료 0 */ if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; + strncpy(h_status, LG_ST_DONE, sizeof(h_status)); + h_fee = h_amount / 100; } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + strncpy(h_status, LG_ST_FAIL, sizeof(h_status)); + h_fee = 0; + } + h_status[sizeof(h_status) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set status = $1 , fee = $2 , upd_ts = now ( ) where key = $3 ", + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 81 "app/batch/lg_bt_0013.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE 전기 lg_bt_0013"); + errcnt++; + continue; } - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (strcmp(h_status, LG_ST_DONE) == 0) { + posted++; + sum_fee += h_fee; + } else { + held++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0013", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0013.pgc" +#line 97 "app/batch/lg_bt_0013.pgc" if (errcnt == 0) @@ -195,16 +214,19 @@ static int run_lg_bt_0013(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[lg_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_fee, won, sizeof(won)); - printf("========== 원장 배치 결과 [lg_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + tx_log(TX_LOG_INFO, + "[lg_bt_0013] 요약 date=%s 대상=%ld 전기=%ld 보류=%ld 수수료=%ld 오류=%ld", + biz_date, total, posted, held, sum_fee, errcnt); + + printf("========== 원장 전기 결과 [lg_bt_0013] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상(R) 건수 : %ld\n", total); + printf(" 전기(→M) 건수 : %ld\n", posted); + printf(" 보류(→U) 건수 : %ld\n", held); + printf(" 전기 수수료 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/lg_bt_0013.pgc b/app/batch/lg_bt_0013.pgc index ed0f3b4..a600baf 100644 --- a/app/batch/lg_bt_0013.pgc +++ b/app/batch/lg_bt_0013.pgc @@ -1,9 +1,11 @@ /* @tier hard @module lg @transform ledger-batch */ /* - * lg_bt_0013.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=hard] + * lg_bt_0013.pgc - 전기(轉記) 배치 (접수건 원장전기) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수(status='R') 원장을 커서로 순회하며 업무규칙으로 + * 판정하여 유효건은 완료('M')로 전기하고, 무효건은 불일치('U')로 보류한다. + * 표준 API 대신 직접 EXEC SQL UPDATE 로 status 와 fee(=amount/100)를 + * 함께 갱신한다. * * 실행: lg_bt_0013 [db@host] */ @@ -23,19 +25,20 @@ static int run_lg_bt_0013(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; + char h_status[2]; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, posted = 0, held = 0, errcnt = 0; + long sum_fee = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_lg_bt_0013 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount FROM lg_ledger WHERE biz_date = :h_biz_date AND status = 'R' @@ -51,7 +54,7 @@ static int run_lg_bt_0013(const char *biz_date) tx_log(TX_LOG_WARN, "[lg_bt_0013] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_lg_bt_0013 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_lg_bt_0013 INTO :h_key, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,19 +66,31 @@ static int run_lg_bt_0013(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ + /* 전기 판정: 유효 → 완료(M)+수수료 산정, 무효 → 보류(U)+수수료 0 */ if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; + strncpy(h_status, LG_ST_DONE, sizeof(h_status)); + h_fee = h_amount / 100; } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + strncpy(h_status, LG_ST_FAIL, sizeof(h_status)); + h_fee = 0; + } + h_status[sizeof(h_status) - 1] = '\0'; + + EXEC SQL UPDATE lg_ledger + SET status = :h_status, fee = :h_fee, upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE 전기 lg_bt_0013"); + errcnt++; + continue; } - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (strcmp(h_status, LG_ST_DONE) == 0) { + posted++; + sum_fee += h_fee; + } else { + held++; } } @@ -86,16 +101,19 @@ static int run_lg_bt_0013(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[lg_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_fee, won, sizeof(won)); - printf("========== 원장 배치 결과 [lg_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + tx_log(TX_LOG_INFO, + "[lg_bt_0013] 요약 date=%s 대상=%ld 전기=%ld 보류=%ld 수수료=%ld 오류=%ld", + biz_date, total, posted, held, sum_fee, errcnt); + + printf("========== 원장 전기 결과 [lg_bt_0013] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상(R) 건수 : %ld\n", total); + printf(" 전기(→M) 건수 : %ld\n", posted); + printf(" 보류(→U) 건수 : %ld\n", held); + printf(" 전기 수수료 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/lg_bt_0014.c b/app/batch/lg_bt_0014.c index ec37547..11292a9 100644 --- a/app/batch/lg_bt_0014.c +++ b/app/batch/lg_bt_0014.c @@ -8,10 +8,11 @@ #line 1 "app/batch/lg_bt_0014.pgc" /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0014.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0014.pgc - 역분개 배치 (취소/불일치건 역분개 생성) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 불일치(status='U') 원장을 커서로 순회하며 각 건에 대해 + * 반대부호(-amount)의 역분개행(key+'R', status 'M')을 INSERT 한다. + * 역분개 건수/금액을 요약한다. * * 실행: lg_bt_0014 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/lg_bt_0014.pgc" +#line 20 "app/batch/lg_bt_0014.pgc" static int run_lg_bt_0014(const char *biz_date) @@ -104,42 +105,51 @@ static int run_lg_bt_0014(const char *biz_date) + + -#line 24 "app/batch/lg_bt_0014.pgc" +#line 25 "app/batch/lg_bt_0014.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/lg_bt_0014.pgc" +#line 26 "app/batch/lg_bt_0014.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/lg_bt_0014.pgc" +#line 27 "app/batch/lg_bt_0014.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/lg_bt_0014.pgc" +#line 28 "app/batch/lg_bt_0014.pgc" + char h_card_no [ 20 ] ; + +#line 29 "app/batch/lg_bt_0014.pgc" long h_amount ; -#line 28 "app/batch/lg_bt_0014.pgc" - char h_new_status [ 2 ] ; +#line 30 "app/batch/lg_bt_0014.pgc" + char h_rkey [ 16 ] ; + +#line 31 "app/batch/lg_bt_0014.pgc" + long h_ramount ; /* exec sql end declare section */ -#line 29 "app/batch/lg_bt_0014.pgc" +#line 32 "app/batch/lg_bt_0014.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, reversed = 0, errcnt = 0; + long sum_rev = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0014 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/lg_bt_0014.pgc" + /* declare cur_lg_bt_0014 cursor for select key , merch_id , card_no , amount from lg_ledger where biz_date = $1 and status = 'U' order by key */ +#line 46 "app/batch/lg_bt_0014.pgc" -#line 42 "app/batch/lg_bt_0014.pgc" +#line 46 "app/batch/lg_bt_0014.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0014 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0014 cursor for select key , merch_id , card_no , amount from lg_ledger where biz_date = $1 and status = 'U' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/lg_bt_0014.pgc" +#line 48 "app/batch/lg_bt_0014.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0014"); @@ -155,9 +165,11 @@ static int run_lg_bt_0014(const char *biz_date) ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/lg_bt_0014.pgc" +#line 59 "app/batch/lg_bt_0014.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +182,35 @@ static int run_lg_bt_0014(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* 역분개행: 원키(최대 14자) + 'R', 금액 반대부호, 상태 완료(M) */ + snprintf(h_rkey, sizeof(h_rkey), "%.14sR", h_key); + h_ramount = -h_amount; - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into lg_ledger ( key , merch_id , card_no , amount , biz_date , status , fee ) values ( $1 , $2 , $3 , $4 , $5 , 'M' , 0 )", + ECPGt_char,(h_rkey),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_ramount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 78 "app/batch/lg_bt_0014.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT 역분개 lg_bt_0014"); errcnt++; + continue; } + reversed++; + sum_rev += h_ramount; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0014", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0014.pgc" +#line 89 "app/batch/lg_bt_0014.pgc" if (errcnt == 0) @@ -195,17 +218,19 @@ static int run_lg_bt_0014(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[lg_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_rev, won, sizeof(won)); - printf("========== 원장 배치 결과 [lg_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[lg_bt_0014] 요약 date=%s 대상=%ld 역분개=%ld 역분개액=%ld 오류=%ld", + biz_date, total, reversed, sum_rev, errcnt); + + printf("========== 역분개 결과 [lg_bt_0014] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상(U) 건수 : %ld\n", total); + printf(" 역분개 건수 : %ld\n", reversed); + printf(" 역분개 금액 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==========================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/lg_bt_0014.pgc b/app/batch/lg_bt_0014.pgc index 692efe9..6c53b3f 100644 --- a/app/batch/lg_bt_0014.pgc +++ b/app/batch/lg_bt_0014.pgc @@ -1,9 +1,10 @@ /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0014.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0014.pgc - 역분개 배치 (취소/불일치건 역분개 생성) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 불일치(status='U') 원장을 커서로 순회하며 각 건에 대해 + * 반대부호(-amount)의 역분개행(key+'R', status 'M')을 INSERT 한다. + * 역분개 건수/금액을 요약한다. * * 실행: lg_bt_0014 [db@host] */ @@ -24,21 +25,24 @@ static int run_lg_bt_0014(const char *biz_date) char h_biz_date[9]; char h_key[16]; char h_merch_id[16]; + char h_card_no[20]; long h_amount; - char h_new_status[2]; + char h_rkey[16]; + long h_ramount; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, reversed = 0, errcnt = 0; + long sum_rev = 0; + char won[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_lg_bt_0014 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, merch_id, card_no, amount FROM lg_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'U' ORDER BY key; EXEC SQL OPEN cur_lg_bt_0014; @@ -51,7 +55,8 @@ static int run_lg_bt_0014(const char *biz_date) tx_log(TX_LOG_WARN, "[lg_bt_0014] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_lg_bt_0014 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_lg_bt_0014 + INTO :h_key, :h_merch_id, :h_card_no, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +68,22 @@ static int run_lg_bt_0014(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* 역분개행: 원키(최대 14자) + 'R', 금액 반대부호, 상태 완료(M) */ + snprintf(h_rkey, sizeof(h_rkey), "%.14sR", h_key); + h_ramount = -h_amount; - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL INSERT INTO lg_ledger + (key, merch_id, card_no, amount, biz_date, status, fee) + VALUES + (:h_rkey, :h_merch_id, :h_card_no, :h_ramount, :h_biz_date, 'M', 0); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT 역분개 lg_bt_0014"); errcnt++; + continue; } + reversed++; + sum_rev += h_ramount; } EXEC SQL CLOSE cur_lg_bt_0014; @@ -86,17 +93,19 @@ static int run_lg_bt_0014(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[lg_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(sum_rev, won, sizeof(won)); - printf("========== 원장 배치 결과 [lg_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[lg_bt_0014] 요약 date=%s 대상=%ld 역분개=%ld 역분개액=%ld 오류=%ld", + biz_date, total, reversed, sum_rev, errcnt); + + printf("========== 역분개 결과 [lg_bt_0014] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상(U) 건수 : %ld\n", total); + printf(" 역분개 건수 : %ld\n", reversed); + printf(" 역분개 금액 : %s\n", won); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==========================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/lg_bt_0015.c b/app/batch/lg_bt_0015.c index d0b3b01..fc9dffd 100644 --- a/app/batch/lg_bt_0015.c +++ b/app/batch/lg_bt_0015.c @@ -8,12 +8,12 @@ #line 1 "app/batch/lg_bt_0015.pgc" /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0015.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0015.pgc - 승인원장 정리 배치 (미처리 접수건 만료처리) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 기준일 이전(biz_date < 기준일)의 오래된 접수(status='R') 미처리 원장을 + * 커서로 순회하며 불일치('U')로 만료 전이한다. 정리 건수를 요약한다. * - * 실행: lg_bt_0015 [db@host] + * 실행: lg_bt_0015 <기준일 YYYYMMDD> [db@host] */ #include #include @@ -96,50 +96,42 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/lg_bt_0015.pgc" -static int run_lg_bt_0015(const char *biz_date) +static int run_lg_bt_0015(const char *base_date) { /* exec sql begin declare section */ - - #line 24 "app/batch/lg_bt_0015.pgc" - char h_biz_date [ 9 ] ; + char h_base_date [ 9 ] ; #line 25 "app/batch/lg_bt_0015.pgc" char h_key [ 16 ] ; #line 26 "app/batch/lg_bt_0015.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/lg_bt_0015.pgc" - long h_amount ; - -#line 28 "app/batch/lg_bt_0015.pgc" - char h_new_status [ 2 ] ; + char h_biz_date [ 9 ] ; /* exec sql end declare section */ -#line 29 "app/batch/lg_bt_0015.pgc" +#line 27 "app/batch/lg_bt_0015.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, expired = 0, errcnt = 0; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_base_date, base_date, sizeof(h_base_date) - 1); + h_base_date[sizeof(h_base_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0015 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/lg_bt_0015.pgc" + ECPGset_var( 0, ( h_base_date ), __LINE__);\ + /* declare cur_lg_bt_0015 cursor for select key , biz_date from lg_ledger where biz_date < $1 and status = 'R' order by biz_date , key */ +#line 40 "app/batch/lg_bt_0015.pgc" -#line 42 "app/batch/lg_bt_0015.pgc" +#line 40 "app/batch/lg_bt_0015.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0015 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0015 cursor for select key , biz_date from lg_ledger where biz_date < $1 and status = 'R' order by biz_date , key", + ECPGt_char,(h_base_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/lg_bt_0015.pgc" +#line 42 "app/batch/lg_bt_0015.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0015"); @@ -153,11 +145,9 @@ static int run_lg_bt_0015(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_lg_bt_0015", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/lg_bt_0015.pgc" +#line 52 "app/batch/lg_bt_0015.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +160,18 @@ static int run_lg_bt_0015(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); + /* 기준일 이전 미처리 접수건 → 불일치(U)로 만료처리 */ + rc = lg_rec_update_status(h_key, LG_ST_FAIL); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[lg_bt_0015] 만료처리 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + expired++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0015", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0015.pgc" +#line 74 "app/batch/lg_bt_0015.pgc" if (errcnt == 0) @@ -196,41 +180,40 @@ static int run_lg_bt_0015(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[lg_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[lg_bt_0015] 요약 기준일=%s 대상=%ld 만료=%ld 오류=%ld", + base_date, total, expired, errcnt); - printf("========== 원장 배치 결과 [lg_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 승인원장 정리 결과 [lg_bt_0015] ==========\n"); + printf(" 기준 영업일 : %s\n", base_date); + printf(" 미처리(R) 건수: %ld\n", total); + printf(" 만료(→U) 건수 : %ld\n", expired); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) { - const char *biz_date; + const char *base_date; const char *target; int rc; tx_set_loglevel(TX_LOG_INFO); if (argc < 2) { - fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); + fprintf(stderr, "사용법: %s <기준일 YYYYMMDD> [db@host]\n", argv[0]); return 2; } - biz_date = argv[1]; - target = (argc >= 3) ? argv[2] : NULL; + base_date = argv[1]; + target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + if (!date_is_valid(base_date)) { + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", base_date); return 2; } - tx_log(TX_LOG_INFO, "[lg_bt_0015] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[lg_bt_0015] 배치 시작 기준일=%s", base_date); rc = lg_dbio_connect(target); if (rc != TX_OK) { @@ -238,7 +221,7 @@ int main(int argc, char **argv) return 1; } - rc = run_lg_bt_0015(biz_date); + rc = run_lg_bt_0015(base_date); lg_dbio_disconnect(); diff --git a/app/batch/lg_bt_0015.pgc b/app/batch/lg_bt_0015.pgc index 9a34e7b..4514a4d 100644 --- a/app/batch/lg_bt_0015.pgc +++ b/app/batch/lg_bt_0015.pgc @@ -1,11 +1,11 @@ /* @tier easy @module lg @transform ledger-batch */ /* - * lg_bt_0015.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=easy] + * lg_bt_0015.pgc - 승인원장 정리 배치 (미처리 접수건 만료처리) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 기준일 이전(biz_date < 기준일)의 오래된 접수(status='R') 미처리 원장을 + * 커서로 순회하며 불일치('U')로 만료 전이한다. 정리 건수를 요약한다. * - * 실행: lg_bt_0015 [db@host] + * 실행: lg_bt_0015 <기준일 YYYYMMDD> [db@host] */ #include #include @@ -18,28 +18,26 @@ EXEC SQL INCLUDE sqlca; -static int run_lg_bt_0015(const char *biz_date) +static int run_lg_bt_0015(const char *base_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; + char h_base_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, expired = 0, errcnt = 0; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_base_date, base_date, sizeof(h_base_date) - 1); + h_base_date[sizeof(h_base_date) - 1] = '\0'; EXEC SQL DECLARE cur_lg_bt_0015 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, biz_date FROM lg_ledger - WHERE biz_date = :h_biz_date + WHERE biz_date < :h_base_date AND status = 'R' - ORDER BY key; + ORDER BY biz_date, key; EXEC SQL OPEN cur_lg_bt_0015; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +49,7 @@ static int run_lg_bt_0015(const char *biz_date) tx_log(TX_LOG_WARN, "[lg_bt_0015] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_lg_bt_0015 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_lg_bt_0015 INTO :h_key, :h_biz_date; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +61,14 @@ static int run_lg_bt_0015(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); + /* 기준일 이전 미처리 접수건 → 불일치(U)로 만료처리 */ + rc = lg_rec_update_status(h_key, LG_ST_FAIL); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[lg_bt_0015] 만료처리 실패 key=%s rc=%d", h_key, rc); errcnt++; + continue; } + expired++; } EXEC SQL CLOSE cur_lg_bt_0015; @@ -87,41 +79,40 @@ static int run_lg_bt_0015(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[lg_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[lg_bt_0015] 요약 기준일=%s 대상=%ld 만료=%ld 오류=%ld", + base_date, total, expired, errcnt); - printf("========== 원장 배치 결과 [lg_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 승인원장 정리 결과 [lg_bt_0015] ==========\n"); + printf(" 기준 영업일 : %s\n", base_date); + printf(" 미처리(R) 건수: %ld\n", total); + printf(" 만료(→U) 건수 : %ld\n", expired); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) { - const char *biz_date; + const char *base_date; const char *target; int rc; tx_set_loglevel(TX_LOG_INFO); if (argc < 2) { - fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); + fprintf(stderr, "사용법: %s <기준일 YYYYMMDD> [db@host]\n", argv[0]); return 2; } - biz_date = argv[1]; - target = (argc >= 3) ? argv[2] : NULL; + base_date = argv[1]; + target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + if (!date_is_valid(base_date)) { + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", base_date); return 2; } - tx_log(TX_LOG_INFO, "[lg_bt_0015] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[lg_bt_0015] 배치 시작 기준일=%s", base_date); rc = lg_dbio_connect(target); if (rc != TX_OK) { @@ -129,7 +120,7 @@ int main(int argc, char **argv) return 1; } - rc = run_lg_bt_0015(biz_date); + rc = run_lg_bt_0015(base_date); lg_dbio_disconnect(); diff --git a/app/batch/lg_bt_0016.c b/app/batch/lg_bt_0016.c index 72b1ae0..538da74 100644 --- a/app/batch/lg_bt_0016.c +++ b/app/batch/lg_bt_0016.c @@ -8,10 +8,11 @@ #line 1 "app/batch/lg_bt_0016.pgc" /* @tier medium @module lg @transform ledger-batch */ /* - * lg_bt_0016.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=medium] + * lg_bt_0016.pgc - 원장 배치반영 배치 (일일 접수건 일괄반영) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수(status='R') 원장 건수를 확인한 뒤 단일 일괄 UPDATE 로 + * 처리완료('M')로 일괄 반영한다. 반영 후 완료건의 거래금액/수수료 합계를 + * SELECT 로 집계하여 반영건수/합계/수수료를 요약한다. * * 실행: lg_bt_0016 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/lg_bt_0016.pgc" +#line 20 "app/batch/lg_bt_0016.pgc" static int run_lg_bt_0016(const char *biz_date) @@ -103,109 +104,94 @@ static int run_lg_bt_0016(const char *biz_date) - -#line 24 "app/batch/lg_bt_0016.pgc" +#line 25 "app/batch/lg_bt_0016.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/lg_bt_0016.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/lg_bt_0016.pgc" - char h_merch_id [ 16 ] ; + long h_recv_cnt ; #line 27 "app/batch/lg_bt_0016.pgc" - long h_amount ; + long h_sum ; #line 28 "app/batch/lg_bt_0016.pgc" - char h_new_status [ 2 ] ; + long h_fee ; /* exec sql end declare section */ #line 29 "app/batch/lg_bt_0016.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long applied = 0, errcnt = 0; + char won_amt[32], won_fee[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_lg_bt_0016 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/lg_bt_0016.pgc" - -#line 42 "app/batch/lg_bt_0016.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_bt_0016 cursor for select key , merch_id , amount from lg_ledger where biz_date = $1 and status = 'R' order by key", + /* 반영 대상(접수 R) 건수 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from lg_ledger where biz_date = $1 and status = 'R'", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/lg_bt_0016.pgc" + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_recv_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 42 "app/batch/lg_bt_0016.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0016"); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 접수건수 lg_bt_0016"); return TX_EDB; } if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[lg_bt_0016] tx_begin 경고"); - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_lg_bt_0016", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/lg_bt_0016.pgc" + /* 접수(R) 건 일괄 반영 → 완료(M) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set status = 'M' , upd_ts = now ( ) where biz_date = $1 and status = 'R'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 55 "app/batch/lg_bt_0016.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_lg_bt_0016"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE 배치반영 lg_bt_0016"); + tx_abort(); + return TX_EDB; } + applied = sqlca.sqlerrd[2]; - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_bt_0016", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/lg_bt_0016.pgc" + /* 반영 결과 집계 (완료 M) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from lg_ledger where biz_date = $1 and status = 'M'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 68 "app/batch/lg_bt_0016.pgc" + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 반영집계 lg_bt_0016"); + errcnt++; + h_sum = 0; + h_fee = 0; + } if (errcnt == 0) tx_commit(); else tx_abort(); - tx_log(TX_LOG_INFO, - "[lg_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(h_sum, won_amt, sizeof(won_amt)); + amount_format_won(h_fee, won_fee, sizeof(won_fee)); - printf("========== 원장 배치 결과 [lg_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[lg_bt_0016] 요약 date=%s 접수=%ld 반영=%ld 합계=%ld 수수료=%ld 오류=%ld", + biz_date, h_recv_cnt, applied, h_sum, h_fee, errcnt); + + printf("========== 원장 배치반영 결과 [lg_bt_0016] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 접수(R) 건수 : %ld\n", h_recv_cnt); + printf(" 반영(→M) 건수 : %ld\n", applied); + printf(" 완료 거래합계 : %s\n", won_amt); + printf(" 완료 수수료 : %s\n", won_fee); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/lg_bt_0016.pgc b/app/batch/lg_bt_0016.pgc index b30eaf1..daf0f4a 100644 --- a/app/batch/lg_bt_0016.pgc +++ b/app/batch/lg_bt_0016.pgc @@ -1,9 +1,10 @@ /* @tier medium @module lg @transform ledger-batch */ /* - * lg_bt_0016.pgc - 원장 배치 (커서 순회 + 상태전이) [tier=medium] + * lg_bt_0016.pgc - 원장 배치반영 배치 (일일 접수건 일괄반영) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수(status='R') 원장 건수를 확인한 뒤 단일 일괄 UPDATE 로 + * 처리완료('M')로 일괄 반영한다. 반영 후 완료건의 거래금액/수수료 합계를 + * SELECT 로 집계하여 반영건수/합계/수수료를 요약한다. * * 실행: lg_bt_0016 [db@host] */ @@ -22,81 +23,76 @@ static int run_lg_bt_0016(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_recv_cnt; + long h_sum; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long applied = 0, errcnt = 0; + char won_amt[32], won_fee[32]; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - EXEC SQL DECLARE cur_lg_bt_0016 CURSOR FOR - SELECT key, merch_id, amount + /* 반영 대상(접수 R) 건수 확인 */ + EXEC SQL SELECT count(*) + INTO :h_recv_cnt FROM lg_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_lg_bt_0016; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_lg_bt_0016"); + AND status = 'R'; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 접수건수 lg_bt_0016"); return TX_EDB; } if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[lg_bt_0016] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_lg_bt_0016 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_lg_bt_0016"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, LG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, LG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = lg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[lg_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 접수(R) 건 일괄 반영 → 완료(M) */ + EXEC SQL UPDATE lg_ledger + SET status = 'M', upd_ts = now() + WHERE biz_date = :h_biz_date + AND status = 'R'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE 배치반영 lg_bt_0016"); + tx_abort(); + return TX_EDB; } + applied = sqlca.sqlerrd[2]; - EXEC SQL CLOSE cur_lg_bt_0016; + /* 반영 결과 집계 (완료 M) */ + EXEC SQL SELECT coalesce(sum(amount), 0), coalesce(sum(fee), 0) + INTO :h_sum, :h_fee + FROM lg_ledger + WHERE biz_date = :h_biz_date + AND status = 'M'; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT 반영집계 lg_bt_0016"); + errcnt++; + h_sum = 0; + h_fee = 0; + } if (errcnt == 0) tx_commit(); else tx_abort(); - tx_log(TX_LOG_INFO, - "[lg_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(h_sum, won_amt, sizeof(won_amt)); + amount_format_won(h_fee, won_fee, sizeof(won_fee)); - printf("========== 원장 배치 결과 [lg_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + tx_log(TX_LOG_INFO, + "[lg_bt_0016] 요약 date=%s 접수=%ld 반영=%ld 합계=%ld 수수료=%ld 오류=%ld", + biz_date, h_recv_cnt, applied, h_sum, h_fee, errcnt); + + printf("========== 원장 배치반영 결과 [lg_bt_0016] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 접수(R) 건수 : %ld\n", h_recv_cnt); + printf(" 반영(→M) 건수 : %ld\n", applied); + printf(" 완료 거래합계 : %s\n", won_amt); + printf(" 완료 수수료 : %s\n", won_fee); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/mg_bt_0001.c b/app/batch/mg_bt_0001.c index 4420cd8..dfa79e5 100644 --- a/app/batch/mg_bt_0001.c +++ b/app/batch/mg_bt_0001.c @@ -8,10 +8,11 @@ #line 1 "app/batch/mg_bt_0001.pgc" /* @tier easy @module mg @transform msg-gateway-batch */ /* - * mg_bt_0001.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0001.pgc - 전문게이트웨이 오류전문 재전송 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 실패전문(status='U')을 커서로 순회하며 재전송을 시도한다. + * 재전송 성공 건은 완료('M')로 전이하고, 재전송 한도(3회) 초과 건은 + * 실패로 확정한다. 배치 main 엔트리 포함. * * 실행: mg_bt_0001 [db@host] */ @@ -93,53 +94,66 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0001.pgc" +#line 20 "app/batch/mg_bt_0001.pgc" +#define MG_RESEND_LIMIT 3 + +/* 재전송 시뮬레이션: 금액 유효성 + 재시도 횟수로 성공/실패 판정 */ +static int mg_try_resend(const char *key, long amount, int retry) +{ + if (retry >= MG_RESEND_LIMIT) + return TX_FAIL; + if (!amount_is_valid(amount)) + return TX_FAIL; + tx_log(TX_LOG_DEBUG, "[mg_bt_0001] 재전송 시도 key=%s retry=%d", key, retry); + return TX_OK; +} + static int run_mg_bt_0001(const char *biz_date) { /* exec sql begin declare section */ - + -#line 24 "app/batch/mg_bt_0001.pgc" +#line 38 "app/batch/mg_bt_0001.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mg_bt_0001.pgc" +#line 39 "app/batch/mg_bt_0001.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/mg_bt_0001.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/mg_bt_0001.pgc" +#line 40 "app/batch/mg_bt_0001.pgc" long h_amount ; -#line 28 "app/batch/mg_bt_0001.pgc" +#line 41 "app/batch/mg_bt_0001.pgc" + int h_retry ; + +#line 42 "app/batch/mg_bt_0001.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/mg_bt_0001.pgc" +#line 43 "app/batch/mg_bt_0001.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, resent = 0, dropped = 0, errcnt = 0; int rc; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0001 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mg_bt_0001.pgc" + /* declare cur_mg_bt_0001 cursor for select key , amount , coalesce ( retry_cnt , 0 ) from mg_ledger where biz_date = $1 and status = 'U' order by key */ +#line 56 "app/batch/mg_bt_0001.pgc" -#line 42 "app/batch/mg_bt_0001.pgc" +#line 56 "app/batch/mg_bt_0001.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0001 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0001 cursor for select key , amount , coalesce ( retry_cnt , 0 ) from mg_ledger where biz_date = $1 and status = 'U' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mg_bt_0001.pgc" +#line 58 "app/batch/mg_bt_0001.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0001"); @@ -153,11 +167,11 @@ static int run_mg_bt_0001(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0001", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_retry),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0001.pgc" +#line 68 "app/batch/mg_bt_0001.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,13 +184,12 @@ static int run_mg_bt_0001(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + if (mg_try_resend(h_key, h_amount, h_retry) == TX_OK) { strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; + resent++; } else { strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + dropped++; } rc = mg_rec_update_status(h_key, h_new_status); @@ -187,7 +200,7 @@ static int run_mg_bt_0001(const char *biz_date) } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0001", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0001.pgc" +#line 95 "app/batch/mg_bt_0001.pgc" if (errcnt == 0) @@ -196,14 +209,14 @@ static int run_mg_bt_0001(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mg_bt_0001] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mg_bt_0001] 요약 date=%s 대상=%ld 재전송=%ld 폐기=%ld 오류=%ld", + biz_date, total, resent, dropped, errcnt); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0001] ==========\n"); + printf("========== 오류전문 재전송 배치 [mg_bt_0001] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 재전송(M) : %ld\n", resent); + printf(" 폐기(U) : %ld\n", dropped); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); @@ -230,7 +243,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0001] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0001] 재전송 배치 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0001.pgc b/app/batch/mg_bt_0001.pgc index f42bc2a..9b6f85e 100644 --- a/app/batch/mg_bt_0001.pgc +++ b/app/batch/mg_bt_0001.pgc @@ -1,9 +1,10 @@ /* @tier easy @module mg @transform msg-gateway-batch */ /* - * mg_bt_0001.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0001.pgc - 전문게이트웨이 오류전문 재전송 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 실패전문(status='U')을 커서로 순회하며 재전송을 시도한다. + * 재전송 성공 건은 완료('M')로 전이하고, 재전송 한도(3회) 초과 건은 + * 실패로 확정한다. 배치 main 엔트리 포함. * * 실행: mg_bt_0001 [db@host] */ @@ -18,28 +19,41 @@ EXEC SQL INCLUDE sqlca; +#define MG_RESEND_LIMIT 3 + +/* 재전송 시뮬레이션: 금액 유효성 + 재시도 횟수로 성공/실패 판정 */ +static int mg_try_resend(const char *key, long amount, int retry) +{ + if (retry >= MG_RESEND_LIMIT) + return TX_FAIL; + if (!amount_is_valid(amount)) + return TX_FAIL; + tx_log(TX_LOG_DEBUG, "[mg_bt_0001] 재전송 시도 key=%s retry=%d", key, retry); + return TX_OK; +} + static int run_mg_bt_0001(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; + int h_retry; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, resent = 0, dropped = 0, errcnt = 0; int rc; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mg_bt_0001 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT key, amount, COALESCE(retry_cnt, 0) + FROM mg_ledger + WHERE biz_date = :h_biz_date + AND status = 'U' + ORDER BY key; EXEC SQL OPEN cur_mg_bt_0001; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +65,7 @@ static int run_mg_bt_0001(const char *biz_date) tx_log(TX_LOG_WARN, "[mg_bt_0001] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mg_bt_0001 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mg_bt_0001 INTO :h_key, :h_amount, :h_retry; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,13 +77,12 @@ static int run_mg_bt_0001(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + if (mg_try_resend(h_key, h_amount, h_retry) == TX_OK) { strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; + resent++; } else { strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + dropped++; } rc = mg_rec_update_status(h_key, h_new_status); @@ -87,14 +100,14 @@ static int run_mg_bt_0001(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mg_bt_0001] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mg_bt_0001] 요약 date=%s 대상=%ld 재전송=%ld 폐기=%ld 오류=%ld", + biz_date, total, resent, dropped, errcnt); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0001] ==========\n"); + printf("========== 오류전문 재전송 배치 [mg_bt_0001] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 재전송(M) : %ld\n", resent); + printf(" 폐기(U) : %ld\n", dropped); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); @@ -121,7 +134,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0001] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0001] 재전송 배치 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0002.c b/app/batch/mg_bt_0002.c index 4d1bd42..b96bcb2 100644 --- a/app/batch/mg_bt_0002.c +++ b/app/batch/mg_bt_0002.c @@ -6,12 +6,13 @@ /* End of automatic include section */ #line 1 "app/batch/mg_bt_0002.pgc" -/* @tier medium @module mg @transform msg-gateway-batch */ +/* @tier easy @module mg @transform msg-gateway-batch */ /* - * mg_bt_0002.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=medium] + * mg_bt_0002.pgc - 전문게이트웨이 큐 적체 정리 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 접수(R) 상태로 큐(mg_queue)에 등록된 뒤 일정 시간(기본 1800초)을 + * 초과하도록 처리되지 못한 적체 전문을 커서로 순회하며 실패(U)로 + * 만료처리한다. 만료 건수와 최장 적체시간을 집계하여 리포트한다. * * 실행: mg_bt_0002 [db@host] */ @@ -93,53 +94,54 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0002.pgc" +#line 20 "app/batch/mg_bt_0002.pgc" +#define MG_QUEUE_TTL_SEC 1800 /* 큐 적체 만료 임계(초) */ + +/* 적체시간(초)이 임계를 넘었는지 판정 */ +static int mg_is_stale(long elapsed) +{ + return (elapsed >= MG_QUEUE_TTL_SEC) ? 1 : 0; +} + static int run_mg_bt_0002(const char *biz_date) { /* exec sql begin declare section */ - - -#line 24 "app/batch/mg_bt_0002.pgc" +#line 33 "app/batch/mg_bt_0002.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mg_bt_0002.pgc" +#line 34 "app/batch/mg_bt_0002.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/mg_bt_0002.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/mg_bt_0002.pgc" - long h_amount ; - -#line 28 "app/batch/mg_bt_0002.pgc" - char h_new_status [ 2 ] ; +#line 35 "app/batch/mg_bt_0002.pgc" + long h_elapsed ; /* exec sql end declare section */ -#line 29 "app/batch/mg_bt_0002.pgc" +#line 36 "app/batch/mg_bt_0002.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, expired = 0, alive = 0, errcnt = 0; + long max_wait = 0; int rc; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0002 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mg_bt_0002.pgc" + /* declare cur_mg_bt_0002 cursor for select key , cast ( extract ( EPOCH from ( now ( ) - enq_ts ) ) as bigint ) from mg_queue where biz_date = $1 and status = 'R' order by enq_ts */ +#line 50 "app/batch/mg_bt_0002.pgc" -#line 42 "app/batch/mg_bt_0002.pgc" +#line 50 "app/batch/mg_bt_0002.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0002 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0002 cursor for select key , cast ( extract ( EPOCH from ( now ( ) - enq_ts ) ) as bigint ) from mg_queue where biz_date = $1 and status = 'R' order by enq_ts", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mg_bt_0002.pgc" +#line 52 "app/batch/mg_bt_0002.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0002"); @@ -153,11 +155,9 @@ static int run_mg_bt_0002(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0002", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_elapsed),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0002.pgc" +#line 62 "app/batch/mg_bt_0002.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,25 +169,26 @@ static int run_mg_bt_0002(const char *biz_date) } total++; + if (h_elapsed > max_wait) + max_wait = h_elapsed; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (!mg_is_stale(h_elapsed)) { + alive++; + continue; } - rc = mg_rec_update_status(h_key, h_new_status); + rc = mg_rec_update_status(h_key, MG_ST_FAIL); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[mg_bt_0002] 만료처리 실패 key=%s rc=%d", h_key, rc); errcnt++; + } else { + expired++; + tx_log(TX_LOG_DEBUG, "[mg_bt_0002] 적체만료 key=%s wait=%lds", h_key, h_elapsed); } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0002", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0002.pgc" +#line 91 "app/batch/mg_bt_0002.pgc" if (errcnt == 0) @@ -196,15 +197,17 @@ static int run_mg_bt_0002(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mg_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mg_bt_0002] 요약 date=%s 대상=%ld 만료=%ld 유지=%ld 오류=%ld", + biz_date, total, expired, alive, errcnt); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 큐 적체 정리 배치 [mg_bt_0002] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 적체 임계(초) : %d\n", MG_QUEUE_TTL_SEC); + printf(" 점검 건수 : %ld\n", total); + printf(" 만료처리(U) : %ld\n", expired); + printf(" 유지 건수 : %ld\n", alive); + printf(" 최장 적체(초) : %ld\n", max_wait); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -230,7 +233,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0002] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0002] 큐 적체 정리 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0002.pgc b/app/batch/mg_bt_0002.pgc index f04039d..76326cf 100644 --- a/app/batch/mg_bt_0002.pgc +++ b/app/batch/mg_bt_0002.pgc @@ -1,9 +1,10 @@ -/* @tier medium @module mg @transform msg-gateway-batch */ +/* @tier easy @module mg @transform msg-gateway-batch */ /* - * mg_bt_0002.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=medium] + * mg_bt_0002.pgc - 전문게이트웨이 큐 적체 정리 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 접수(R) 상태로 큐(mg_queue)에 등록된 뒤 일정 시간(기본 1800초)을 + * 초과하도록 처리되지 못한 적체 전문을 커서로 순회하며 실패(U)로 + * 만료처리한다. 만료 건수와 최장 적체시간을 집계하여 리포트한다. * * 실행: mg_bt_0002 [db@host] */ @@ -18,28 +19,35 @@ EXEC SQL INCLUDE sqlca; +#define MG_QUEUE_TTL_SEC 1800 /* 큐 적체 만료 임계(초) */ + +/* 적체시간(초)이 임계를 넘었는지 판정 */ +static int mg_is_stale(long elapsed) +{ + return (elapsed >= MG_QUEUE_TTL_SEC) ? 1 : 0; +} + static int run_mg_bt_0002(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_elapsed; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, expired = 0, alive = 0, errcnt = 0; + long max_wait = 0; int rc; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mg_bt_0002 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT key, CAST(EXTRACT(EPOCH FROM (now() - enq_ts)) AS bigint) + FROM mg_queue + WHERE biz_date = :h_biz_date + AND status = 'R' + ORDER BY enq_ts; EXEC SQL OPEN cur_mg_bt_0002; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +59,7 @@ static int run_mg_bt_0002(const char *biz_date) tx_log(TX_LOG_WARN, "[mg_bt_0002] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mg_bt_0002 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mg_bt_0002 INTO :h_key, :h_elapsed; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,20 +70,21 @@ static int run_mg_bt_0002(const char *biz_date) } total++; + if (h_elapsed > max_wait) + max_wait = h_elapsed; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (!mg_is_stale(h_elapsed)) { + alive++; + continue; } - rc = mg_rec_update_status(h_key, h_new_status); + rc = mg_rec_update_status(h_key, MG_ST_FAIL); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[mg_bt_0002] 만료처리 실패 key=%s rc=%d", h_key, rc); errcnt++; + } else { + expired++; + tx_log(TX_LOG_DEBUG, "[mg_bt_0002] 적체만료 key=%s wait=%lds", h_key, h_elapsed); } } @@ -87,15 +96,17 @@ static int run_mg_bt_0002(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mg_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mg_bt_0002] 요약 date=%s 대상=%ld 만료=%ld 유지=%ld 오류=%ld", + biz_date, total, expired, alive, errcnt); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 큐 적체 정리 배치 [mg_bt_0002] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 적체 임계(초) : %d\n", MG_QUEUE_TTL_SEC); + printf(" 점검 건수 : %ld\n", total); + printf(" 만료처리(U) : %ld\n", expired); + printf(" 유지 건수 : %ld\n", alive); + printf(" 최장 적체(초) : %ld\n", max_wait); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -121,7 +132,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0002] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0002] 큐 적체 정리 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0003.c b/app/batch/mg_bt_0003.c index 2a8e61e..7158c15 100644 --- a/app/batch/mg_bt_0003.c +++ b/app/batch/mg_bt_0003.c @@ -6,12 +6,13 @@ /* End of automatic include section */ #line 1 "app/batch/mg_bt_0003.pgc" -/* @tier easy @module mg @transform msg-gateway-batch */ +/* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0003.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0003.pgc - 전문 로그 아카이빙 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 전문 로그(mg_log)를 커서로 순회하며 아카이브 + * 테이블(mg_log_arch)로 한 건씩 이관한 뒤 원본을 삭제한다. + * 이관 성공 건수와 삭제 건수, 이관 바이트를 리포트한다. * * 실행: mg_bt_0003 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0003.pgc" +#line 20 "app/batch/mg_bt_0003.pgc" static int run_mg_bt_0003(const char *biz_date) @@ -105,38 +106,38 @@ static int run_mg_bt_0003(const char *biz_date) -#line 24 "app/batch/mg_bt_0003.pgc" +#line 25 "app/batch/mg_bt_0003.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mg_bt_0003.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/mg_bt_0003.pgc" - char h_merch_id [ 16 ] ; + long h_log_id ; #line 27 "app/batch/mg_bt_0003.pgc" - long h_amount ; + char h_chan_cd [ 8 ] ; #line 28 "app/batch/mg_bt_0003.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + char h_payload [ 256 ] ; + #line 29 "app/batch/mg_bt_0003.pgc" + long h_bytelen ; +/* exec sql end declare section */ +#line 30 "app/batch/mg_bt_0003.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, moved = 0, deleted = 0, errcnt = 0; + long movebytes = 0; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0003 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_mg_bt_0003 cursor for select log_id , chan_cd , payload , octet_length ( payload ) from mg_log where biz_date = $1 order by log_id */ #line 42 "app/batch/mg_bt_0003.pgc" #line 42 "app/batch/mg_bt_0003.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0003 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0003 cursor for select log_id , chan_cd , payload , octet_length ( payload ) from mg_log where biz_date = $1 order by log_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/mg_bt_0003.pgc" @@ -151,11 +152,13 @@ static int run_mg_bt_0003(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0003", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_log_id),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_chan_cd),(long)8,(long)1,(8)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_payload),(long)256,(long)1,(256)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_bytelen),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 54 "app/batch/mg_bt_0003.pgc" @@ -170,24 +173,40 @@ static int run_mg_bt_0003(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mg_log_arch ( log_id , biz_date , chan_cd , payload , arch_ts ) values ( $1 , $2 , $3 , $4 , now ( ) )", + ECPGt_long,&(h_log_id),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chan_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_payload),(long)256,(long)1,(256)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 67 "app/batch/mg_bt_0003.pgc" - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mg_log_arch"); errcnt++; + continue; + } + moved++; + movebytes += h_bytelen; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "delete from mg_log where log_id = $1 ", + ECPGt_long,&(h_log_id),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 76 "app/batch/mg_bt_0003.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("DELETE mg_log"); + errcnt++; + } else { + deleted++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0003", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0003.pgc" +#line 85 "app/batch/mg_bt_0003.pgc" if (errcnt == 0) @@ -196,14 +215,15 @@ static int run_mg_bt_0003(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mg_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mg_bt_0003] 요약 date=%s 대상=%ld 이관=%ld 삭제=%ld 오류=%ld", + biz_date, total, moved, deleted, errcnt); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0003] ==========\n"); + printf("========== 전문 로그 아카이빙 [mg_bt_0003] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 대상 로그 : %ld\n", total); + printf(" 이관 건수 : %ld\n", moved); + printf(" 삭제 건수 : %ld\n", deleted); + printf(" 이관 바이트 : %ld\n", movebytes); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); @@ -230,7 +250,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0003] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0003] 로그 아카이빙 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0003.pgc b/app/batch/mg_bt_0003.pgc index 293eb02..8794b0a 100644 --- a/app/batch/mg_bt_0003.pgc +++ b/app/batch/mg_bt_0003.pgc @@ -1,9 +1,10 @@ -/* @tier easy @module mg @transform msg-gateway-batch */ +/* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0003.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0003.pgc - 전문 로그 아카이빙 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 전문 로그(mg_log)를 커서로 순회하며 아카이브 + * 테이블(mg_log_arch)로 한 건씩 이관한 뒤 원본을 삭제한다. + * 이관 성공 건수와 삭제 건수, 이관 바이트를 리포트한다. * * 실행: mg_bt_0003 [db@host] */ @@ -22,24 +23,23 @@ static int run_mg_bt_0003(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_log_id; + char h_chan_cd[8]; + char h_payload[256]; + long h_bytelen; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, moved = 0, deleted = 0, errcnt = 0; + long movebytes = 0; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mg_bt_0003 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT log_id, chan_cd, payload, octet_length(payload) + FROM mg_log + WHERE biz_date = :h_biz_date + ORDER BY log_id; EXEC SQL OPEN cur_mg_bt_0003; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +51,7 @@ static int run_mg_bt_0003(const char *biz_date) tx_log(TX_LOG_WARN, "[mg_bt_0003] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mg_bt_0003 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mg_bt_0003 INTO :h_log_id, :h_chan_cd, :h_payload, :h_bytelen; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,19 +63,22 @@ static int run_mg_bt_0003(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL INSERT INTO mg_log_arch (log_id, biz_date, chan_cd, payload, arch_ts) + VALUES (:h_log_id, :h_biz_date, :h_chan_cd, :h_payload, now()); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mg_log_arch"); errcnt++; + continue; + } + moved++; + movebytes += h_bytelen; + + EXEC SQL DELETE FROM mg_log WHERE log_id = :h_log_id; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("DELETE mg_log"); + errcnt++; + } else { + deleted++; } } @@ -87,14 +90,15 @@ static int run_mg_bt_0003(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mg_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mg_bt_0003] 요약 date=%s 대상=%ld 이관=%ld 삭제=%ld 오류=%ld", + biz_date, total, moved, deleted, errcnt); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0003] ==========\n"); + printf("========== 전문 로그 아카이빙 [mg_bt_0003] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 대상 로그 : %ld\n", total); + printf(" 이관 건수 : %ld\n", moved); + printf(" 삭제 건수 : %ld\n", deleted); + printf(" 이관 바이트 : %ld\n", movebytes); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); @@ -121,7 +125,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0003] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0003] 로그 아카이빙 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0004.c b/app/batch/mg_bt_0004.c index 0703624..5f9fc15 100644 --- a/app/batch/mg_bt_0004.c +++ b/app/batch/mg_bt_0004.c @@ -8,10 +8,11 @@ #line 1 "app/batch/mg_bt_0004.pgc" /* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0004.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=medium] + * mg_bt_0004.pgc - 채널별 전문 집계 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 원장 전문을 채널코드(chan_cd) 단위로 집계하여 + * 채널별 건수/금액을 산출하고 통계 테이블(mg_stat)에 적재한다. + * GROUP BY 결과를 커서로 순회하며 채널수/총건수/총금액을 리포트한다. * * 실행: mg_bt_0004 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0004.pgc" +#line 20 "app/batch/mg_bt_0004.pgc" static int run_mg_bt_0004(const char *biz_date) @@ -103,40 +104,36 @@ static int run_mg_bt_0004(const char *biz_date) - -#line 24 "app/batch/mg_bt_0004.pgc" +#line 25 "app/batch/mg_bt_0004.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mg_bt_0004.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/mg_bt_0004.pgc" - char h_merch_id [ 16 ] ; + char h_chan_cd [ 8 ] ; #line 27 "app/batch/mg_bt_0004.pgc" - long h_amount ; + long h_cnt ; #line 28 "app/batch/mg_bt_0004.pgc" - char h_new_status [ 2 ] ; + long h_amt ; /* exec sql end declare section */ #line 29 "app/batch/mg_bt_0004.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long channels = 0, grand_cnt = 0, grand_amt = 0, errcnt = 0; + char amtbuf[32]; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0004 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_mg_bt_0004 cursor for select chan_cd , count ( * ) , coalesce ( SUM ( amount ) , 0 ) from mg_ledger where biz_date = $1 group by chan_cd order by chan_cd */ #line 42 "app/batch/mg_bt_0004.pgc" #line 42 "app/batch/mg_bt_0004.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0004 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0004 cursor for select chan_cd , count ( * ) , coalesce ( SUM ( amount ) , 0 ) from mg_ledger where biz_date = $1 group by chan_cd order by chan_cd", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/mg_bt_0004.pgc" @@ -149,15 +146,20 @@ static int run_mg_bt_0004(const char *biz_date) if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[mg_bt_0004] tx_begin 경고"); + printf("========== 채널별 전문 집계 [mg_bt_0004] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" %-8s %10s %16s\n", "채널", "건수", "금액"); + printf(" ------------------------------------------\n"); + for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0004", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_chan_cd),(long)8,(long)1,(8)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_amt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0004.pgc" +#line 59 "app/batch/mg_bt_0004.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,26 +170,32 @@ static int run_mg_bt_0004(const char *biz_date) break; } - total++; + channels++; + grand_cnt += h_cnt; + grand_amt += h_amt; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + amount_format_won(h_amt, amtbuf, sizeof(amtbuf)); + printf(" %-8s %10ld %16s\n", h_chan_cd, h_cnt, amtbuf); - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mg_stat ( biz_date , chan_cd , tx_cnt , tx_amt , made_ts ) values ( $1 , $2 , $3 , $4 , now ( ) )", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chan_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 77 "app/batch/mg_bt_0004.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mg_stat"); errcnt++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0004", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0004.pgc" +#line 84 "app/batch/mg_bt_0004.pgc" if (errcnt == 0) @@ -195,18 +203,18 @@ static int run_mg_bt_0004(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[mg_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(grand_amt, amtbuf, sizeof(amtbuf)); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" ------------------------------------------\n"); + printf(" 채널수 : %ld\n", channels); + printf(" 총 건수 : %ld\n", grand_cnt); + printf(" 총 금액 : %s\n", amtbuf); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[mg_bt_0004] 집계 date=%s 채널=%ld 건수=%ld", + biz_date, channels, grand_cnt); + return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -230,7 +238,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0004] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0004] 채널 집계 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0004.pgc b/app/batch/mg_bt_0004.pgc index ecf9e92..0da29bd 100644 --- a/app/batch/mg_bt_0004.pgc +++ b/app/batch/mg_bt_0004.pgc @@ -1,9 +1,10 @@ /* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0004.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=medium] + * mg_bt_0004.pgc - 채널별 전문 집계 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 원장 전문을 채널코드(chan_cd) 단위로 집계하여 + * 채널별 건수/금액을 산출하고 통계 테이블(mg_stat)에 적재한다. + * GROUP BY 결과를 커서로 순회하며 채널수/총건수/총금액을 리포트한다. * * 실행: mg_bt_0004 [db@host] */ @@ -22,24 +23,23 @@ static int run_mg_bt_0004(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_chan_cd[8]; + long h_cnt; + long h_amt; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long channels = 0, grand_cnt = 0, grand_amt = 0, errcnt = 0; + char amtbuf[32]; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mg_bt_0004 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT chan_cd, COUNT(*), COALESCE(SUM(amount), 0) + FROM mg_ledger + WHERE biz_date = :h_biz_date + GROUP BY chan_cd + ORDER BY chan_cd; EXEC SQL OPEN cur_mg_bt_0004; if (sqlca.sqlcode != TX_SQL_OK) { @@ -50,8 +50,13 @@ static int run_mg_bt_0004(const char *biz_date) if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[mg_bt_0004] tx_begin 경고"); + printf("========== 채널별 전문 집계 [mg_bt_0004] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" %-8s %10s %16s\n", "채널", "건수", "금액"); + printf(" ------------------------------------------\n"); + for (;;) { - EXEC SQL FETCH cur_mg_bt_0004 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mg_bt_0004 INTO :h_chan_cd, :h_cnt, :h_amt; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,20 +66,17 @@ static int run_mg_bt_0004(const char *biz_date) break; } - total++; + channels++; + grand_cnt += h_cnt; + grand_amt += h_amt; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + amount_format_won(h_amt, amtbuf, sizeof(amtbuf)); + printf(" %-8s %10ld %16s\n", h_chan_cd, h_cnt, amtbuf); - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL INSERT INTO mg_stat (biz_date, chan_cd, tx_cnt, tx_amt, made_ts) + VALUES (:h_biz_date, :h_chan_cd, :h_cnt, :h_amt, now()); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mg_stat"); errcnt++; } } @@ -86,18 +88,18 @@ static int run_mg_bt_0004(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[mg_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + amount_format_won(grand_amt, amtbuf, sizeof(amtbuf)); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" ------------------------------------------\n"); + printf(" 채널수 : %ld\n", channels); + printf(" 총 건수 : %ld\n", grand_cnt); + printf(" 총 금액 : %s\n", amtbuf); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[mg_bt_0004] 집계 date=%s 채널=%ld 건수=%ld", + biz_date, channels, grand_cnt); + return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -121,7 +123,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0004] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0004] 채널 집계 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0005.c b/app/batch/mg_bt_0005.c index 1c08743..741b979 100644 --- a/app/batch/mg_bt_0005.c +++ b/app/batch/mg_bt_0005.c @@ -8,10 +8,11 @@ #line 1 "app/batch/mg_bt_0005.pgc" /* @tier easy @module mg @transform msg-gateway-batch */ /* - * mg_bt_0005.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0005.pgc - 전문 통계 리포트 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 커서를 사용하지 않고 표준 DBIO 집계 API(mg_rec_count/mg_rec_sum)만 + * 호출하여 지정 영업일의 상태별(R/M/U/S) 건수와 총 거래금액을 산출, + * 처리율(완료+정산 / 전체)을 계산하여 리포트한다. * * 실행: mg_bt_0005 [db@host] */ @@ -93,121 +94,62 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0005.pgc" +#line 20 "app/batch/mg_bt_0005.pgc" +/* 백분율(정수 소수 1자리 스케일) 계산 */ +static long mg_pct_x10(long part, long whole) +{ + if (whole <= 0) + return 0; + return (part * 1000) / whole; +} + static int run_mg_bt_0005(const char *biz_date) { - /* exec sql begin declare section */ - - - - - - -#line 24 "app/batch/mg_bt_0005.pgc" - char h_biz_date [ 9 ] ; - -#line 25 "app/batch/mg_bt_0005.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/mg_bt_0005.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/mg_bt_0005.pgc" - long h_amount ; - -#line 28 "app/batch/mg_bt_0005.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ -#line 29 "app/batch/mg_bt_0005.pgc" - - - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long c_recv, c_done, c_fail, c_settled, total; + long sum_amt = 0; + long rate; int rc; + char amtbuf[32]; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + c_recv = mg_rec_count(biz_date, MG_ST_RECV); + c_done = mg_rec_count(biz_date, MG_ST_DONE); + c_fail = mg_rec_count(biz_date, MG_ST_FAIL); + c_settled = mg_rec_count(biz_date, MG_ST_SETTLED); - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0005 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mg_bt_0005.pgc" - -#line 42 "app/batch/mg_bt_0005.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0005 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mg_bt_0005.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0005"); + if (c_recv < 0 || c_done < 0 || c_fail < 0 || c_settled < 0) { + tx_log(TX_LOG_ERROR, "[mg_bt_0005] 상태별 건수 조회 실패"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0005] tx_begin 경고"); + total = c_recv + c_done + c_fail + c_settled; - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0005", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0005.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_mg_bt_0005"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + rc = mg_rec_sum(biz_date, &sum_amt); + if (rc != TX_OK) { + tx_log(TX_LOG_WARN, "[mg_bt_0005] 금액 합계 조회 경고 rc=%d", rc); + sum_amt = 0; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0005", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0005.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + rate = mg_pct_x10(c_done + c_settled, total); + amount_format_won(sum_amt, amtbuf, sizeof(amtbuf)); tx_log(TX_LOG_INFO, - "[mg_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mg_bt_0005] 통계 date=%s 전체=%ld 완료=%ld 실패=%ld 정산=%ld", + biz_date, total, c_done, c_fail, c_settled); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 전문 통계 리포트 [mg_bt_0005] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 접수(R) : %ld\n", c_recv); + printf(" 처리완료(M) : %ld\n", c_done); + printf(" 실패(U) : %ld\n", c_fail); + printf(" 정산완료(S) : %ld\n", c_settled); + printf(" 전체 건수 : %ld\n", total); + printf(" 총 거래금액 : %s\n", amtbuf); + printf(" 처리율 : %ld.%ld%%\n", rate / 10, rate % 10); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) @@ -230,7 +172,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0005] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0005] 통계 리포트 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0005.pgc b/app/batch/mg_bt_0005.pgc index 1f915e5..00d2fd6 100644 --- a/app/batch/mg_bt_0005.pgc +++ b/app/batch/mg_bt_0005.pgc @@ -1,9 +1,10 @@ /* @tier easy @module mg @transform msg-gateway-batch */ /* - * mg_bt_0005.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0005.pgc - 전문 통계 리포트 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 커서를 사용하지 않고 표준 DBIO 집계 API(mg_rec_count/mg_rec_sum)만 + * 호출하여 지정 영업일의 상태별(R/M/U/S) 건수와 총 거래금액을 산출, + * 처리율(완료+정산 / 전체)을 계산하여 리포트한다. * * 실행: mg_bt_0005 [db@host] */ @@ -18,87 +19,59 @@ EXEC SQL INCLUDE sqlca; +/* 백분율(정수 소수 1자리 스케일) 계산 */ +static long mg_pct_x10(long part, long whole) +{ + if (whole <= 0) + return 0; + return (part * 1000) / whole; +} + static int run_mg_bt_0005(const char *biz_date) { - EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; - EXEC SQL END DECLARE SECTION; - - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long c_recv, c_done, c_fail, c_settled, total; + long sum_amt = 0; + long rate; int rc; + char amtbuf[32]; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + c_recv = mg_rec_count(biz_date, MG_ST_RECV); + c_done = mg_rec_count(biz_date, MG_ST_DONE); + c_fail = mg_rec_count(biz_date, MG_ST_FAIL); + c_settled = mg_rec_count(biz_date, MG_ST_SETTLED); - EXEC SQL DECLARE cur_mg_bt_0005 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_mg_bt_0005; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0005"); + if (c_recv < 0 || c_done < 0 || c_fail < 0 || c_settled < 0) { + tx_log(TX_LOG_ERROR, "[mg_bt_0005] 상태별 건수 조회 실패"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0005] tx_begin 경고"); + total = c_recv + c_done + c_fail + c_settled; - for (;;) { - EXEC SQL FETCH cur_mg_bt_0005 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_mg_bt_0005"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + rc = mg_rec_sum(biz_date, &sum_amt); + if (rc != TX_OK) { + tx_log(TX_LOG_WARN, "[mg_bt_0005] 금액 합계 조회 경고 rc=%d", rc); + sum_amt = 0; } - EXEC SQL CLOSE cur_mg_bt_0005; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + rate = mg_pct_x10(c_done + c_settled, total); + amount_format_won(sum_amt, amtbuf, sizeof(amtbuf)); tx_log(TX_LOG_INFO, - "[mg_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mg_bt_0005] 통계 date=%s 전체=%ld 완료=%ld 실패=%ld 정산=%ld", + biz_date, total, c_done, c_fail, c_settled); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 전문 통계 리포트 [mg_bt_0005] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 접수(R) : %ld\n", c_recv); + printf(" 처리완료(M) : %ld\n", c_done); + printf(" 실패(U) : %ld\n", c_fail); + printf(" 정산완료(S) : %ld\n", c_settled); + printf(" 전체 건수 : %ld\n", total); + printf(" 총 거래금액 : %s\n", amtbuf); + printf(" 처리율 : %ld.%ld%%\n", rate / 10, rate % 10); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) @@ -121,7 +94,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0005] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0005] 통계 리포트 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0006.c b/app/batch/mg_bt_0006.c index 8cb2010..e47ac35 100644 --- a/app/batch/mg_bt_0006.c +++ b/app/batch/mg_bt_0006.c @@ -8,10 +8,11 @@ #line 1 "app/batch/mg_bt_0006.pgc" /* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0006.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=medium] + * mg_bt_0006.pgc - 오류전문 원인분석 리포트 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 실패(U) 전문을 커서로 순회하며 오류코드(err_cd)를 + * 코드 대역별로 분류(형식/한도/통신/기타)하여 원인별 건수를 집계한다. + * 최다 발생 원인을 판정하여 리포트한다. (커서 순회, 상태변경 없음) * * 실행: mg_bt_0006 [db@host] */ @@ -93,71 +94,80 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0006.pgc" +#line 20 "app/batch/mg_bt_0006.pgc" +#define MG_CAUSE_FORMAT 0 /* 형식오류 */ +#define MG_CAUSE_LIMIT 1 /* 한도초과 */ +#define MG_CAUSE_COMM 2 /* 통신오류 */ +#define MG_CAUSE_ETC 3 /* 기타 */ +#define MG_CAUSE_N 4 + +/* 오류코드 대역으로 원인 카테고리 분류 */ +static int mg_classify_err(long err_cd) +{ + if (err_cd >= 100 && err_cd < 200) + return MG_CAUSE_FORMAT; + if (err_cd >= 200 && err_cd < 300) + return MG_CAUSE_LIMIT; + if (err_cd >= 300 && err_cd < 400) + return MG_CAUSE_COMM; + return MG_CAUSE_ETC; +} + static int run_mg_bt_0006(const char *biz_date) { /* exec sql begin declare section */ - - -#line 24 "app/batch/mg_bt_0006.pgc" +#line 43 "app/batch/mg_bt_0006.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mg_bt_0006.pgc" +#line 44 "app/batch/mg_bt_0006.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/mg_bt_0006.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/mg_bt_0006.pgc" - long h_amount ; - -#line 28 "app/batch/mg_bt_0006.pgc" - char h_new_status [ 2 ] ; +#line 45 "app/batch/mg_bt_0006.pgc" + long h_err_cd ; /* exec sql end declare section */ -#line 29 "app/batch/mg_bt_0006.pgc" +#line 46 "app/batch/mg_bt_0006.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + static const char *cause_name[MG_CAUSE_N] = { + "형식오류", "한도초과", "통신오류", "기타" + }; + long bucket[MG_CAUSE_N] = {0, 0, 0, 0}; + long total = 0, errcnt = 0; + int cat, top, i; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0006 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mg_bt_0006.pgc" + /* declare cur_mg_bt_0006 cursor for select key , coalesce ( err_cd , 0 ) from mg_ledger where biz_date = $1 and status = 'U' order by key */ +#line 63 "app/batch/mg_bt_0006.pgc" -#line 42 "app/batch/mg_bt_0006.pgc" +#line 63 "app/batch/mg_bt_0006.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0006 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0006 cursor for select key , coalesce ( err_cd , 0 ) from mg_ledger where biz_date = $1 and status = 'U' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mg_bt_0006.pgc" +#line 65 "app/batch/mg_bt_0006.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0006"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0006] tx_begin 경고"); - for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0006", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_err_cd),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0006.pgc" +#line 72 "app/batch/mg_bt_0006.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,42 +179,32 @@ static int run_mg_bt_0006(const char *biz_date) } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + cat = mg_classify_err(h_err_cd); + bucket[cat]++; + tx_log(TX_LOG_DEBUG, "[mg_bt_0006] key=%s errcd=%ld cause=%s", + h_key, h_err_cd, cause_name[cat]); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0006", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0006.pgc" +#line 89 "app/batch/mg_bt_0006.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + top = 0; + for (i = 1; i < MG_CAUSE_N; i++) { + if (bucket[i] > bucket[top]) + top = i; + } - tx_log(TX_LOG_INFO, - "[mg_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_log(TX_LOG_INFO, "[mg_bt_0006] 오류분석 date=%s 실패=%ld 최다원인=%s", + biz_date, total, cause_name[top]); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0006] ==========\n"); + printf("========== 오류전문 원인분석 [mg_bt_0006] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 실패 전문 : %ld\n", total); + for (i = 0; i < MG_CAUSE_N; i++) + printf(" %-10s: %ld\n", cause_name[i], bucket[i]); + printf(" 최다 원인 : %s (%ld건)\n", cause_name[top], bucket[top]); + printf(" 조회 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -230,7 +230,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0006] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0006] 원인분석 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0006.pgc b/app/batch/mg_bt_0006.pgc index beb8145..e53c9b0 100644 --- a/app/batch/mg_bt_0006.pgc +++ b/app/batch/mg_bt_0006.pgc @@ -1,9 +1,10 @@ /* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0006.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=medium] + * mg_bt_0006.pgc - 오류전문 원인분석 리포트 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 실패(U) 전문을 커서로 순회하며 오류코드(err_cd)를 + * 코드 대역별로 분류(형식/한도/통신/기타)하여 원인별 건수를 집계한다. + * 최다 발생 원인을 판정하여 리포트한다. (커서 순회, 상태변경 없음) * * 실행: mg_bt_0006 [db@host] */ @@ -18,28 +19,48 @@ EXEC SQL INCLUDE sqlca; +#define MG_CAUSE_FORMAT 0 /* 형식오류 */ +#define MG_CAUSE_LIMIT 1 /* 한도초과 */ +#define MG_CAUSE_COMM 2 /* 통신오류 */ +#define MG_CAUSE_ETC 3 /* 기타 */ +#define MG_CAUSE_N 4 + +/* 오류코드 대역으로 원인 카테고리 분류 */ +static int mg_classify_err(long err_cd) +{ + if (err_cd >= 100 && err_cd < 200) + return MG_CAUSE_FORMAT; + if (err_cd >= 200 && err_cd < 300) + return MG_CAUSE_LIMIT; + if (err_cd >= 300 && err_cd < 400) + return MG_CAUSE_COMM; + return MG_CAUSE_ETC; +} + static int run_mg_bt_0006(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_err_cd; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + static const char *cause_name[MG_CAUSE_N] = { + "형식오류", "한도초과", "통신오류", "기타" + }; + long bucket[MG_CAUSE_N] = {0, 0, 0, 0}; + long total = 0, errcnt = 0; + int cat, top, i; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mg_bt_0006 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT key, COALESCE(err_cd, 0) + FROM mg_ledger + WHERE biz_date = :h_biz_date + AND status = 'U' + ORDER BY key; EXEC SQL OPEN cur_mg_bt_0006; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,11 +68,8 @@ static int run_mg_bt_0006(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0006] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_mg_bt_0006 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mg_bt_0006 INTO :h_key, :h_err_cd; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,40 +80,30 @@ static int run_mg_bt_0006(const char *biz_date) } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + cat = mg_classify_err(h_err_cd); + bucket[cat]++; + tx_log(TX_LOG_DEBUG, "[mg_bt_0006] key=%s errcd=%ld cause=%s", + h_key, h_err_cd, cause_name[cat]); } EXEC SQL CLOSE cur_mg_bt_0006; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + top = 0; + for (i = 1; i < MG_CAUSE_N; i++) { + if (bucket[i] > bucket[top]) + top = i; + } - tx_log(TX_LOG_INFO, - "[mg_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_log(TX_LOG_INFO, "[mg_bt_0006] 오류분석 date=%s 실패=%ld 최다원인=%s", + biz_date, total, cause_name[top]); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0006] ==========\n"); + printf("========== 오류전문 원인분석 [mg_bt_0006] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 실패 전문 : %ld\n", total); + for (i = 0; i < MG_CAUSE_N; i++) + printf(" %-10s: %ld\n", cause_name[i], bucket[i]); + printf(" 최다 원인 : %s (%ld건)\n", cause_name[top], bucket[top]); + printf(" 조회 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -121,7 +129,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0006] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0006] 원인분석 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0007.c b/app/batch/mg_bt_0007.c index 7cc46e4..09dadfa 100644 --- a/app/batch/mg_bt_0007.c +++ b/app/batch/mg_bt_0007.c @@ -8,10 +8,11 @@ #line 1 "app/batch/mg_bt_0007.pgc" /* @tier easy @module mg @transform msg-gateway-batch */ /* - * mg_bt_0007.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0007.pgc - 재전송 한도초과 폐기 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 재전송 큐(mg_queue)에서 재시도 횟수(retry_cnt)가 한도(기본 5회)에 + * 도달한 전문을 커서로 순회하며 폐기 상태(U)로 확정한다. + * 폐기 건수와 누적 재시도 횟수를 리포트한다. * * 실행: mg_bt_0007 [db@host] */ @@ -93,53 +94,56 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0007.pgc" +#line 20 "app/batch/mg_bt_0007.pgc" +#define MG_RETRY_MAX 5 /* 재전송 허용 한도 */ + static int run_mg_bt_0007(const char *biz_date) { /* exec sql begin declare section */ - - + -#line 24 "app/batch/mg_bt_0007.pgc" +#line 27 "app/batch/mg_bt_0007.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mg_bt_0007.pgc" +#line 28 "app/batch/mg_bt_0007.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/mg_bt_0007.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/mg_bt_0007.pgc" - long h_amount ; - -#line 28 "app/batch/mg_bt_0007.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ #line 29 "app/batch/mg_bt_0007.pgc" + long h_retry ; + +#line 30 "app/batch/mg_bt_0007.pgc" + int h_limit ; +/* exec sql end declare section */ +#line 31 "app/batch/mg_bt_0007.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, dropped = 0, errcnt = 0; + long retry_accum = 0; int rc; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + h_limit = MG_RETRY_MAX; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0007 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mg_bt_0007.pgc" + ECPGset_var( 0, &( h_limit ), __LINE__);\ + ECPGset_var( 1, ( h_biz_date ), __LINE__);\ + /* declare cur_mg_bt_0007 cursor for select key , coalesce ( retry_cnt , 0 ) from mg_queue where biz_date = $1 and status = 'R' and retry_cnt >= $2 order by key */ +#line 47 "app/batch/mg_bt_0007.pgc" -#line 42 "app/batch/mg_bt_0007.pgc" +#line 47 "app/batch/mg_bt_0007.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0007 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0007 cursor for select key , coalesce ( retry_cnt , 0 ) from mg_queue where biz_date = $1 and status = 'R' and retry_cnt >= $2 order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_limit),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mg_bt_0007.pgc" +#line 49 "app/batch/mg_bt_0007.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0007"); @@ -153,11 +157,9 @@ static int run_mg_bt_0007(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0007", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_retry),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0007.pgc" +#line 59 "app/batch/mg_bt_0007.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,20 +171,15 @@ static int run_mg_bt_0007(const char *biz_date) } total++; + retry_accum += h_retry; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); + rc = mg_rec_update_status(h_key, MG_ST_FAIL); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[mg_bt_0007] 폐기 실패 key=%s rc=%d", h_key, rc); errcnt++; + } else { + dropped++; + tx_log(TX_LOG_WARN, "[mg_bt_0007] 한도초과 폐기 key=%s retry=%ld", h_key, h_retry); } } @@ -195,16 +192,16 @@ static int run_mg_bt_0007(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[mg_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_log(TX_LOG_INFO, "[mg_bt_0007] 요약 date=%s 폐기=%ld 오류=%ld", + biz_date, dropped, errcnt); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 재전송 한도초과 폐기 [mg_bt_0007] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 재시도 한도 : %d\n", MG_RETRY_MAX); + printf(" 대상 건수 : %ld\n", total); + printf(" 폐기(U) : %ld\n", dropped); + printf(" 누적 재시도 : %ld\n", retry_accum); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -230,7 +227,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0007] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0007] 한도초과 폐기 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0007.pgc b/app/batch/mg_bt_0007.pgc index e1d1b97..ddb8691 100644 --- a/app/batch/mg_bt_0007.pgc +++ b/app/batch/mg_bt_0007.pgc @@ -1,9 +1,10 @@ /* @tier easy @module mg @transform msg-gateway-batch */ /* - * mg_bt_0007.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0007.pgc - 재전송 한도초과 폐기 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 재전송 큐(mg_queue)에서 재시도 횟수(retry_cnt)가 한도(기본 5회)에 + * 도달한 전문을 커서로 순회하며 폐기 상태(U)로 확정한다. + * 폐기 건수와 누적 재시도 횟수를 리포트한다. * * 실행: mg_bt_0007 [db@host] */ @@ -18,28 +19,32 @@ EXEC SQL INCLUDE sqlca; +#define MG_RETRY_MAX 5 /* 재전송 허용 한도 */ + static int run_mg_bt_0007(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_retry; + int h_limit; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, dropped = 0, errcnt = 0; + long retry_accum = 0; int rc; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + h_limit = MG_RETRY_MAX; EXEC SQL DECLARE cur_mg_bt_0007 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT key, COALESCE(retry_cnt, 0) + FROM mg_queue + WHERE biz_date = :h_biz_date + AND status = 'R' + AND retry_cnt >= :h_limit + ORDER BY key; EXEC SQL OPEN cur_mg_bt_0007; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +56,7 @@ static int run_mg_bt_0007(const char *biz_date) tx_log(TX_LOG_WARN, "[mg_bt_0007] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mg_bt_0007 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mg_bt_0007 INTO :h_key, :h_retry; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,20 +67,15 @@ static int run_mg_bt_0007(const char *biz_date) } total++; + retry_accum += h_retry; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); + rc = mg_rec_update_status(h_key, MG_ST_FAIL); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[mg_bt_0007] 폐기 실패 key=%s rc=%d", h_key, rc); errcnt++; + } else { + dropped++; + tx_log(TX_LOG_WARN, "[mg_bt_0007] 한도초과 폐기 key=%s retry=%ld", h_key, h_retry); } } @@ -86,16 +86,16 @@ static int run_mg_bt_0007(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[mg_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_log(TX_LOG_INFO, "[mg_bt_0007] 요약 date=%s 폐기=%ld 오류=%ld", + biz_date, dropped, errcnt); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 재전송 한도초과 폐기 [mg_bt_0007] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 재시도 한도 : %d\n", MG_RETRY_MAX); + printf(" 대상 건수 : %ld\n", total); + printf(" 폐기(U) : %ld\n", dropped); + printf(" 누적 재시도 : %ld\n", retry_accum); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -121,7 +121,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0007] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0007] 한도초과 폐기 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0008.c b/app/batch/mg_bt_0008.c index 48d7b35..b18b56f 100644 --- a/app/batch/mg_bt_0008.c +++ b/app/batch/mg_bt_0008.c @@ -6,12 +6,13 @@ /* End of automatic include section */ #line 1 "app/batch/mg_bt_0008.pgc" -/* @tier hard @module mg @transform msg-gateway-batch */ +/* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0008.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=hard] + * mg_bt_0008.pgc - 채널 세션 만료 정리 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 채널 세션(mg_chan) 중 마지막 활동시각(last_ts)이 유휴 임계(기본 + * 900초)를 초과한 세션을 커서로 순회하며 raw SQL UPDATE 로 세션을 + * 종료(sess_st='C') 처리한다. 정리 세션수/유지 세션수를 리포트한다. * * 실행: mg_bt_0008 [db@host] */ @@ -93,54 +94,51 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0008.pgc" +#line 20 "app/batch/mg_bt_0008.pgc" +#define MG_SESS_IDLE_SEC 900 /* 세션 유휴 만료 임계(초) */ + static int run_mg_bt_0008(const char *biz_date) { /* exec sql begin declare section */ - - + -#line 24 "app/batch/mg_bt_0008.pgc" - char h_biz_date [ 9 ] ; - -#line 25 "app/batch/mg_bt_0008.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/mg_bt_0008.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/mg_bt_0008.pgc" - long h_amount ; + char h_chan_cd [ 8 ] ; #line 28 "app/batch/mg_bt_0008.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_sess_id ; + #line 29 "app/batch/mg_bt_0008.pgc" + long h_idle ; + +#line 30 "app/batch/mg_bt_0008.pgc" + int h_idle_max ; +/* exec sql end declare section */ +#line 31 "app/batch/mg_bt_0008.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, closed = 0, errcnt = 0; + long longest = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + h_idle_max = MG_SESS_IDLE_SEC; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0008 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mg_bt_0008.pgc" - -#line 42 "app/batch/mg_bt_0008.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0008 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} + ECPGset_var( 0, &( h_idle_max ), __LINE__);\ + /* declare cur_mg_bt_0008 cursor for select chan_cd , sess_id , cast ( extract ( EPOCH from ( now ( ) - last_ts ) ) as bigint ) from mg_chan where sess_st = 'A' and extract ( EPOCH from ( now ( ) - last_ts ) ) >= $1 order by chan_cd , sess_id */ #line 44 "app/batch/mg_bt_0008.pgc" +#line 44 "app/batch/mg_bt_0008.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0008 cursor for select chan_cd , sess_id , cast ( extract ( EPOCH from ( now ( ) - last_ts ) ) as bigint ) from mg_chan where sess_st = 'A' and extract ( EPOCH from ( now ( ) - last_ts ) ) >= $1 order by chan_cd , sess_id", + ECPGt_int,&(h_idle_max),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 46 "app/batch/mg_bt_0008.pgc" + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0008"); return TX_EDB; @@ -151,13 +149,13 @@ static int run_mg_bt_0008(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0008", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_chan_cd),(long)8,(long)1,(8)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_sess_id),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_idle),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0008.pgc" +#line 56 "app/batch/mg_bt_0008.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,25 +167,26 @@ static int run_mg_bt_0008(const char *biz_date) } total++; + if (h_idle > longest) + longest = h_idle; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mg_chan set sess_st = 'C' , close_ts = now ( ) where sess_id = $1 ", + ECPGt_long,&(h_sess_id),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 72 "app/batch/mg_bt_0008.pgc" - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mg_chan close"); errcnt++; + } else { + closed++; + tx_log(TX_LOG_DEBUG, "[mg_bt_0008] 세션종료 chan=%s sess=%ld idle=%lds", + h_chan_cd, h_sess_id, h_idle); } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0008", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0008.pgc" +#line 83 "app/batch/mg_bt_0008.pgc" if (errcnt == 0) @@ -195,16 +194,16 @@ static int run_mg_bt_0008(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[mg_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_log(TX_LOG_INFO, "[mg_bt_0008] 세션정리 date=%s 종료=%ld 오류=%ld", + biz_date, closed, errcnt); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 세션 만료 정리 [mg_bt_0008] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 유휴 임계(초) : %d\n", MG_SESS_IDLE_SEC); + printf(" 만료 대상 : %ld\n", total); + printf(" 종료 세션 : %ld\n", closed); + printf(" 최장 유휴(초) : %ld\n", longest); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -230,7 +229,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0008] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0008] 세션 만료 정리 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0008.pgc b/app/batch/mg_bt_0008.pgc index 39c354f..73ee00c 100644 --- a/app/batch/mg_bt_0008.pgc +++ b/app/batch/mg_bt_0008.pgc @@ -1,9 +1,10 @@ -/* @tier hard @module mg @transform msg-gateway-batch */ +/* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0008.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=hard] + * mg_bt_0008.pgc - 채널 세션 만료 정리 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 채널 세션(mg_chan) 중 마지막 활동시각(last_ts)이 유휴 임계(기본 + * 900초)를 초과한 세션을 커서로 순회하며 raw SQL UPDATE 로 세션을 + * 종료(sess_st='C') 처리한다. 정리 세션수/유지 세션수를 리포트한다. * * 실행: mg_bt_0008 [db@host] */ @@ -18,28 +19,29 @@ EXEC SQL INCLUDE sqlca; +#define MG_SESS_IDLE_SEC 900 /* 세션 유휴 만료 임계(초) */ + static int run_mg_bt_0008(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_chan_cd[8]; + long h_sess_id; + long h_idle; + int h_idle_max; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, closed = 0, errcnt = 0; + long longest = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + h_idle_max = MG_SESS_IDLE_SEC; EXEC SQL DECLARE cur_mg_bt_0008 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT chan_cd, sess_id, + CAST(EXTRACT(EPOCH FROM (now() - last_ts)) AS bigint) + FROM mg_chan + WHERE sess_st = 'A' + AND EXTRACT(EPOCH FROM (now() - last_ts)) >= :h_idle_max + ORDER BY chan_cd, sess_id; EXEC SQL OPEN cur_mg_bt_0008; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +53,7 @@ static int run_mg_bt_0008(const char *biz_date) tx_log(TX_LOG_WARN, "[mg_bt_0008] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mg_bt_0008 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mg_bt_0008 INTO :h_chan_cd, :h_sess_id, :h_idle; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,20 +64,19 @@ static int run_mg_bt_0008(const char *biz_date) } total++; + if (h_idle > longest) + longest = h_idle; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL UPDATE mg_chan + SET sess_st = 'C', close_ts = now() + WHERE sess_id = :h_sess_id; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mg_chan close"); errcnt++; + } else { + closed++; + tx_log(TX_LOG_DEBUG, "[mg_bt_0008] 세션종료 chan=%s sess=%ld idle=%lds", + h_chan_cd, h_sess_id, h_idle); } } @@ -86,16 +87,16 @@ static int run_mg_bt_0008(const char *biz_date) else tx_abort(); - tx_log(TX_LOG_INFO, - "[mg_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_log(TX_LOG_INFO, "[mg_bt_0008] 세션정리 date=%s 종료=%ld 오류=%ld", + biz_date, closed, errcnt); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 세션 만료 정리 [mg_bt_0008] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 유휴 임계(초) : %d\n", MG_SESS_IDLE_SEC); + printf(" 만료 대상 : %ld\n", total); + printf(" 종료 세션 : %ld\n", closed); + printf(" 최장 유휴(초) : %ld\n", longest); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -121,7 +122,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0008] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0008] 세션 만료 정리 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0009.c b/app/batch/mg_bt_0009.c index 6dea119..3aecb61 100644 --- a/app/batch/mg_bt_0009.c +++ b/app/batch/mg_bt_0009.c @@ -6,12 +6,14 @@ /* End of automatic include section */ #line 1 "app/batch/mg_bt_0009.pgc" -/* @tier easy @module mg @transform msg-gateway-batch */ +/* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0009.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0009.pgc - 전문 포맷 검증 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수(R) 전문을 커서로 순회하며 금액 유효성, + * 카드번호 길이(16~19), 영업일 형식을 검증한다. 위반 건은 경고 + * 로그를 남기고 위반유형별 카운트와 정상/위반 건수를 리포트한다. + * (검증만 수행, 원장 변경 없음) * * 실행: mg_bt_0009 [db@host] */ @@ -93,9 +95,16 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0009.pgc" +#line 21 "app/batch/mg_bt_0009.pgc" +/* 카드번호 자릿수 유효성(16~19자리) */ +static int mg_card_len_ok(const char *card) +{ + size_t n = strlen(card); + return (n >= 16 && n <= 19) ? 1 : 0; +} + static int run_mg_bt_0009(const char *biz_date) { /* exec sql begin declare section */ @@ -105,59 +114,60 @@ static int run_mg_bt_0009(const char *biz_date) -#line 24 "app/batch/mg_bt_0009.pgc" +#line 33 "app/batch/mg_bt_0009.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mg_bt_0009.pgc" +#line 34 "app/batch/mg_bt_0009.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/mg_bt_0009.pgc" - char h_merch_id [ 16 ] ; +#line 35 "app/batch/mg_bt_0009.pgc" + char h_card_no [ 20 ] ; -#line 27 "app/batch/mg_bt_0009.pgc" +#line 36 "app/batch/mg_bt_0009.pgc" long h_amount ; -#line 28 "app/batch/mg_bt_0009.pgc" - char h_new_status [ 2 ] ; +#line 37 "app/batch/mg_bt_0009.pgc" + char h_rec_date [ 9 ] ; /* exec sql end declare section */ -#line 29 "app/batch/mg_bt_0009.pgc" +#line 38 "app/batch/mg_bt_0009.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, ok = 0, bad = 0, errcnt = 0; + long v_amount = 0, v_card = 0, v_date = 0; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0009 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mg_bt_0009.pgc" + /* declare cur_mg_bt_0009 cursor for select key , card_no , amount , biz_date from mg_ledger where biz_date = $1 and status = 'R' order by key */ +#line 51 "app/batch/mg_bt_0009.pgc" -#line 42 "app/batch/mg_bt_0009.pgc" +#line 51 "app/batch/mg_bt_0009.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0009 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0009 cursor for select key , card_no , amount , biz_date from mg_ledger where biz_date = $1 and status = 'R' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mg_bt_0009.pgc" +#line 53 "app/batch/mg_bt_0009.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0009"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0009] tx_begin 경고"); - for (;;) { + int viol = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0009", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_rec_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0009.pgc" +#line 62 "app/batch/mg_bt_0009.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,41 +180,44 @@ static int run_mg_bt_0009(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (!amount_is_valid(h_amount)) { + v_amount++; + viol = 1; + tx_log(TX_LOG_WARN, "[mg_bt_0009] 금액위반 key=%s amt=%ld", h_key, h_amount); + } + if (!mg_card_len_ok(h_card_no)) { + v_card++; + viol = 1; + tx_log(TX_LOG_WARN, "[mg_bt_0009] 카드길이위반 key=%s", h_key); + } + if (!date_is_valid(h_rec_date)) { + v_date++; + viol = 1; + tx_log(TX_LOG_WARN, "[mg_bt_0009] 일자위반 key=%s date=%s", h_key, h_rec_date); } - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (viol) + bad++; + else + ok++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0009", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0009.pgc" +#line 96 "app/batch/mg_bt_0009.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + tx_log(TX_LOG_INFO, "[mg_bt_0009] 검증 date=%s 정상=%ld 위반=%ld", + biz_date, ok, bad); - tx_log(TX_LOG_INFO, - "[mg_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0009] ==========\n"); + printf("========== 전문 포맷 검증 [mg_bt_0009] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 검증 건수 : %ld\n", total); + printf(" 정상 건수 : %ld\n", ok); + printf(" 위반 건수 : %ld\n", bad); + printf(" - 금액위반 : %ld\n", v_amount); + printf(" - 카드위반 : %ld\n", v_card); + printf(" - 일자위반 : %ld\n", v_date); + printf(" 조회 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -230,7 +243,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0009] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0009] 포맷 검증 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0009.pgc b/app/batch/mg_bt_0009.pgc index bf32706..72466f6 100644 --- a/app/batch/mg_bt_0009.pgc +++ b/app/batch/mg_bt_0009.pgc @@ -1,9 +1,11 @@ -/* @tier easy @module mg @transform msg-gateway-batch */ +/* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0009.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0009.pgc - 전문 포맷 검증 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수(R) 전문을 커서로 순회하며 금액 유효성, + * 카드번호 길이(16~19), 영업일 형식을 검증한다. 위반 건은 경고 + * 로그를 남기고 위반유형별 카운트와 정상/위반 건수를 리포트한다. + * (검증만 수행, 원장 변경 없음) * * 실행: mg_bt_0009 [db@host] */ @@ -18,28 +20,35 @@ EXEC SQL INCLUDE sqlca; +/* 카드번호 자릿수 유효성(16~19자리) */ +static int mg_card_len_ok(const char *card) +{ + size_t n = strlen(card); + return (n >= 16 && n <= 19) ? 1 : 0; +} + static int run_mg_bt_0009(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; + char h_card_no[20]; long h_amount; - char h_new_status[2]; + char h_rec_date[9]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, ok = 0, bad = 0, errcnt = 0; + long v_amount = 0, v_card = 0, v_date = 0; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mg_bt_0009 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT key, card_no, amount, biz_date + FROM mg_ledger + WHERE biz_date = :h_biz_date + AND status = 'R' + ORDER BY key; EXEC SQL OPEN cur_mg_bt_0009; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,11 +56,10 @@ static int run_mg_bt_0009(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0009] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_mg_bt_0009 INTO :h_key, :h_merch_id, :h_amount; + int viol = 0; + + EXEC SQL FETCH cur_mg_bt_0009 INTO :h_key, :h_card_no, :h_amount, :h_rec_date; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,39 +71,42 @@ static int run_mg_bt_0009(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (!amount_is_valid(h_amount)) { + v_amount++; + viol = 1; + tx_log(TX_LOG_WARN, "[mg_bt_0009] 금액위반 key=%s amt=%ld", h_key, h_amount); + } + if (!mg_card_len_ok(h_card_no)) { + v_card++; + viol = 1; + tx_log(TX_LOG_WARN, "[mg_bt_0009] 카드길이위반 key=%s", h_key); + } + if (!date_is_valid(h_rec_date)) { + v_date++; + viol = 1; + tx_log(TX_LOG_WARN, "[mg_bt_0009] 일자위반 key=%s date=%s", h_key, h_rec_date); } - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (viol) + bad++; + else + ok++; } EXEC SQL CLOSE cur_mg_bt_0009; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + tx_log(TX_LOG_INFO, "[mg_bt_0009] 검증 date=%s 정상=%ld 위반=%ld", + biz_date, ok, bad); - tx_log(TX_LOG_INFO, - "[mg_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0009] ==========\n"); + printf("========== 전문 포맷 검증 [mg_bt_0009] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 검증 건수 : %ld\n", total); + printf(" 정상 건수 : %ld\n", ok); + printf(" 위반 건수 : %ld\n", bad); + printf(" - 금액위반 : %ld\n", v_amount); + printf(" - 카드위반 : %ld\n", v_card); + printf(" - 일자위반 : %ld\n", v_date); + printf(" 조회 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -121,7 +132,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0009] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0009] 포맷 검증 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0010.c b/app/batch/mg_bt_0010.c index 93690f8..5a71efa 100644 --- a/app/batch/mg_bt_0010.c +++ b/app/batch/mg_bt_0010.c @@ -6,12 +6,13 @@ /* End of automatic include section */ #line 1 "app/batch/mg_bt_0010.pgc" -/* @tier hard @module mg @transform msg-gateway-batch */ +/* @tier easy @module mg @transform msg-gateway-batch */ /* - * mg_bt_0010.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=hard] + * mg_bt_0010.pgc - 이중화 동기화 점검 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * Active 원장(mg_ledger)과 Standby 복제원장(mg_ledger_sby)의 지정 + * 영업일 건수/금액을 raw SQL 단건 조회로 비교하여 동기화 지연을 + * 판정한다. 건수 차이가 허용치(기본 0)를 넘으면 지연으로 경보한다. * * 실행: mg_bt_0010 [db@host] */ @@ -93,9 +94,17 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0010.pgc" +#line 20 "app/batch/mg_bt_0010.pgc" +#define MG_SYNC_TOLERANCE 0 /* 허용 건수 차이 */ + +/* 절댓값 헬퍼 */ +static long mg_labs(long v) +{ + return (v < 0) ? -v : v; +} + static int run_mg_bt_0010(const char *biz_date) { /* exec sql begin declare section */ @@ -105,109 +114,81 @@ static int run_mg_bt_0010(const char *biz_date) -#line 24 "app/batch/mg_bt_0010.pgc" +#line 33 "app/batch/mg_bt_0010.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mg_bt_0010.pgc" - char h_key [ 16 ] ; +#line 34 "app/batch/mg_bt_0010.pgc" + long h_act_cnt ; -#line 26 "app/batch/mg_bt_0010.pgc" - char h_merch_id [ 16 ] ; +#line 35 "app/batch/mg_bt_0010.pgc" + long h_sby_cnt ; -#line 27 "app/batch/mg_bt_0010.pgc" - long h_amount ; +#line 36 "app/batch/mg_bt_0010.pgc" + long h_act_amt ; -#line 28 "app/batch/mg_bt_0010.pgc" - char h_new_status [ 2 ] ; +#line 37 "app/batch/mg_bt_0010.pgc" + long h_sby_amt ; /* exec sql end declare section */ -#line 29 "app/batch/mg_bt_0010.pgc" +#line 38 "app/batch/mg_bt_0010.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long cnt_diff, amt_diff; + int lagged; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + h_act_cnt = h_sby_cnt = h_act_amt = h_sby_amt = 0; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0010 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mg_bt_0010.pgc" - -#line 42 "app/batch/mg_bt_0010.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0010 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( SUM ( amount ) , 0 ) from mg_ledger where biz_date = $1 ", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mg_bt_0010.pgc" + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_act_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_act_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 50 "app/batch/mg_bt_0010.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0010"); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT active count"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0010] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0010", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( SUM ( amount ) , 0 ) from mg_ledger_sby where biz_date = $1 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_sby_cnt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sby_amt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0010.pgc" +#line 59 "app/batch/mg_bt_0010.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_mg_bt_0010"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT standby count"); + return TX_EDB; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0010", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0010.pgc" + cnt_diff = mg_labs(h_act_cnt - h_sby_cnt); + amt_diff = mg_labs(h_act_amt - h_sby_amt); + lagged = (cnt_diff > MG_SYNC_TOLERANCE || amt_diff != 0) ? 1 : 0; - - if (errcnt == 0) - tx_commit(); + if (lagged) + tx_log(TX_LOG_ERROR, "[mg_bt_0010] 동기화 지연 감지 건수차=%ld 금액차=%ld", + cnt_diff, amt_diff); else - tx_abort(); + tx_log(TX_LOG_INFO, "[mg_bt_0010] 동기화 정상 date=%s", biz_date); - tx_log(TX_LOG_INFO, - "[mg_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 이중화 동기화 점검 [mg_bt_0010] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" Active 건수 : %ld\n", h_act_cnt); + printf(" Standby 건수 : %ld\n", h_sby_cnt); + printf(" 건수 차이 : %ld\n", cnt_diff); + printf(" Active 금액 : %ld\n", h_act_amt); + printf(" Standby 금액 : %ld\n", h_sby_amt); + printf(" 금액 차이 : %ld\n", amt_diff); + printf(" 판정 : %s\n", lagged ? "지연(SYNC-LAG)" : "정상(IN-SYNC)"); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return lagged ? TX_FAIL : TX_OK; } int main(int argc, char **argv) @@ -230,7 +211,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0010] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0010] 동기화 점검 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0010.pgc b/app/batch/mg_bt_0010.pgc index f51e393..0567a8e 100644 --- a/app/batch/mg_bt_0010.pgc +++ b/app/batch/mg_bt_0010.pgc @@ -1,9 +1,10 @@ -/* @tier hard @module mg @transform msg-gateway-batch */ +/* @tier easy @module mg @transform msg-gateway-batch */ /* - * mg_bt_0010.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=hard] + * mg_bt_0010.pgc - 이중화 동기화 점검 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * Active 원장(mg_ledger)과 Standby 복제원장(mg_ledger_sby)의 지정 + * 영업일 건수/금액을 raw SQL 단건 조회로 비교하여 동기화 지연을 + * 판정한다. 건수 차이가 허용치(기본 0)를 넘으면 지연으로 경보한다. * * 실행: mg_bt_0010 [db@host] */ @@ -18,87 +19,71 @@ EXEC SQL INCLUDE sqlca; +#define MG_SYNC_TOLERANCE 0 /* 허용 건수 차이 */ + +/* 절댓값 헬퍼 */ +static long mg_labs(long v) +{ + return (v < 0) ? -v : v; +} + static int run_mg_bt_0010(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_act_cnt; + long h_sby_cnt; + long h_act_amt; + long h_sby_amt; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long cnt_diff, amt_diff; + int lagged; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + h_act_cnt = h_sby_cnt = h_act_amt = h_sby_amt = 0; - EXEC SQL DECLARE cur_mg_bt_0010 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_mg_bt_0010; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0010"); + EXEC SQL SELECT COUNT(*), COALESCE(SUM(amount), 0) + INTO :h_act_cnt, :h_act_amt + FROM mg_ledger + WHERE biz_date = :h_biz_date; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT active count"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0010] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_mg_bt_0010 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_mg_bt_0010"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL SELECT COUNT(*), COALESCE(SUM(amount), 0) + INTO :h_sby_cnt, :h_sby_amt + FROM mg_ledger_sby + WHERE biz_date = :h_biz_date; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT standby count"); + return TX_EDB; } - EXEC SQL CLOSE cur_mg_bt_0010; + cnt_diff = mg_labs(h_act_cnt - h_sby_cnt); + amt_diff = mg_labs(h_act_amt - h_sby_amt); + lagged = (cnt_diff > MG_SYNC_TOLERANCE || amt_diff != 0) ? 1 : 0; - if (errcnt == 0) - tx_commit(); + if (lagged) + tx_log(TX_LOG_ERROR, "[mg_bt_0010] 동기화 지연 감지 건수차=%ld 금액차=%ld", + cnt_diff, amt_diff); else - tx_abort(); + tx_log(TX_LOG_INFO, "[mg_bt_0010] 동기화 정상 date=%s", biz_date); - tx_log(TX_LOG_INFO, - "[mg_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 이중화 동기화 점검 [mg_bt_0010] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" Active 건수 : %ld\n", h_act_cnt); + printf(" Standby 건수 : %ld\n", h_sby_cnt); + printf(" 건수 차이 : %ld\n", cnt_diff); + printf(" Active 금액 : %ld\n", h_act_amt); + printf(" Standby 금액 : %ld\n", h_sby_amt); + printf(" 금액 차이 : %ld\n", amt_diff); + printf(" 판정 : %s\n", lagged ? "지연(SYNC-LAG)" : "정상(IN-SYNC)"); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return lagged ? TX_FAIL : TX_OK; } int main(int argc, char **argv) @@ -121,7 +106,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0010] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0010] 동기화 점검 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0011.c b/app/batch/mg_bt_0011.c index c1ab7e6..d4115ac 100644 --- a/app/batch/mg_bt_0011.c +++ b/app/batch/mg_bt_0011.c @@ -6,12 +6,14 @@ /* End of automatic include section */ #line 1 "app/batch/mg_bt_0011.pgc" -/* @tier easy @module mg @transform msg-gateway-batch */ +/* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0011.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0011.pgc - 전문 압축 아카이브 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 전문 페이로드(mg_log.payload)를 커서로 읽어 RLE + * (Run-Length Encoding) 유사 방식으로 압축크기를 산정, 원본/압축 + * 바이트를 누적하고 압축률을 계산하여 리포트한다. (실제 저장은 + * 하지 않는 압축효과 시뮬레이션) * * 실행: mg_bt_0011 [db@host] */ @@ -93,71 +95,77 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0011.pgc" +#line 21 "app/batch/mg_bt_0011.pgc" +/* RLE 압축 후 바이트 수 계산: 연속 동일문자 런을 (문자+길이) 2바이트로 */ +static long mg_rle_size(const char *buf) +{ + long out = 0; + size_t i = 0, n = strlen(buf); + + while (i < n) { + size_t j = i + 1; + while (j < n && buf[j] == buf[i] && (j - i) < 255) + j++; + out += 2; /* 1바이트 문자 + 1바이트 런길이 */ + i = j; + } + return out; +} + static int run_mg_bt_0011(const char *biz_date) { /* exec sql begin declare section */ - - -#line 24 "app/batch/mg_bt_0011.pgc" +#line 42 "app/batch/mg_bt_0011.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mg_bt_0011.pgc" - char h_key [ 16 ] ; +#line 43 "app/batch/mg_bt_0011.pgc" + long h_log_id ; -#line 26 "app/batch/mg_bt_0011.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/mg_bt_0011.pgc" - long h_amount ; - -#line 28 "app/batch/mg_bt_0011.pgc" - char h_new_status [ 2 ] ; +#line 44 "app/batch/mg_bt_0011.pgc" + char h_payload [ 256 ] ; /* exec sql end declare section */ -#line 29 "app/batch/mg_bt_0011.pgc" +#line 45 "app/batch/mg_bt_0011.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0; + long orig_bytes = 0, comp_bytes = 0; + long ratio_x10 = 0; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0011 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mg_bt_0011.pgc" + /* declare cur_mg_bt_0011 cursor for select log_id , payload from mg_log where biz_date = $1 order by log_id */ +#line 58 "app/batch/mg_bt_0011.pgc" -#line 42 "app/batch/mg_bt_0011.pgc" +#line 58 "app/batch/mg_bt_0011.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0011 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0011 cursor for select log_id , payload from mg_log where biz_date = $1 order by log_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mg_bt_0011.pgc" +#line 60 "app/batch/mg_bt_0011.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0011"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0011] tx_begin 경고"); - for (;;) { + long olen, clen; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0011", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_log_id),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_payload),(long)256,(long)1,(256)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0011.pgc" +#line 69 "app/batch/mg_bt_0011.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,42 +177,32 @@ static int run_mg_bt_0011(const char *biz_date) } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + olen = (long)strlen(h_payload); + clen = mg_rle_size(h_payload); + orig_bytes += olen; + comp_bytes += clen; + tx_log(TX_LOG_DEBUG, "[mg_bt_0011] log=%ld orig=%ld comp=%ld", + h_log_id, olen, clen); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0011", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0011.pgc" +#line 88 "app/batch/mg_bt_0011.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + if (orig_bytes > 0) + ratio_x10 = (comp_bytes * 1000) / orig_bytes; - tx_log(TX_LOG_INFO, - "[mg_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_log(TX_LOG_INFO, "[mg_bt_0011] 압축 date=%s 건수=%ld 원본=%ld 압축=%ld", + biz_date, total, orig_bytes, comp_bytes); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0011] ==========\n"); + printf("========== 전문 압축 아카이브 [mg_bt_0011] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 대상 전문 : %ld\n", total); + printf(" 원본 바이트 : %ld\n", orig_bytes); + printf(" 압축 바이트 : %ld\n", comp_bytes); + printf(" 절감 바이트 : %ld\n", orig_bytes - comp_bytes); + printf(" 압축률 : %ld.%ld%%\n", ratio_x10 / 10, ratio_x10 % 10); + printf(" 조회 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -230,7 +228,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0011] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0011] 압축 아카이브 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0011.pgc b/app/batch/mg_bt_0011.pgc index 96ef4fd..746114d 100644 --- a/app/batch/mg_bt_0011.pgc +++ b/app/batch/mg_bt_0011.pgc @@ -1,9 +1,11 @@ -/* @tier easy @module mg @transform msg-gateway-batch */ +/* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0011.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0011.pgc - 전문 압축 아카이브 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 전문 페이로드(mg_log.payload)를 커서로 읽어 RLE + * (Run-Length Encoding) 유사 방식으로 압축크기를 산정, 원본/압축 + * 바이트를 누적하고 압축률을 계산하여 리포트한다. (실제 저장은 + * 하지 않는 압축효과 시뮬레이션) * * 실행: mg_bt_0011 [db@host] */ @@ -18,28 +20,42 @@ EXEC SQL INCLUDE sqlca; +/* RLE 압축 후 바이트 수 계산: 연속 동일문자 런을 (문자+길이) 2바이트로 */ +static long mg_rle_size(const char *buf) +{ + long out = 0; + size_t i = 0, n = strlen(buf); + + while (i < n) { + size_t j = i + 1; + while (j < n && buf[j] == buf[i] && (j - i) < 255) + j++; + out += 2; /* 1바이트 문자 + 1바이트 런길이 */ + i = j; + } + return out; +} + static int run_mg_bt_0011(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_log_id; + char h_payload[256]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0; + long orig_bytes = 0, comp_bytes = 0; + long ratio_x10 = 0; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mg_bt_0011 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT log_id, payload + FROM mg_log + WHERE biz_date = :h_biz_date + ORDER BY log_id; EXEC SQL OPEN cur_mg_bt_0011; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,11 +63,10 @@ static int run_mg_bt_0011(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0011] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_mg_bt_0011 INTO :h_key, :h_merch_id, :h_amount; + long olen, clen; + + EXEC SQL FETCH cur_mg_bt_0011 INTO :h_log_id, :h_payload; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,40 +77,30 @@ static int run_mg_bt_0011(const char *biz_date) } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + olen = (long)strlen(h_payload); + clen = mg_rle_size(h_payload); + orig_bytes += olen; + comp_bytes += clen; + tx_log(TX_LOG_DEBUG, "[mg_bt_0011] log=%ld orig=%ld comp=%ld", + h_log_id, olen, clen); } EXEC SQL CLOSE cur_mg_bt_0011; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + if (orig_bytes > 0) + ratio_x10 = (comp_bytes * 1000) / orig_bytes; - tx_log(TX_LOG_INFO, - "[mg_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_log(TX_LOG_INFO, "[mg_bt_0011] 압축 date=%s 건수=%ld 원본=%ld 압축=%ld", + biz_date, total, orig_bytes, comp_bytes); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0011] ==========\n"); + printf("========== 전문 압축 아카이브 [mg_bt_0011] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 대상 전문 : %ld\n", total); + printf(" 원본 바이트 : %ld\n", orig_bytes); + printf(" 압축 바이트 : %ld\n", comp_bytes); + printf(" 절감 바이트 : %ld\n", orig_bytes - comp_bytes); + printf(" 압축률 : %ld.%ld%%\n", ratio_x10 / 10, ratio_x10 % 10); + printf(" 조회 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -121,7 +126,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0011] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0011] 압축 아카이브 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0012.c b/app/batch/mg_bt_0012.c index fabed96..1126d0a 100644 --- a/app/batch/mg_bt_0012.c +++ b/app/batch/mg_bt_0012.c @@ -6,12 +6,13 @@ /* End of automatic include section */ #line 1 "app/batch/mg_bt_0012.pgc" -/* @tier easy @module mg @transform msg-gateway-batch */ +/* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0012.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0012.pgc - 라우팅 테이블 리로드 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 라우팅 규칙 테이블(mg_route)을 규칙키 순으로 커서 전량 로드하여 + * 캐시 리로드를 시뮬레이션한다. 사용(use_yn='Y') 규칙수, 비활성 + * 규칙수, 인접 중복 규칙키 건수를 집계하여 리포트한다. * * 실행: mg_bt_0012 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0012.pgc" +#line 20 "app/batch/mg_bt_0012.pgc" static int run_mg_bt_0012(const char *biz_date) @@ -102,62 +103,45 @@ static int run_mg_bt_0012(const char *biz_date) - - -#line 24 "app/batch/mg_bt_0012.pgc" - char h_biz_date [ 9 ] ; - #line 25 "app/batch/mg_bt_0012.pgc" - char h_key [ 16 ] ; + char h_rule_key [ 24 ] ; #line 26 "app/batch/mg_bt_0012.pgc" - char h_merch_id [ 16 ] ; + char h_chan_cd [ 8 ] ; #line 27 "app/batch/mg_bt_0012.pgc" - long h_amount ; - -#line 28 "app/batch/mg_bt_0012.pgc" - char h_new_status [ 2 ] ; + char h_use_yn [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/mg_bt_0012.pgc" +#line 28 "app/batch/mg_bt_0012.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, valid = 0, disabled = 0, dup = 0, errcnt = 0; + char prev_key[24]; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + memset(prev_key, 0, sizeof(prev_key)); - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0012 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mg_bt_0012.pgc" - -#line 42 "app/batch/mg_bt_0012.pgc" + /* declare cur_mg_bt_0012 cursor for select rule_key , chan_cd , use_yn from mg_route order by rule_key , chan_cd */ +#line 38 "app/batch/mg_bt_0012.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0012 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mg_bt_0012.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0012 cursor for select rule_key , chan_cd , use_yn from mg_route order by rule_key , chan_cd", ECPGt_EOIT, ECPGt_EORT);} +#line 40 "app/batch/mg_bt_0012.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0012"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0012] tx_begin 경고"); - for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0012", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_rule_key),(long)24,(long)1,(24)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_chan_cd),(long)8,(long)1,(8)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_use_yn),(long)2,(long)1,(2)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0012.pgc" +#line 47 "app/batch/mg_bt_0012.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,41 +154,34 @@ static int run_mg_bt_0012(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (total > 1 && strcmp(prev_key, h_rule_key) == 0) { + dup++; + tx_log(TX_LOG_WARN, "[mg_bt_0012] 중복 규칙키 %s", h_rule_key); } + strncpy(prev_key, h_rule_key, sizeof(prev_key) - 1); + prev_key[sizeof(prev_key) - 1] = '\0'; - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (h_use_yn[0] == 'Y') + valid++; + else + disabled++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0012", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0012.pgc" +#line 72 "app/batch/mg_bt_0012.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + tx_log(TX_LOG_INFO, "[mg_bt_0012] 라우팅 리로드 전체=%ld 유효=%ld 중복=%ld", + total, valid, dup); - tx_log(TX_LOG_INFO, - "[mg_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 라우팅 테이블 리로드 [mg_bt_0012] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 전체 규칙 : %ld\n", total); + printf(" 유효 규칙 : %ld\n", valid); + printf(" 비활성 규칙 : %ld\n", disabled); + printf(" 중복 규칙 : %ld\n", dup); + printf(" 로드 오류 : %ld\n", errcnt); + printf(" 캐시 상태 : %s\n", (errcnt == 0) ? "RELOADED" : "PARTIAL"); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -230,7 +207,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0012] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0012] 라우팅 리로드 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0012.pgc b/app/batch/mg_bt_0012.pgc index ef69e64..4edcb4d 100644 --- a/app/batch/mg_bt_0012.pgc +++ b/app/batch/mg_bt_0012.pgc @@ -1,9 +1,10 @@ -/* @tier easy @module mg @transform msg-gateway-batch */ +/* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0012.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0012.pgc - 라우팅 테이블 리로드 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 라우팅 규칙 테이블(mg_route)을 규칙키 순으로 커서 전량 로드하여 + * 캐시 리로드를 시뮬레이션한다. 사용(use_yn='Y') 규칙수, 비활성 + * 규칙수, 인접 중복 규칙키 건수를 집계하여 리포트한다. * * 실행: mg_bt_0012 [db@host] */ @@ -21,25 +22,20 @@ EXEC SQL INCLUDE sqlca; static int run_mg_bt_0012(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_rule_key[24]; + char h_chan_cd[8]; + char h_use_yn[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, valid = 0, disabled = 0, dup = 0, errcnt = 0; + char prev_key[24]; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + memset(prev_key, 0, sizeof(prev_key)); EXEC SQL DECLARE cur_mg_bt_0012 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT rule_key, chan_cd, use_yn + FROM mg_route + ORDER BY rule_key, chan_cd; EXEC SQL OPEN cur_mg_bt_0012; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,11 +43,8 @@ static int run_mg_bt_0012(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0012] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_mg_bt_0012 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mg_bt_0012 INTO :h_rule_key, :h_chan_cd, :h_use_yn; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,39 +56,32 @@ static int run_mg_bt_0012(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (total > 1 && strcmp(prev_key, h_rule_key) == 0) { + dup++; + tx_log(TX_LOG_WARN, "[mg_bt_0012] 중복 규칙키 %s", h_rule_key); } + strncpy(prev_key, h_rule_key, sizeof(prev_key) - 1); + prev_key[sizeof(prev_key) - 1] = '\0'; - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (h_use_yn[0] == 'Y') + valid++; + else + disabled++; } EXEC SQL CLOSE cur_mg_bt_0012; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + tx_log(TX_LOG_INFO, "[mg_bt_0012] 라우팅 리로드 전체=%ld 유효=%ld 중복=%ld", + total, valid, dup); - tx_log(TX_LOG_INFO, - "[mg_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 라우팅 테이블 리로드 [mg_bt_0012] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 전체 규칙 : %ld\n", total); + printf(" 유효 규칙 : %ld\n", valid); + printf(" 비활성 규칙 : %ld\n", disabled); + printf(" 중복 규칙 : %ld\n", dup); + printf(" 로드 오류 : %ld\n", errcnt); + printf(" 캐시 상태 : %s\n", (errcnt == 0) ? "RELOADED" : "PARTIAL"); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -121,7 +107,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0012] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0012] 라우팅 리로드 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0013.c b/app/batch/mg_bt_0013.c index e352f9a..17b512d 100644 --- a/app/batch/mg_bt_0013.c +++ b/app/batch/mg_bt_0013.c @@ -8,10 +8,11 @@ #line 1 "app/batch/mg_bt_0013.pgc" /* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0013.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=medium] + * mg_bt_0013.pgc - 전문 처리지연 모니터 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 전문 중 수신(recv_ts)~처리(proc_ts) 경과시간이 + * 임계(기본 3초)를 초과한 지연 전문을 커서로 집계하여 지연 건수, + * 최대 지연, 평균 지연(ms)을 산출하여 리포트한다. (모니터링 전용) * * 실행: mg_bt_0013 [db@host] */ @@ -93,71 +94,68 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0013.pgc" +#line 20 "app/batch/mg_bt_0013.pgc" +#define MG_LATENCY_THRESH_MS 3000 /* 지연 임계(ms) */ + static int run_mg_bt_0013(const char *biz_date) { /* exec sql begin declare section */ - - + -#line 24 "app/batch/mg_bt_0013.pgc" +#line 27 "app/batch/mg_bt_0013.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mg_bt_0013.pgc" +#line 28 "app/batch/mg_bt_0013.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/mg_bt_0013.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/mg_bt_0013.pgc" - long h_amount ; - -#line 28 "app/batch/mg_bt_0013.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ #line 29 "app/batch/mg_bt_0013.pgc" + long h_lat_ms ; + +#line 30 "app/batch/mg_bt_0013.pgc" + int h_thresh ; +/* exec sql end declare section */ +#line 31 "app/batch/mg_bt_0013.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long scanned = 0, delayed = 0, errcnt = 0; + long max_lat = 0, sum_lat = 0, avg_lat = 0; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + h_thresh = MG_LATENCY_THRESH_MS; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0013 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mg_bt_0013.pgc" + ECPGset_var( 0, &( h_thresh ), __LINE__);\ + ECPGset_var( 1, ( h_biz_date ), __LINE__);\ + /* declare cur_mg_bt_0013 cursor for select key , cast ( extract ( EPOCH from ( proc_ts - recv_ts ) ) * 1000 as bigint ) from mg_ledger where biz_date = $1 and proc_ts is not null and extract ( EPOCH from ( proc_ts - recv_ts ) ) * 1000 >= $2 order by key */ +#line 47 "app/batch/mg_bt_0013.pgc" -#line 42 "app/batch/mg_bt_0013.pgc" +#line 47 "app/batch/mg_bt_0013.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0013 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0013 cursor for select key , cast ( extract ( EPOCH from ( proc_ts - recv_ts ) ) * 1000 as bigint ) from mg_ledger where biz_date = $1 and proc_ts is not null and extract ( EPOCH from ( proc_ts - recv_ts ) ) * 1000 >= $2 order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_thresh),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mg_bt_0013.pgc" +#line 49 "app/batch/mg_bt_0013.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0013"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0013] tx_begin 경고"); - for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0013", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_lat_ms),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0013.pgc" +#line 56 "app/batch/mg_bt_0013.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,43 +166,31 @@ static int run_mg_bt_0013(const char *biz_date) break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + scanned++; + delayed++; + sum_lat += h_lat_ms; + if (h_lat_ms > max_lat) + max_lat = h_lat_ms; + tx_log(TX_LOG_DEBUG, "[mg_bt_0013] 지연 key=%s lat=%ldms", h_key, h_lat_ms); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0013", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0013.pgc" +#line 74 "app/batch/mg_bt_0013.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + if (delayed > 0) + avg_lat = sum_lat / delayed; - tx_log(TX_LOG_INFO, - "[mg_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_log(TX_LOG_INFO, "[mg_bt_0013] 지연모니터 date=%s 지연=%ld 최대=%ldms", + biz_date, delayed, max_lat); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 전문 처리지연 모니터 [mg_bt_0013] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 지연 임계(ms) : %d\n", MG_LATENCY_THRESH_MS); + printf(" 지연 건수 : %ld\n", delayed); + printf(" 최대 지연(ms) : %ld\n", max_lat); + printf(" 평균 지연(ms) : %ld\n", avg_lat); + printf(" 조회 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -230,7 +216,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0013] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0013] 지연 모니터 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0013.pgc b/app/batch/mg_bt_0013.pgc index b9c760e..21f6e0e 100644 --- a/app/batch/mg_bt_0013.pgc +++ b/app/batch/mg_bt_0013.pgc @@ -1,9 +1,10 @@ /* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0013.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=medium] + * mg_bt_0013.pgc - 전문 처리지연 모니터 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 전문 중 수신(recv_ts)~처리(proc_ts) 경과시간이 + * 임계(기본 3초)를 초과한 지연 전문을 커서로 집계하여 지연 건수, + * 최대 지연, 평균 지연(ms)을 산출하여 리포트한다. (모니터링 전용) * * 실행: mg_bt_0013 [db@host] */ @@ -18,28 +19,32 @@ EXEC SQL INCLUDE sqlca; +#define MG_LATENCY_THRESH_MS 3000 /* 지연 임계(ms) */ + static int run_mg_bt_0013(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_lat_ms; + int h_thresh; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long scanned = 0, delayed = 0, errcnt = 0; + long max_lat = 0, sum_lat = 0, avg_lat = 0; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + h_thresh = MG_LATENCY_THRESH_MS; EXEC SQL DECLARE cur_mg_bt_0013 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT key, + CAST(EXTRACT(EPOCH FROM (proc_ts - recv_ts)) * 1000 AS bigint) + FROM mg_ledger + WHERE biz_date = :h_biz_date + AND proc_ts IS NOT NULL + AND EXTRACT(EPOCH FROM (proc_ts - recv_ts)) * 1000 >= :h_thresh + ORDER BY key; EXEC SQL OPEN cur_mg_bt_0013; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,11 +52,8 @@ static int run_mg_bt_0013(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0013] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_mg_bt_0013 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mg_bt_0013 INTO :h_key, :h_lat_ms; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,41 +63,29 @@ static int run_mg_bt_0013(const char *biz_date) break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + scanned++; + delayed++; + sum_lat += h_lat_ms; + if (h_lat_ms > max_lat) + max_lat = h_lat_ms; + tx_log(TX_LOG_DEBUG, "[mg_bt_0013] 지연 key=%s lat=%ldms", h_key, h_lat_ms); } EXEC SQL CLOSE cur_mg_bt_0013; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + if (delayed > 0) + avg_lat = sum_lat / delayed; - tx_log(TX_LOG_INFO, - "[mg_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_log(TX_LOG_INFO, "[mg_bt_0013] 지연모니터 date=%s 지연=%ld 최대=%ldms", + biz_date, delayed, max_lat); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 전문 처리지연 모니터 [mg_bt_0013] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 지연 임계(ms) : %d\n", MG_LATENCY_THRESH_MS); + printf(" 지연 건수 : %ld\n", delayed); + printf(" 최대 지연(ms) : %ld\n", max_lat); + printf(" 평균 지연(ms) : %ld\n", avg_lat); + printf(" 조회 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -121,7 +111,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0013] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0013] 지연 모니터 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0014.c b/app/batch/mg_bt_0014.c index c831036..8eafdcf 100644 --- a/app/batch/mg_bt_0014.c +++ b/app/batch/mg_bt_0014.c @@ -8,10 +8,11 @@ #line 1 "app/batch/mg_bt_0014.pgc" /* @tier easy @module mg @transform msg-gateway-batch */ /* - * mg_bt_0014.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0014.pgc - 일별 전문량 시간대 집계 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 전문을 수신시각 기준 시간대(0~23시)별로 GROUP BY + * 집계하여 커서로 순회, 시간대별 전문 건수를 24구간 배열에 적재하고 + * 피크 시간대와 총 전문량을 리포트한다. * * 실행: mg_bt_0014 [db@host] */ @@ -93,71 +94,63 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0014.pgc" +#line 20 "app/batch/mg_bt_0014.pgc" +#define MG_HOURS 24 + static int run_mg_bt_0014(const char *biz_date) { /* exec sql begin declare section */ - - - + -#line 24 "app/batch/mg_bt_0014.pgc" +#line 27 "app/batch/mg_bt_0014.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mg_bt_0014.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/mg_bt_0014.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/mg_bt_0014.pgc" - long h_amount ; - #line 28 "app/batch/mg_bt_0014.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + int h_hour ; + #line 29 "app/batch/mg_bt_0014.pgc" + long h_cnt ; +/* exec sql end declare section */ +#line 30 "app/batch/mg_bt_0014.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long hourcnt[MG_HOURS]; + long total = 0, errcnt = 0; + int peak = 0, i; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + for (i = 0; i < MG_HOURS; i++) + hourcnt[i] = 0; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0014 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mg_bt_0014.pgc" + /* declare cur_mg_bt_0014 cursor for select cast ( extract ( hour from recv_ts ) as int ) , count ( * ) from mg_ledger where biz_date = $1 group by extract ( hour from recv_ts ) order by 1 */ +#line 46 "app/batch/mg_bt_0014.pgc" -#line 42 "app/batch/mg_bt_0014.pgc" +#line 46 "app/batch/mg_bt_0014.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0014 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0014 cursor for select cast ( extract ( hour from recv_ts ) as int ) , count ( * ) from mg_ledger where biz_date = $1 group by extract ( hour from recv_ts ) order by 1", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mg_bt_0014.pgc" +#line 48 "app/batch/mg_bt_0014.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0014"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0014] tx_begin 경고"); - for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0014", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_int,&(h_hour),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0014.pgc" +#line 55 "app/batch/mg_bt_0014.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,43 +161,33 @@ static int run_mg_bt_0014(const char *biz_date) break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (h_hour >= 0 && h_hour < MG_HOURS) { + hourcnt[h_hour] = h_cnt; + total += h_cnt; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0014", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0014.pgc" +#line 71 "app/batch/mg_bt_0014.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + for (i = 1; i < MG_HOURS; i++) { + if (hourcnt[i] > hourcnt[peak]) + peak = i; + } - tx_log(TX_LOG_INFO, - "[mg_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_log(TX_LOG_INFO, "[mg_bt_0014] 전문량 date=%s 총=%ld 피크=%02d시(%ld)", + biz_date, total, peak, hourcnt[peak]); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0014] ==========\n"); + printf("========== 일별 전문량 시간대 집계 [mg_bt_0014] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 총 전문량 : %ld\n", total); + for (i = 0; i < MG_HOURS; i++) { + if (hourcnt[i] > 0) + printf(" %02d시 : %ld\n", i, hourcnt[i]); + } + printf(" 피크 시간대 : %02d시 (%ld건)\n", peak, hourcnt[peak]); + printf(" 조회 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -230,7 +213,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0014] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0014] 전문량 집계 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0014.pgc b/app/batch/mg_bt_0014.pgc index 6c52e49..927ec84 100644 --- a/app/batch/mg_bt_0014.pgc +++ b/app/batch/mg_bt_0014.pgc @@ -1,9 +1,10 @@ /* @tier easy @module mg @transform msg-gateway-batch */ /* - * mg_bt_0014.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0014.pgc - 일별 전문량 시간대 집계 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 전문을 수신시각 기준 시간대(0~23시)별로 GROUP BY + * 집계하여 커서로 순회, 시간대별 전문 건수를 24구간 배열에 적재하고 + * 피크 시간대와 총 전문량을 리포트한다. * * 실행: mg_bt_0014 [db@host] */ @@ -18,28 +19,31 @@ EXEC SQL INCLUDE sqlca; +#define MG_HOURS 24 + static int run_mg_bt_0014(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + int h_hour; + long h_cnt; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long hourcnt[MG_HOURS]; + long total = 0, errcnt = 0; + int peak = 0, i; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + for (i = 0; i < MG_HOURS; i++) + hourcnt[i] = 0; EXEC SQL DECLARE cur_mg_bt_0014 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT CAST(EXTRACT(HOUR FROM recv_ts) AS int), COUNT(*) + FROM mg_ledger + WHERE biz_date = :h_biz_date + GROUP BY EXTRACT(HOUR FROM recv_ts) + ORDER BY 1; EXEC SQL OPEN cur_mg_bt_0014; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,11 +51,8 @@ static int run_mg_bt_0014(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0014] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_mg_bt_0014 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mg_bt_0014 INTO :h_hour, :h_cnt; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,41 +62,31 @@ static int run_mg_bt_0014(const char *biz_date) break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (h_hour >= 0 && h_hour < MG_HOURS) { + hourcnt[h_hour] = h_cnt; + total += h_cnt; } } EXEC SQL CLOSE cur_mg_bt_0014; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + for (i = 1; i < MG_HOURS; i++) { + if (hourcnt[i] > hourcnt[peak]) + peak = i; + } - tx_log(TX_LOG_INFO, - "[mg_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + tx_log(TX_LOG_INFO, "[mg_bt_0014] 전문량 date=%s 총=%ld 피크=%02d시(%ld)", + biz_date, total, peak, hourcnt[peak]); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0014] ==========\n"); + printf("========== 일별 전문량 시간대 집계 [mg_bt_0014] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 총 전문량 : %ld\n", total); + for (i = 0; i < MG_HOURS; i++) { + if (hourcnt[i] > 0) + printf(" %02d시 : %ld\n", i, hourcnt[i]); + } + printf(" 피크 시간대 : %02d시 (%ld건)\n", peak, hourcnt[peak]); + printf(" 조회 오류 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -121,7 +112,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0014] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0014] 전문량 집계 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0015.c b/app/batch/mg_bt_0015.c index 2f317b1..6890aea 100644 --- a/app/batch/mg_bt_0015.c +++ b/app/batch/mg_bt_0015.c @@ -6,12 +6,13 @@ /* End of automatic include section */ #line 1 "app/batch/mg_bt_0015.pgc" -/* @tier medium @module mg @transform msg-gateway-batch */ +/* @tier easy @module mg @transform msg-gateway-batch */ /* - * mg_bt_0015.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=medium] + * mg_bt_0015.pgc - 전문게이트웨이 승인/정산 대사 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 처리완료(status='M') 전문을 커서로 순회하며 정산대사를 + * 수행하고 정산완료('S')로 전이한다. 금액이 유효하지 않은 건은 대사보류 + * 로 남긴다. 대사 건수/총액을 리포트한다. 배치 main 엔트리 포함. * * 실행: mg_bt_0015 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0015.pgc" +#line 20 "app/batch/mg_bt_0015.pgc" static int run_mg_bt_0015(const char *biz_date) @@ -103,43 +104,40 @@ static int run_mg_bt_0015(const char *biz_date) - -#line 24 "app/batch/mg_bt_0015.pgc" +#line 25 "app/batch/mg_bt_0015.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mg_bt_0015.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/mg_bt_0015.pgc" - char h_merch_id [ 16 ] ; + char h_key [ 16 ] ; #line 27 "app/batch/mg_bt_0015.pgc" long h_amount ; #line 28 "app/batch/mg_bt_0015.pgc" - char h_new_status [ 2 ] ; + long h_fee ; /* exec sql end declare section */ #line 29 "app/batch/mg_bt_0015.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, settled = 0, held = 0, errcnt = 0; + long sum_amt = 0, sum_fee = 0; int rc; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0015 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mg_bt_0015.pgc" + /* declare cur_mg_bt_0015 cursor for select key , amount , coalesce ( fee , 0 ) from mg_ledger where biz_date = $1 and status = 'M' order by key */ +#line 43 "app/batch/mg_bt_0015.pgc" -#line 42 "app/batch/mg_bt_0015.pgc" +#line 43 "app/batch/mg_bt_0015.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0015 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0015 cursor for select key , amount , coalesce ( fee , 0 ) from mg_ledger where biz_date = $1 and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mg_bt_0015.pgc" +#line 45 "app/batch/mg_bt_0015.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0015"); @@ -153,11 +151,11 @@ static int run_mg_bt_0015(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0015", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0015.pgc" +#line 55 "app/batch/mg_bt_0015.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +168,25 @@ static int run_mg_bt_0015(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; + /* 대사규칙: 금액 유효 + 수수료 비음수 이면 정산확정 */ + if (amount_is_valid(h_amount) && h_fee >= 0) { + rc = mg_rec_update_status(h_key, MG_ST_SETTLED); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[mg_bt_0015] 정산전이 실패 key=%s rc=%d", h_key, rc); + errcnt++; + continue; + } + settled++; + sum_amt += h_amount; + sum_fee += h_fee; } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + held++; + tx_log(TX_LOG_WARN, "[mg_bt_0015] 대사보류 key=%s amount=%ld", h_key, h_amount); } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0015", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0015.pgc" +#line 84 "app/batch/mg_bt_0015.pgc" if (errcnt == 0) @@ -196,15 +195,16 @@ static int run_mg_bt_0015(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mg_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mg_bt_0015] 대사 date=%s 대상=%ld 정산=%ld 보류=%ld 총액=%ld", + biz_date, total, settled, held, sum_amt); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0015] ==========\n"); + printf("========== 승인/정산 대사 [mg_bt_0015] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 대상(M) : %ld\n", total); + printf(" 정산완료(S) : %ld\n", settled); + printf(" 대사보류 : %ld\n", held); + printf(" 정산금액 : %ld\n", sum_amt); + printf(" 수수료합 : %ld\n", sum_fee); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -230,7 +230,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0015] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0015] 정산대사 배치 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0015.pgc b/app/batch/mg_bt_0015.pgc index cf3ccfd..76e8f4d 100644 --- a/app/batch/mg_bt_0015.pgc +++ b/app/batch/mg_bt_0015.pgc @@ -1,9 +1,10 @@ -/* @tier medium @module mg @transform msg-gateway-batch */ +/* @tier easy @module mg @transform msg-gateway-batch */ /* - * mg_bt_0015.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=medium] + * mg_bt_0015.pgc - 전문게이트웨이 승인/정산 대사 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 처리완료(status='M') 전문을 커서로 순회하며 정산대사를 + * 수행하고 정산완료('S')로 전이한다. 금액이 유효하지 않은 건은 대사보류 + * 로 남긴다. 대사 건수/총액을 리포트한다. 배치 main 엔트리 포함. * * 실행: mg_bt_0015 [db@host] */ @@ -23,23 +24,23 @@ static int run_mg_bt_0015(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, settled = 0, held = 0, errcnt = 0; + long sum_amt = 0, sum_fee = 0; int rc; + memset(h_biz_date, 0, sizeof(h_biz_date)); strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mg_bt_0015 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT key, amount, COALESCE(fee, 0) + FROM mg_ledger + WHERE biz_date = :h_biz_date + AND status = 'M' + ORDER BY key; EXEC SQL OPEN cur_mg_bt_0015; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +52,7 @@ static int run_mg_bt_0015(const char *biz_date) tx_log(TX_LOG_WARN, "[mg_bt_0015] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mg_bt_0015 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mg_bt_0015 INTO :h_key, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,19 +64,20 @@ static int run_mg_bt_0015(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; + /* 대사규칙: 금액 유효 + 수수료 비음수 이면 정산확정 */ + if (amount_is_valid(h_amount) && h_fee >= 0) { + rc = mg_rec_update_status(h_key, MG_ST_SETTLED); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[mg_bt_0015] 정산전이 실패 key=%s rc=%d", h_key, rc); + errcnt++; + continue; + } + settled++; + sum_amt += h_amount; + sum_fee += h_fee; } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + held++; + tx_log(TX_LOG_WARN, "[mg_bt_0015] 대사보류 key=%s amount=%ld", h_key, h_amount); } } @@ -87,15 +89,16 @@ static int run_mg_bt_0015(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mg_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mg_bt_0015] 대사 date=%s 대상=%ld 정산=%ld 보류=%ld 총액=%ld", + biz_date, total, settled, held, sum_amt); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0015] ==========\n"); + printf("========== 승인/정산 대사 [mg_bt_0015] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 대상(M) : %ld\n", total); + printf(" 정산완료(S) : %ld\n", settled); + printf(" 대사보류 : %ld\n", held); + printf(" 정산금액 : %ld\n", sum_amt); + printf(" 수수료합 : %ld\n", sum_fee); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; @@ -121,7 +124,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0015] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0015] 정산대사 배치 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0016.c b/app/batch/mg_bt_0016.c index 5c776fb..b084592 100644 --- a/app/batch/mg_bt_0016.c +++ b/app/batch/mg_bt_0016.c @@ -6,12 +6,13 @@ /* End of automatic include section */ #line 1 "app/batch/mg_bt_0016.pgc" -/* @tier easy @module mg @transform msg-gateway-batch */ +/* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0016.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0016.pgc - 전문게이트웨이 채널 헬스체크 집계 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 채널 상태 테이블(mg_chan)을 커서로 순회하며 채널별 헬스 응답률(성공/총 + * 요청)을 계산하고, 임계(95%) 미만 채널을 비정상으로 분류·집계한다. + * 배치 main 엔트리 포함. * * 실행: mg_bt_0016 [db@host] */ @@ -93,121 +94,98 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0016.pgc" +#line 20 "app/batch/mg_bt_0016.pgc" -static int run_mg_bt_0016(const char *biz_date) +#define MG_HEALTH_THRESHOLD 95 /* 정상 판정 응답률(%) */ + +static int run_mg_bt_0016(void) { /* exec sql begin declare section */ - - -#line 24 "app/batch/mg_bt_0016.pgc" - char h_biz_date [ 9 ] ; - -#line 25 "app/batch/mg_bt_0016.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/mg_bt_0016.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/mg_bt_0016.pgc" - long h_amount ; + char h_chan_cd [ 8 ] ; #line 28 "app/batch/mg_bt_0016.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_req ; + #line 29 "app/batch/mg_bt_0016.pgc" + long h_ok ; +/* exec sql end declare section */ +#line 30 "app/batch/mg_bt_0016.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long chans = 0, healthy = 0, unhealthy = 0, idle = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0016 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mg_bt_0016.pgc" - -#line 42 "app/batch/mg_bt_0016.pgc" + /* declare cur_mg_bt_0016 cursor for select chan_cd , coalesce ( req_cnt , 0 ) , coalesce ( ok_cnt , 0 ) from mg_chan order by chan_cd */ +#line 37 "app/batch/mg_bt_0016.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0016 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mg_bt_0016.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0016 cursor for select chan_cd , coalesce ( req_cnt , 0 ) , coalesce ( ok_cnt , 0 ) from mg_chan order by chan_cd", ECPGt_EOIT, ECPGt_EORT);} +#line 39 "app/batch/mg_bt_0016.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0016"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0016] tx_begin 경고"); - for (;;) { + long rate; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0016", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_chan_cd),(long)8,(long)1,(8)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_req),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_ok),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0016.pgc" +#line 48 "app/batch/mg_bt_0016.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_mg_bt_0016"); - errcnt++; break; } - total++; + chans++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (h_req <= 0) { + idle++; + tx_log(TX_LOG_DEBUG, "[mg_bt_0016] 유휴채널 chan=%s", h_chan_cd); + continue; } - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + rate = (h_ok * 100) / h_req; + if (rate >= MG_HEALTH_THRESHOLD) { + healthy++; + } else { + unhealthy++; + tx_log(TX_LOG_WARN, "[mg_bt_0016] 비정상 채널 chan=%s 응답률=%ld%%", + h_chan_cd, rate); } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0016", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0016.pgc" +#line 75 "app/batch/mg_bt_0016.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[mg_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mg_bt_0016] 헬스집계 채널=%ld 정상=%ld 비정상=%ld 유휴=%ld", + chans, healthy, unhealthy, idle); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 채널 헬스체크 집계 [mg_bt_0016] ==========\n"); + printf(" 총 채널수 : %ld\n", chans); + printf(" 정상 채널 : %ld\n", healthy); + printf(" 비정상 채널 : %ld\n", unhealthy); + printf(" 유휴 채널 : %ld\n", idle); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (unhealthy == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -230,7 +208,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0016] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0016] 헬스체크 배치 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { @@ -238,7 +216,7 @@ int main(int argc, char **argv) return 1; } - rc = run_mg_bt_0016(biz_date); + rc = run_mg_bt_0016(); mg_dbio_disconnect(); diff --git a/app/batch/mg_bt_0016.pgc b/app/batch/mg_bt_0016.pgc index 937d69f..a006e84 100644 --- a/app/batch/mg_bt_0016.pgc +++ b/app/batch/mg_bt_0016.pgc @@ -1,9 +1,10 @@ -/* @tier easy @module mg @transform msg-gateway-batch */ +/* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0016.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=easy] + * mg_bt_0016.pgc - 전문게이트웨이 채널 헬스체크 집계 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 채널 상태 테이블(mg_chan)을 커서로 순회하며 채널별 헬스 응답률(성공/총 + * 요청)을 계산하고, 임계(95%) 미만 채널을 비정상으로 분류·집계한다. + * 배치 main 엔트리 포함. * * 실행: mg_bt_0016 [db@host] */ @@ -18,28 +19,22 @@ EXEC SQL INCLUDE sqlca; -static int run_mg_bt_0016(const char *biz_date) +#define MG_HEALTH_THRESHOLD 95 /* 정상 판정 응답률(%) */ + +static int run_mg_bt_0016(void) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_chan_cd[8]; + long h_req; + long h_ok; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; - - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + long chans = 0, healthy = 0, unhealthy = 0, idle = 0; EXEC SQL DECLARE cur_mg_bt_0016 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT chan_cd, COALESCE(req_cnt, 0), COALESCE(ok_cnt, 0) + FROM mg_chan + ORDER BY chan_cd; EXEC SQL OPEN cur_mg_bt_0016; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,58 +42,50 @@ static int run_mg_bt_0016(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mg_bt_0016] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_mg_bt_0016 INTO :h_key, :h_merch_id, :h_amount; + long rate; + + EXEC SQL FETCH cur_mg_bt_0016 INTO :h_chan_cd, :h_req, :h_ok; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_mg_bt_0016"); - errcnt++; break; } - total++; + chans++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (h_req <= 0) { + idle++; + tx_log(TX_LOG_DEBUG, "[mg_bt_0016] 유휴채널 chan=%s", h_chan_cd); + continue; } - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + rate = (h_ok * 100) / h_req; + if (rate >= MG_HEALTH_THRESHOLD) { + healthy++; + } else { + unhealthy++; + tx_log(TX_LOG_WARN, "[mg_bt_0016] 비정상 채널 chan=%s 응답률=%ld%%", + h_chan_cd, rate); } } EXEC SQL CLOSE cur_mg_bt_0016; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[mg_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mg_bt_0016] 헬스집계 채널=%ld 정상=%ld 비정상=%ld 유휴=%ld", + chans, healthy, unhealthy, idle); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 채널 헬스체크 집계 [mg_bt_0016] ==========\n"); + printf(" 총 채널수 : %ld\n", chans); + printf(" 정상 채널 : %ld\n", healthy); + printf(" 비정상 채널 : %ld\n", unhealthy); + printf(" 유휴 채널 : %ld\n", idle); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return (unhealthy == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -121,7 +108,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0016] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0016] 헬스체크 배치 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { @@ -129,7 +116,7 @@ int main(int argc, char **argv) return 1; } - rc = run_mg_bt_0016(biz_date); + rc = run_mg_bt_0016(); mg_dbio_disconnect(); diff --git a/app/batch/mg_bt_0017.c b/app/batch/mg_bt_0017.c index 8c26889..d595888 100644 --- a/app/batch/mg_bt_0017.c +++ b/app/batch/mg_bt_0017.c @@ -8,10 +8,11 @@ #line 1 "app/batch/mg_bt_0017.pgc" /* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0017.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=medium] + * mg_bt_0017.pgc - 전문게이트웨이 전문 순번(STAN) 리셋 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 일 마감 시 채널별 전문 순번(mg_seq) 테이블을 리셋한다. 현재 순번을 + * 조회·기록한 뒤 0 으로 초기화하고, 채번 상한 근접(90% 이상) 채널을 + * 경고한다. 배치 main 엔트리 포함. * * 실행: mg_bt_0017 [db@host] */ @@ -93,54 +94,38 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mg_bt_0017.pgc" +#line 20 "app/batch/mg_bt_0017.pgc" +#define MG_STAN_MAX 999999L + static int run_mg_bt_0017(const char *biz_date) { /* exec sql begin declare section */ - - - -#line 24 "app/batch/mg_bt_0017.pgc" - char h_biz_date [ 9 ] ; - -#line 25 "app/batch/mg_bt_0017.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/mg_bt_0017.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/mg_bt_0017.pgc" - long h_amount ; + char h_seq_key [ 16 ] ; #line 28 "app/batch/mg_bt_0017.pgc" - char h_new_status [ 2 ] ; + long h_cur_val ; /* exec sql end declare section */ #line 29 "app/batch/mg_bt_0017.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, reset = 0, warned = 0; + long peak = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + (void)biz_date; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mg_bt_0017 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_mg_bt_0017 cursor for select seq_key , coalesce ( cur_val , 0 ) from mg_seq order by seq_key for update */ +#line 40 "app/batch/mg_bt_0017.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0017 cursor for select seq_key , coalesce ( cur_val , 0 ) from mg_seq order by seq_key for update", ECPGt_EOIT, ECPGt_EORT);} #line 42 "app/batch/mg_bt_0017.pgc" -#line 42 "app/batch/mg_bt_0017.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_bt_0017 cursor for select key , merch_id , amount from mg_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mg_bt_0017.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mg_bt_0017"); return TX_EDB; @@ -151,63 +136,60 @@ static int run_mg_bt_0017(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_bt_0017", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_seq_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_cur_val),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mg_bt_0017.pgc" +#line 52 "app/batch/mg_bt_0017.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_mg_bt_0017"); - errcnt++; break; } total++; + if (h_cur_val > peak) + peak = h_cur_val; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (h_cur_val * 100 >= MG_STAN_MAX * 90) { + warned++; + tx_log(TX_LOG_WARN, "[mg_bt_0017] 채번 상한근접 key=%s val=%ld", + h_seq_key, h_cur_val); } - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0017] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mg_seq set cur_val = 0 , reset_ts = current_timestamp where seq_key = $1 ", + ECPGt_char,(h_seq_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 74 "app/batch/mg_bt_0017.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mg_seq"); + continue; } + reset++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_bt_0017", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mg_bt_0017.pgc" +#line 83 "app/batch/mg_bt_0017.pgc" - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + tx_commit(); tx_log(TX_LOG_INFO, - "[mg_bt_0017] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mg_bt_0017] 순번리셋 대상=%ld 리셋=%ld 경고=%ld 최대순번=%ld", + total, reset, warned, peak); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0017] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 전문 순번 리셋 [mg_bt_0017] ==========\n"); + printf(" 채번키 수 : %ld\n", total); + printf(" 리셋 완료 : %ld\n", reset); + printf(" 상한근접 : %ld\n", warned); + printf(" 당일 최대 : %ld\n", peak); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) @@ -230,7 +212,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0017] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0017] 순번리셋 배치 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mg_bt_0017.pgc b/app/batch/mg_bt_0017.pgc index 44866fa..51ceaf4 100644 --- a/app/batch/mg_bt_0017.pgc +++ b/app/batch/mg_bt_0017.pgc @@ -1,9 +1,10 @@ /* @tier medium @module mg @transform msg-gateway-batch */ /* - * mg_bt_0017.pgc - 전문게이트웨이 배치 (커서 순회 + 상태전이) [tier=medium] + * mg_bt_0017.pgc - 전문게이트웨이 전문 순번(STAN) 리셋 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 일 마감 시 채널별 전문 순번(mg_seq) 테이블을 리셋한다. 현재 순번을 + * 조회·기록한 뒤 0 으로 초기화하고, 채번 상한 근접(90% 이상) 채널을 + * 경고한다. 배치 main 엔트리 포함. * * 실행: mg_bt_0017 [db@host] */ @@ -18,28 +19,25 @@ EXEC SQL INCLUDE sqlca; +#define MG_STAN_MAX 999999L + static int run_mg_bt_0017(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_seq_key[16]; + long h_cur_val; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, reset = 0, warned = 0; + long peak = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + (void)biz_date; EXEC SQL DECLARE cur_mg_bt_0017 CURSOR FOR - SELECT key, merch_id, amount - FROM mg_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT seq_key, COALESCE(cur_val, 0) + FROM mg_seq + ORDER BY seq_key + FOR UPDATE; EXEC SQL OPEN cur_mg_bt_0017; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,54 +49,52 @@ static int run_mg_bt_0017(const char *biz_date) tx_log(TX_LOG_WARN, "[mg_bt_0017] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mg_bt_0017 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mg_bt_0017 INTO :h_seq_key, :h_cur_val; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_mg_bt_0017"); - errcnt++; break; } total++; + if (h_cur_val > peak) + peak = h_cur_val; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MG_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MG_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (h_cur_val * 100 >= MG_STAN_MAX * 90) { + warned++; + tx_log(TX_LOG_WARN, "[mg_bt_0017] 채번 상한근접 key=%s val=%ld", + h_seq_key, h_cur_val); } - rc = mg_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mg_bt_0017] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + EXEC SQL UPDATE mg_seq + SET cur_val = 0, + reset_ts = CURRENT_TIMESTAMP + WHERE seq_key = :h_seq_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mg_seq"); + continue; } + reset++; } EXEC SQL CLOSE cur_mg_bt_0017; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + tx_commit(); tx_log(TX_LOG_INFO, - "[mg_bt_0017] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mg_bt_0017] 순번리셋 대상=%ld 리셋=%ld 경고=%ld 최대순번=%ld", + total, reset, warned, peak); - printf("========== 전문게이트웨이 배치 결과 [mg_bt_0017] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 전문 순번 리셋 [mg_bt_0017] ==========\n"); + printf(" 채번키 수 : %ld\n", total); + printf(" 리셋 완료 : %ld\n", reset); + printf(" 상한근접 : %ld\n", warned); + printf(" 당일 최대 : %ld\n", peak); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) @@ -121,7 +117,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[mg_bt_0017] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mg_bt_0017] 순번리셋 배치 시작 date=%s", biz_date); rc = mg_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mm_bt_0002.c b/app/batch/mm_bt_0002.c index 7c05ba4..d20923e 100644 --- a/app/batch/mm_bt_0002.c +++ b/app/batch/mm_bt_0002.c @@ -8,10 +8,12 @@ #line 1 "app/batch/mm_bt_0002.pgc" /* @tier hard @module mm @transform master-batch */ /* - * mm_bt_0002.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=hard] + * mm_bt_0002.pgc - 가맹점 마스터 일괄적재 배치 (MM_BT_0002) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 신규 등록 스테이징(mm_stage_merchant) 을 커서로 순회하며 필수항목을 + * 검증한다. 유효건은 가맹점 원장(mm_merchant) 에 INSERT 하고 적재이력 + * (mm_load_hist) 을 남긴다. 사업자번호/계좌/수수료율 누락 등 오류건은 + * 적재하지 않고 스킵 집계한다. 배치 main 엔트리 포함. * * 실행: mm_bt_0002 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0002.pgc" +#line 21 "app/batch/mm_bt_0002.pgc" static int run_mm_bt_0002(const char *biz_date) @@ -104,42 +106,51 @@ static int run_mm_bt_0002(const char *biz_date) + + -#line 24 "app/batch/mm_bt_0002.pgc" +#line 26 "app/batch/mm_bt_0002.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mm_bt_0002.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/mm_bt_0002.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/mm_bt_0002.pgc" - long h_amount ; + char h_stage_id [ 16 ] ; #line 28 "app/batch/mm_bt_0002.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + char h_merch_id [ 16 ] ; + #line 29 "app/batch/mm_bt_0002.pgc" + char h_merch_name [ 64 ] ; + +#line 30 "app/batch/mm_bt_0002.pgc" + char h_biz_reg_no [ 16 ] ; + +#line 31 "app/batch/mm_bt_0002.pgc" + char h_acct_no [ 24 ] ; + +#line 32 "app/batch/mm_bt_0002.pgc" + long h_fee_rate ; +/* exec sql end declare section */ +#line 33 "app/batch/mm_bt_0002.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, loaded = 0, skipped = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 미적재 스테이징만 대상 */ ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0002 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mm_bt_0002.pgc" + /* declare cur_mm_bt_0002 cursor for select stage_id , merch_id , merch_name , biz_reg_no , acct_no , fee_rate from mm_stage_merchant where recv_date = $1 and load_yn = 'N' order by stage_id */ +#line 47 "app/batch/mm_bt_0002.pgc" -#line 42 "app/batch/mm_bt_0002.pgc" +#line 47 "app/batch/mm_bt_0002.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0002 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0002 cursor for select stage_id , merch_id , merch_name , biz_reg_no , acct_no , fee_rate from mm_stage_merchant where recv_date = $1 and load_yn = 'N' order by stage_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mm_bt_0002.pgc" +#line 49 "app/batch/mm_bt_0002.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mm_bt_0002"); @@ -151,13 +162,19 @@ static int run_mm_bt_0002(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0002", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_stage_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_merch_name),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_reg_no),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_acct_no),(long)24,(long)1,(24)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee_rate),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mm_bt_0002.pgc" +#line 61 "app/batch/mm_bt_0002.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,42 +187,77 @@ static int run_mm_bt_0002(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 필수항목 검증: 사업자번호 10자리, 계좌 비어있지 않음, 수수료율 범위 */ + if (strlen(h_biz_reg_no) < 10 || + strlen(h_acct_no) == 0 || + h_fee_rate <= 0 || h_fee_rate > 1000) { + tx_log(TX_LOG_WARN, + "[mm_bt_0002] 검증실패 스킵 stage=%s merch=%s", + h_stage_id, h_merch_id); + skipped++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); + /* 원장 적재 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_merchant ( merch_id , merch_name , biz_reg_no , acct_no , fee_rate , status , reg_date ) values ( $1 , $2 , $3 , $4 , $5 , 'M' , $6 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_name),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_reg_no),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_acct_no),(long)24,(long)1,(24)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee_rate),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 89 "app/batch/mm_bt_0002.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_merchant"); errcnt++; + continue; } + + /* 적재이력 기록 + 스테이징 마킹 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_load_hist ( stage_id , merch_id , load_date , result ) values ( $1 , $2 , $3 , 'OK' )", + ECPGt_char,(h_stage_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 99 "app/batch/mm_bt_0002.pgc" + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_stage_merchant set load_yn = 'Y' where stage_id = $1 ", + ECPGt_char,(h_stage_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 102 "app/batch/mm_bt_0002.pgc" + + + loaded++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0002", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mm_bt_0002.pgc" +#line 107 "app/batch/mm_bt_0002.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + rc = (errcnt == 0) ? tx_commit() : tx_abort(); + if (rc != TX_OK) + tx_log(TX_LOG_WARN, "[mm_bt_0002] 트랜잭션 종료 경고 rc=%d", rc); tx_log(TX_LOG_INFO, - "[mm_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0002] 요약 date=%s 대상=%ld 적재=%ld 스킵=%ld 오류=%ld", + biz_date, total, loaded, skipped, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 가맹점 마스터 일괄적재 [mm_bt_0002] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 적재 성공 : %ld\n", loaded); + printf(" 검증 스킵 : %ld\n", skipped); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,7 +278,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0002.pgc b/app/batch/mm_bt_0002.pgc index 4ebfea3..f55b330 100644 --- a/app/batch/mm_bt_0002.pgc +++ b/app/batch/mm_bt_0002.pgc @@ -1,9 +1,11 @@ /* @tier hard @module mm @transform master-batch */ /* - * mm_bt_0002.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=hard] + * mm_bt_0002.pgc - 가맹점 마스터 일괄적재 배치 (MM_BT_0002) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 신규 등록 스테이징(mm_stage_merchant) 을 커서로 순회하며 필수항목을 + * 검증한다. 유효건은 가맹점 원장(mm_merchant) 에 INSERT 하고 적재이력 + * (mm_load_hist) 을 남긴다. 사업자번호/계좌/수수료율 누락 등 오류건은 + * 적재하지 않고 스킵 집계한다. 배치 main 엔트리 포함. * * 실행: mm_bt_0002 [db@host] */ @@ -22,24 +24,27 @@ static int run_mm_bt_0002(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; + char h_stage_id[16]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_merch_name[64]; + char h_biz_reg_no[16]; + char h_acct_no[24]; + long h_fee_rate; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, loaded = 0, skipped = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 미적재 스테이징만 대상 */ EXEC SQL DECLARE cur_mm_bt_0002 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT stage_id, merch_id, merch_name, biz_reg_no, acct_no, fee_rate + FROM mm_stage_merchant + WHERE recv_date = :h_biz_date + AND load_yn = 'N' + ORDER BY stage_id; EXEC SQL OPEN cur_mm_bt_0002; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +56,9 @@ static int run_mm_bt_0002(const char *biz_date) tx_log(TX_LOG_WARN, "[mm_bt_0002] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mm_bt_0002 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mm_bt_0002 + INTO :h_stage_id, :h_merch_id, :h_merch_name, + :h_biz_reg_no, :h_acct_no, :h_fee_rate; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,40 +70,57 @@ static int run_mm_bt_0002(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 필수항목 검증: 사업자번호 10자리, 계좌 비어있지 않음, 수수료율 범위 */ + if (strlen(h_biz_reg_no) < 10 || + strlen(h_acct_no) == 0 || + h_fee_rate <= 0 || h_fee_rate > 1000) { + tx_log(TX_LOG_WARN, + "[mm_bt_0002] 검증실패 스킵 stage=%s merch=%s", + h_stage_id, h_merch_id); + skipped++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); + /* 원장 적재 */ + EXEC SQL INSERT INTO mm_merchant + (merch_id, merch_name, biz_reg_no, acct_no, + fee_rate, status, reg_date) + VALUES (:h_merch_id, :h_merch_name, :h_biz_reg_no, + :h_acct_no, :h_fee_rate, 'M', :h_biz_date); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_merchant"); errcnt++; + continue; } + + /* 적재이력 기록 + 스테이징 마킹 */ + EXEC SQL INSERT INTO mm_load_hist + (stage_id, merch_id, load_date, result) + VALUES (:h_stage_id, :h_merch_id, :h_biz_date, 'OK'); + EXEC SQL UPDATE mm_stage_merchant + SET load_yn = 'Y' + WHERE stage_id = :h_stage_id; + + loaded++; } EXEC SQL CLOSE cur_mm_bt_0002; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + rc = (errcnt == 0) ? tx_commit() : tx_abort(); + if (rc != TX_OK) + tx_log(TX_LOG_WARN, "[mm_bt_0002] 트랜잭션 종료 경고 rc=%d", rc); tx_log(TX_LOG_INFO, - "[mm_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0002] 요약 date=%s 대상=%ld 적재=%ld 스킵=%ld 오류=%ld", + biz_date, total, loaded, skipped, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 가맹점 마스터 일괄적재 [mm_bt_0002] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 적재 성공 : %ld\n", loaded); + printf(" 검증 스킵 : %ld\n", skipped); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,7 +141,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0003.c b/app/batch/mm_bt_0003.c index 4800970..4d09f4e 100644 --- a/app/batch/mm_bt_0003.c +++ b/app/batch/mm_bt_0003.c @@ -8,10 +8,11 @@ #line 1 "app/batch/mm_bt_0003.pgc" /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0003.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0003.pgc - 수수료율 이력 마감배치 (MM_BT_0003) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 적용종료일(end_date) 이 기준일에 도래한 유효(status='A') 수수료율 이력 + * (mm_fee_rate) 을 커서로 순회하며 마감(status='C') 처리한다. 마감 건수를 + * 집계한다. 배치 main 엔트리 포함. * * 실행: mm_bt_0003 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0003.pgc" +#line 20 "app/batch/mm_bt_0003.pgc" static int run_mm_bt_0003(const char *biz_date) @@ -105,38 +106,37 @@ static int run_mm_bt_0003(const char *biz_date) -#line 24 "app/batch/mm_bt_0003.pgc" +#line 25 "app/batch/mm_bt_0003.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mm_bt_0003.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/mm_bt_0003.pgc" - char h_merch_id [ 16 ] ; + char h_rate_id [ 16 ] ; #line 27 "app/batch/mm_bt_0003.pgc" - long h_amount ; + char h_merch_id [ 16 ] ; #line 28 "app/batch/mm_bt_0003.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + char h_end_date [ 9 ] ; + #line 29 "app/batch/mm_bt_0003.pgc" + long h_fee_rate ; +/* exec sql end declare section */ +#line 30 "app/batch/mm_bt_0003.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, closed = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0003 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_mm_bt_0003 cursor for select rate_id , merch_id , end_date , fee_rate from mm_fee_rate where status = 'A' and end_date <= $1 order by rate_id */ #line 42 "app/batch/mm_bt_0003.pgc" #line 42 "app/batch/mm_bt_0003.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0003 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0003 cursor for select rate_id , merch_id , end_date , fee_rate from mm_fee_rate where status = 'A' and end_date <= $1 order by rate_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/mm_bt_0003.pgc" @@ -151,13 +151,15 @@ static int run_mm_bt_0003(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0003", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_rate_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_end_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee_rate),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mm_bt_0003.pgc" +#line 55 "app/batch/mm_bt_0003.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,20 +172,22 @@ static int run_mm_bt_0003(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_fee_rate set status = 'C' , close_date = $1 where rate_id = $2 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_rate_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 70 "app/batch/mm_bt_0003.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + tx_log(TX_LOG_ERROR, + "[mm_bt_0003] 마감 실패 rate=%s merch=%s", + h_rate_id, h_merch_id); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + closed++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0003", ECPGt_EOIT, ECPGt_EORT);} @@ -196,16 +200,15 @@ static int run_mm_bt_0003(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0003] 요약 date=%s 대상=%ld 마감=%ld 오류=%ld", + biz_date, total, closed, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 수수료율 이력 마감배치 [mm_bt_0003] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 마감 대상 : %ld\n", total); + printf(" 마감 완료(C) : %ld\n", closed); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,7 +229,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0003.pgc b/app/batch/mm_bt_0003.pgc index 57bcf05..2e551b4 100644 --- a/app/batch/mm_bt_0003.pgc +++ b/app/batch/mm_bt_0003.pgc @@ -1,9 +1,10 @@ /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0003.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0003.pgc - 수수료율 이력 마감배치 (MM_BT_0003) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 적용종료일(end_date) 이 기준일에 도래한 유효(status='A') 수수료율 이력 + * (mm_fee_rate) 을 커서로 순회하며 마감(status='C') 처리한다. 마감 건수를 + * 집계한다. 배치 main 엔트리 포함. * * 실행: mm_bt_0003 [db@host] */ @@ -22,24 +23,23 @@ static int run_mm_bt_0003(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; + char h_rate_id[16]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_end_date[9]; + long h_fee_rate; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, closed = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mm_bt_0003 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT rate_id, merch_id, end_date, fee_rate + FROM mm_fee_rate + WHERE status = 'A' + AND end_date <= :h_biz_date + ORDER BY rate_id; EXEC SQL OPEN cur_mm_bt_0003; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +51,8 @@ static int run_mm_bt_0003(const char *biz_date) tx_log(TX_LOG_WARN, "[mm_bt_0003] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mm_bt_0003 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mm_bt_0003 + INTO :h_rate_id, :h_merch_id, :h_end_date, :h_fee_rate; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +64,19 @@ static int run_mm_bt_0003(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + EXEC SQL UPDATE mm_fee_rate + SET status = 'C', + close_date = :h_biz_date + WHERE rate_id = :h_rate_id; + if (sqlca.sqlcode != TX_SQL_OK) { + tx_log(TX_LOG_ERROR, + "[mm_bt_0003] 마감 실패 rate=%s merch=%s", + h_rate_id, h_merch_id); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + closed++; } EXEC SQL CLOSE cur_mm_bt_0003; @@ -87,16 +87,15 @@ static int run_mm_bt_0003(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0003] 요약 date=%s 대상=%ld 마감=%ld 오류=%ld", + biz_date, total, closed, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 수수료율 이력 마감배치 [mm_bt_0003] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 마감 대상 : %ld\n", total); + printf(" 마감 완료(C) : %ld\n", closed); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,7 +116,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0004.c b/app/batch/mm_bt_0004.c index f70af41..283c0c6 100644 --- a/app/batch/mm_bt_0004.c +++ b/app/batch/mm_bt_0004.c @@ -8,10 +8,11 @@ #line 1 "app/batch/mm_bt_0004.pgc" /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0004.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0004.pgc - 공통코드 캐시적재 배치 (MM_BT_0004) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 사용중(use_yn='Y') 공통코드(mm_common_code) 를 커서로 순회하며 조회용 + * 코드캐시(mm_code_cache) 를 재적재한다. 코드별로 기존 캐시행을 DELETE + * 한 뒤 최신 값을 INSERT 하여 온라인 조회 지연을 없앤다. 배치 main 포함. * * 실행: mm_bt_0004 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0004.pgc" +#line 20 "app/batch/mm_bt_0004.pgc" static int run_mm_bt_0004(const char *biz_date) @@ -103,43 +104,37 @@ static int run_mm_bt_0004(const char *biz_date) - + -#line 24 "app/batch/mm_bt_0004.pgc" +#line 25 "app/batch/mm_bt_0004.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mm_bt_0004.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/mm_bt_0004.pgc" - char h_merch_id [ 16 ] ; + char h_code_grp [ 16 ] ; #line 27 "app/batch/mm_bt_0004.pgc" - long h_amount ; + char h_code_id [ 16 ] ; #line 28 "app/batch/mm_bt_0004.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + char h_code_nm [ 64 ] ; + #line 29 "app/batch/mm_bt_0004.pgc" + int h_sort_no ; +/* exec sql end declare section */ +#line 30 "app/batch/mm_bt_0004.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, reloaded = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0004 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mm_bt_0004.pgc" - -#line 42 "app/batch/mm_bt_0004.pgc" + /* declare cur_mm_bt_0004 cursor for select code_grp , code_id , code_nm , sort_no from mm_common_code where use_yn = 'Y' order by code_grp , sort_no */ +#line 41 "app/batch/mm_bt_0004.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0004 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mm_bt_0004.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0004 cursor for select code_grp , code_id , code_nm , sort_no from mm_common_code where use_yn = 'Y' order by code_grp , sort_no", ECPGt_EOIT, ECPGt_EORT);} +#line 43 "app/batch/mm_bt_0004.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mm_bt_0004"); @@ -151,11 +146,13 @@ static int run_mm_bt_0004(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0004", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_code_grp),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_code_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_code_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_sort_no),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 54 "app/batch/mm_bt_0004.pgc" @@ -170,24 +167,44 @@ static int run_mm_bt_0004(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 기존 캐시 제거 후 재적재 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "delete from mm_code_cache where code_grp = $1 and code_id = $2 ", + ECPGt_char,(h_code_grp),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_code_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 69 "app/batch/mm_bt_0004.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("DELETE mm_code_cache"); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_code_cache ( code_grp , code_id , code_nm , sort_no , cache_date ) values ( $1 , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_code_grp),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_code_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_code_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_sort_no),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 79 "app/batch/mm_bt_0004.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_code_cache"); errcnt++; + continue; } + + reloaded++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0004", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mm_bt_0004.pgc" +#line 89 "app/batch/mm_bt_0004.pgc" if (errcnt == 0) @@ -196,16 +213,15 @@ static int run_mm_bt_0004(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0004] 요약 date=%s 대상=%ld 재적재=%ld 오류=%ld", + biz_date, total, reloaded, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 공통코드 캐시적재 [mm_bt_0004] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 코드 건수 : %ld\n", total); + printf(" 재적재 완료 : %ld\n", reloaded); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,7 +242,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0004.pgc b/app/batch/mm_bt_0004.pgc index 10b841e..0c6e0e9 100644 --- a/app/batch/mm_bt_0004.pgc +++ b/app/batch/mm_bt_0004.pgc @@ -1,9 +1,10 @@ /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0004.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0004.pgc - 공통코드 캐시적재 배치 (MM_BT_0004) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 사용중(use_yn='Y') 공통코드(mm_common_code) 를 커서로 순회하며 조회용 + * 코드캐시(mm_code_cache) 를 재적재한다. 코드별로 기존 캐시행을 DELETE + * 한 뒤 최신 값을 INSERT 하여 온라인 조회 지연을 없앤다. 배치 main 포함. * * 실행: mm_bt_0004 [db@host] */ @@ -22,24 +23,22 @@ static int run_mm_bt_0004(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_code_grp[16]; + char h_code_id[16]; + char h_code_nm[64]; + int h_sort_no; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, reloaded = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mm_bt_0004 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT code_grp, code_id, code_nm, sort_no + FROM mm_common_code + WHERE use_yn = 'Y' + ORDER BY code_grp, sort_no; EXEC SQL OPEN cur_mm_bt_0004; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +50,8 @@ static int run_mm_bt_0004(const char *biz_date) tx_log(TX_LOG_WARN, "[mm_bt_0004] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mm_bt_0004 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mm_bt_0004 + INTO :h_code_grp, :h_code_id, :h_code_nm, :h_sort_no; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +63,27 @@ static int run_mm_bt_0004(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 기존 캐시 제거 후 재적재 */ + EXEC SQL DELETE FROM mm_code_cache + WHERE code_grp = :h_code_grp + AND code_id = :h_code_id; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("DELETE mm_code_cache"); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL INSERT INTO mm_code_cache + (code_grp, code_id, code_nm, sort_no, cache_date) + VALUES (:h_code_grp, :h_code_id, :h_code_nm, + :h_sort_no, :h_biz_date); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_code_cache"); errcnt++; + continue; } + + reloaded++; } EXEC SQL CLOSE cur_mm_bt_0004; @@ -87,16 +94,15 @@ static int run_mm_bt_0004(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0004] 요약 date=%s 대상=%ld 재적재=%ld 오류=%ld", + biz_date, total, reloaded, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 공통코드 캐시적재 [mm_bt_0004] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 코드 건수 : %ld\n", total); + printf(" 재적재 완료 : %ld\n", reloaded); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,7 +123,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0005.c b/app/batch/mm_bt_0005.c index 22f8719..6d05045 100644 --- a/app/batch/mm_bt_0005.c +++ b/app/batch/mm_bt_0005.c @@ -8,10 +8,12 @@ #line 1 "app/batch/mm_bt_0005.pgc" /* @tier medium @module mm @transform master-batch */ /* - * mm_bt_0005.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=medium] + * mm_bt_0005.pgc - 가맹점 등급 재산정 배치 (MM_BT_0005) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 가맹점별 최근 3개월 누적매출(mm_merchant_sales) 을 커서로 집계하며, + * 매출 규모 기준으로 등급(A/B/C) 을 재산정한다. 등급이 변경된 가맹점은 + * 원장(mm_merchant) 을 UPDATE 하고 등급변경이력(mm_grade_hist) 을 + * 남긴다. 배치 main 엔트리 포함. * * 실행: mm_bt_0005 [db@host] */ @@ -93,9 +95,13 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0005.pgc" +#line 21 "app/batch/mm_bt_0005.pgc" +/* 등급 기준(원): A >= 3억, B >= 5천만, 그 외 C */ +#define MM_GRADE_A_MIN 300000000L +#define MM_GRADE_B_MIN 50000000L + static int run_mm_bt_0005(const char *biz_date) { /* exec sql begin declare section */ @@ -105,41 +111,41 @@ static int run_mm_bt_0005(const char *biz_date) -#line 24 "app/batch/mm_bt_0005.pgc" +#line 30 "app/batch/mm_bt_0005.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mm_bt_0005.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/mm_bt_0005.pgc" +#line 31 "app/batch/mm_bt_0005.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/mm_bt_0005.pgc" - long h_amount ; +#line 32 "app/batch/mm_bt_0005.pgc" + char h_old_grade [ 2 ] ; -#line 28 "app/batch/mm_bt_0005.pgc" - char h_new_status [ 2 ] ; +#line 33 "app/batch/mm_bt_0005.pgc" + char h_new_grade [ 2 ] ; + +#line 34 "app/batch/mm_bt_0005.pgc" + long h_sale_sum ; /* exec sql end declare section */ -#line 29 "app/batch/mm_bt_0005.pgc" +#line 35 "app/batch/mm_bt_0005.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, changed = 0, kept = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 최근 3개월 매출을 가맹점 단위로 집계 */ ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0005 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mm_bt_0005.pgc" + /* declare cur_mm_bt_0005 cursor for select s . merch_id , m . grade , SUM ( s . sale_amt ) from mm_merchant_sales s join mm_merchant m on m . merch_id = s . merch_id where s . sale_ym >= $1 and m . status = 'M' group by s . merch_id , m . grade order by s . merch_id */ +#line 50 "app/batch/mm_bt_0005.pgc" -#line 42 "app/batch/mm_bt_0005.pgc" +#line 50 "app/batch/mm_bt_0005.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0005 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0005 cursor for select s . merch_id , m . grade , SUM ( s . sale_amt ) from mm_merchant_sales s join mm_merchant m on m . merch_id = s . merch_id where s . sale_ym >= $1 and m . status = 'M' group by s . merch_id , m . grade order by s . merch_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mm_bt_0005.pgc" +#line 52 "app/batch/mm_bt_0005.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mm_bt_0005"); @@ -151,13 +157,13 @@ static int run_mm_bt_0005(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0005", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_old_grade),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sale_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mm_bt_0005.pgc" +#line 63 "app/batch/mm_bt_0005.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +176,54 @@ static int run_mm_bt_0005(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 매출 규모 → 신규 등급 */ + if (h_sale_sum >= MM_GRADE_A_MIN) + strncpy(h_new_grade, "A", sizeof(h_new_grade)); + else if (h_sale_sum >= MM_GRADE_B_MIN) + strncpy(h_new_grade, "B", sizeof(h_new_grade)); + else + strncpy(h_new_grade, "C", sizeof(h_new_grade)); + + if (strcmp(h_new_grade, h_old_grade) == 0) { + kept++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_merchant set grade = $1 , grade_date = $2 where merch_id = $3 ", + ECPGt_char,(h_new_grade),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 91 "app/batch/mm_bt_0005.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + tx_log(TX_LOG_ERROR, + "[mm_bt_0005] 등급갱신 실패 merch=%s", h_merch_id); errcnt++; + continue; } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_grade_hist ( merch_id , old_grade , new_grade , sale_sum , chg_date ) values ( $1 , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_old_grade),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_new_grade),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sale_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 102 "app/batch/mm_bt_0005.pgc" + + + changed++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0005", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mm_bt_0005.pgc" +#line 107 "app/batch/mm_bt_0005.pgc" if (errcnt == 0) @@ -196,16 +232,16 @@ static int run_mm_bt_0005(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0005] 요약 date=%s 대상=%ld 변경=%ld 유지=%ld 오류=%ld", + biz_date, total, changed, kept, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 가맹점 등급 재산정 [mm_bt_0005] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 평가 대상 : %ld\n", total); + printf(" 등급 변경 : %ld\n", changed); + printf(" 등급 유지 : %ld\n", kept); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,7 +262,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0005.pgc b/app/batch/mm_bt_0005.pgc index d46dda8..a042bff 100644 --- a/app/batch/mm_bt_0005.pgc +++ b/app/batch/mm_bt_0005.pgc @@ -1,9 +1,11 @@ /* @tier medium @module mm @transform master-batch */ /* - * mm_bt_0005.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=medium] + * mm_bt_0005.pgc - 가맹점 등급 재산정 배치 (MM_BT_0005) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 가맹점별 최근 3개월 누적매출(mm_merchant_sales) 을 커서로 집계하며, + * 매출 규모 기준으로 등급(A/B/C) 을 재산정한다. 등급이 변경된 가맹점은 + * 원장(mm_merchant) 을 UPDATE 하고 등급변경이력(mm_grade_hist) 을 + * 남긴다. 배치 main 엔트리 포함. * * 실행: mm_bt_0005 [db@host] */ @@ -18,28 +20,34 @@ EXEC SQL INCLUDE sqlca; +/* 등급 기준(원): A >= 3억, B >= 5천만, 그 외 C */ +#define MM_GRADE_A_MIN 300000000L +#define MM_GRADE_B_MIN 50000000L + static int run_mm_bt_0005(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_old_grade[2]; + char h_new_grade[2]; + long h_sale_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, changed = 0, kept = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 최근 3개월 매출을 가맹점 단위로 집계 */ EXEC SQL DECLARE cur_mm_bt_0005 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT s.merch_id, m.grade, SUM(s.sale_amt) + FROM mm_merchant_sales s + JOIN mm_merchant m ON m.merch_id = s.merch_id + WHERE s.sale_ym >= :h_biz_date + AND m.status = 'M' + GROUP BY s.merch_id, m.grade + ORDER BY s.merch_id; EXEC SQL OPEN cur_mm_bt_0005; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +59,8 @@ static int run_mm_bt_0005(const char *biz_date) tx_log(TX_LOG_WARN, "[mm_bt_0005] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mm_bt_0005 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mm_bt_0005 + INTO :h_merch_id, :h_old_grade, :h_sale_sum; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +72,36 @@ static int run_mm_bt_0005(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 매출 규모 → 신규 등급 */ + if (h_sale_sum >= MM_GRADE_A_MIN) + strncpy(h_new_grade, "A", sizeof(h_new_grade)); + else if (h_sale_sum >= MM_GRADE_B_MIN) + strncpy(h_new_grade, "B", sizeof(h_new_grade)); + else + strncpy(h_new_grade, "C", sizeof(h_new_grade)); + + if (strcmp(h_new_grade, h_old_grade) == 0) { + kept++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL UPDATE mm_merchant + SET grade = :h_new_grade, + grade_date = :h_biz_date + WHERE merch_id = :h_merch_id; + if (sqlca.sqlcode != TX_SQL_OK) { + tx_log(TX_LOG_ERROR, + "[mm_bt_0005] 등급갱신 실패 merch=%s", h_merch_id); errcnt++; + continue; } + + EXEC SQL INSERT INTO mm_grade_hist + (merch_id, old_grade, new_grade, sale_sum, chg_date) + VALUES (:h_merch_id, :h_old_grade, :h_new_grade, + :h_sale_sum, :h_biz_date); + + changed++; } EXEC SQL CLOSE cur_mm_bt_0005; @@ -87,16 +112,16 @@ static int run_mm_bt_0005(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0005] 요약 date=%s 대상=%ld 변경=%ld 유지=%ld 오류=%ld", + biz_date, total, changed, kept, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 가맹점 등급 재산정 [mm_bt_0005] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 평가 대상 : %ld\n", total); + printf(" 등급 변경 : %ld\n", changed); + printf(" 등급 유지 : %ld\n", kept); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,7 +142,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0006.c b/app/batch/mm_bt_0006.c index 098c533..1434832 100644 --- a/app/batch/mm_bt_0006.c +++ b/app/batch/mm_bt_0006.c @@ -8,10 +8,11 @@ #line 1 "app/batch/mm_bt_0006.pgc" /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0006.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0006.pgc - 한도 마스터 갱신배치 (MM_BT_0006) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 가맹점(mm_merchant) 과 등급별 기준한도(mm_grade_limit) 를 조인한 결과를 + * 커서로 순회하며, 가맹점별 승인한도(mm_limit) 를 등급 기준한도로 갱신한다. + * 배치 main 엔트리 포함. * * 실행: mm_bt_0006 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0006.pgc" +#line 20 "app/batch/mm_bt_0006.pgc" static int run_mm_bt_0006(const char *biz_date) @@ -103,43 +104,33 @@ static int run_mm_bt_0006(const char *biz_date) - -#line 24 "app/batch/mm_bt_0006.pgc" - char h_biz_date [ 9 ] ; - #line 25 "app/batch/mm_bt_0006.pgc" - char h_key [ 16 ] ; + char h_biz_date [ 9 ] ; #line 26 "app/batch/mm_bt_0006.pgc" char h_merch_id [ 16 ] ; #line 27 "app/batch/mm_bt_0006.pgc" - long h_amount ; + char h_grade [ 2 ] ; #line 28 "app/batch/mm_bt_0006.pgc" - char h_new_status [ 2 ] ; + long h_base_limit ; /* exec sql end declare section */ #line 29 "app/batch/mm_bt_0006.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, updated = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0006 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mm_bt_0006.pgc" - -#line 42 "app/batch/mm_bt_0006.pgc" + /* declare cur_mm_bt_0006 cursor for select m . merch_id , m . grade , g . base_limit from mm_merchant m join mm_grade_limit g on g . grade = m . grade where m . status = 'M' order by m . merch_id */ +#line 41 "app/batch/mm_bt_0006.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0006 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mm_bt_0006.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0006 cursor for select m . merch_id , m . grade , g . base_limit from mm_merchant m join mm_grade_limit g on g . grade = m . grade where m . status = 'M' order by m . merch_id", ECPGt_EOIT, ECPGt_EORT);} +#line 43 "app/batch/mm_bt_0006.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mm_bt_0006"); @@ -151,11 +142,11 @@ static int run_mm_bt_0006(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0006", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_grade),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_base_limit),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 54 "app/batch/mm_bt_0006.pgc" @@ -170,24 +161,28 @@ static int run_mm_bt_0006(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_limit set limit_amt = $1 , apply_date = $2 where merch_id = $3 ", + ECPGt_long,&(h_base_limit),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 69 "app/batch/mm_bt_0006.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + tx_log(TX_LOG_ERROR, + "[mm_bt_0006] 한도갱신 실패 merch=%s grade=%s", + h_merch_id, h_grade); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + updated++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0006", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mm_bt_0006.pgc" +#line 81 "app/batch/mm_bt_0006.pgc" if (errcnt == 0) @@ -196,16 +191,15 @@ static int run_mm_bt_0006(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0006] 요약 date=%s 대상=%ld 갱신=%ld 오류=%ld", + biz_date, total, updated, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 한도 마스터 갱신배치 [mm_bt_0006] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 한도 갱신 : %ld\n", updated); + printf(" 오류 건수 : %ld\n", errcnt); + printf("====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,7 +220,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0006.pgc b/app/batch/mm_bt_0006.pgc index 388736d..b8a79cc 100644 --- a/app/batch/mm_bt_0006.pgc +++ b/app/batch/mm_bt_0006.pgc @@ -1,9 +1,10 @@ /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0006.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0006.pgc - 한도 마스터 갱신배치 (MM_BT_0006) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 가맹점(mm_merchant) 과 등급별 기준한도(mm_grade_limit) 를 조인한 결과를 + * 커서로 순회하며, 가맹점별 승인한도(mm_limit) 를 등급 기준한도로 갱신한다. + * 배치 main 엔트리 포함. * * 실행: mm_bt_0006 [db@host] */ @@ -22,24 +23,22 @@ static int run_mm_bt_0006(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_grade[2]; + long h_base_limit; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, updated = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mm_bt_0006 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT m.merch_id, m.grade, g.base_limit + FROM mm_merchant m + JOIN mm_grade_limit g ON g.grade = m.grade + WHERE m.status = 'M' + ORDER BY m.merch_id; EXEC SQL OPEN cur_mm_bt_0006; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +50,8 @@ static int run_mm_bt_0006(const char *biz_date) tx_log(TX_LOG_WARN, "[mm_bt_0006] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mm_bt_0006 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mm_bt_0006 + INTO :h_merch_id, :h_grade, :h_base_limit; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +63,19 @@ static int run_mm_bt_0006(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + EXEC SQL UPDATE mm_limit + SET limit_amt = :h_base_limit, + apply_date = :h_biz_date + WHERE merch_id = :h_merch_id; + if (sqlca.sqlcode != TX_SQL_OK) { + tx_log(TX_LOG_ERROR, + "[mm_bt_0006] 한도갱신 실패 merch=%s grade=%s", + h_merch_id, h_grade); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + updated++; } EXEC SQL CLOSE cur_mm_bt_0006; @@ -87,16 +86,15 @@ static int run_mm_bt_0006(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0006] 요약 date=%s 대상=%ld 갱신=%ld 오류=%ld", + biz_date, total, updated, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 한도 마스터 갱신배치 [mm_bt_0006] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 한도 갱신 : %ld\n", updated); + printf(" 오류 건수 : %ld\n", errcnt); + printf("====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,7 +115,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0007.c b/app/batch/mm_bt_0007.c index ff5d2c8..69be6b3 100644 --- a/app/batch/mm_bt_0007.c +++ b/app/batch/mm_bt_0007.c @@ -8,10 +8,12 @@ #line 1 "app/batch/mm_bt_0007.pgc" /* @tier medium @module mm @transform master-batch */ /* - * mm_bt_0007.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=medium] + * mm_bt_0007.pgc - BIN 마스터 적재배치 (MM_BT_0007) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 카드사에서 제공한 BIN 스테이징(mm_stage_bin) 을 커서로 순회하며 BIN + * 마스터(mm_card_bin) 에 UPSERT 한다. 동일 BIN 이 존재하면 발급사/브랜드/ + * 카드종류를 UPDATE 하고, 없으면 INSERT 한다. 신규/갱신 건수를 집계한다. + * 배치 main 엔트리 포함. * * 실행: mm_bt_0007 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0007.pgc" +#line 21 "app/batch/mm_bt_0007.pgc" static int run_mm_bt_0007(const char *biz_date) @@ -104,42 +106,45 @@ static int run_mm_bt_0007(const char *biz_date) + -#line 24 "app/batch/mm_bt_0007.pgc" +#line 26 "app/batch/mm_bt_0007.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mm_bt_0007.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/mm_bt_0007.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/mm_bt_0007.pgc" - long h_amount ; + char h_bin [ 8 ] ; #line 28 "app/batch/mm_bt_0007.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + char h_issuer_cd [ 8 ] ; + #line 29 "app/batch/mm_bt_0007.pgc" + char h_card_brand [ 16 ] ; + +#line 30 "app/batch/mm_bt_0007.pgc" + char h_card_type [ 8 ] ; + +#line 31 "app/batch/mm_bt_0007.pgc" + int h_exist ; +/* exec sql end declare section */ +#line 32 "app/batch/mm_bt_0007.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, inserted = 0, updated = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0007 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mm_bt_0007.pgc" + /* declare cur_mm_bt_0007 cursor for select bin , issuer_cd , card_brand , card_type from mm_stage_bin where recv_date = $1 order by bin */ +#line 43 "app/batch/mm_bt_0007.pgc" -#line 42 "app/batch/mm_bt_0007.pgc" +#line 43 "app/batch/mm_bt_0007.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0007 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0007 cursor for select bin , issuer_cd , card_brand , card_type from mm_stage_bin where recv_date = $1 order by bin", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mm_bt_0007.pgc" +#line 45 "app/batch/mm_bt_0007.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mm_bt_0007"); @@ -151,13 +156,15 @@ static int run_mm_bt_0007(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0007", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_bin),(long)8,(long)1,(8)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_issuer_cd),(long)8,(long)1,(8)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_card_brand),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_type),(long)8,(long)1,(8)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mm_bt_0007.pgc" +#line 56 "app/batch/mm_bt_0007.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +177,66 @@ static int run_mm_bt_0007(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 존재 여부 판정 */ + h_exist = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_card_bin where bin = $1 ", + ECPGt_char,(h_bin),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_exist),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 72 "app/batch/mm_bt_0007.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT COUNT mm_card_bin"); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (h_exist > 0) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_card_bin set issuer_cd = $1 , card_brand = $2 , card_type = $3 , upd_date = $4 where bin = $5 ", + ECPGt_char,(h_issuer_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_brand),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_type),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bin),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 85 "app/batch/mm_bt_0007.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_card_bin"); + errcnt++; + continue; + } + updated++; + } else { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_card_bin ( bin , issuer_cd , card_brand , card_type , reg_date ) values ( $1 , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_bin),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_issuer_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_brand),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_type),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 96 "app/batch/mm_bt_0007.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_card_bin"); + errcnt++; + continue; + } + inserted++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0007", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mm_bt_0007.pgc" +#line 106 "app/batch/mm_bt_0007.pgc" if (errcnt == 0) @@ -196,16 +245,16 @@ static int run_mm_bt_0007(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0007] 요약 date=%s 대상=%ld 신규=%ld 갱신=%ld 오류=%ld", + biz_date, total, inserted, updated, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== BIN 마스터 적재배치 [mm_bt_0007] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 신규 등록 : %ld\n", inserted); + printf(" 기존 갱신 : %ld\n", updated); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,7 +275,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0007.pgc b/app/batch/mm_bt_0007.pgc index 9870493..7bd8acd 100644 --- a/app/batch/mm_bt_0007.pgc +++ b/app/batch/mm_bt_0007.pgc @@ -1,9 +1,11 @@ /* @tier medium @module mm @transform master-batch */ /* - * mm_bt_0007.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=medium] + * mm_bt_0007.pgc - BIN 마스터 적재배치 (MM_BT_0007) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 카드사에서 제공한 BIN 스테이징(mm_stage_bin) 을 커서로 순회하며 BIN + * 마스터(mm_card_bin) 에 UPSERT 한다. 동일 BIN 이 존재하면 발급사/브랜드/ + * 카드종류를 UPDATE 하고, 없으면 INSERT 한다. 신규/갱신 건수를 집계한다. + * 배치 main 엔트리 포함. * * 실행: mm_bt_0007 [db@host] */ @@ -22,24 +24,23 @@ static int run_mm_bt_0007(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_bin[8]; + char h_issuer_cd[8]; + char h_card_brand[16]; + char h_card_type[8]; + int h_exist; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, inserted = 0, updated = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mm_bt_0007 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT bin, issuer_cd, card_brand, card_type + FROM mm_stage_bin + WHERE recv_date = :h_biz_date + ORDER BY bin; EXEC SQL OPEN cur_mm_bt_0007; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +52,8 @@ static int run_mm_bt_0007(const char *biz_date) tx_log(TX_LOG_WARN, "[mm_bt_0007] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mm_bt_0007 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mm_bt_0007 + INTO :h_bin, :h_issuer_cd, :h_card_brand, :h_card_type; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,19 +65,41 @@ static int run_mm_bt_0007(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 존재 여부 판정 */ + h_exist = 0; + EXEC SQL SELECT COUNT(*) INTO :h_exist + FROM mm_card_bin + WHERE bin = :h_bin; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT COUNT mm_card_bin"); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (h_exist > 0) { + EXEC SQL UPDATE mm_card_bin + SET issuer_cd = :h_issuer_cd, + card_brand = :h_card_brand, + card_type = :h_card_type, + upd_date = :h_biz_date + WHERE bin = :h_bin; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_card_bin"); + errcnt++; + continue; + } + updated++; + } else { + EXEC SQL INSERT INTO mm_card_bin + (bin, issuer_cd, card_brand, card_type, reg_date) + VALUES (:h_bin, :h_issuer_cd, :h_card_brand, + :h_card_type, :h_biz_date); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_card_bin"); + errcnt++; + continue; + } + inserted++; } } @@ -87,16 +111,16 @@ static int run_mm_bt_0007(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0007] 요약 date=%s 대상=%ld 신규=%ld 갱신=%ld 오류=%ld", + biz_date, total, inserted, updated, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== BIN 마스터 적재배치 [mm_bt_0007] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 신규 등록 : %ld\n", inserted); + printf(" 기존 갱신 : %ld\n", updated); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,7 +141,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0008.c b/app/batch/mm_bt_0008.c index 385329b..98145b6 100644 --- a/app/batch/mm_bt_0008.c +++ b/app/batch/mm_bt_0008.c @@ -8,10 +8,12 @@ #line 1 "app/batch/mm_bt_0008.pgc" /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0008.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0008.pgc - 가맹점 마스터 검증배치 (MM_BT_0008) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 유효 가맹점(mm_merchant) 을 커서로 순회하며 필수항목(정산계좌/사업자 + * 번호/수수료율) 누락 여부를 점검한다. 누락이 발견되면 결함내역 + * (mm_master_defect) 에 결함코드와 함께 INSERT 한다. 결함 건수를 집계한다. + * 배치 main 엔트리 포함. * * 실행: mm_bt_0008 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0008.pgc" +#line 21 "app/batch/mm_bt_0008.pgc" static int run_mm_bt_0008(const char *biz_date) @@ -104,42 +106,40 @@ static int run_mm_bt_0008(const char *biz_date) + -#line 24 "app/batch/mm_bt_0008.pgc" +#line 26 "app/batch/mm_bt_0008.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mm_bt_0008.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/mm_bt_0008.pgc" +#line 27 "app/batch/mm_bt_0008.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/mm_bt_0008.pgc" - long h_amount ; - #line 28 "app/batch/mm_bt_0008.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + char h_acct_no [ 24 ] ; + #line 29 "app/batch/mm_bt_0008.pgc" + char h_biz_reg_no [ 16 ] ; + +#line 30 "app/batch/mm_bt_0008.pgc" + char h_defect_cd [ 8 ] ; + +#line 31 "app/batch/mm_bt_0008.pgc" + long h_fee_rate ; +/* exec sql end declare section */ +#line 32 "app/batch/mm_bt_0008.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, defect = 0, ok = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0008 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mm_bt_0008.pgc" - -#line 42 "app/batch/mm_bt_0008.pgc" + /* declare cur_mm_bt_0008 cursor for select merch_id , acct_no , biz_reg_no , fee_rate from mm_merchant where status = 'M' order by merch_id */ +#line 43 "app/batch/mm_bt_0008.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0008 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mm_bt_0008.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0008 cursor for select merch_id , acct_no , biz_reg_no , fee_rate from mm_merchant where status = 'M' order by merch_id", ECPGt_EOIT, ECPGt_EORT);} +#line 45 "app/batch/mm_bt_0008.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mm_bt_0008"); @@ -151,13 +151,15 @@ static int run_mm_bt_0008(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0008", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_acct_no),(long)24,(long)1,(24)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_reg_no),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee_rate),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mm_bt_0008.pgc" +#line 56 "app/batch/mm_bt_0008.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +172,42 @@ static int run_mm_bt_0008(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 필수항목 결함코드 판정 (우선순위: 계좌 > 사업자번호 > 수수료율) */ + h_defect_cd[0] = '\0'; + if (strlen(h_acct_no) == 0) + strncpy(h_defect_cd, "E-ACCT", sizeof(h_defect_cd)); + else if (strlen(h_biz_reg_no) < 10) + strncpy(h_defect_cd, "E-BIZNO", sizeof(h_defect_cd)); + else if (h_fee_rate <= 0) + strncpy(h_defect_cd, "E-FEE", sizeof(h_defect_cd)); + + if (h_defect_cd[0] == '\0') { + ok++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_master_defect ( merch_id , defect_cd , chk_date ) values ( $1 , $2 , $3 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_defect_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 84 "app/batch/mm_bt_0008.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + tx_log(TX_LOG_ERROR, + "[mm_bt_0008] 결함적재 실패 merch=%s cd=%s", + h_merch_id, h_defect_cd); errcnt++; + continue; } + + defect++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0008", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mm_bt_0008.pgc" +#line 96 "app/batch/mm_bt_0008.pgc" if (errcnt == 0) @@ -196,16 +216,16 @@ static int run_mm_bt_0008(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0008] 요약 date=%s 대상=%ld 정상=%ld 결함=%ld 오류=%ld", + biz_date, total, ok, defect, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 가맹점 마스터 검증배치 [mm_bt_0008] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 검증 대상 : %ld\n", total); + printf(" 정상 건수 : %ld\n", ok); + printf(" 결함 검출 : %ld\n", defect); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,7 +246,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0008.pgc b/app/batch/mm_bt_0008.pgc index 60e52c5..fba78c9 100644 --- a/app/batch/mm_bt_0008.pgc +++ b/app/batch/mm_bt_0008.pgc @@ -1,9 +1,11 @@ /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0008.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0008.pgc - 가맹점 마스터 검증배치 (MM_BT_0008) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 유효 가맹점(mm_merchant) 을 커서로 순회하며 필수항목(정산계좌/사업자 + * 번호/수수료율) 누락 여부를 점검한다. 누락이 발견되면 결함내역 + * (mm_master_defect) 에 결함코드와 함께 INSERT 한다. 결함 건수를 집계한다. + * 배치 main 엔트리 포함. * * 실행: mm_bt_0008 [db@host] */ @@ -22,24 +24,23 @@ static int run_mm_bt_0008(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_acct_no[24]; + char h_biz_reg_no[16]; + char h_defect_cd[8]; + long h_fee_rate; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, defect = 0, ok = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mm_bt_0008 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT merch_id, acct_no, biz_reg_no, fee_rate + FROM mm_merchant + WHERE status = 'M' + ORDER BY merch_id; EXEC SQL OPEN cur_mm_bt_0008; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +52,8 @@ static int run_mm_bt_0008(const char *biz_date) tx_log(TX_LOG_WARN, "[mm_bt_0008] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mm_bt_0008 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mm_bt_0008 + INTO :h_merch_id, :h_acct_no, :h_biz_reg_no, :h_fee_rate; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +65,32 @@ static int run_mm_bt_0008(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 필수항목 결함코드 판정 (우선순위: 계좌 > 사업자번호 > 수수료율) */ + h_defect_cd[0] = '\0'; + if (strlen(h_acct_no) == 0) + strncpy(h_defect_cd, "E-ACCT", sizeof(h_defect_cd)); + else if (strlen(h_biz_reg_no) < 10) + strncpy(h_defect_cd, "E-BIZNO", sizeof(h_defect_cd)); + else if (h_fee_rate <= 0) + strncpy(h_defect_cd, "E-FEE", sizeof(h_defect_cd)); + + if (h_defect_cd[0] == '\0') { + ok++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL INSERT INTO mm_master_defect + (merch_id, defect_cd, chk_date) + VALUES (:h_merch_id, :h_defect_cd, :h_biz_date); + if (sqlca.sqlcode != TX_SQL_OK) { + tx_log(TX_LOG_ERROR, + "[mm_bt_0008] 결함적재 실패 merch=%s cd=%s", + h_merch_id, h_defect_cd); errcnt++; + continue; } + + defect++; } EXEC SQL CLOSE cur_mm_bt_0008; @@ -87,16 +101,16 @@ static int run_mm_bt_0008(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0008] 요약 date=%s 대상=%ld 정상=%ld 결함=%ld 오류=%ld", + biz_date, total, ok, defect, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 가맹점 마스터 검증배치 [mm_bt_0008] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 검증 대상 : %ld\n", total); + printf(" 정상 건수 : %ld\n", ok); + printf(" 결함 검출 : %ld\n", defect); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,7 +131,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0009.c b/app/batch/mm_bt_0009.c index 349c8ea..9858b5b 100644 --- a/app/batch/mm_bt_0009.c +++ b/app/batch/mm_bt_0009.c @@ -8,10 +8,12 @@ #line 1 "app/batch/mm_bt_0009.pgc" /* @tier medium @module mm @transform master-batch */ /* - * mm_bt_0009.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=medium] + * mm_bt_0009.pgc - 휴면가맹점 처리배치 (MM_BT_0009) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 최종거래일(last_txn_date) 이 휴면기준일을 초과하도록 오래된 유효 + * 가맹점(mm_merchant, status='M') 을 커서로 순회하며 휴면(status='U') 으로 + * 전이하고 상태변경이력(mm_status_hist) 을 남긴다. 인자로 받은 기준일을 + * 휴면 판정 컷오프로 사용한다. 배치 main 엔트리 포함. * * 실행: mm_bt_0009 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0009.pgc" +#line 21 "app/batch/mm_bt_0009.pgc" static int run_mm_bt_0009(const char *biz_date) @@ -102,44 +104,35 @@ static int run_mm_bt_0009(const char *biz_date) - - -#line 24 "app/batch/mm_bt_0009.pgc" - char h_biz_date [ 9 ] ; - -#line 25 "app/batch/mm_bt_0009.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/mm_bt_0009.pgc" - char h_merch_id [ 16 ] ; + char h_cutoff [ 9 ] ; #line 27 "app/batch/mm_bt_0009.pgc" - long h_amount ; + char h_merch_id [ 16 ] ; #line 28 "app/batch/mm_bt_0009.pgc" - char h_new_status [ 2 ] ; + char h_last_txn [ 9 ] ; /* exec sql end declare section */ #line 29 "app/batch/mm_bt_0009.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, dormant = 0, errcnt = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_cutoff, biz_date, sizeof(h_cutoff) - 1); + h_cutoff[sizeof(h_cutoff) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0009 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mm_bt_0009.pgc" + ECPGset_var( 0, ( h_cutoff ), __LINE__);\ + /* declare cur_mm_bt_0009 cursor for select merch_id , last_txn_date from mm_merchant where status = 'M' and last_txn_date < $1 order by merch_id */ +#line 41 "app/batch/mm_bt_0009.pgc" -#line 42 "app/batch/mm_bt_0009.pgc" +#line 41 "app/batch/mm_bt_0009.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0009 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0009 cursor for select merch_id , last_txn_date from mm_merchant where status = 'M' and last_txn_date < $1 order by merch_id", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mm_bt_0009.pgc" +#line 43 "app/batch/mm_bt_0009.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mm_bt_0009"); @@ -151,13 +144,11 @@ static int run_mm_bt_0009(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0009", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_last_txn),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mm_bt_0009.pgc" +#line 53 "app/batch/mm_bt_0009.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +161,33 @@ static int run_mm_bt_0009(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_merchant set status = 'U' , status_date = $1 where merch_id = $2 ", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 68 "app/batch/mm_bt_0009.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + tx_log(TX_LOG_ERROR, + "[mm_bt_0009] 휴면전이 실패 merch=%s", h_merch_id); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_status_hist ( merch_id , old_status , new_status , reason , chg_date ) values ( $1 , 'M' , 'U' , 'DORMANT' , $2 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 78 "app/batch/mm_bt_0009.pgc" + + + dormant++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0009", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mm_bt_0009.pgc" +#line 83 "app/batch/mm_bt_0009.pgc" if (errcnt == 0) @@ -196,16 +196,15 @@ static int run_mm_bt_0009(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0009] 요약 cutoff=%s 대상=%ld 휴면=%ld 오류=%ld", + biz_date, total, dormant, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 휴면가맹점 처리배치 [mm_bt_0009] ==========\n"); + printf(" 휴면기준일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 휴면 전이(U) : %ld\n", dormant); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,11 +225,11 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[mm_bt_0009] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mm_bt_0009] 배치 시작 cutoff=%s", biz_date); rc = mm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mm_bt_0009.pgc b/app/batch/mm_bt_0009.pgc index c9f5c46..fb87540 100644 --- a/app/batch/mm_bt_0009.pgc +++ b/app/batch/mm_bt_0009.pgc @@ -1,9 +1,11 @@ /* @tier medium @module mm @transform master-batch */ /* - * mm_bt_0009.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=medium] + * mm_bt_0009.pgc - 휴면가맹점 처리배치 (MM_BT_0009) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 최종거래일(last_txn_date) 이 휴면기준일을 초과하도록 오래된 유효 + * 가맹점(mm_merchant, status='M') 을 커서로 순회하며 휴면(status='U') 으로 + * 전이하고 상태변경이력(mm_status_hist) 을 남긴다. 인자로 받은 기준일을 + * 휴면 판정 컷오프로 사용한다. 배치 main 엔트리 포함. * * 실행: mm_bt_0009 [db@host] */ @@ -21,25 +23,22 @@ EXEC SQL INCLUDE sqlca; static int run_mm_bt_0009(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; + char h_cutoff[9]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_last_txn[9]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, dormant = 0, errcnt = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_cutoff, biz_date, sizeof(h_cutoff) - 1); + h_cutoff[sizeof(h_cutoff) - 1] = '\0'; EXEC SQL DECLARE cur_mm_bt_0009 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT merch_id, last_txn_date + FROM mm_merchant + WHERE status = 'M' + AND last_txn_date < :h_cutoff + ORDER BY merch_id; EXEC SQL OPEN cur_mm_bt_0009; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +50,7 @@ static int run_mm_bt_0009(const char *biz_date) tx_log(TX_LOG_WARN, "[mm_bt_0009] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mm_bt_0009 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mm_bt_0009 INTO :h_merch_id, :h_last_txn; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +62,22 @@ static int run_mm_bt_0009(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + EXEC SQL UPDATE mm_merchant + SET status = 'U', + status_date = :h_cutoff + WHERE merch_id = :h_merch_id; + if (sqlca.sqlcode != TX_SQL_OK) { + tx_log(TX_LOG_ERROR, + "[mm_bt_0009] 휴면전이 실패 merch=%s", h_merch_id); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL INSERT INTO mm_status_hist + (merch_id, old_status, new_status, reason, chg_date) + VALUES (:h_merch_id, 'M', 'U', 'DORMANT', :h_cutoff); + + dormant++; } EXEC SQL CLOSE cur_mm_bt_0009; @@ -87,16 +88,15 @@ static int run_mm_bt_0009(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0009] 요약 cutoff=%s 대상=%ld 휴면=%ld 오류=%ld", + biz_date, total, dormant, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 휴면가맹점 처리배치 [mm_bt_0009] ==========\n"); + printf(" 휴면기준일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 휴면 전이(U) : %ld\n", dormant); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,11 +117,11 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[mm_bt_0009] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mm_bt_0009] 배치 시작 cutoff=%s", biz_date); rc = mm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mm_bt_0010.c b/app/batch/mm_bt_0010.c index 404696e..20f9aa6 100644 --- a/app/batch/mm_bt_0010.c +++ b/app/batch/mm_bt_0010.c @@ -8,10 +8,11 @@ #line 1 "app/batch/mm_bt_0010.pgc" /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0010.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0010.pgc - 수수료율 일괄변경 배치 (MM_BT_0010) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 특정 업종(industry_cd) 가맹점을 커서로 순회하며 수수료율을 일괄 변경한다. + * 가맹점별로 기존 유효 수수료율(mm_fee_rate) 을 종료(status='C') 처리하고 + * 신규 요율행을 INSERT 한다. 배치 main 엔트리 포함. * * 실행: mm_bt_0010 [db@host] */ @@ -93,9 +94,13 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0010.pgc" +#line 20 "app/batch/mm_bt_0010.pgc" +/* 대상 업종(일반음식점) 및 신규 적용 요율(2.30% = 230 bp) */ +#define MM_TARGET_INDUSTRY "7011" +#define MM_NEW_FEE_RATE 230L + static int run_mm_bt_0010(const char *biz_date) { /* exec sql begin declare section */ @@ -103,43 +108,41 @@ static int run_mm_bt_0010(const char *biz_date) - -#line 24 "app/batch/mm_bt_0010.pgc" +#line 29 "app/batch/mm_bt_0010.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mm_bt_0010.pgc" - char h_key [ 16 ] ; +#line 30 "app/batch/mm_bt_0010.pgc" + char h_industry [ 8 ] ; -#line 26 "app/batch/mm_bt_0010.pgc" +#line 31 "app/batch/mm_bt_0010.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/mm_bt_0010.pgc" - long h_amount ; - -#line 28 "app/batch/mm_bt_0010.pgc" - char h_new_status [ 2 ] ; +#line 32 "app/batch/mm_bt_0010.pgc" + long h_new_rate ; /* exec sql end declare section */ -#line 29 "app/batch/mm_bt_0010.pgc" +#line 33 "app/batch/mm_bt_0010.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, applied = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_industry, MM_TARGET_INDUSTRY, sizeof(h_industry) - 1); + h_industry[sizeof(h_industry) - 1] = '\0'; + h_new_rate = MM_NEW_FEE_RATE; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0010 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mm_bt_0010.pgc" + ECPGset_var( 0, ( h_industry ), __LINE__);\ + /* declare cur_mm_bt_0010 cursor for select merch_id from mm_merchant where status = 'M' and industry_cd = $1 order by merch_id */ +#line 48 "app/batch/mm_bt_0010.pgc" -#line 42 "app/batch/mm_bt_0010.pgc" +#line 48 "app/batch/mm_bt_0010.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0010 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0010 cursor for select merch_id from mm_merchant where status = 'M' and industry_cd = $1 order by merch_id", + ECPGt_char,(h_industry),(long)8,(long)1,(8)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mm_bt_0010.pgc" +#line 50 "app/batch/mm_bt_0010.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mm_bt_0010"); @@ -151,13 +154,9 @@ static int run_mm_bt_0010(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0010", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mm_bt_0010.pgc" +#line 60 "app/batch/mm_bt_0010.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +169,41 @@ static int run_mm_bt_0010(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 기존 유효 요율 종료 처리 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_fee_rate set status = 'C' , close_date = $1 where merch_id = $2 and status = 'A'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 77 "app/batch/mm_bt_0010.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("UPDATE mm_fee_rate close"); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); + /* 신규 요율 등록 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_fee_rate ( merch_id , fee_rate , status , start_date ) values ( $1 , $2 , 'A' , $3 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_new_rate),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 87 "app/batch/mm_bt_0010.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_fee_rate"); errcnt++; + continue; } + + applied++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0010", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mm_bt_0010.pgc" +#line 97 "app/batch/mm_bt_0010.pgc" if (errcnt == 0) @@ -196,16 +212,17 @@ static int run_mm_bt_0010(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0010] 요약 date=%s 업종=%s 대상=%ld 변경=%ld 오류=%ld", + biz_date, h_industry, total, applied, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 수수료율 일괄변경 [mm_bt_0010] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 대상 업종 : %s\n", h_industry); + printf(" 신규 요율(bp): %ld\n", h_new_rate); + printf(" 대상 건수 : %ld\n", total); + printf(" 변경 완료 : %ld\n", applied); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,7 +243,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0010.pgc b/app/batch/mm_bt_0010.pgc index 100bc33..3f4f0af 100644 --- a/app/batch/mm_bt_0010.pgc +++ b/app/batch/mm_bt_0010.pgc @@ -1,9 +1,10 @@ /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0010.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0010.pgc - 수수료율 일괄변경 배치 (MM_BT_0010) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 특정 업종(industry_cd) 가맹점을 커서로 순회하며 수수료율을 일괄 변경한다. + * 가맹점별로 기존 유효 수수료율(mm_fee_rate) 을 종료(status='C') 처리하고 + * 신규 요율행을 INSERT 한다. 배치 main 엔트리 포함. * * 실행: mm_bt_0010 [db@host] */ @@ -18,28 +19,33 @@ EXEC SQL INCLUDE sqlca; +/* 대상 업종(일반음식점) 및 신규 적용 요율(2.30% = 230 bp) */ +#define MM_TARGET_INDUSTRY "7011" +#define MM_NEW_FEE_RATE 230L + static int run_mm_bt_0010(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; + char h_industry[8]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_new_rate; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, applied = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_industry, MM_TARGET_INDUSTRY, sizeof(h_industry) - 1); + h_industry[sizeof(h_industry) - 1] = '\0'; + h_new_rate = MM_NEW_FEE_RATE; EXEC SQL DECLARE cur_mm_bt_0010 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT merch_id + FROM mm_merchant + WHERE status = 'M' + AND industry_cd = :h_industry + ORDER BY merch_id; EXEC SQL OPEN cur_mm_bt_0010; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +57,7 @@ static int run_mm_bt_0010(const char *biz_date) tx_log(TX_LOG_WARN, "[mm_bt_0010] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mm_bt_0010 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mm_bt_0010 INTO :h_merch_id; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +69,29 @@ static int run_mm_bt_0010(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 기존 유효 요율 종료 처리 */ + EXEC SQL UPDATE mm_fee_rate + SET status = 'C', + close_date = :h_biz_date + WHERE merch_id = :h_merch_id + AND status = 'A'; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("UPDATE mm_fee_rate close"); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); + /* 신규 요율 등록 */ + EXEC SQL INSERT INTO mm_fee_rate + (merch_id, fee_rate, status, start_date) + VALUES (:h_merch_id, :h_new_rate, 'A', :h_biz_date); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_fee_rate"); errcnt++; + continue; } + + applied++; } EXEC SQL CLOSE cur_mm_bt_0010; @@ -87,16 +102,17 @@ static int run_mm_bt_0010(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0010] 요약 date=%s 업종=%s 대상=%ld 변경=%ld 오류=%ld", + biz_date, h_industry, total, applied, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 수수료율 일괄변경 [mm_bt_0010] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 대상 업종 : %s\n", h_industry); + printf(" 신규 요율(bp): %ld\n", h_new_rate); + printf(" 대상 건수 : %ld\n", total); + printf(" 변경 완료 : %ld\n", applied); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,7 +133,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0011.c b/app/batch/mm_bt_0011.c index 18fc9d4..92e23c2 100644 --- a/app/batch/mm_bt_0011.c +++ b/app/batch/mm_bt_0011.c @@ -8,10 +8,12 @@ #line 1 "app/batch/mm_bt_0011.pgc" /* @tier hard @module mm @transform master-batch */ /* - * mm_bt_0011.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=hard] + * mm_bt_0011.pgc - 가맹점 상태 일괄전이 배치 (MM_BT_0011) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 접수(status='R') 상태의 가맹점을 심사결과(mm_review) 와 조인하여 커서로 + * 순회한다. 심사 통과(result='P') 이고 심사점수가 기준 이상이면 처리완료 + * ('M') 로, 부결이면 실패('U') 로 전이한다. 심사행이 없으면 보류하여 + * 접수 상태를 유지한다. 배치 main 엔트리 포함. * * 실행: mm_bt_0011 [db@host] */ @@ -93,9 +95,12 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0011.pgc" +#line 21 "app/batch/mm_bt_0011.pgc" +/* 심사 합격 최소 점수 */ +#define MM_PASS_SCORE 60 + static int run_mm_bt_0011(const char *biz_date) { /* exec sql begin declare section */ @@ -103,43 +108,37 @@ static int run_mm_bt_0011(const char *biz_date) - + -#line 24 "app/batch/mm_bt_0011.pgc" +#line 29 "app/batch/mm_bt_0011.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mm_bt_0011.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/mm_bt_0011.pgc" +#line 30 "app/batch/mm_bt_0011.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/mm_bt_0011.pgc" - long h_amount ; +#line 31 "app/batch/mm_bt_0011.pgc" + char h_result [ 2 ] ; -#line 28 "app/batch/mm_bt_0011.pgc" +#line 32 "app/batch/mm_bt_0011.pgc" char h_new_status [ 2 ] ; + +#line 33 "app/batch/mm_bt_0011.pgc" + int h_score ; /* exec sql end declare section */ -#line 29 "app/batch/mm_bt_0011.pgc" +#line 34 "app/batch/mm_bt_0011.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, approved = 0, rejected = 0, held = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0011 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mm_bt_0011.pgc" - -#line 42 "app/batch/mm_bt_0011.pgc" + /* declare cur_mm_bt_0011 cursor for select m . merch_id , r . result , r . score from mm_merchant m join mm_review r on r . merch_id = m . merch_id where m . status = 'R' order by m . merch_id */ +#line 46 "app/batch/mm_bt_0011.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0011 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mm_bt_0011.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0011 cursor for select m . merch_id , r . result , r . score from mm_merchant m join mm_review r on r . merch_id = m . merch_id where m . status = 'R' order by m . merch_id", ECPGt_EOIT, ECPGt_EORT);} +#line 48 "app/batch/mm_bt_0011.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mm_bt_0011"); @@ -151,13 +150,13 @@ static int run_mm_bt_0011(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0011", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_result),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_score),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mm_bt_0011.pgc" +#line 59 "app/batch/mm_bt_0011.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +169,51 @@ static int run_mm_bt_0011(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + /* 심사완료 규칙: 통과 + 기준점수 이상 → 처리완료, 부결 → 실패 */ + if (strcmp(h_result, "P") == 0 && h_score >= MM_PASS_SCORE) { strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { + } else if (strcmp(h_result, "F") == 0) { strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + } else { + /* 보류: 상태 유지 */ + held++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_merchant set status = $1 , status_date = $2 where merch_id = $3 ", + ECPGt_char,(h_new_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 85 "app/batch/mm_bt_0011.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + tx_log(TX_LOG_ERROR, + "[mm_bt_0011] 상태전이 실패 merch=%s", h_merch_id); errcnt++; + continue; } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_status_hist ( merch_id , old_status , new_status , reason , chg_date ) values ( $1 , 'R' , $2 , 'REVIEW' , $3 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_new_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 96 "app/batch/mm_bt_0011.pgc" + + + if (strcmp(h_new_status, MM_ST_DONE) == 0) + approved++; + else + rejected++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0011", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mm_bt_0011.pgc" +#line 104 "app/batch/mm_bt_0011.pgc" if (errcnt == 0) @@ -196,16 +222,17 @@ static int run_mm_bt_0011(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0011] 요약 date=%s 대상=%ld 승인=%ld 부결=%ld 보류=%ld 오류=%ld", + biz_date, total, approved, rejected, held, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 가맹점 상태 일괄전이 [mm_bt_0011] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 심사 대상 : %ld\n", total); + printf(" 승인(M) : %ld\n", approved); + printf(" 부결(U) : %ld\n", rejected); + printf(" 보류(R유지) : %ld\n", held); + printf(" 오류 건수 : %ld\n", errcnt); + printf("====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,7 +253,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0011.pgc b/app/batch/mm_bt_0011.pgc index 48a6ec3..d99eae4 100644 --- a/app/batch/mm_bt_0011.pgc +++ b/app/batch/mm_bt_0011.pgc @@ -1,9 +1,11 @@ /* @tier hard @module mm @transform master-batch */ /* - * mm_bt_0011.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=hard] + * mm_bt_0011.pgc - 가맹점 상태 일괄전이 배치 (MM_BT_0011) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 접수(status='R') 상태의 가맹점을 심사결과(mm_review) 와 조인하여 커서로 + * 순회한다. 심사 통과(result='P') 이고 심사점수가 기준 이상이면 처리완료 + * ('M') 로, 부결이면 실패('U') 로 전이한다. 심사행이 없으면 보류하여 + * 접수 상태를 유지한다. 배치 main 엔트리 포함. * * 실행: mm_bt_0011 [db@host] */ @@ -18,28 +20,30 @@ EXEC SQL INCLUDE sqlca; +/* 심사 합격 최소 점수 */ +#define MM_PASS_SCORE 60 + static int run_mm_bt_0011(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; char h_merch_id[16]; - long h_amount; + char h_result[2]; char h_new_status[2]; + int h_score; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, approved = 0, rejected = 0, held = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mm_bt_0011 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT m.merch_id, r.result, r.score + FROM mm_merchant m + JOIN mm_review r ON r.merch_id = m.merch_id + WHERE m.status = 'R' + ORDER BY m.merch_id; EXEC SQL OPEN cur_mm_bt_0011; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +55,8 @@ static int run_mm_bt_0011(const char *biz_date) tx_log(TX_LOG_WARN, "[mm_bt_0011] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mm_bt_0011 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mm_bt_0011 + INTO :h_merch_id, :h_result, :h_score; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +68,37 @@ static int run_mm_bt_0011(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + /* 심사완료 규칙: 통과 + 기준점수 이상 → 처리완료, 부결 → 실패 */ + if (strcmp(h_result, "P") == 0 && h_score >= MM_PASS_SCORE) { strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { + } else if (strcmp(h_result, "F") == 0) { strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + } else { + /* 보류: 상태 유지 */ + held++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL UPDATE mm_merchant + SET status = :h_new_status, + status_date = :h_biz_date + WHERE merch_id = :h_merch_id; + if (sqlca.sqlcode != TX_SQL_OK) { + tx_log(TX_LOG_ERROR, + "[mm_bt_0011] 상태전이 실패 merch=%s", h_merch_id); errcnt++; + continue; } + + EXEC SQL INSERT INTO mm_status_hist + (merch_id, old_status, new_status, reason, chg_date) + VALUES (:h_merch_id, 'R', :h_new_status, 'REVIEW', + :h_biz_date); + + if (strcmp(h_new_status, MM_ST_DONE) == 0) + approved++; + else + rejected++; } EXEC SQL CLOSE cur_mm_bt_0011; @@ -87,16 +109,17 @@ static int run_mm_bt_0011(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0011] 요약 date=%s 대상=%ld 승인=%ld 부결=%ld 보류=%ld 오류=%ld", + biz_date, total, approved, rejected, held, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 가맹점 상태 일괄전이 [mm_bt_0011] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 심사 대상 : %ld\n", total); + printf(" 승인(M) : %ld\n", approved); + printf(" 부결(U) : %ld\n", rejected); + printf(" 보류(R유지) : %ld\n", held); + printf(" 오류 건수 : %ld\n", errcnt); + printf("====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,7 +140,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0012.c b/app/batch/mm_bt_0012.c index f815f69..33a196f 100644 --- a/app/batch/mm_bt_0012.c +++ b/app/batch/mm_bt_0012.c @@ -8,10 +8,12 @@ #line 1 "app/batch/mm_bt_0012.pgc" /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0012.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0012.pgc - 가맹점 마스터 추출배치 (MM_BT_0012) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 가맹점 원장(mm_merchant) 을 커서로 순회하며 상호/상태/등록일을 표준출력 + * 으로 파일 덤프(추출) 한다. 다운스트림 연계용 추출파일 생성을 모사하며 + * 원장은 변경하지 않는 읽기전용 배치이다. 추출 건수를 집계한다. + * 배치 main 엔트리 포함. * * 실행: mm_bt_0012 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0012.pgc" +#line 21 "app/batch/mm_bt_0012.pgc" static int run_mm_bt_0012(const char *biz_date) @@ -105,38 +107,37 @@ static int run_mm_bt_0012(const char *biz_date) -#line 24 "app/batch/mm_bt_0012.pgc" +#line 26 "app/batch/mm_bt_0012.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mm_bt_0012.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/mm_bt_0012.pgc" +#line 27 "app/batch/mm_bt_0012.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/mm_bt_0012.pgc" - long h_amount ; - #line 28 "app/batch/mm_bt_0012.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + char h_merch_name [ 64 ] ; + #line 29 "app/batch/mm_bt_0012.pgc" + char h_status [ 2 ] ; + +#line 30 "app/batch/mm_bt_0012.pgc" + char h_reg_date [ 9 ] ; +/* exec sql end declare section */ +#line 31 "app/batch/mm_bt_0012.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0012 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_mm_bt_0012 cursor for select merch_id , merch_name , status , reg_date from mm_merchant where reg_date <= $1 order by merch_id */ #line 42 "app/batch/mm_bt_0012.pgc" #line 42 "app/batch/mm_bt_0012.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0012 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0012 cursor for select merch_id , merch_name , status , reg_date from mm_merchant where reg_date <= $1 order by merch_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/mm_bt_0012.pgc" @@ -146,18 +147,20 @@ static int run_mm_bt_0012(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mm_bt_0012] tx_begin 경고"); + /* 추출 헤더 (파이프 구분 레코드) */ + printf("#MERCH_ID|MERCH_NAME|STATUS|REG_DATE\n"); for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0012", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_merch_name),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_reg_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mm_bt_0012.pgc" +#line 55 "app/batch/mm_bt_0012.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,42 +173,24 @@ static int run_mm_bt_0012(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 표준출력 덤프 (추출파일 라인) */ + printf("%s|%s|%s|%s\n", + h_merch_id, h_merch_name, h_status, h_reg_date); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0012", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mm_bt_0012.pgc" +#line 72 "app/batch/mm_bt_0012.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[mm_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0012] 요약 date=%s 추출=%ld 오류=%ld", + biz_date, total, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("#========== 가맹점 마스터 추출 [mm_bt_0012] ==========\n"); + printf("# 기준일 : %s\n", biz_date); + printf("# 추출 건수 : %ld\n", total); + printf("# 오류 건수 : %ld\n", errcnt); + printf("#==================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,7 +211,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0012.pgc b/app/batch/mm_bt_0012.pgc index 552bc52..0df6464 100644 --- a/app/batch/mm_bt_0012.pgc +++ b/app/batch/mm_bt_0012.pgc @@ -1,9 +1,11 @@ /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0012.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0012.pgc - 가맹점 마스터 추출배치 (MM_BT_0012) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 가맹점 원장(mm_merchant) 을 커서로 순회하며 상호/상태/등록일을 표준출력 + * 으로 파일 덤프(추출) 한다. 다운스트림 연계용 추출파일 생성을 모사하며 + * 원장은 변경하지 않는 읽기전용 배치이다. 추출 건수를 집계한다. + * 배치 main 엔트리 포함. * * 실행: mm_bt_0012 [db@host] */ @@ -22,24 +24,22 @@ static int run_mm_bt_0012(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_merch_name[64]; + char h_status[2]; + char h_reg_date[9]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mm_bt_0012 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT merch_id, merch_name, status, reg_date + FROM mm_merchant + WHERE reg_date <= :h_biz_date + ORDER BY merch_id; EXEC SQL OPEN cur_mm_bt_0012; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,11 +47,12 @@ static int run_mm_bt_0012(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[mm_bt_0012] tx_begin 경고"); + /* 추출 헤더 (파이프 구분 레코드) */ + printf("#MERCH_ID|MERCH_NAME|STATUS|REG_DATE\n"); for (;;) { - EXEC SQL FETCH cur_mm_bt_0012 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mm_bt_0012 + INTO :h_merch_id, :h_merch_name, :h_status, :h_reg_date; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,40 +64,22 @@ static int run_mm_bt_0012(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 표준출력 덤프 (추출파일 라인) */ + printf("%s|%s|%s|%s\n", + h_merch_id, h_merch_name, h_status, h_reg_date); } EXEC SQL CLOSE cur_mm_bt_0012; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[mm_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0012] 요약 date=%s 추출=%ld 오류=%ld", + biz_date, total, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("#========== 가맹점 마스터 추출 [mm_bt_0012] ==========\n"); + printf("# 기준일 : %s\n", biz_date); + printf("# 추출 건수 : %ld\n", total); + printf("# 오류 건수 : %ld\n", errcnt); + printf("#==================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,7 +100,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0013.c b/app/batch/mm_bt_0013.c index 7c3f27e..bebe368 100644 --- a/app/batch/mm_bt_0013.c +++ b/app/batch/mm_bt_0013.c @@ -8,10 +8,12 @@ #line 1 "app/batch/mm_bt_0013.pgc" /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0013.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0013.pgc - 정산주기 재설정 배치 (MM_BT_0013) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 가맹점(mm_merchant) 을 커서로 순회하며 등급과 최근 거래량(txn_cnt) 기준 + * 으로 정산주기(settle_cycle) 를 재설정한다. 우량(A등급 또는 대량거래) 은 + * D+1, 그 외는 D+2 를 적용하며, 변경건은 원장 UPDATE 후 정산주기 변경 + * 이력(mm_cycle_hist) 을 남긴다. 배치 main 엔트리 포함. * * 실행: mm_bt_0013 [db@host] */ @@ -93,9 +95,12 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0013.pgc" +#line 21 "app/batch/mm_bt_0013.pgc" +/* 대량거래 기준 건수 */ +#define MM_BULK_TXN_MIN 1000L + static int run_mm_bt_0013(const char *biz_date) { /* exec sql begin declare section */ @@ -104,42 +109,40 @@ static int run_mm_bt_0013(const char *biz_date) + -#line 24 "app/batch/mm_bt_0013.pgc" +#line 29 "app/batch/mm_bt_0013.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mm_bt_0013.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/mm_bt_0013.pgc" +#line 30 "app/batch/mm_bt_0013.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/mm_bt_0013.pgc" - long h_amount ; +#line 31 "app/batch/mm_bt_0013.pgc" + char h_grade [ 2 ] ; -#line 28 "app/batch/mm_bt_0013.pgc" - char h_new_status [ 2 ] ; +#line 32 "app/batch/mm_bt_0013.pgc" + char h_old_cycle [ 4 ] ; + +#line 33 "app/batch/mm_bt_0013.pgc" + char h_new_cycle [ 4 ] ; + +#line 34 "app/batch/mm_bt_0013.pgc" + long h_txn_cnt ; /* exec sql end declare section */ -#line 29 "app/batch/mm_bt_0013.pgc" +#line 35 "app/batch/mm_bt_0013.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, changed = 0, kept = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0013 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mm_bt_0013.pgc" - -#line 42 "app/batch/mm_bt_0013.pgc" + /* declare cur_mm_bt_0013 cursor for select merch_id , grade , settle_cycle , txn_cnt from mm_merchant where status = 'M' order by merch_id */ +#line 46 "app/batch/mm_bt_0013.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0013 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mm_bt_0013.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0013 cursor for select merch_id , grade , settle_cycle , txn_cnt from mm_merchant where status = 'M' order by merch_id", ECPGt_EOIT, ECPGt_EORT);} +#line 48 "app/batch/mm_bt_0013.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mm_bt_0013"); @@ -151,13 +154,15 @@ static int run_mm_bt_0013(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0013", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_grade),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_old_cycle),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_txn_cnt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mm_bt_0013.pgc" +#line 59 "app/batch/mm_bt_0013.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +175,50 @@ static int run_mm_bt_0013(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 우량 가맹점은 D+1, 그 외 D+2 */ + if (strcmp(h_grade, "A") == 0 || h_txn_cnt >= MM_BULK_TXN_MIN) + strncpy(h_new_cycle, "D+1", sizeof(h_new_cycle)); + else + strncpy(h_new_cycle, "D+2", sizeof(h_new_cycle)); + + if (strcmp(h_new_cycle, h_old_cycle) == 0) { + kept++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_merchant set settle_cycle = $1 , cycle_date = $2 where merch_id = $3 ", + ECPGt_char,(h_new_cycle),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 85 "app/batch/mm_bt_0013.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + tx_log(TX_LOG_ERROR, + "[mm_bt_0013] 주기갱신 실패 merch=%s", h_merch_id); errcnt++; + continue; } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_cycle_hist ( merch_id , old_cycle , new_cycle , chg_date ) values ( $1 , $2 , $3 , $4 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_old_cycle),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_new_cycle),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 96 "app/batch/mm_bt_0013.pgc" + + + changed++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0013", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mm_bt_0013.pgc" +#line 101 "app/batch/mm_bt_0013.pgc" if (errcnt == 0) @@ -196,16 +227,16 @@ static int run_mm_bt_0013(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0013] 요약 date=%s 대상=%ld 변경=%ld 유지=%ld 오류=%ld", + biz_date, total, changed, kept, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 정산주기 재설정 [mm_bt_0013] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 주기 변경 : %ld\n", changed); + printf(" 주기 유지 : %ld\n", kept); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,7 +257,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0013.pgc b/app/batch/mm_bt_0013.pgc index 1f953d8..bd8c254 100644 --- a/app/batch/mm_bt_0013.pgc +++ b/app/batch/mm_bt_0013.pgc @@ -1,9 +1,11 @@ /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0013.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0013.pgc - 정산주기 재설정 배치 (MM_BT_0013) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 가맹점(mm_merchant) 을 커서로 순회하며 등급과 최근 거래량(txn_cnt) 기준 + * 으로 정산주기(settle_cycle) 를 재설정한다. 우량(A등급 또는 대량거래) 은 + * D+1, 그 외는 D+2 를 적용하며, 변경건은 원장 UPDATE 후 정산주기 변경 + * 이력(mm_cycle_hist) 을 남긴다. 배치 main 엔트리 포함. * * 실행: mm_bt_0013 [db@host] */ @@ -18,28 +20,30 @@ EXEC SQL INCLUDE sqlca; +/* 대량거래 기준 건수 */ +#define MM_BULK_TXN_MIN 1000L + static int run_mm_bt_0013(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_grade[2]; + char h_old_cycle[4]; + char h_new_cycle[4]; + long h_txn_cnt; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, changed = 0, kept = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mm_bt_0013 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT merch_id, grade, settle_cycle, txn_cnt + FROM mm_merchant + WHERE status = 'M' + ORDER BY merch_id; EXEC SQL OPEN cur_mm_bt_0013; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +55,8 @@ static int run_mm_bt_0013(const char *biz_date) tx_log(TX_LOG_WARN, "[mm_bt_0013] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mm_bt_0013 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mm_bt_0013 + INTO :h_merch_id, :h_grade, :h_old_cycle, :h_txn_cnt; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +68,34 @@ static int run_mm_bt_0013(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 우량 가맹점은 D+1, 그 외 D+2 */ + if (strcmp(h_grade, "A") == 0 || h_txn_cnt >= MM_BULK_TXN_MIN) + strncpy(h_new_cycle, "D+1", sizeof(h_new_cycle)); + else + strncpy(h_new_cycle, "D+2", sizeof(h_new_cycle)); + + if (strcmp(h_new_cycle, h_old_cycle) == 0) { + kept++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL UPDATE mm_merchant + SET settle_cycle = :h_new_cycle, + cycle_date = :h_biz_date + WHERE merch_id = :h_merch_id; + if (sqlca.sqlcode != TX_SQL_OK) { + tx_log(TX_LOG_ERROR, + "[mm_bt_0013] 주기갱신 실패 merch=%s", h_merch_id); errcnt++; + continue; } + + EXEC SQL INSERT INTO mm_cycle_hist + (merch_id, old_cycle, new_cycle, chg_date) + VALUES (:h_merch_id, :h_old_cycle, :h_new_cycle, + :h_biz_date); + + changed++; } EXEC SQL CLOSE cur_mm_bt_0013; @@ -87,16 +106,16 @@ static int run_mm_bt_0013(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0013] 요약 date=%s 대상=%ld 변경=%ld 유지=%ld 오류=%ld", + biz_date, total, changed, kept, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 정산주기 재설정 [mm_bt_0013] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 주기 변경 : %ld\n", changed); + printf(" 주기 유지 : %ld\n", kept); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,7 +136,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0014.c b/app/batch/mm_bt_0014.c index a76d4ea..3c07fb9 100644 --- a/app/batch/mm_bt_0014.c +++ b/app/batch/mm_bt_0014.c @@ -8,10 +8,12 @@ #line 1 "app/batch/mm_bt_0014.pgc" /* @tier medium @module mm @transform master-batch */ /* - * mm_bt_0014.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=medium] + * mm_bt_0014.pgc - 카드사 마스터 동기화 배치 (MM_BT_0014) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 외부 연계 스테이징(mm_stage_issuer) 을 커서로 순회하며 카드사 마스터 + * (mm_issuer) 의 카드사명/대표번호/상태를 동기화(UPDATE) 한다. 실제로 + * 값이 반영된 건(rows affected>0) 만 변경으로 집계하고, 마스터에 없는 + * 카드사코드는 미매칭으로 집계한다. 배치 main 엔트리 포함. * * 실행: mm_bt_0014 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0014.pgc" +#line 21 "app/batch/mm_bt_0014.pgc" static int run_mm_bt_0014(const char *biz_date) @@ -105,38 +107,37 @@ static int run_mm_bt_0014(const char *biz_date) -#line 24 "app/batch/mm_bt_0014.pgc" +#line 26 "app/batch/mm_bt_0014.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mm_bt_0014.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/mm_bt_0014.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/mm_bt_0014.pgc" - long h_amount ; + char h_issuer_cd [ 8 ] ; #line 28 "app/batch/mm_bt_0014.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + char h_issuer_nm [ 64 ] ; + #line 29 "app/batch/mm_bt_0014.pgc" + char h_tel [ 20 ] ; + +#line 30 "app/batch/mm_bt_0014.pgc" + char h_status [ 2 ] ; +/* exec sql end declare section */ +#line 31 "app/batch/mm_bt_0014.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, synced = 0, nomatch = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0014 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_mm_bt_0014 cursor for select issuer_cd , issuer_nm , tel , status from mm_stage_issuer where recv_date = $1 order by issuer_cd */ #line 42 "app/batch/mm_bt_0014.pgc" #line 42 "app/batch/mm_bt_0014.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0014 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0014 cursor for select issuer_cd , issuer_nm , tel , status from mm_stage_issuer where recv_date = $1 order by issuer_cd", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/mm_bt_0014.pgc" @@ -151,13 +152,15 @@ static int run_mm_bt_0014(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0014", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_issuer_cd),(long)8,(long)1,(8)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_issuer_nm),(long)64,(long)1,(64)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_tel),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mm_bt_0014.pgc" +#line 55 "app/batch/mm_bt_0014.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +173,34 @@ static int run_mm_bt_0014(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_issuer set issuer_nm = $1 , tel = $2 , status = $3 , sync_date = $4 where issuer_cd = $5 ", + ECPGt_char,(h_issuer_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_tel),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_issuer_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 72 "app/batch/mm_bt_0014.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("UPDATE mm_issuer"); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 반영 행수로 동기화/미매칭 판정 */ + if (sqlca.sqlerrd[2] > 0) + synced++; + else + nomatch++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0014", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mm_bt_0014.pgc" +#line 86 "app/batch/mm_bt_0014.pgc" if (errcnt == 0) @@ -196,16 +209,16 @@ static int run_mm_bt_0014(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0014] 요약 date=%s 대상=%ld 동기화=%ld 미매칭=%ld 오류=%ld", + biz_date, total, synced, nomatch, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 카드사 마스터 동기화 [mm_bt_0014] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 연계 건수 : %ld\n", total); + printf(" 동기화 반영 : %ld\n", synced); + printf(" 미매칭 건수 : %ld\n", nomatch); + printf(" 오류 건수 : %ld\n", errcnt); + printf("====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,7 +239,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0014.pgc b/app/batch/mm_bt_0014.pgc index 79e5d4e..a93b3e6 100644 --- a/app/batch/mm_bt_0014.pgc +++ b/app/batch/mm_bt_0014.pgc @@ -1,9 +1,11 @@ /* @tier medium @module mm @transform master-batch */ /* - * mm_bt_0014.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=medium] + * mm_bt_0014.pgc - 카드사 마스터 동기화 배치 (MM_BT_0014) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 외부 연계 스테이징(mm_stage_issuer) 을 커서로 순회하며 카드사 마스터 + * (mm_issuer) 의 카드사명/대표번호/상태를 동기화(UPDATE) 한다. 실제로 + * 값이 반영된 건(rows affected>0) 만 변경으로 집계하고, 마스터에 없는 + * 카드사코드는 미매칭으로 집계한다. 배치 main 엔트리 포함. * * 실행: mm_bt_0014 [db@host] */ @@ -22,24 +24,22 @@ static int run_mm_bt_0014(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_issuer_cd[8]; + char h_issuer_nm[64]; + char h_tel[20]; + char h_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, synced = 0, nomatch = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_mm_bt_0014 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT issuer_cd, issuer_nm, tel, status + FROM mm_stage_issuer + WHERE recv_date = :h_biz_date + ORDER BY issuer_cd; EXEC SQL OPEN cur_mm_bt_0014; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +51,8 @@ static int run_mm_bt_0014(const char *biz_date) tx_log(TX_LOG_WARN, "[mm_bt_0014] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mm_bt_0014 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mm_bt_0014 + INTO :h_issuer_cd, :h_issuer_nm, :h_tel, :h_status; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +64,23 @@ static int run_mm_bt_0014(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + EXEC SQL UPDATE mm_issuer + SET issuer_nm = :h_issuer_nm, + tel = :h_tel, + status = :h_status, + sync_date = :h_biz_date + WHERE issuer_cd = :h_issuer_cd; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("UPDATE mm_issuer"); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 반영 행수로 동기화/미매칭 판정 */ + if (sqlca.sqlerrd[2] > 0) + synced++; + else + nomatch++; } EXEC SQL CLOSE cur_mm_bt_0014; @@ -87,16 +91,16 @@ static int run_mm_bt_0014(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0014] 요약 date=%s 대상=%ld 동기화=%ld 미매칭=%ld 오류=%ld", + biz_date, total, synced, nomatch, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 카드사 마스터 동기화 [mm_bt_0014] ==========\n"); + printf(" 기준일 : %s\n", biz_date); + printf(" 연계 건수 : %ld\n", total); + printf(" 동기화 반영 : %ld\n", synced); + printf(" 미매칭 건수 : %ld\n", nomatch); + printf(" 오류 건수 : %ld\n", errcnt); + printf("====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,7 +121,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0015.c b/app/batch/mm_bt_0015.c index 3700175..af32927 100644 --- a/app/batch/mm_bt_0015.c +++ b/app/batch/mm_bt_0015.c @@ -8,10 +8,12 @@ #line 1 "app/batch/mm_bt_0015.pgc" /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0015.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0015.pgc - 마스터 변경이력 마감배치 (MM_BT_0015) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 기준일 이전에 발생한 미마감(arch_yn='N') 마스터 변경이력 + * (mm_merchant_hist) 을 커서로 순회하며 이력 아카이브(mm_hist_archive) 로 + * INSERT 한 뒤 원본을 마감표시(arch_yn='Y') 한다. 온라인 이력테이블 용량을 + * 정리하는 아카이빙 배치이다. 배치 main 엔트리 포함. * * 실행: mm_bt_0015 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0015.pgc" +#line 21 "app/batch/mm_bt_0015.pgc" static int run_mm_bt_0015(const char *biz_date) @@ -105,41 +107,40 @@ static int run_mm_bt_0015(const char *biz_date) -#line 24 "app/batch/mm_bt_0015.pgc" - char h_biz_date [ 9 ] ; - -#line 25 "app/batch/mm_bt_0015.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/mm_bt_0015.pgc" - char h_merch_id [ 16 ] ; + char h_cutoff [ 9 ] ; #line 27 "app/batch/mm_bt_0015.pgc" - long h_amount ; + char h_hist_id [ 20 ] ; #line 28 "app/batch/mm_bt_0015.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + char h_merch_id [ 16 ] ; + #line 29 "app/batch/mm_bt_0015.pgc" + char h_chg_field [ 24 ] ; + +#line 30 "app/batch/mm_bt_0015.pgc" + char h_chg_date [ 9 ] ; +/* exec sql end declare section */ +#line 31 "app/batch/mm_bt_0015.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, archived = 0, errcnt = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_cutoff, biz_date, sizeof(h_cutoff) - 1); + h_cutoff[sizeof(h_cutoff) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0015 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mm_bt_0015.pgc" + ECPGset_var( 0, ( h_cutoff ), __LINE__);\ + /* declare cur_mm_bt_0015 cursor for select hist_id , merch_id , chg_field , chg_date from mm_merchant_hist where arch_yn = 'N' and chg_date < $1 order by hist_id */ +#line 43 "app/batch/mm_bt_0015.pgc" -#line 42 "app/batch/mm_bt_0015.pgc" +#line 43 "app/batch/mm_bt_0015.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0015 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0015 cursor for select hist_id , merch_id , chg_field , chg_date from mm_merchant_hist where arch_yn = 'N' and chg_date < $1 order by hist_id", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mm_bt_0015.pgc" +#line 45 "app/batch/mm_bt_0015.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_mm_bt_0015"); @@ -151,13 +152,15 @@ static int run_mm_bt_0015(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0015", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_hist_id),(long)20,(long)1,(20)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_chg_field),(long)24,(long)1,(24)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/mm_bt_0015.pgc" +#line 56 "app/batch/mm_bt_0015.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +173,43 @@ static int run_mm_bt_0015(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 아카이브 적재 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_hist_archive ( hist_id , merch_id , chg_field , chg_date , arch_date ) values ( $1 , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_hist_id),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_field),(long)24,(long)1,(24)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 72 "app/batch/mm_bt_0015.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_hist_archive"); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); + /* 원본 마감표시 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_merchant_hist set arch_yn = 'Y' where hist_id = $1 ", + ECPGt_char,(h_hist_id),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 82 "app/batch/mm_bt_0015.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant_hist"); errcnt++; + continue; } + + archived++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0015", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mm_bt_0015.pgc" +#line 92 "app/batch/mm_bt_0015.pgc" if (errcnt == 0) @@ -196,16 +218,15 @@ static int run_mm_bt_0015(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0015] 요약 cutoff=%s 대상=%ld 아카이브=%ld 오류=%ld", + biz_date, total, archived, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 마스터 변경이력 마감배치 [mm_bt_0015] ==========\n"); + printf(" 마감기준일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 아카이브 : %ld\n", archived); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,11 +247,11 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[mm_bt_0015] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mm_bt_0015] 배치 시작 cutoff=%s", biz_date); rc = mm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mm_bt_0015.pgc b/app/batch/mm_bt_0015.pgc index 84db1a6..9fd154f 100644 --- a/app/batch/mm_bt_0015.pgc +++ b/app/batch/mm_bt_0015.pgc @@ -1,9 +1,11 @@ /* @tier easy @module mm @transform master-batch */ /* - * mm_bt_0015.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=easy] + * mm_bt_0015.pgc - 마스터 변경이력 마감배치 (MM_BT_0015) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 기준일 이전에 발생한 미마감(arch_yn='N') 마스터 변경이력 + * (mm_merchant_hist) 을 커서로 순회하며 이력 아카이브(mm_hist_archive) 로 + * INSERT 한 뒤 원본을 마감표시(arch_yn='Y') 한다. 온라인 이력테이블 용량을 + * 정리하는 아카이빙 배치이다. 배치 main 엔트리 포함. * * 실행: mm_bt_0015 [db@host] */ @@ -21,25 +23,24 @@ EXEC SQL INCLUDE sqlca; static int run_mm_bt_0015(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; + char h_cutoff[9]; + char h_hist_id[20]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_chg_field[24]; + char h_chg_date[9]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, archived = 0, errcnt = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_cutoff, biz_date, sizeof(h_cutoff) - 1); + h_cutoff[sizeof(h_cutoff) - 1] = '\0'; EXEC SQL DECLARE cur_mm_bt_0015 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT hist_id, merch_id, chg_field, chg_date + FROM mm_merchant_hist + WHERE arch_yn = 'N' + AND chg_date < :h_cutoff + ORDER BY hist_id; EXEC SQL OPEN cur_mm_bt_0015; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +52,8 @@ static int run_mm_bt_0015(const char *biz_date) tx_log(TX_LOG_WARN, "[mm_bt_0015] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_mm_bt_0015 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_mm_bt_0015 + INTO :h_hist_id, :h_merch_id, :h_chg_field, :h_chg_date; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,20 +65,28 @@ static int run_mm_bt_0015(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 아카이브 적재 */ + EXEC SQL INSERT INTO mm_hist_archive + (hist_id, merch_id, chg_field, chg_date, arch_date) + VALUES (:h_hist_id, :h_merch_id, :h_chg_field, + :h_chg_date, :h_cutoff); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_hist_archive"); + errcnt++; + continue; } - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); + /* 원본 마감표시 */ + EXEC SQL UPDATE mm_merchant_hist + SET arch_yn = 'Y' + WHERE hist_id = :h_hist_id; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant_hist"); errcnt++; + continue; } + + archived++; } EXEC SQL CLOSE cur_mm_bt_0015; @@ -87,16 +97,15 @@ static int run_mm_bt_0015(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0015] 요약 cutoff=%s 대상=%ld 아카이브=%ld 오류=%ld", + biz_date, total, archived, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 마스터 변경이력 마감배치 [mm_bt_0015] ==========\n"); + printf(" 마감기준일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 아카이브 : %ld\n", archived); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,11 +126,11 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[mm_bt_0015] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[mm_bt_0015] 배치 시작 cutoff=%s", biz_date); rc = mm_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/mm_bt_0016.c b/app/batch/mm_bt_0016.c index 1703690..5acbde8 100644 --- a/app/batch/mm_bt_0016.c +++ b/app/batch/mm_bt_0016.c @@ -8,10 +8,12 @@ #line 1 "app/batch/mm_bt_0016.pgc" /* @tier medium @module mm @transform master-batch */ /* - * mm_bt_0016.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=medium] + * mm_bt_0016.pgc - 마스터 통계집계 배치 (MM_BT_0016) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 가맹점 원장(mm_merchant) 을 대상으로 상태별/등급별 가맹점 수를 집계 + * SELECT(SELECT ... INTO) 로 산출하여 일별 통계(mm_stat_daily) 에 한 행을 + * INSERT 한다. 커서 순회 대신 집계쿼리를 사용하는 통계 배치이다. + * 배치 main 엔트리 포함. * * 실행: mm_bt_0016 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/mm_bt_0016.pgc" +#line 21 "app/batch/mm_bt_0016.pgc" static int run_mm_bt_0016(const char *biz_date) @@ -104,108 +106,124 @@ static int run_mm_bt_0016(const char *biz_date) + + + -#line 24 "app/batch/mm_bt_0016.pgc" +#line 26 "app/batch/mm_bt_0016.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/mm_bt_0016.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/mm_bt_0016.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/mm_bt_0016.pgc" - long h_amount ; + long h_total ; #line 28 "app/batch/mm_bt_0016.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_cnt_active ; + #line 29 "app/batch/mm_bt_0016.pgc" + long h_cnt_dormant ; + +#line 30 "app/batch/mm_bt_0016.pgc" + long h_cnt_recv ; + +#line 31 "app/batch/mm_bt_0016.pgc" + long h_cnt_a ; + +#line 32 "app/batch/mm_bt_0016.pgc" + long h_cnt_b ; + +#line 33 "app/batch/mm_bt_0016.pgc" + long h_cnt_c ; +/* exec sql end declare section */ +#line 34 "app/batch/mm_bt_0016.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_mm_bt_0016 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/mm_bt_0016.pgc" - -#line 42 "app/batch/mm_bt_0016.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mm_bt_0016 cursor for select key , merch_id , amount from mm_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/mm_bt_0016.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_mm_bt_0016"); - return TX_EDB; - } + h_total = h_cnt_active = h_cnt_dormant = h_cnt_recv = 0; + h_cnt_a = h_cnt_b = h_cnt_c = 0; if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[mm_bt_0016] tx_begin 경고"); - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mm_bt_0016", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + /* 상태별 집계 (조건부 합계) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , SUM ( case when status = 'M' then 1 else 0 end ) , SUM ( case when status = 'U' then 1 else 0 end ) , SUM ( case when status = 'R' then 1 else 0 end ) from mm_merchant", ECPGt_EOIT, + ECPGt_long,&(h_total),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_cnt_active),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_cnt_dormant),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt_recv),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 54 "app/batch/mm_bt_0016.pgc" + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT status agg"); + errcnt++; + } + + /* 등급별 집계 (유효 가맹점 한정) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select SUM ( case when grade = 'A' then 1 else 0 end ) , SUM ( case when grade = 'B' then 1 else 0 end ) , SUM ( case when grade = 'C' then 1 else 0 end ) from mm_merchant where status = 'M'", ECPGt_EOIT, + ECPGt_long,&(h_cnt_a),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt_b),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt_c),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 67 "app/batch/mm_bt_0016.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT grade agg"); + errcnt++; + } + + if (errcnt == 0) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_stat_daily ( stat_date , total_cnt , active_cnt , dormant_cnt , recv_cnt , grade_a_cnt , grade_b_cnt , grade_c_cnt ) values ( $1 , $2 , $3 , $4 , $5 , $6 , $7 , $8 )", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_total),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt_active),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt_dormant),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt_recv),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt_a),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt_b),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt_c),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 79 "app/batch/mm_bt_0016.pgc" - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_mm_bt_0016"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); + TX_DBIO_LOG_ERR("INSERT mm_stat_daily"); errcnt++; } } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mm_bt_0016", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/mm_bt_0016.pgc" - - if (errcnt == 0) tx_commit(); else tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0016] 요약 date=%s 전체=%ld 유효=%ld 휴면=%ld 접수=%ld 오류=%ld", + biz_date, h_total, h_cnt_active, h_cnt_dormant, h_cnt_recv, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 마스터 통계집계 배치 [mm_bt_0016] ==========\n"); + printf(" 집계일 : %s\n", biz_date); + printf(" 전체 가맹점 : %ld\n", h_total); + printf(" 유효(M) : %ld\n", h_cnt_active); + printf(" 휴면(U) : %ld\n", h_cnt_dormant); + printf(" 접수(R) : %ld\n", h_cnt_recv); + printf(" 등급 A/B/C : %ld / %ld / %ld\n", h_cnt_a, h_cnt_b, h_cnt_c); + printf(" 오류 건수 : %ld\n", errcnt); + printf("====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -226,7 +244,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/mm_bt_0016.pgc b/app/batch/mm_bt_0016.pgc index 7c51447..60d4a46 100644 --- a/app/batch/mm_bt_0016.pgc +++ b/app/batch/mm_bt_0016.pgc @@ -1,9 +1,11 @@ /* @tier medium @module mm @transform master-batch */ /* - * mm_bt_0016.pgc - 마스터 배치 (커서 순회 + 상태전이) [tier=medium] + * mm_bt_0016.pgc - 마스터 통계집계 배치 (MM_BT_0016) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 가맹점 원장(mm_merchant) 을 대상으로 상태별/등급별 가맹점 수를 집계 + * SELECT(SELECT ... INTO) 로 산출하여 일별 통계(mm_stat_daily) 에 한 행을 + * INSERT 한다. 커서 순회 대신 집계쿼리를 사용하는 통계 배치이다. + * 배치 main 엔트리 포함. * * 실행: mm_bt_0016 [db@host] */ @@ -22,81 +24,83 @@ static int run_mm_bt_0016(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_total; + long h_cnt_active; + long h_cnt_dormant; + long h_cnt_recv; + long h_cnt_a; + long h_cnt_b; + long h_cnt_c; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - EXEC SQL DECLARE cur_mm_bt_0016 CURSOR FOR - SELECT key, merch_id, amount - FROM mm_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_mm_bt_0016; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_mm_bt_0016"); - return TX_EDB; - } + h_total = h_cnt_active = h_cnt_dormant = h_cnt_recv = 0; + h_cnt_a = h_cnt_b = h_cnt_c = 0; if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[mm_bt_0016] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_mm_bt_0016 INTO :h_key, :h_merch_id, :h_amount; + /* 상태별 집계 (조건부 합계) */ + EXEC SQL SELECT + COUNT(*), + SUM(CASE WHEN status = 'M' THEN 1 ELSE 0 END), + SUM(CASE WHEN status = 'U' THEN 1 ELSE 0 END), + SUM(CASE WHEN status = 'R' THEN 1 ELSE 0 END) + INTO :h_total, :h_cnt_active, :h_cnt_dormant, :h_cnt_recv + FROM mm_merchant; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT status agg"); + errcnt++; + } - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; + /* 등급별 집계 (유효 가맹점 한정) */ + EXEC SQL SELECT + SUM(CASE WHEN grade = 'A' THEN 1 ELSE 0 END), + SUM(CASE WHEN grade = 'B' THEN 1 ELSE 0 END), + SUM(CASE WHEN grade = 'C' THEN 1 ELSE 0 END) + INTO :h_cnt_a, :h_cnt_b, :h_cnt_c + FROM mm_merchant + WHERE status = 'M'; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT grade agg"); + errcnt++; + } + + if (errcnt == 0) { + EXEC SQL INSERT INTO mm_stat_daily + (stat_date, total_cnt, active_cnt, dormant_cnt, + recv_cnt, grade_a_cnt, grade_b_cnt, grade_c_cnt) + VALUES (:h_biz_date, :h_total, :h_cnt_active, + :h_cnt_dormant, :h_cnt_recv, + :h_cnt_a, :h_cnt_b, :h_cnt_c); if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_mm_bt_0016"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, MM_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = mm_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[mm_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); + TX_DBIO_LOG_ERR("INSERT mm_stat_daily"); errcnt++; } } - EXEC SQL CLOSE cur_mm_bt_0016; - if (errcnt == 0) tx_commit(); else tx_abort(); tx_log(TX_LOG_INFO, - "[mm_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[mm_bt_0016] 요약 date=%s 전체=%ld 유효=%ld 휴면=%ld 접수=%ld 오류=%ld", + biz_date, h_total, h_cnt_active, h_cnt_dormant, h_cnt_recv, errcnt); - printf("========== 마스터 배치 결과 [mm_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 마스터 통계집계 배치 [mm_bt_0016] ==========\n"); + printf(" 집계일 : %s\n", biz_date); + printf(" 전체 가맹점 : %ld\n", h_total); + printf(" 유효(M) : %ld\n", h_cnt_active); + printf(" 휴면(U) : %ld\n", h_cnt_dormant); + printf(" 접수(R) : %ld\n", h_cnt_recv); + printf(" 등급 A/B/C : %ld / %ld / %ld\n", h_cnt_a, h_cnt_b, h_cnt_c); + printf(" 오류 건수 : %ld\n", errcnt); + printf("====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -117,7 +121,7 @@ int main(int argc, char **argv) target = (argc >= 3) ? argv[2] : NULL; if (!date_is_valid(biz_date)) { - fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); + fprintf(stderr, "오류: 유효하지 않은 기준일 '%s'\n", biz_date); return 2; } diff --git a/app/batch/py_bt_0002.c b/app/batch/py_bt_0002.c index 77c0117..607b511 100644 --- a/app/batch/py_bt_0002.c +++ b/app/batch/py_bt_0002.c @@ -8,12 +8,9 @@ #line 1 "app/batch/py_bt_0002.pgc" /* @tier hard @module py @transform payment-batch */ /* - * py_bt_0002.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=hard] + * py_bt_0002.pgc - 지급 전송파일 생성 배치 (확정건 고정길이 전문 write) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0002 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0002 [db@host] */ #include #include @@ -93,121 +90,102 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0002.pgc" +#line 16 "app/batch/py_bt_0002.pgc" static int run_py_bt_0002(const char *biz_date) { /* exec sql begin declare section */ - - - - - + + -#line 24 "app/batch/py_bt_0002.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/py_bt_0002.pgc" + char h_biz_date [ 9 ] , h_key [ 16 ] , h_merch [ 16 ] , h_acct [ 20 ] ; -#line 25 "app/batch/py_bt_0002.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/py_bt_0002.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0002.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0002.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/py_bt_0002.pgc" + long h_amount , h_fee ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0002.pgc" +#line 23 "app/batch/py_bt_0002.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + FILE *fp; + char path[80], raw[512], numbuf[16]; + acq_msg_t msg; + long total = 0, written = 0, netsum = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; + snprintf(path, sizeof(path), "py_pay_%s.dat", biz_date); + fp = fopen(path, "wb"); + if (!fp) { + tx_log(TX_LOG_ERROR, "[py_bt_0002] 전송파일 생성 실패 %s", path); + return TX_FAIL; + } ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0002 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0002.pgc" + /* declare cur_payfile cursor for select key , merch_id , card_no , amount , fee from py_ledger where biz_date = $1 and status = 'M' order by key */ +#line 41 "app/batch/py_bt_0002.pgc" -#line 42 "app/batch/py_bt_0002.pgc" +#line 41 "app/batch/py_bt_0002.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0002 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_payfile cursor for select key , merch_id , card_no , amount , fee from py_ledger where biz_date = $1 and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0002.pgc" +#line 42 "app/batch/py_bt_0002.pgc" if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0002"); + TX_DBIO_LOG_ERR("OPEN cur_payfile"); + fclose(fp); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0002] tx_begin 경고"); - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0002", ECPGt_EOIT, + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_payfile", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_acct),(long)20,(long)1,(20)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0002.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0002"); - errcnt++; - break; - } +#line 50 "app/batch/py_bt_0002.pgc" + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_payfile"); break; } total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* 고정길이 지급전문(電文) 1레코드 구성 */ + memset(&msg, ' ', sizeof(msg)); + msg_field_set(msg.msg_type, MSG_TYPE_RECON, FLD_MSG_TYPE_LEN); + msg_field_set(msg.trans_code, "PAYOUT", FLD_TRANS_CODE_LEN); + msg_field_set(msg.merch_id, h_merch, FLD_MERCH_ID_LEN); + msg_field_set(msg.card_no, h_acct, FLD_CARD_NO_LEN); + msg_field_set(msg.approval_no, h_key, FLD_APPROVAL_LEN); + snprintf(numbuf, sizeof(numbuf), "%011ld", h_amount - h_fee); + msg_field_set_num(msg.amount, numbuf, FLD_AMOUNT_LEN); + msg_field_set(msg.txn_date, h_biz_date, FLD_TXN_DATE_LEN); + msg_field_set(msg.resp_code, RESP_OK, FLD_RESP_LEN); - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (msg_pack(&msg, raw, sizeof(raw)) > 0 && + fwrite(raw, 1, (size_t)MSG_ACQ_LEN, fp) == (size_t)MSG_ACQ_LEN) { + written++; + netsum += h_amount - h_fee; } } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_payfile", ECPGt_EOIT, ECPGt_EORT);} +#line 73 "app/batch/py_bt_0002.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0002", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0002.pgc" + fclose(fp); - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 지급 전송파일 생성 [py_bt_0002] =====\n"); + printf(" 파일 : %s (레코드길이 %d)\n", path, MSG_ACQ_LEN); + printf(" 대상건수 : %ld\n", total); + printf(" 기록건수 : %ld\n", written); + printf(" 순지급액 : %ld\n", netsum); + tx_log(TX_LOG_INFO, "[py_bt_0002] 전송파일 %s 기록=%ld net=%ld", path, written, netsum); + return (written == total) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -217,31 +195,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0002] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0002] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0002(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0002] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0002.pgc b/app/batch/py_bt_0002.pgc index b908524..8c6fb68 100644 --- a/app/batch/py_bt_0002.pgc +++ b/app/batch/py_bt_0002.pgc @@ -1,11 +1,8 @@ /* @tier hard @module py @transform payment-batch */ /* - * py_bt_0002.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=hard] + * py_bt_0002.pgc - 지급 전송파일 생성 배치 (확정건 고정길이 전문 write) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0002 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0002 [db@host] */ #include #include @@ -21,84 +18,68 @@ EXEC SQL INCLUDE sqlca; static int run_py_bt_0002(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_key[16], h_merch[16], h_acct[20]; + long h_amount, h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + FILE *fp; + char path[80], raw[512], numbuf[16]; + acq_msg_t msg; + long total = 0, written = 0, netsum = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; + snprintf(path, sizeof(path), "py_pay_%s.dat", biz_date); + fp = fopen(path, "wb"); + if (!fp) { + tx_log(TX_LOG_ERROR, "[py_bt_0002] 전송파일 생성 실패 %s", path); + return TX_FAIL; + } - EXEC SQL DECLARE cur_py_bt_0002 CURSOR FOR - SELECT key, merch_id, amount - FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' + EXEC SQL DECLARE cur_payfile CURSOR FOR + SELECT key, merch_id, card_no, amount, fee FROM py_ledger + WHERE biz_date = :h_biz_date AND status = 'M' ORDER BY key; - - EXEC SQL OPEN cur_py_bt_0002; + EXEC SQL OPEN cur_payfile; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0002"); + TX_DBIO_LOG_ERR("OPEN cur_payfile"); + fclose(fp); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0002] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_py_bt_0002 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0002"); - errcnt++; - break; - } - + EXEC SQL FETCH cur_payfile INTO :h_key, :h_merch, :h_acct, :h_amount, :h_fee; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("FETCH cur_payfile"); break; } total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* 고정길이 지급전문(電文) 1레코드 구성 */ + memset(&msg, ' ', sizeof(msg)); + msg_field_set(msg.msg_type, MSG_TYPE_RECON, FLD_MSG_TYPE_LEN); + msg_field_set(msg.trans_code, "PAYOUT", FLD_TRANS_CODE_LEN); + msg_field_set(msg.merch_id, h_merch, FLD_MERCH_ID_LEN); + msg_field_set(msg.card_no, h_acct, FLD_CARD_NO_LEN); + msg_field_set(msg.approval_no, h_key, FLD_APPROVAL_LEN); + snprintf(numbuf, sizeof(numbuf), "%011ld", h_amount - h_fee); + msg_field_set_num(msg.amount, numbuf, FLD_AMOUNT_LEN); + msg_field_set(msg.txn_date, h_biz_date, FLD_TXN_DATE_LEN); + msg_field_set(msg.resp_code, RESP_OK, FLD_RESP_LEN); - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (msg_pack(&msg, raw, sizeof(raw)) > 0 && + fwrite(raw, 1, (size_t)MSG_ACQ_LEN, fp) == (size_t)MSG_ACQ_LEN) { + written++; + netsum += h_amount - h_fee; } } + EXEC SQL CLOSE cur_payfile; + fclose(fp); - EXEC SQL CLOSE cur_py_bt_0002; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 지급 전송파일 생성 [py_bt_0002] =====\n"); + printf(" 파일 : %s (레코드길이 %d)\n", path, MSG_ACQ_LEN); + printf(" 대상건수 : %ld\n", total); + printf(" 기록건수 : %ld\n", written); + printf(" 순지급액 : %ld\n", netsum); + tx_log(TX_LOG_INFO, "[py_bt_0002] 전송파일 %s 기록=%ld net=%ld", path, written, netsum); + return (written == total) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -108,31 +89,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0002] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0002] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0002(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0002] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0003.c b/app/batch/py_bt_0003.c index 4f7826a..9fc5a04 100644 --- a/app/batch/py_bt_0003.c +++ b/app/batch/py_bt_0003.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/py_bt_0003.pgc" -/* @tier easy @module py @transform payment-batch */ +/* @tier hard @module py @transform payment-batch */ /* - * py_bt_0003.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0003.pgc - 지급결과 수신반영 배치 (고정길이 결과파일 read) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0003 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0003 [db@host] */ #include #include @@ -93,120 +90,69 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0003.pgc" +#line 16 "app/batch/py_bt_0003.pgc" static int run_py_bt_0003(const char *biz_date) { /* exec sql begin declare section */ - - - - -#line 24 "app/batch/py_bt_0003.pgc" - char h_biz_date [ 9 ] ; - -#line 25 "app/batch/py_bt_0003.pgc" +#line 21 "app/batch/py_bt_0003.pgc" char h_key [ 16 ] ; - -#line 26 "app/batch/py_bt_0003.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0003.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0003.pgc" - char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0003.pgc" +#line 22 "app/batch/py_bt_0003.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + FILE *fp; + char path[80], raw[512], keybuf[16], respbuf[8]; + acq_msg_t msg; + long total = 0, applied = 0, failed = 0, errcnt = 0; + int i; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0003 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0003.pgc" - -#line 42 "app/batch/py_bt_0003.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0003 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0003.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0003"); - return TX_EDB; + snprintf(path, sizeof(path), "py_result_%s.dat", biz_date); + fp = fopen(path, "rb"); + if (!fp) { + tx_log(TX_LOG_WARN, "[py_bt_0003] 결과파일 없음 %s - 반영대상 0", path); + printf("===== 지급결과 수신반영 [py_bt_0003] 결과파일 없음 =====\n"); + return TX_OK; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0003] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0003", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0003.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0003"); - errcnt++; - break; - } - + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0003] tx_begin 경고"); + while (fread(raw, 1, (size_t)MSG_ACQ_LEN, fp) == (size_t)MSG_ACQ_LEN) { total++; + if (msg_unpack(&msg, raw, MSG_ACQ_LEN) != 0) { errcnt++; continue; } + msg_field_copy(keybuf, msg.approval_no, FLD_APPROVAL_LEN); + keybuf[FLD_APPROVAL_LEN] = '\0'; + msg_field_copy(respbuf, msg.resp_code, FLD_RESP_LEN); + respbuf[FLD_RESP_LEN] = '\0'; + for (i = (int)strlen(keybuf) - 1; i >= 0 && keybuf[i] == ' '; i--) + keybuf[i] = '\0'; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; + strncpy(h_key, keybuf, 15); h_key[15] = '\0'; + if (strncmp(respbuf, RESP_OK, 4) == 0) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'S' , upd_ts = now ( ) where key = $1 and status = 'M'", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 52 "app/batch/py_bt_0003.pgc" + + applied++; } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'U' , upd_ts = now ( ) where key = $1 and status = 'M'", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 56 "app/batch/py_bt_0003.pgc" - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + failed++; } + if (sqlca.sqlcode != TX_SQL_OK) errcnt++; } + fclose(fp); + if (errcnt == 0) tx_commit(); else tx_abort(); - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0003", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0003.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - + printf("===== 지급결과 수신반영 [py_bt_0003] =====\n"); + printf(" 수신=%ld 성공반영=%ld 실패=%ld 오류=%ld\n", total, applied, failed, errcnt); + tx_log(TX_LOG_INFO, "[py_bt_0003] 결과반영 date=%s applied=%ld", biz_date, applied); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -217,31 +163,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0003] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0003] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0003(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0003] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0003.pgc b/app/batch/py_bt_0003.pgc index a7dd5ac..a7261f5 100644 --- a/app/batch/py_bt_0003.pgc +++ b/app/batch/py_bt_0003.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module py @transform payment-batch */ +/* @tier hard @module py @transform payment-batch */ /* - * py_bt_0003.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0003.pgc - 지급결과 수신반영 배치 (고정길이 결과파일 read) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0003 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0003 [db@host] */ #include #include @@ -21,83 +18,52 @@ EXEC SQL INCLUDE sqlca; static int run_py_bt_0003(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + FILE *fp; + char path[80], raw[512], keybuf[16], respbuf[8]; + acq_msg_t msg; + long total = 0, applied = 0, failed = 0, errcnt = 0; + int i; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - EXEC SQL DECLARE cur_py_bt_0003 CURSOR FOR - SELECT key, merch_id, amount - FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_py_bt_0003; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0003"); - return TX_EDB; + snprintf(path, sizeof(path), "py_result_%s.dat", biz_date); + fp = fopen(path, "rb"); + if (!fp) { + tx_log(TX_LOG_WARN, "[py_bt_0003] 결과파일 없음 %s - 반영대상 0", path); + printf("===== 지급결과 수신반영 [py_bt_0003] 결과파일 없음 =====\n"); + return TX_OK; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0003] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_py_bt_0003 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0003"); - errcnt++; - break; - } - + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0003] tx_begin 경고"); + while (fread(raw, 1, (size_t)MSG_ACQ_LEN, fp) == (size_t)MSG_ACQ_LEN) { total++; + if (msg_unpack(&msg, raw, MSG_ACQ_LEN) != 0) { errcnt++; continue; } + msg_field_copy(keybuf, msg.approval_no, FLD_APPROVAL_LEN); + keybuf[FLD_APPROVAL_LEN] = '\0'; + msg_field_copy(respbuf, msg.resp_code, FLD_RESP_LEN); + respbuf[FLD_RESP_LEN] = '\0'; + for (i = (int)strlen(keybuf) - 1; i >= 0 && keybuf[i] == ' '; i--) + keybuf[i] = '\0'; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; + strncpy(h_key, keybuf, 15); h_key[15] = '\0'; + if (strncmp(respbuf, RESP_OK, 4) == 0) { + EXEC SQL UPDATE py_ledger SET status = 'S', upd_ts = now() + WHERE key = :h_key AND status = 'M'; + applied++; } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + EXEC SQL UPDATE py_ledger SET status = 'U', upd_ts = now() + WHERE key = :h_key AND status = 'M'; + failed++; } + if (sqlca.sqlcode != TX_SQL_OK) errcnt++; } + fclose(fp); + if (errcnt == 0) tx_commit(); else tx_abort(); - EXEC SQL CLOSE cur_py_bt_0003; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - + printf("===== 지급결과 수신반영 [py_bt_0003] =====\n"); + printf(" 수신=%ld 성공반영=%ld 실패=%ld 오류=%ld\n", total, applied, failed, errcnt); + tx_log(TX_LOG_INFO, "[py_bt_0003] 결과반영 date=%s applied=%ld", biz_date, applied); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -108,31 +74,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0003] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0003] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0003(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0003] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0004.c b/app/batch/py_bt_0004.c index 820d36c..fc60767 100644 --- a/app/batch/py_bt_0004.c +++ b/app/batch/py_bt_0004.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/py_bt_0004.pgc" -/* @tier hard @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0004.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=hard] + * py_bt_0004.pgc - 지급 배치마감 배치 (당일 지급 확정/마감) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0004 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0004 [db@host] */ #include #include @@ -93,121 +90,55 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0004.pgc" +#line 16 "app/batch/py_bt_0004.pgc" static int run_py_bt_0004(const char *biz_date) { /* exec sql begin declare section */ - - - + -#line 24 "app/batch/py_bt_0004.pgc" +#line 21 "app/batch/py_bt_0004.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/py_bt_0004.pgc" - char h_key [ 16 ] ; +#line 22 "app/batch/py_bt_0004.pgc" + int h_wait , h_confirm , h_done ; -#line 26 "app/batch/py_bt_0004.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0004.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0004.pgc" - char h_new_status [ 2 ] ; +#line 23 "app/batch/py_bt_0004.pgc" + long h_net ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0004.pgc" +#line 24 "app/batch/py_bt_0004.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0004 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0004.pgc" - -#line 42 "app/batch/py_bt_0004.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0004 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) filter ( where status = 'R' ) , count ( * ) filter ( where status = 'M' ) , count ( * ) filter ( where status = 'S' ) , coalesce ( sum ( amount - fee ) filter ( where status = 'S' ) , 0 ) from py_ledger where biz_date = $1 ", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0004.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0004"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0004] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0004", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_wait),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_int,&(h_confirm),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_int,&(h_done),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_net),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0004.pgc" +#line 34 "app/batch/py_bt_0004.pgc" + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_bt_0004 마감집계"); return TX_EDB; } - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0004"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0004", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0004.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 지급 배치마감 [py_bt_0004] date=%s =====\n", biz_date); + printf(" 대기(R) : %d\n", h_wait); + printf(" 확정(M) : %d\n", h_confirm); + printf(" 완료(S) : %d\n", h_done); + printf(" 순지급액: %ld\n", h_net); + printf(" 마감결과: %s\n", (h_wait == 0 && h_confirm == 0) ? "정상마감" : "미완료 잔존"); + if (h_wait || h_confirm) + tx_log(TX_LOG_WARN, "[py_bt_0004] 미완료 대기=%d 확정=%d", h_wait, h_confirm); + tx_log(TX_LOG_INFO, "[py_bt_0004] 마감 date=%s net=%ld", biz_date, h_net); + return (h_wait == 0 && h_confirm == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -217,31 +148,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0004] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0004] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0004(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0004] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0004.pgc b/app/batch/py_bt_0004.pgc index f43c42c..81ee9b7 100644 --- a/app/batch/py_bt_0004.pgc +++ b/app/batch/py_bt_0004.pgc @@ -1,11 +1,8 @@ -/* @tier hard @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0004.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=hard] + * py_bt_0004.pgc - 지급 배치마감 배치 (당일 지급 확정/마감) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0004 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0004 [db@host] */ #include #include @@ -22,83 +19,31 @@ static int run_py_bt_0004(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + int h_wait, h_confirm, h_done; + long h_net; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + EXEC SQL SELECT + count(*) FILTER (WHERE status = 'R'), + count(*) FILTER (WHERE status = 'M'), + count(*) FILTER (WHERE status = 'S'), + coalesce(sum(amount - fee) FILTER (WHERE status = 'S'), 0) + INTO :h_wait, :h_confirm, :h_done, :h_net + FROM py_ledger WHERE biz_date = :h_biz_date; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_bt_0004 마감집계"); return TX_EDB; } - EXEC SQL DECLARE cur_py_bt_0004 CURSOR FOR - SELECT key, merch_id, amount - FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_py_bt_0004; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0004"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0004] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_py_bt_0004 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0004"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_py_bt_0004; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 지급 배치마감 [py_bt_0004] date=%s =====\n", biz_date); + printf(" 대기(R) : %d\n", h_wait); + printf(" 확정(M) : %d\n", h_confirm); + printf(" 완료(S) : %d\n", h_done); + printf(" 순지급액: %ld\n", h_net); + printf(" 마감결과: %s\n", (h_wait == 0 && h_confirm == 0) ? "정상마감" : "미완료 잔존"); + if (h_wait || h_confirm) + tx_log(TX_LOG_WARN, "[py_bt_0004] 미완료 대기=%d 확정=%d", h_wait, h_confirm); + tx_log(TX_LOG_INFO, "[py_bt_0004] 마감 date=%s net=%ld", biz_date, h_net); + return (h_wait == 0 && h_confirm == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) @@ -108,31 +53,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0004] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0004] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0004(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0004] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0005.c b/app/batch/py_bt_0005.c index c6a38f9..10c3138 100644 --- a/app/batch/py_bt_0005.c +++ b/app/batch/py_bt_0005.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/py_bt_0005.pgc" -/* @tier easy @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0005.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0005.pgc - 가맹점 지급집계 배치 (정산 지급명세 리포트) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0005 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0005 [db@host] */ #include #include @@ -93,121 +90,71 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0005.pgc" +#line 16 "app/batch/py_bt_0005.pgc" static int run_py_bt_0005(const char *biz_date) { /* exec sql begin declare section */ - - - - - + + + -#line 24 "app/batch/py_bt_0005.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/py_bt_0005.pgc" + char h_biz_date [ 9 ] , h_merch [ 16 ] ; -#line 25 "app/batch/py_bt_0005.pgc" - char h_key [ 16 ] ; +#line 22 "app/batch/py_bt_0005.pgc" + int h_cnt ; -#line 26 "app/batch/py_bt_0005.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0005.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0005.pgc" - char h_new_status [ 2 ] ; +#line 23 "app/batch/py_bt_0005.pgc" + long h_gross , h_fee ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0005.pgc" +#line 24 "app/batch/py_bt_0005.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merchants = 0, grandnet = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0005 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0005.pgc" + /* declare cur_pysum cursor for select merch_id , count ( * ) , sum ( amount ) , sum ( fee ) from py_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by merch_id order by sum ( amount ) desc */ +#line 34 "app/batch/py_bt_0005.pgc" -#line 42 "app/batch/py_bt_0005.pgc" +#line 34 "app/batch/py_bt_0005.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0005 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_pysum cursor for select merch_id , count ( * ) , sum ( amount ) , sum ( fee ) from py_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by merch_id order by sum ( amount ) desc", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0005.pgc" +#line 35 "app/batch/py_bt_0005.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0005"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0005] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pysum"); return TX_EDB; } + printf("===== 가맹점 지급집계 [py_bt_0005] date=%s =====\n", biz_date); + printf(" %-15s %8s %15s %12s\n", "가맹점", "건수", "지급총액", "순지급"); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0005", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_pysum", ECPGt_EOIT, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_gross),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0005.pgc" +#line 41 "app/batch/py_bt_0005.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0005"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + merchants++; + grandnet += h_gross - h_fee; + printf(" %-15s %8d %15ld %12ld\n", h_merch, h_cnt, h_gross, h_gross - h_fee); } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_pysum", ECPGt_EOIT, ECPGt_EORT);} +#line 48 "app/batch/py_bt_0005.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0005", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0005.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf(" 가맹점수=%ld 총순지급=%ld\n", merchants, grandnet); + tx_log(TX_LOG_INFO, "[py_bt_0005] 지급집계 date=%s merch=%ld", biz_date, merchants); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +164,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0005] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0005] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0005(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0005] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0005.pgc b/app/batch/py_bt_0005.pgc index 16f3833..7b5a709 100644 --- a/app/batch/py_bt_0005.pgc +++ b/app/batch/py_bt_0005.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0005.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0005.pgc - 가맹점 지급집계 배치 (정산 지급명세 리포트) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0005 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0005 [db@host] */ #include #include @@ -21,84 +18,37 @@ EXEC SQL INCLUDE sqlca; static int run_py_bt_0005(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_merch[16]; + int h_cnt; + long h_gross, h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merchants = 0, grandnet = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_py_bt_0005 CURSOR FOR - SELECT key, merch_id, amount + EXEC SQL DECLARE cur_pysum CURSOR FOR + SELECT merch_id, count(*), sum(amount), sum(fee) FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_py_bt_0005; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0005"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0005] tx_begin 경고"); + WHERE biz_date = :h_biz_date AND status IN ('M','S') + GROUP BY merch_id ORDER BY sum(amount) DESC; + EXEC SQL OPEN cur_pysum; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pysum"); return TX_EDB; } + printf("===== 가맹점 지급집계 [py_bt_0005] date=%s =====\n", biz_date); + printf(" %-15s %8s %15s %12s\n", "가맹점", "건수", "지급총액", "순지급"); for (;;) { - EXEC SQL FETCH cur_py_bt_0005 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0005"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL FETCH cur_pysum INTO :h_merch, :h_cnt, :h_gross, :h_fee; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + merchants++; + grandnet += h_gross - h_fee; + printf(" %-15s %8d %15ld %12ld\n", h_merch, h_cnt, h_gross, h_gross - h_fee); } - - EXEC SQL CLOSE cur_py_bt_0005; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + EXEC SQL CLOSE cur_pysum; + printf(" 가맹점수=%ld 총순지급=%ld\n", merchants, grandnet); + tx_log(TX_LOG_INFO, "[py_bt_0005] 지급집계 date=%s merch=%ld", biz_date, merchants); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +58,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0005] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0005] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0005(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0005] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0006.c b/app/batch/py_bt_0006.c index b14156f..72b9d67 100644 --- a/app/batch/py_bt_0006.c +++ b/app/batch/py_bt_0006.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/py_bt_0006.pgc" -/* @tier easy @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0006.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0006.pgc - 지급 일별통계 배치 (상태별 건수/금액 집계) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0006 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0006 [db@host] */ #include #include @@ -93,121 +90,56 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0006.pgc" +#line 16 "app/batch/py_bt_0006.pgc" static int run_py_bt_0006(const char *biz_date) { /* exec sql begin declare section */ - - - - + + -#line 24 "app/batch/py_bt_0006.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/py_bt_0006.pgc" + char h_biz_date [ 9 ] , h_st [ 2 ] ; -#line 25 "app/batch/py_bt_0006.pgc" - char h_key [ 16 ] ; +#line 22 "app/batch/py_bt_0006.pgc" + int h_cnt ; -#line 26 "app/batch/py_bt_0006.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0006.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0006.pgc" - char h_new_status [ 2 ] ; +#line 23 "app/batch/py_bt_0006.pgc" + long h_sum ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0006.pgc" +#line 24 "app/batch/py_bt_0006.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const char *codes = "RMUS"; + const char *names[] = { "대기", "확정", "실패", "완료" }; + int i; + long grand = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0006 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0006.pgc" - -#line 42 "app/batch/py_bt_0006.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0006 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", + printf("===== 지급 일별통계 [py_bt_0006] date=%s =====\n", biz_date); + for (i = 0; codes[i]; i++) { + h_st[0] = codes[i]; h_st[1] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from py_ledger where biz_date = $1 and status = $2 ", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0006.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0006"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0006] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0006", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_st),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0006.pgc" +#line 38 "app/batch/py_bt_0006.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0006"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_bt_0006 통계"); return TX_EDB; } + printf(" %s(%c) : %6d 건 %15ld 원\n", names[i], codes[i], h_cnt, h_sum); + grand += h_sum; + tx_log(TX_LOG_INFO, "[py_bt_0006] %c cnt=%d sum=%ld", codes[i], h_cnt, h_sum); } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0006", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0006.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf(" 총지급요청액 : %ld 원\n", grand); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +149,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0006] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0006] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0006(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0006] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0006.pgc b/app/batch/py_bt_0006.pgc index 6a18784..5a2cbf8 100644 --- a/app/batch/py_bt_0006.pgc +++ b/app/batch/py_bt_0006.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0006.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0006.pgc - 지급 일별통계 배치 (상태별 건수/금액 집계) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0006 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0006 [db@host] */ #include #include @@ -21,84 +18,31 @@ EXEC SQL INCLUDE sqlca; static int run_py_bt_0006(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_st[2]; + int h_cnt; + long h_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const char *codes = "RMUS"; + const char *names[] = { "대기", "확정", "실패", "완료" }; + int i; + long grand = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_py_bt_0006 CURSOR FOR - SELECT key, merch_id, amount - FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_py_bt_0006; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0006"); - return TX_EDB; + printf("===== 지급 일별통계 [py_bt_0006] date=%s =====\n", biz_date); + for (i = 0; codes[i]; i++) { + h_st[0] = codes[i]; h_st[1] = '\0'; + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_cnt, :h_sum + FROM py_ledger WHERE biz_date = :h_biz_date AND status = :h_st; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_bt_0006 통계"); return TX_EDB; } + printf(" %s(%c) : %6d 건 %15ld 원\n", names[i], codes[i], h_cnt, h_sum); + grand += h_sum; + tx_log(TX_LOG_INFO, "[py_bt_0006] %c cnt=%d sum=%ld", codes[i], h_cnt, h_sum); } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0006] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_py_bt_0006 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0006"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_py_bt_0006; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf(" 총지급요청액 : %ld 원\n", grand); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +52,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0006] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0006] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0006(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0006] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0007.c b/app/batch/py_bt_0007.c index db054ab..1e5271e 100644 --- a/app/batch/py_bt_0007.c +++ b/app/batch/py_bt_0007.c @@ -8,12 +8,9 @@ #line 1 "app/batch/py_bt_0007.pgc" /* @tier medium @module py @transform payment-batch */ /* - * py_bt_0007.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=medium] + * py_bt_0007.pgc - 지급 명세 리포트 배치 (건별 지급내역 출력) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0007 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0007 [db@host] */ #include #include @@ -93,121 +90,70 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0007.pgc" +#line 16 "app/batch/py_bt_0007.pgc" static int run_py_bt_0007(const char *biz_date) { /* exec sql begin declare section */ - - - - - + + -#line 24 "app/batch/py_bt_0007.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/py_bt_0007.pgc" + char h_biz_date [ 9 ] , h_key [ 16 ] , h_merch [ 16 ] , h_status [ 2 ] ; -#line 25 "app/batch/py_bt_0007.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/py_bt_0007.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0007.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0007.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/py_bt_0007.pgc" + long h_amount , h_fee ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0007.pgc" +#line 23 "app/batch/py_bt_0007.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long rows = 0, netsum = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0007 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0007.pgc" + /* declare cur_pyrpt cursor for select key , merch_id , amount , fee , status from py_ledger where biz_date = $1 and status in ( 'M' , 'S' , 'U' ) order by merch_id , key */ +#line 32 "app/batch/py_bt_0007.pgc" -#line 42 "app/batch/py_bt_0007.pgc" +#line 32 "app/batch/py_bt_0007.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0007 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_pyrpt cursor for select key , merch_id , amount , fee , status from py_ledger where biz_date = $1 and status in ( 'M' , 'S' , 'U' ) order by merch_id , key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0007.pgc" +#line 33 "app/batch/py_bt_0007.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0007"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0007] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pyrpt"); return TX_EDB; } + printf("===== 지급 명세 리포트 [py_bt_0007] date=%s =====\n", biz_date); + printf(" %-14s %-14s %13s %10s %s\n", "지급키", "가맹점", "지급액", "수수료", "상태"); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0007", ECPGt_EOIT, + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_pyrpt", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0007.pgc" +#line 39 "app/batch/py_bt_0007.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0007"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + rows++; + if (h_status[0] == 'S') netsum += h_amount - h_fee; + printf(" %-14s %-14s %13ld %10ld %c\n", + h_key, h_merch, h_amount, h_fee, h_status[0]); } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_pyrpt", ECPGt_EOIT, ECPGt_EORT);} +#line 47 "app/batch/py_bt_0007.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0007", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0007.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf(" 출력=%ld 건, 완료순지급합=%ld\n", rows, netsum); + tx_log(TX_LOG_INFO, "[py_bt_0007] 리포트 date=%s rows=%ld", biz_date, rows); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +163,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0007] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0007] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0007(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0007] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0007.pgc b/app/batch/py_bt_0007.pgc index 81ec393..180e23b 100644 --- a/app/batch/py_bt_0007.pgc +++ b/app/batch/py_bt_0007.pgc @@ -1,11 +1,8 @@ /* @tier medium @module py @transform payment-batch */ /* - * py_bt_0007.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=medium] + * py_bt_0007.pgc - 지급 명세 리포트 배치 (건별 지급내역 출력) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0007 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0007 [db@host] */ #include #include @@ -21,84 +18,36 @@ EXEC SQL INCLUDE sqlca; static int run_py_bt_0007(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_key[16], h_merch[16], h_status[2]; + long h_amount, h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long rows = 0, netsum = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_py_bt_0007 CURSOR FOR - SELECT key, merch_id, amount - FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_py_bt_0007; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0007"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0007] tx_begin 경고"); + EXEC SQL DECLARE cur_pyrpt CURSOR FOR + SELECT key, merch_id, amount, fee, status FROM py_ledger + WHERE biz_date = :h_biz_date AND status IN ('M','S','U') + ORDER BY merch_id, key; + EXEC SQL OPEN cur_pyrpt; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pyrpt"); return TX_EDB; } + printf("===== 지급 명세 리포트 [py_bt_0007] date=%s =====\n", biz_date); + printf(" %-14s %-14s %13s %10s %s\n", "지급키", "가맹점", "지급액", "수수료", "상태"); for (;;) { - EXEC SQL FETCH cur_py_bt_0007 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0007"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL FETCH cur_pyrpt INTO :h_key, :h_merch, :h_amount, :h_fee, :h_status; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + rows++; + if (h_status[0] == 'S') netsum += h_amount - h_fee; + printf(" %-14s %-14s %13ld %10ld %c\n", + h_key, h_merch, h_amount, h_fee, h_status[0]); } - - EXEC SQL CLOSE cur_py_bt_0007; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + EXEC SQL CLOSE cur_pyrpt; + printf(" 출력=%ld 건, 완료순지급합=%ld\n", rows, netsum); + tx_log(TX_LOG_INFO, "[py_bt_0007] 리포트 date=%s rows=%ld", biz_date, rows); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +57,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0007] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0007] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0007(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0007] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0008.c b/app/batch/py_bt_0008.c index 6d7e412..be6e4ef 100644 --- a/app/batch/py_bt_0008.c +++ b/app/batch/py_bt_0008.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/py_bt_0008.pgc" -/* @tier easy @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0008.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0008.pgc - 지급 지연이자 산출 배치 (지연건 이자 UPDATE) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0008 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0008 [db@host] */ #include #include @@ -93,120 +90,79 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0008.pgc" +#line 16 "app/batch/py_bt_0008.pgc" static int run_py_bt_0008(const char *biz_date) { /* exec sql begin declare section */ - - - - - + + -#line 24 "app/batch/py_bt_0008.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/py_bt_0008.pgc" + char h_biz_date [ 9 ] , h_key [ 16 ] , h_pdate [ 9 ] ; -#line 25 "app/batch/py_bt_0008.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/py_bt_0008.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0008.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0008.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/py_bt_0008.pgc" + long h_amount , h_interest ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0008.pgc" +#line 23 "app/batch/py_bt_0008.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0, intsum = 0; + long days; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0008 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0008.pgc" + /* declare cur_pyint cursor for select key , amount , biz_date from py_ledger where status = 'R' and biz_date < $1 order by biz_date , key */ +#line 33 "app/batch/py_bt_0008.pgc" -#line 42 "app/batch/py_bt_0008.pgc" +#line 33 "app/batch/py_bt_0008.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0008 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_pyint cursor for select key , amount , biz_date from py_ledger where status = 'R' and biz_date < $1 order by biz_date , key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0008.pgc" +#line 34 "app/batch/py_bt_0008.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0008"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0008] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pyint"); return TX_EDB; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0008] tx_begin 경고"); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0008", ECPGt_EOIT, + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_pyint", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_pdate),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0008.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0008"); - errcnt++; - break; - } +#line 39 "app/batch/py_bt_0008.pgc" + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; break; } total++; + /* 근사 지연일수 = 기준일 - 지급예정일 (YYYYMMDD 정수차 근사) */ + days = atol(biz_date) - atol(h_pdate); + if (days < 0) days = 0; + if (days > 60) days = 60; + h_interest = h_amount * 2 * days / 10000; /* 일 0.02% */ + intsum += h_interest; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set fee = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_long,&(h_interest),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 49 "app/batch/py_bt_0008.pgc" - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK) errcnt++; } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_pyint", ECPGt_EOIT, ECPGt_EORT);} +#line 52 "app/batch/py_bt_0008.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0008", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0008.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + if (errcnt == 0) tx_commit(); else tx_abort(); + printf("===== 지급 지연이자 산출 [py_bt_0008] 대상=%ld 이자합=%ld 오류=%ld =====\n", + total, intsum, errcnt); + tx_log(TX_LOG_INFO, "[py_bt_0008] 지연이자 date=%s intsum=%ld", biz_date, intsum); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -217,31 +173,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0008] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0008] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0008(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0008] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0008.pgc b/app/batch/py_bt_0008.pgc index ea0c4a0..2a0f8d7 100644 --- a/app/batch/py_bt_0008.pgc +++ b/app/batch/py_bt_0008.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0008.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0008.pgc - 지급 지연이자 산출 배치 (지연건 이자 UPDATE) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0008 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0008 [db@host] */ #include #include @@ -21,83 +18,43 @@ EXEC SQL INCLUDE sqlca; static int run_py_bt_0008(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_key[16], h_pdate[9]; + long h_amount, h_interest; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0, intsum = 0; + long days; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_py_bt_0008 CURSOR FOR - SELECT key, merch_id, amount - FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_py_bt_0008; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0008"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0008] tx_begin 경고"); + EXEC SQL DECLARE cur_pyint CURSOR FOR + SELECT key, amount, biz_date FROM py_ledger + WHERE status = 'R' AND biz_date < :h_biz_date + ORDER BY biz_date, key; + EXEC SQL OPEN cur_pyint; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pyint"); return TX_EDB; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0008] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_py_bt_0008 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0008"); - errcnt++; - break; - } - + EXEC SQL FETCH cur_pyint INTO :h_key, :h_amount, :h_pdate; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; break; } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 근사 지연일수 = 기준일 - 지급예정일 (YYYYMMDD 정수차 근사) */ + days = atol(biz_date) - atol(h_pdate); + if (days < 0) days = 0; + if (days > 60) days = 60; + h_interest = h_amount * 2 * days / 10000; /* 일 0.02% */ + intsum += h_interest; + EXEC SQL UPDATE py_ledger SET fee = :h_interest, upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) errcnt++; } + EXEC SQL CLOSE cur_pyint; + if (errcnt == 0) tx_commit(); else tx_abort(); - EXEC SQL CLOSE cur_py_bt_0008; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - + printf("===== 지급 지연이자 산출 [py_bt_0008] 대상=%ld 이자합=%ld 오류=%ld =====\n", + total, intsum, errcnt); + tx_log(TX_LOG_INFO, "[py_bt_0008] 지연이자 date=%s intsum=%ld", biz_date, intsum); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -108,31 +65,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0008] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0008] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0008(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0008] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0009.c b/app/batch/py_bt_0009.c index 07827d1..9c80f42 100644 --- a/app/batch/py_bt_0009.c +++ b/app/batch/py_bt_0009.c @@ -8,12 +8,9 @@ #line 1 "app/batch/py_bt_0009.pgc" /* @tier medium @module py @transform payment-batch */ /* - * py_bt_0009.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=medium] + * py_bt_0009.pgc - 지급 스케줄 실행 배치 (예약 지급건 대기전환) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0009 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0009 [db@host] */ #include #include @@ -93,121 +90,53 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0009.pgc" +#line 16 "app/batch/py_bt_0009.pgc" static int run_py_bt_0009(const char *biz_date) { /* exec sql begin declare section */ - - - - + -#line 24 "app/batch/py_bt_0009.pgc" +#line 21 "app/batch/py_bt_0009.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/py_bt_0009.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/py_bt_0009.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0009.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0009.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/py_bt_0009.pgc" + int h_aff , h_cnt ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0009.pgc" +#line 23 "app/batch/py_bt_0009.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from py_ledger where biz_date = $1 and status = 'U'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 28 "app/batch/py_bt_0009.pgc" - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0009 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0009.pgc" + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_bt_0009 집계"); return TX_EDB; } -#line 42 "app/batch/py_bt_0009.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0009 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0009] tx_begin 경고"); + /* 오늘이 예약 지급일인 보류건을 지급대기(R)로 승격 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'R' , upd_ts = now ( ) where biz_date = $1 and status = 'U' and fee = 0", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0009.pgc" +#line 34 "app/batch/py_bt_0009.pgc" if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0009"); + TX_DBIO_LOG_ERR("py_bt_0009 승격"); tx_abort(); return TX_EDB; } + h_aff = (int)sqlca.sqlerrd[2]; + tx_commit(); - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0009] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0009", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0009.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0009"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0009", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0009.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 지급 스케줄 실행 [py_bt_0009] date=%s =====\n", biz_date); + printf(" 보류건=%d 대기전환=%d\n", h_cnt, h_aff); + tx_log(TX_LOG_INFO, "[py_bt_0009] 스케줄 실행 승격=%d건", h_aff); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +146,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0009] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0009] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0009(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0009] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0009.pgc b/app/batch/py_bt_0009.pgc index 2b6e21e..bff8cfd 100644 --- a/app/batch/py_bt_0009.pgc +++ b/app/batch/py_bt_0009.pgc @@ -1,11 +1,8 @@ /* @tier medium @module py @transform payment-batch */ /* - * py_bt_0009.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=medium] + * py_bt_0009.pgc - 지급 스케줄 실행 배치 (예약 지급건 대기전환) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0009 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0009 [db@host] */ #include #include @@ -22,83 +19,30 @@ static int run_py_bt_0009(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + int h_aff, h_cnt; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + EXEC SQL SELECT count(*) INTO :h_cnt + FROM py_ledger WHERE biz_date = :h_biz_date AND status = 'U'; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_bt_0009 집계"); return TX_EDB; } - EXEC SQL DECLARE cur_py_bt_0009 CURSOR FOR - SELECT key, merch_id, amount - FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_py_bt_0009; + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0009] tx_begin 경고"); + /* 오늘이 예약 지급일인 보류건을 지급대기(R)로 승격 */ + EXEC SQL UPDATE py_ledger SET status = 'R', upd_ts = now() + WHERE biz_date = :h_biz_date AND status = 'U' AND fee = 0; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0009"); + TX_DBIO_LOG_ERR("py_bt_0009 승격"); tx_abort(); return TX_EDB; } + h_aff = (int)sqlca.sqlerrd[2]; + tx_commit(); - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0009] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_py_bt_0009 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0009"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_py_bt_0009; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 지급 스케줄 실행 [py_bt_0009] date=%s =====\n", biz_date); + printf(" 보류건=%d 대기전환=%d\n", h_cnt, h_aff); + tx_log(TX_LOG_INFO, "[py_bt_0009] 스케줄 실행 승격=%d건", h_aff); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +52,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0009] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0009] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0009(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0009] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0010.c b/app/batch/py_bt_0010.c index cf355a0..48c007f 100644 --- a/app/batch/py_bt_0010.c +++ b/app/batch/py_bt_0010.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/py_bt_0010.pgc" -/* @tier easy @module py @transform payment-batch */ +/* @tier hard @module py @transform payment-batch */ /* - * py_bt_0010.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0010.pgc - 지급 이체연계 배치 (확정건 이체 tx_call 후 완료) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0010 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0010 [db@host] */ #include #include @@ -93,121 +90,90 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0010.pgc" +#line 16 "app/batch/py_bt_0010.pgc" static int run_py_bt_0010(const char *biz_date) { /* exec sql begin declare section */ - - - - - + + -#line 24 "app/batch/py_bt_0010.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/py_bt_0010.pgc" + char h_biz_date [ 9 ] , h_key [ 16 ] , h_acct [ 20 ] ; -#line 25 "app/batch/py_bt_0010.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/py_bt_0010.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0010.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0010.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/py_bt_0010.pgc" + long h_amount , h_fee ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0010.pgc" +#line 23 "app/batch/py_bt_0010.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + TXBUF *req, *resp; + long total = 0, ok = 0, fail = 0, netsum = 0; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0010 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0010.pgc" + /* declare cur_pytr cursor for select key , card_no , amount , fee from py_ledger where biz_date = $1 and status = 'M' order by key */ +#line 34 "app/batch/py_bt_0010.pgc" -#line 42 "app/batch/py_bt_0010.pgc" +#line 34 "app/batch/py_bt_0010.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0010 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_pytr cursor for select key , card_no , amount , fee from py_ledger where biz_date = $1 and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0010.pgc" +#line 35 "app/batch/py_bt_0010.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0010"); - return TX_EDB; - } + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pytr"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0010] tx_begin 경고"); + req = tx_buf_alloc(); + resp = tx_buf_alloc(); + if (!req || !resp) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_pytr", ECPGt_EOIT, ECPGt_EORT);} +#line 40 "app/batch/py_bt_0010.pgc" + return TX_ENOMEM; } for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0010", ECPGt_EOIT, + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_pytr", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_acct),(long)20,(long)1,(20)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0010.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0010"); - errcnt++; - break; - } +#line 43 "app/batch/py_bt_0010.pgc" + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; total++; + tx_buf_reset(req); + tx_buf_reset(resp); + tx_buf_sets(req, "ACCTNO", h_acct); + tx_buf_setlong(req, "AMOUNT", h_amount - h_fee); + rc = tx_call("PY_TRANSFER", req, resp); + if (rc == TX_OK) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'S' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 53 "app/batch/py_bt_0010.pgc" - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; + if (sqlca.sqlcode == TX_SQL_OK) { tx_commit(); ok++; netsum += h_amount - h_fee; } } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + fail++; } } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0010", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0010.pgc" + tx_buf_free(req); + tx_buf_free(resp); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_pytr", ECPGt_EOIT, ECPGt_EORT);} +#line 61 "app/batch/py_bt_0010.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 지급 이체연계 [py_bt_0010] 대상=%ld 이체=%ld 실패=%ld 순지급=%ld =====\n", + total, ok, fail, netsum); + tx_log(TX_LOG_INFO, "[py_bt_0010] 이체연계 date=%s ok=%ld net=%ld", biz_date, ok, netsum); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +183,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0010] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0010] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0010(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0010] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0010.pgc b/app/batch/py_bt_0010.pgc index ceb0f8f..5e61fa4 100644 --- a/app/batch/py_bt_0010.pgc +++ b/app/batch/py_bt_0010.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module py @transform payment-batch */ +/* @tier hard @module py @transform payment-batch */ /* - * py_bt_0010.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0010.pgc - 지급 이체연계 배치 (확정건 이체 tx_call 후 완료) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0010 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0010 [db@host] */ #include #include @@ -21,84 +18,52 @@ EXEC SQL INCLUDE sqlca; static int run_py_bt_0010(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_key[16], h_acct[20]; + long h_amount, h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + TXBUF *req, *resp; + long total = 0, ok = 0, fail = 0, netsum = 0; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_py_bt_0010 CURSOR FOR - SELECT key, merch_id, amount - FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' + EXEC SQL DECLARE cur_pytr CURSOR FOR + SELECT key, card_no, amount, fee FROM py_ledger + WHERE biz_date = :h_biz_date AND status = 'M' ORDER BY key; + EXEC SQL OPEN cur_pytr; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pytr"); return TX_EDB; } - EXEC SQL OPEN cur_py_bt_0010; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0010"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0010] tx_begin 경고"); + req = tx_buf_alloc(); + resp = tx_buf_alloc(); + if (!req || !resp) { EXEC SQL CLOSE cur_pytr; return TX_ENOMEM; } for (;;) { - EXEC SQL FETCH cur_py_bt_0010 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0010"); - errcnt++; - break; - } - + EXEC SQL FETCH cur_pytr INTO :h_key, :h_acct, :h_amount, :h_fee; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; + tx_buf_reset(req); + tx_buf_reset(resp); + tx_buf_sets(req, "ACCTNO", h_acct); + tx_buf_setlong(req, "AMOUNT", h_amount - h_fee); + rc = tx_call("PY_TRANSFER", req, resp); + if (rc == TX_OK) { + EXEC SQL UPDATE py_ledger SET status = 'S', upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_OK) { tx_commit(); ok++; netsum += h_amount - h_fee; } } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + fail++; } } + tx_buf_free(req); + tx_buf_free(resp); + EXEC SQL CLOSE cur_pytr; - EXEC SQL CLOSE cur_py_bt_0010; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 지급 이체연계 [py_bt_0010] 대상=%ld 이체=%ld 실패=%ld 순지급=%ld =====\n", + total, ok, fail, netsum); + tx_log(TX_LOG_INFO, "[py_bt_0010] 이체연계 date=%s ok=%ld net=%ld", biz_date, ok, netsum); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +73,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0010] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0010] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0010(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0010] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0011.c b/app/batch/py_bt_0011.c index 1de4538..5f9090c 100644 --- a/app/batch/py_bt_0011.c +++ b/app/batch/py_bt_0011.c @@ -8,12 +8,9 @@ #line 1 "app/batch/py_bt_0011.pgc" /* @tier medium @module py @transform payment-batch */ /* - * py_bt_0011.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=medium] + * py_bt_0011.pgc - 지급 보류 일괄해제 배치 (검토완료 보류건 재개) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0011 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0011 [db@host] */ #include #include @@ -93,121 +90,52 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0011.pgc" +#line 16 "app/batch/py_bt_0011.pgc" static int run_py_bt_0011(const char *biz_date) { /* exec sql begin declare section */ - - - - + -#line 24 "app/batch/py_bt_0011.pgc" +#line 21 "app/batch/py_bt_0011.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/py_bt_0011.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/py_bt_0011.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0011.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0011.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/py_bt_0011.pgc" + int h_before , h_aff ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0011.pgc" +#line 23 "app/batch/py_bt_0011.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from py_ledger where biz_date = $1 and status = 'U'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_before),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 28 "app/batch/py_bt_0011.pgc" - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0011 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0011.pgc" + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_bt_0011 집계"); return TX_EDB; } -#line 42 "app/batch/py_bt_0011.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0011 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0011] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'R' , upd_ts = now ( ) where biz_date = $1 and status = 'U' and amount > 0", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0011.pgc" +#line 33 "app/batch/py_bt_0011.pgc" if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0011"); + TX_DBIO_LOG_ERR("py_bt_0011 일괄해제"); tx_abort(); return TX_EDB; } + h_aff = (int)sqlca.sqlerrd[2]; + tx_commit(); - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0011] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0011", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0011.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0011"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0011", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0011.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 지급 보류 일괄해제 [py_bt_0011] date=%s =====\n", biz_date); + printf(" 보류=%d 해제=%d\n", h_before, h_aff); + tx_log(TX_LOG_INFO, "[py_bt_0011] 보류해제 %d건", h_aff); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +145,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0011] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0011] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0011(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0011] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0011.pgc b/app/batch/py_bt_0011.pgc index e3a48a6..96d7d96 100644 --- a/app/batch/py_bt_0011.pgc +++ b/app/batch/py_bt_0011.pgc @@ -1,11 +1,8 @@ /* @tier medium @module py @transform payment-batch */ /* - * py_bt_0011.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=medium] + * py_bt_0011.pgc - 지급 보류 일괄해제 배치 (검토완료 보류건 재개) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0011 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0011 [db@host] */ #include #include @@ -22,83 +19,29 @@ static int run_py_bt_0011(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + int h_before, h_aff; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + EXEC SQL SELECT count(*) INTO :h_before + FROM py_ledger WHERE biz_date = :h_biz_date AND status = 'U'; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_bt_0011 집계"); return TX_EDB; } - EXEC SQL DECLARE cur_py_bt_0011 CURSOR FOR - SELECT key, merch_id, amount - FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_py_bt_0011; + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0011] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET status = 'R', upd_ts = now() + WHERE biz_date = :h_biz_date AND status = 'U' AND amount > 0; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0011"); + TX_DBIO_LOG_ERR("py_bt_0011 일괄해제"); tx_abort(); return TX_EDB; } + h_aff = (int)sqlca.sqlerrd[2]; + tx_commit(); - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0011] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_py_bt_0011 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0011"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_py_bt_0011; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 지급 보류 일괄해제 [py_bt_0011] date=%s =====\n", biz_date); + printf(" 보류=%d 해제=%d\n", h_before, h_aff); + tx_log(TX_LOG_INFO, "[py_bt_0011] 보류해제 %d건", h_aff); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +51,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0011] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0011] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0011(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0011] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0012.c b/app/batch/py_bt_0012.c index 6a13089..26bc6da 100644 --- a/app/batch/py_bt_0012.c +++ b/app/batch/py_bt_0012.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/py_bt_0012.pgc" -/* @tier easy @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0012.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0012.pgc - 지급 취소건 정리 배치 (장기 취소건 아카이브) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0012 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0012 [db@host] */ #include #include @@ -93,121 +90,52 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0012.pgc" +#line 16 "app/batch/py_bt_0012.pgc" static int run_py_bt_0012(const char *biz_date) { /* exec sql begin declare section */ - - - - + -#line 24 "app/batch/py_bt_0012.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/py_bt_0012.pgc" + char h_cutoff [ 9 ] ; -#line 25 "app/batch/py_bt_0012.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/py_bt_0012.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0012.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0012.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/py_bt_0012.pgc" + int h_cnt , h_del ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0012.pgc" +#line 23 "app/batch/py_bt_0012.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_cutoff, biz_date, 8); h_cutoff[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from py_ledger where status = 'U' and biz_date < $1 ", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 28 "app/batch/py_bt_0012.pgc" - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0012 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0012.pgc" + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_bt_0012 집계"); return TX_EDB; } -#line 42 "app/batch/py_bt_0012.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0012 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0012] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "delete from py_ledger where status = 'U' and biz_date < $1 and card_no = 'SPLIT'", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0012.pgc" +#line 33 "app/batch/py_bt_0012.pgc" if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0012"); + TX_DBIO_LOG_ERR("py_bt_0012 삭제"); tx_abort(); return TX_EDB; } + h_del = (int)sqlca.sqlerrd[2]; + tx_commit(); - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0012] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0012", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0012.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0012"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0012", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0012.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 지급 취소건 정리 [py_bt_0012] 기준일<%s =====\n", biz_date); + printf(" 취소건=%d 정리(분할)=%d\n", h_cnt, h_del); + tx_log(TX_LOG_INFO, "[py_bt_0012] 취소정리 삭제=%d건", h_del); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +145,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0012] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0012] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0012(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0012] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0012.pgc b/app/batch/py_bt_0012.pgc index 136beb6..3c08d37 100644 --- a/app/batch/py_bt_0012.pgc +++ b/app/batch/py_bt_0012.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0012.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0012.pgc - 지급 취소건 정리 배치 (장기 취소건 아카이브) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0012 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0012 [db@host] */ #include #include @@ -21,84 +18,30 @@ EXEC SQL INCLUDE sqlca; static int run_py_bt_0012(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_cutoff[9]; + int h_cnt, h_del; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_cutoff, biz_date, 8); h_cutoff[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + EXEC SQL SELECT count(*) INTO :h_cnt + FROM py_ledger WHERE status = 'U' AND biz_date < :h_cutoff; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_bt_0012 집계"); return TX_EDB; } - EXEC SQL DECLARE cur_py_bt_0012 CURSOR FOR - SELECT key, merch_id, amount - FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_py_bt_0012; + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0012] tx_begin 경고"); + EXEC SQL DELETE FROM py_ledger + WHERE status = 'U' AND biz_date < :h_cutoff AND card_no = 'SPLIT'; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0012"); + TX_DBIO_LOG_ERR("py_bt_0012 삭제"); tx_abort(); return TX_EDB; } + h_del = (int)sqlca.sqlerrd[2]; + tx_commit(); - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0012] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_py_bt_0012 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0012"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_py_bt_0012; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 지급 취소건 정리 [py_bt_0012] 기준일<%s =====\n", biz_date); + printf(" 취소건=%d 정리(분할)=%d\n", h_cnt, h_del); + tx_log(TX_LOG_INFO, "[py_bt_0012] 취소정리 삭제=%d건", h_del); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +51,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0012] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0012] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0012(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0012] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0013.c b/app/batch/py_bt_0013.c index 81d2c22..a8f415c 100644 --- a/app/batch/py_bt_0013.c +++ b/app/batch/py_bt_0013.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/py_bt_0013.pgc" -/* @tier hard @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0013.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=hard] + * py_bt_0013.pgc - 지급 수수료 정산 배치 (지급건 수수료 재산정) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0013 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0013 [db@host] */ #include #include @@ -93,120 +90,75 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0013.pgc" +#line 16 "app/batch/py_bt_0013.pgc" static int run_py_bt_0013(const char *biz_date) { /* exec sql begin declare section */ - - - - - + + -#line 24 "app/batch/py_bt_0013.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/py_bt_0013.pgc" + char h_biz_date [ 9 ] , h_key [ 16 ] ; -#line 25 "app/batch/py_bt_0013.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/py_bt_0013.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0013.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0013.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/py_bt_0013.pgc" + long h_amount , h_fee ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0013.pgc" +#line 23 "app/batch/py_bt_0013.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0, feesum = 0; + int rate; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0013 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0013.pgc" + /* declare cur_pyfee cursor for select key , amount from py_ledger where biz_date = $1 and status in ( 'M' , 'S' ) order by key */ +#line 33 "app/batch/py_bt_0013.pgc" -#line 42 "app/batch/py_bt_0013.pgc" +#line 33 "app/batch/py_bt_0013.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0013 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_pyfee cursor for select key , amount from py_ledger where biz_date = $1 and status in ( 'M' , 'S' ) order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0013.pgc" +#line 34 "app/batch/py_bt_0013.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0013"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0013] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pyfee"); return TX_EDB; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0013] tx_begin 경고"); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0013", ECPGt_EOIT, + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_pyfee", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0013.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0013"); - errcnt++; - break; - } +#line 39 "app/batch/py_bt_0013.pgc" + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; break; } total++; + /* 지급액 구간별 정산수수료 (basis point) */ + rate = (h_amount >= 10000000L) ? 8 : (h_amount >= 1000000L) ? 12 : 18; + h_fee = h_amount * rate / 10000; + feesum += h_fee; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set fee = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 47 "app/batch/py_bt_0013.pgc" - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK) errcnt++; } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_pyfee", ECPGt_EOIT, ECPGt_EORT);} +#line 50 "app/batch/py_bt_0013.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0013", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0013.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + if (errcnt == 0) tx_commit(); else tx_abort(); + printf("===== 지급 수수료 정산 [py_bt_0013] 대상=%ld 수수료합=%ld 오류=%ld =====\n", + total, feesum, errcnt); + tx_log(TX_LOG_INFO, "[py_bt_0013] 수수료 정산 date=%s feesum=%ld", biz_date, feesum); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -217,31 +169,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0013] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0013] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0013(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0013] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0013.pgc b/app/batch/py_bt_0013.pgc index cdd71c6..c2f2c93 100644 --- a/app/batch/py_bt_0013.pgc +++ b/app/batch/py_bt_0013.pgc @@ -1,11 +1,8 @@ -/* @tier hard @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0013.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=hard] + * py_bt_0013.pgc - 지급 수수료 정산 배치 (지급건 수수료 재산정) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0013 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0013 [db@host] */ #include #include @@ -21,83 +18,41 @@ EXEC SQL INCLUDE sqlca; static int run_py_bt_0013(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_key[16]; + long h_amount, h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0, feesum = 0; + int rate; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_py_bt_0013 CURSOR FOR - SELECT key, merch_id, amount - FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' + EXEC SQL DECLARE cur_pyfee CURSOR FOR + SELECT key, amount FROM py_ledger + WHERE biz_date = :h_biz_date AND status IN ('M','S') ORDER BY key; + EXEC SQL OPEN cur_pyfee; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pyfee"); return TX_EDB; } - EXEC SQL OPEN cur_py_bt_0013; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0013"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0013] tx_begin 경고"); - + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0013] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_py_bt_0013 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0013"); - errcnt++; - break; - } - + EXEC SQL FETCH cur_pyfee INTO :h_key, :h_amount; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; break; } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 지급액 구간별 정산수수료 (basis point) */ + rate = (h_amount >= 10000000L) ? 8 : (h_amount >= 1000000L) ? 12 : 18; + h_fee = h_amount * rate / 10000; + feesum += h_fee; + EXEC SQL UPDATE py_ledger SET fee = :h_fee, upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) errcnt++; } + EXEC SQL CLOSE cur_pyfee; + if (errcnt == 0) tx_commit(); else tx_abort(); - EXEC SQL CLOSE cur_py_bt_0013; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - + printf("===== 지급 수수료 정산 [py_bt_0013] 대상=%ld 수수료합=%ld 오류=%ld =====\n", + total, feesum, errcnt); + tx_log(TX_LOG_INFO, "[py_bt_0013] 수수료 정산 date=%s feesum=%ld", biz_date, feesum); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -108,31 +63,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0013] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0013] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0013(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0013] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0014.c b/app/batch/py_bt_0014.c index b154a3d..eb76a3c 100644 --- a/app/batch/py_bt_0014.c +++ b/app/batch/py_bt_0014.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/py_bt_0014.pgc" -/* @tier easy @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0014.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0014.pgc - 미지급 대기건 정리 배치 (장기 대기건 실효) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0014 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0014 [db@host] */ #include #include @@ -93,121 +90,52 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0014.pgc" +#line 16 "app/batch/py_bt_0014.pgc" static int run_py_bt_0014(const char *biz_date) { /* exec sql begin declare section */ - - - - + -#line 24 "app/batch/py_bt_0014.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/py_bt_0014.pgc" + char h_cutoff [ 9 ] ; -#line 25 "app/batch/py_bt_0014.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/py_bt_0014.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0014.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0014.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/py_bt_0014.pgc" + int h_cnt , h_aff ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0014.pgc" +#line 23 "app/batch/py_bt_0014.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_cutoff, biz_date, 8); h_cutoff[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from py_ledger where status = 'R' and biz_date < $1 ", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 28 "app/batch/py_bt_0014.pgc" - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0014 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0014.pgc" + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_bt_0014 집계"); return TX_EDB; } -#line 42 "app/batch/py_bt_0014.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0014 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0014] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'U' , upd_ts = now ( ) where status = 'R' and biz_date < $1 ", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0014.pgc" +#line 33 "app/batch/py_bt_0014.pgc" if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0014"); + TX_DBIO_LOG_ERR("py_bt_0014 실효"); tx_abort(); return TX_EDB; } + h_aff = (int)sqlca.sqlerrd[2]; + tx_commit(); - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0014] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0014", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0014.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0014"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0014", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0014.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 미지급 대기건 정리 [py_bt_0014] 기준일<%s =====\n", biz_date); + printf(" 대상(R)=%d 실효처리=%d\n", h_cnt, h_aff); + tx_log(TX_LOG_INFO, "[py_bt_0014] 미지급 실효 %d건", h_aff); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +145,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0014] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0014] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0014(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0014] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0014.pgc b/app/batch/py_bt_0014.pgc index 5172d3a..8699b23 100644 --- a/app/batch/py_bt_0014.pgc +++ b/app/batch/py_bt_0014.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0014.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0014.pgc - 미지급 대기건 정리 배치 (장기 대기건 실효) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0014 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0014 [db@host] */ #include #include @@ -21,84 +18,30 @@ EXEC SQL INCLUDE sqlca; static int run_py_bt_0014(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_cutoff[9]; + int h_cnt, h_aff; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + strncpy(h_cutoff, biz_date, 8); h_cutoff[8] = '\0'; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + EXEC SQL SELECT count(*) INTO :h_cnt + FROM py_ledger WHERE status = 'R' AND biz_date < :h_cutoff; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_bt_0014 집계"); return TX_EDB; } - EXEC SQL DECLARE cur_py_bt_0014 CURSOR FOR - SELECT key, merch_id, amount - FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_py_bt_0014; + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0014] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET status = 'U', upd_ts = now() + WHERE status = 'R' AND biz_date < :h_cutoff; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0014"); + TX_DBIO_LOG_ERR("py_bt_0014 실효"); tx_abort(); return TX_EDB; } + h_aff = (int)sqlca.sqlerrd[2]; + tx_commit(); - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0014] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_py_bt_0014 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0014"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_py_bt_0014; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 미지급 대기건 정리 [py_bt_0014] 기준일<%s =====\n", biz_date); + printf(" 대상(R)=%d 실효처리=%d\n", h_cnt, h_aff); + tx_log(TX_LOG_INFO, "[py_bt_0014] 미지급 실효 %d건", h_aff); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +51,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0014] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0014] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0014(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0014] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0015.c b/app/batch/py_bt_0015.c index 5ea307f..8a68756 100644 --- a/app/batch/py_bt_0015.c +++ b/app/batch/py_bt_0015.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/py_bt_0015.pgc" -/* @tier easy @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0015.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0015.pgc - 지급 정산반영 배치 (이체완료 정산확정 S 유지/집계) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0015 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0015 [db@host] */ #include #include @@ -93,120 +90,75 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0015.pgc" +#line 16 "app/batch/py_bt_0015.pgc" static int run_py_bt_0015(const char *biz_date) { /* exec sql begin declare section */ - - - - - + + -#line 24 "app/batch/py_bt_0015.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/py_bt_0015.pgc" + char h_biz_date [ 9 ] , h_key [ 16 ] , h_merch [ 16 ] ; -#line 25 "app/batch/py_bt_0015.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/py_bt_0015.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0015.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0015.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/py_bt_0015.pgc" + long h_amount , h_fee ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0015.pgc" +#line 23 "app/batch/py_bt_0015.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, settled = 0, errcnt = 0, netsum = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0015 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0015.pgc" + /* declare cur_pyset cursor for select key , merch_id , amount , fee from py_ledger where biz_date = $1 and status = 'M' order by merch_id , key */ +#line 32 "app/batch/py_bt_0015.pgc" -#line 42 "app/batch/py_bt_0015.pgc" +#line 32 "app/batch/py_bt_0015.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0015 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_pyset cursor for select key , merch_id , amount , fee from py_ledger where biz_date = $1 and status = 'M' order by merch_id , key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0015.pgc" +#line 33 "app/batch/py_bt_0015.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0015"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0015] tx_begin 경고"); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pyset"); return TX_EDB; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0015] tx_begin 경고"); for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0015", ECPGt_EOIT, + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_pyset", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0015.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0015"); - errcnt++; - break; - } +#line 38 "app/batch/py_bt_0015.pgc" + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; break; } total++; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'S' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 42 "app/batch/py_bt_0015.pgc" - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; continue; } + settled++; + netsum += h_amount - h_fee; } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_pyset", ECPGt_EOIT, ECPGt_EORT);} +#line 47 "app/batch/py_bt_0015.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0015", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0015.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + if (errcnt == 0) tx_commit(); else tx_abort(); + printf("===== 지급 정산반영 [py_bt_0015] date=%s =====\n", biz_date); + printf(" 대상(M)=%ld 정산(S)=%ld 순정산액=%ld 오류=%ld\n", + total, settled, netsum, errcnt); + tx_log(TX_LOG_INFO, "[py_bt_0015] 정산반영 date=%s settled=%ld", biz_date, settled); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -217,31 +169,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0015] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0015] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0015(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0015] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0015.pgc b/app/batch/py_bt_0015.pgc index 8dc4c9d..40cb5b4 100644 --- a/app/batch/py_bt_0015.pgc +++ b/app/batch/py_bt_0015.pgc @@ -1,11 +1,8 @@ -/* @tier easy @module py @transform payment-batch */ +/* @tier medium @module py @transform payment-batch */ /* - * py_bt_0015.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=easy] + * py_bt_0015.pgc - 지급 정산반영 배치 (이체완료 정산확정 S 유지/집계) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0015 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0015 [db@host] */ #include #include @@ -21,83 +18,39 @@ EXEC SQL INCLUDE sqlca; static int run_py_bt_0015(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_key[16], h_merch[16]; + long h_amount, h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, settled = 0, errcnt = 0, netsum = 0; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_py_bt_0015 CURSOR FOR - SELECT key, merch_id, amount - FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_py_bt_0015; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0015"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0015] tx_begin 경고"); + EXEC SQL DECLARE cur_pyset CURSOR FOR + SELECT key, merch_id, amount, fee FROM py_ledger + WHERE biz_date = :h_biz_date AND status = 'M' + ORDER BY merch_id, key; + EXEC SQL OPEN cur_pyset; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pyset"); return TX_EDB; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[py_bt_0015] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_py_bt_0015 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0015"); - errcnt++; - break; - } - + EXEC SQL FETCH cur_pyset INTO :h_key, :h_merch, :h_amount, :h_fee; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; break; } total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL UPDATE py_ledger SET status = 'S', upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { errcnt++; continue; } + settled++; + netsum += h_amount - h_fee; } + EXEC SQL CLOSE cur_pyset; + if (errcnt == 0) tx_commit(); else tx_abort(); - EXEC SQL CLOSE cur_py_bt_0015; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - + printf("===== 지급 정산반영 [py_bt_0015] date=%s =====\n", biz_date); + printf(" 대상(M)=%ld 정산(S)=%ld 순정산액=%ld 오류=%ld\n", + total, settled, netsum, errcnt); + tx_log(TX_LOG_INFO, "[py_bt_0015] 정산반영 date=%s settled=%ld", biz_date, settled); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -108,31 +61,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0015] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0015] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0015(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0015] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0016.c b/app/batch/py_bt_0016.c index 463b446..c864366 100644 --- a/app/batch/py_bt_0016.c +++ b/app/batch/py_bt_0016.c @@ -6,14 +6,11 @@ /* End of automatic include section */ #line 1 "app/batch/py_bt_0016.pgc" -/* @tier medium @module py @transform payment-batch */ +/* @tier hard @module py @transform payment-batch */ /* - * py_bt_0016.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=medium] + * py_bt_0016.pgc - 지급 실패 재처리 배치 (실패건 재이체 tx_call) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0016 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0016 [db@host] */ #include #include @@ -93,121 +90,90 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/py_bt_0016.pgc" +#line 16 "app/batch/py_bt_0016.pgc" static int run_py_bt_0016(const char *biz_date) { /* exec sql begin declare section */ - - - - - + + -#line 24 "app/batch/py_bt_0016.pgc" - char h_biz_date [ 9 ] ; +#line 21 "app/batch/py_bt_0016.pgc" + char h_biz_date [ 9 ] , h_key [ 16 ] , h_acct [ 20 ] ; -#line 25 "app/batch/py_bt_0016.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/py_bt_0016.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/py_bt_0016.pgc" - long h_amount ; - -#line 28 "app/batch/py_bt_0016.pgc" - char h_new_status [ 2 ] ; +#line 22 "app/batch/py_bt_0016.pgc" + long h_amount , h_fee ; /* exec sql end declare section */ -#line 29 "app/batch/py_bt_0016.pgc" +#line 23 "app/batch/py_bt_0016.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + TXBUF *req, *resp; + long total = 0, ok = 0, fail = 0; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_py_bt_0016 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/py_bt_0016.pgc" + /* declare cur_pyredo cursor for select key , card_no , amount , fee from py_ledger where biz_date = $1 and status = 'U' and card_no <> 'SPLIT' order by key */ +#line 34 "app/batch/py_bt_0016.pgc" -#line 42 "app/batch/py_bt_0016.pgc" +#line 34 "app/batch/py_bt_0016.pgc" - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_py_bt_0016 cursor for select key , merch_id , amount from py_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_pyredo cursor for select key , card_no , amount , fee from py_ledger where biz_date = $1 and status = 'U' and card_no <> 'SPLIT' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/py_bt_0016.pgc" +#line 35 "app/batch/py_bt_0016.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0016"); - return TX_EDB; - } + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pyredo"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0016] tx_begin 경고"); + req = tx_buf_alloc(); + resp = tx_buf_alloc(); + if (!req || !resp) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_pyredo", ECPGt_EOIT, ECPGt_EORT);} +#line 40 "app/batch/py_bt_0016.pgc" + return TX_ENOMEM; } for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_py_bt_0016", ECPGt_EOIT, + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_pyredo", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_acct),(long)20,(long)1,(20)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/py_bt_0016.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0016"); - errcnt++; - break; - } +#line 43 "app/batch/py_bt_0016.pgc" + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; total++; + tx_buf_reset(req); + tx_buf_reset(resp); + tx_buf_sets(req, "ACCTNO", h_acct); + tx_buf_setlong(req, "AMOUNT", h_amount - h_fee); + rc = tx_call("PY_TRANSFER", req, resp); + if (rc == TX_OK) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'S' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 53 "app/batch/py_bt_0016.pgc" - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; + if (sqlca.sqlcode == TX_SQL_OK) { tx_commit(); ok++; } } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + fail++; } } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_py_bt_0016", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/py_bt_0016.pgc" + tx_buf_free(req); + tx_buf_free(resp); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_pyredo", ECPGt_EOIT, ECPGt_EORT);} +#line 61 "app/batch/py_bt_0016.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 지급 실패 재처리 [py_bt_0016] 대상=%ld 재이체성공=%ld 실패=%ld =====\n", + total, ok, fail); + tx_log(TX_LOG_INFO, "[py_bt_0016] 실패재처리 date=%s ok=%ld fail=%ld", biz_date, ok, fail); + return TX_OK; } int main(int argc, char **argv) @@ -217,31 +183,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0016] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0016] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0016(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0016] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/py_bt_0016.pgc b/app/batch/py_bt_0016.pgc index e1d1973..9a15cec 100644 --- a/app/batch/py_bt_0016.pgc +++ b/app/batch/py_bt_0016.pgc @@ -1,11 +1,8 @@ -/* @tier medium @module py @transform payment-batch */ +/* @tier hard @module py @transform payment-batch */ /* - * py_bt_0016.pgc - 지급 배치 (커서 순회 + 상태전이) [tier=medium] + * py_bt_0016.pgc - 지급 실패 재처리 배치 (실패건 재이체 tx_call) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. - * - * 실행: py_bt_0016 [db@host] + * 배치 main 엔트리 포함. 실행: py_bt_0016 [db@host] */ #include #include @@ -21,84 +18,52 @@ EXEC SQL INCLUDE sqlca; static int run_py_bt_0016(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_biz_date[9], h_key[16], h_acct[20]; + long h_amount, h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + TXBUF *req, *resp; + long total = 0, ok = 0, fail = 0; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_biz_date, biz_date, 8); h_biz_date[8] = '\0'; - EXEC SQL DECLARE cur_py_bt_0016 CURSOR FOR - SELECT key, merch_id, amount - FROM py_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' + EXEC SQL DECLARE cur_pyredo CURSOR FOR + SELECT key, card_no, amount, fee FROM py_ledger + WHERE biz_date = :h_biz_date AND status = 'U' AND card_no <> 'SPLIT' ORDER BY key; + EXEC SQL OPEN cur_pyredo; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pyredo"); return TX_EDB; } - EXEC SQL OPEN cur_py_bt_0016; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_py_bt_0016"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[py_bt_0016] tx_begin 경고"); + req = tx_buf_alloc(); + resp = tx_buf_alloc(); + if (!req || !resp) { EXEC SQL CLOSE cur_pyredo; return TX_ENOMEM; } for (;;) { - EXEC SQL FETCH cur_py_bt_0016 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_py_bt_0016"); - errcnt++; - break; - } - + EXEC SQL FETCH cur_pyredo INTO :h_key, :h_acct, :h_amount, :h_fee; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, PY_ST_DONE, sizeof(h_new_status)); - done++; + tx_buf_reset(req); + tx_buf_reset(resp); + tx_buf_sets(req, "ACCTNO", h_acct); + tx_buf_setlong(req, "AMOUNT", h_amount - h_fee); + rc = tx_call("PY_TRANSFER", req, resp); + if (rc == TX_OK) { + EXEC SQL UPDATE py_ledger SET status = 'S', upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_OK) { tx_commit(); ok++; } } else { - strncpy(h_new_status, PY_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = py_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[py_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + fail++; } } + tx_buf_free(req); + tx_buf_free(resp); + EXEC SQL CLOSE cur_pyredo; - EXEC SQL CLOSE cur_py_bt_0016; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[py_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 지급 배치 결과 [py_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); - - return (errcnt == 0) ? TX_OK : TX_FAIL; + printf("===== 지급 실패 재처리 [py_bt_0016] 대상=%ld 재이체성공=%ld 실패=%ld =====\n", + total, ok, fail); + tx_log(TX_LOG_INFO, "[py_bt_0016] 실패재처리 date=%s ok=%ld fail=%ld", biz_date, ok, fail); + return TX_OK; } int main(int argc, char **argv) @@ -108,31 +73,24 @@ int main(int argc, char **argv) int rc; tx_set_loglevel(TX_LOG_INFO); - if (argc < 2) { fprintf(stderr, "사용법: %s [db@host]\n", argv[0]); return 2; } biz_date = argv[1]; target = (argc >= 3) ? argv[2] : NULL; - if (!date_is_valid(biz_date)) { fprintf(stderr, "오류: 유효하지 않은 영업일 '%s'\n", biz_date); return 2; } - tx_log(TX_LOG_INFO, "[py_bt_0016] 배치 시작 date=%s", biz_date); - rc = py_dbio_connect(target); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[py_bt_0016] DB 접속 실패 rc=%d", rc); return 1; } - rc = run_py_bt_0016(biz_date); - py_dbio_disconnect(); - tx_log(TX_LOG_INFO, "[py_bt_0016] 배치 종료 rc=%d(%s)", rc, tx_strerror(rc)); return (rc == TX_OK) ? 0 : 1; } diff --git a/app/batch/rc_bt_0002.c b/app/batch/rc_bt_0002.c index 1096805..9d2db86 100644 --- a/app/batch/rc_bt_0002.c +++ b/app/batch/rc_bt_0002.c @@ -8,10 +8,12 @@ #line 1 "app/batch/rc_bt_0002.pgc" /* @tier medium @module rc @transform reconciliation-batch */ /* - * rc_bt_0002.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=medium] + * rc_bt_0002.pgc - 승인-매입 대사 배치 (RC_BT_0002) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수('R') 건을 커서로 순회하며 승인-매입 대사를 수행한다. + * 업무규칙: 매입금액(amount) 과 수수료(fee) 로 역산한 "승인 추정금액" + * (amount - fee) 이 유효금액이면 매입확정('M'), 아니면 불일치('U'). + * 매입확정 건의 금액 합계와 불일치 차액 누계를 리포트한다. * * 실행: rc_bt_0002 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0002.pgc" +#line 21 "app/batch/rc_bt_0002.pgc" static int run_rc_bt_0002(const char *biz_date) @@ -105,41 +107,42 @@ static int run_rc_bt_0002(const char *biz_date) -#line 24 "app/batch/rc_bt_0002.pgc" +#line 26 "app/batch/rc_bt_0002.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/rc_bt_0002.pgc" +#line 27 "app/batch/rc_bt_0002.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/rc_bt_0002.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/rc_bt_0002.pgc" +#line 28 "app/batch/rc_bt_0002.pgc" long h_amount ; -#line 28 "app/batch/rc_bt_0002.pgc" +#line 29 "app/batch/rc_bt_0002.pgc" + long h_fee ; + +#line 30 "app/batch/rc_bt_0002.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0002.pgc" +#line 31 "app/batch/rc_bt_0002.pgc" long total = 0, done = 0, unmatch = 0, errcnt = 0; + long matched_sum = 0, diff_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0002 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0002.pgc" + /* declare cur_rc_bt_0002 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'R' order by key */ +#line 45 "app/batch/rc_bt_0002.pgc" -#line 42 "app/batch/rc_bt_0002.pgc" +#line 45 "app/batch/rc_bt_0002.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0002 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0002 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'R' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0002.pgc" +#line 47 "app/batch/rc_bt_0002.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0002"); @@ -150,14 +153,16 @@ static int run_rc_bt_0002(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0002] tx_begin 경고"); for (;;) { + long appr_est; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0002", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0002.pgc" +#line 59 "app/batch/rc_bt_0002.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +175,28 @@ static int run_rc_bt_0002(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + /* 승인 추정금액 = 매입금액 - 수수료 */ + appr_est = h_amount - h_fee; + if (amount_is_valid(appr_est) && appr_est <= h_amount) { strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); done++; + matched_sum += h_amount; } else { strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); unmatch++; + diff_sum += (h_amount - appr_est); } rc = rc_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[rc_bt_0002] 상태전이 실패 key=%s rc=%d", + h_key, rc); errcnt++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0002", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0002.pgc" +#line 91 "app/batch/rc_bt_0002.pgc" if (errcnt == 0) @@ -196,15 +205,17 @@ static int run_rc_bt_0002(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0002] 요약 date=%s 대상=%ld 매입확정=%ld 불일치=%ld", + biz_date, total, done, unmatch); - printf("========== 대사 배치 결과 [rc_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("======= 승인-매입 대사 배치 [rc_bt_0002] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 매입확정(M) : %ld\n", done); + printf(" 불일치(U) : %ld\n", unmatch); + printf(" 매입확정 합계 : %ld\n", matched_sum); + printf(" 불일치 차액누계 : %ld\n", diff_sum); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/rc_bt_0002.pgc b/app/batch/rc_bt_0002.pgc index 3a430c8..6b1272e 100644 --- a/app/batch/rc_bt_0002.pgc +++ b/app/batch/rc_bt_0002.pgc @@ -1,9 +1,11 @@ /* @tier medium @module rc @transform reconciliation-batch */ /* - * rc_bt_0002.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=medium] + * rc_bt_0002.pgc - 승인-매입 대사 배치 (RC_BT_0002) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수('R') 건을 커서로 순회하며 승인-매입 대사를 수행한다. + * 업무규칙: 매입금액(amount) 과 수수료(fee) 로 역산한 "승인 추정금액" + * (amount - fee) 이 유효금액이면 매입확정('M'), 아니면 불일치('U'). + * 매입확정 건의 금액 합계와 불일치 차액 누계를 리포트한다. * * 실행: rc_bt_0002 [db@host] */ @@ -23,19 +25,20 @@ static int run_rc_bt_0002(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; + long h_fee; char h_new_status[2]; EXEC SQL END DECLARE SECTION; long total = 0, done = 0, unmatch = 0, errcnt = 0; + long matched_sum = 0, diff_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_rc_bt_0002 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount, fee FROM rc_ledger WHERE biz_date = :h_biz_date AND status = 'R' @@ -51,7 +54,9 @@ static int run_rc_bt_0002(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0002] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_rc_bt_0002 INTO :h_key, :h_merch_id, :h_amount; + long appr_est; + + EXEC SQL FETCH cur_rc_bt_0002 INTO :h_key, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,18 +68,22 @@ static int run_rc_bt_0002(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + /* 승인 추정금액 = 매입금액 - 수수료 */ + appr_est = h_amount - h_fee; + if (amount_is_valid(appr_est) && appr_est <= h_amount) { strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); done++; + matched_sum += h_amount; } else { strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); unmatch++; + diff_sum += (h_amount - appr_est); } rc = rc_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[rc_bt_0002] 상태전이 실패 key=%s rc=%d", + h_key, rc); errcnt++; } } @@ -87,15 +96,17 @@ static int run_rc_bt_0002(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0002] 요약 date=%s 대상=%ld 매입확정=%ld 불일치=%ld", + biz_date, total, done, unmatch); - printf("========== 대사 배치 결과 [rc_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("======= 승인-매입 대사 배치 [rc_bt_0002] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 매입확정(M) : %ld\n", done); + printf(" 불일치(U) : %ld\n", unmatch); + printf(" 매입확정 합계 : %ld\n", matched_sum); + printf(" 불일치 차액누계 : %ld\n", diff_sum); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/rc_bt_0003.c b/app/batch/rc_bt_0003.c index 09ad3b3..773c82d 100644 --- a/app/batch/rc_bt_0003.c +++ b/app/batch/rc_bt_0003.c @@ -8,10 +8,13 @@ #line 1 "app/batch/rc_bt_0003.pgc" /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0003.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0003.pgc - 매입-입금 대사 배치 (RC_BT_0003) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 매입확정('M') 건을 커서로 순회하며 매입-입금 대사를 수행한다. + * 업무규칙: 순액(net) = 매입금액(amount) - 수수료(fee). + * 순액이 양수이고 유효금액이면 입금확정 → 정산완료('S'), + * 그렇지 않으면 불일치('U') 로 전이한다. + * 정산확정 건수/정산합계 와 불일치 건수를 리포트한다. * * 실행: rc_bt_0003 [db@host] */ @@ -93,7 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0003.pgc" +#line 22 "app/batch/rc_bt_0003.pgc" static int run_rc_bt_0003(const char *biz_date) @@ -105,41 +108,42 @@ static int run_rc_bt_0003(const char *biz_date) -#line 24 "app/batch/rc_bt_0003.pgc" +#line 27 "app/batch/rc_bt_0003.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/rc_bt_0003.pgc" +#line 28 "app/batch/rc_bt_0003.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/rc_bt_0003.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/rc_bt_0003.pgc" +#line 29 "app/batch/rc_bt_0003.pgc" long h_amount ; -#line 28 "app/batch/rc_bt_0003.pgc" +#line 30 "app/batch/rc_bt_0003.pgc" + long h_fee ; + +#line 31 "app/batch/rc_bt_0003.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0003.pgc" +#line 32 "app/batch/rc_bt_0003.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, settled = 0, unmatch = 0, errcnt = 0; + long settled_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0003 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0003.pgc" + /* declare cur_rc_bt_0003 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'M' order by key */ +#line 46 "app/batch/rc_bt_0003.pgc" -#line 42 "app/batch/rc_bt_0003.pgc" +#line 46 "app/batch/rc_bt_0003.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0003 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0003 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0003.pgc" +#line 48 "app/batch/rc_bt_0003.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0003"); @@ -150,14 +154,16 @@ static int run_rc_bt_0003(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0003] tx_begin 경고"); for (;;) { + long net; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0003", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0003.pgc" +#line 60 "app/batch/rc_bt_0003.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,10 +176,12 @@ static int run_rc_bt_0003(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; + /* 순액 = 매입금액 - 수수료 → 입금 대상 금액 */ + net = h_amount - h_fee; + if (net > 0 && amount_is_valid(net)) { + strncpy(h_new_status, RC_ST_SETTLED, sizeof(h_new_status)); + settled++; + settled_sum += net; } else { strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); unmatch++; @@ -181,13 +189,14 @@ static int run_rc_bt_0003(const char *biz_date) rc = rc_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[rc_bt_0003] 상태전이 실패 key=%s rc=%d", + h_key, rc); errcnt++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0003", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0003.pgc" +#line 91 "app/batch/rc_bt_0003.pgc" if (errcnt == 0) @@ -196,15 +205,16 @@ static int run_rc_bt_0003(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0003] 요약 date=%s 대상=%ld 정산=%ld 불일치=%ld", + biz_date, total, settled, unmatch); - printf("========== 대사 배치 결과 [rc_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("======= 매입-입금 대사 배치 [rc_bt_0003] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정산확정(S) : %ld\n", settled); + printf(" 불일치(U) : %ld\n", unmatch); + printf(" 정산 합계 : %ld\n", settled_sum); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/rc_bt_0003.pgc b/app/batch/rc_bt_0003.pgc index 6bc42f4..5d8fb23 100644 --- a/app/batch/rc_bt_0003.pgc +++ b/app/batch/rc_bt_0003.pgc @@ -1,9 +1,12 @@ /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0003.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0003.pgc - 매입-입금 대사 배치 (RC_BT_0003) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 매입확정('M') 건을 커서로 순회하며 매입-입금 대사를 수행한다. + * 업무규칙: 순액(net) = 매입금액(amount) - 수수료(fee). + * 순액이 양수이고 유효금액이면 입금확정 → 정산완료('S'), + * 그렇지 않으면 불일치('U') 로 전이한다. + * 정산확정 건수/정산합계 와 불일치 건수를 리포트한다. * * 실행: rc_bt_0003 [db@host] */ @@ -23,22 +26,23 @@ static int run_rc_bt_0003(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; + long h_fee; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, settled = 0, unmatch = 0, errcnt = 0; + long settled_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_rc_bt_0003 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount, fee FROM rc_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'M' ORDER BY key; EXEC SQL OPEN cur_rc_bt_0003; @@ -51,7 +55,9 @@ static int run_rc_bt_0003(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0003] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_rc_bt_0003 INTO :h_key, :h_merch_id, :h_amount; + long net; + + EXEC SQL FETCH cur_rc_bt_0003 INTO :h_key, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,10 +69,12 @@ static int run_rc_bt_0003(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; + /* 순액 = 매입금액 - 수수료 → 입금 대상 금액 */ + net = h_amount - h_fee; + if (net > 0 && amount_is_valid(net)) { + strncpy(h_new_status, RC_ST_SETTLED, sizeof(h_new_status)); + settled++; + settled_sum += net; } else { strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); unmatch++; @@ -74,7 +82,8 @@ static int run_rc_bt_0003(const char *biz_date) rc = rc_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[rc_bt_0003] 상태전이 실패 key=%s rc=%d", + h_key, rc); errcnt++; } } @@ -87,15 +96,16 @@ static int run_rc_bt_0003(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0003] 요약 date=%s 대상=%ld 정산=%ld 불일치=%ld", + biz_date, total, settled, unmatch); - printf("========== 대사 배치 결과 [rc_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("======= 매입-입금 대사 배치 [rc_bt_0003] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정산확정(S) : %ld\n", settled); + printf(" 불일치(U) : %ld\n", unmatch); + printf(" 정산 합계 : %ld\n", settled_sum); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/rc_bt_0004.c b/app/batch/rc_bt_0004.c index 149a20a..aad3ca2 100644 --- a/app/batch/rc_bt_0004.c +++ b/app/batch/rc_bt_0004.c @@ -8,10 +8,13 @@ #line 1 "app/batch/rc_bt_0004.pgc" /* @tier medium @module rc @transform reconciliation-batch */ /* - * rc_bt_0004.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=medium] + * rc_bt_0004.pgc - 반송(return) 대사 배치 (RC_BT_0004) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수('R') 건을 커서로 순회하며 반송/취소 대사를 수행한다. + * 업무규칙: 매입금액(amount) 이 수수료(fee) 보다 작으면 순액이 음수가 되어 + * 반송/취소 건으로 간주 → 불일치('U') 로 전이하고 반송차액 + * (fee - amount) 을 누계한다. + * 그 외 정상 건은 상태를 변경하지 않고 통과시킨다. * * 실행: rc_bt_0004 [db@host] */ @@ -93,7 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0004.pgc" +#line 22 "app/batch/rc_bt_0004.pgc" static int run_rc_bt_0004(const char *biz_date) @@ -105,41 +108,42 @@ static int run_rc_bt_0004(const char *biz_date) -#line 24 "app/batch/rc_bt_0004.pgc" +#line 27 "app/batch/rc_bt_0004.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/rc_bt_0004.pgc" +#line 28 "app/batch/rc_bt_0004.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/rc_bt_0004.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/rc_bt_0004.pgc" +#line 29 "app/batch/rc_bt_0004.pgc" long h_amount ; -#line 28 "app/batch/rc_bt_0004.pgc" +#line 30 "app/batch/rc_bt_0004.pgc" + long h_fee ; + +#line 31 "app/batch/rc_bt_0004.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0004.pgc" +#line 32 "app/batch/rc_bt_0004.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, return_cnt = 0, skip_cnt = 0, errcnt = 0; + long return_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0004 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0004.pgc" + /* declare cur_rc_bt_0004 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'R' order by key */ +#line 46 "app/batch/rc_bt_0004.pgc" -#line 42 "app/batch/rc_bt_0004.pgc" +#line 46 "app/batch/rc_bt_0004.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0004 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0004 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'R' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0004.pgc" +#line 48 "app/batch/rc_bt_0004.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0004"); @@ -153,11 +157,11 @@ static int run_rc_bt_0004(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0004", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0004.pgc" +#line 58 "app/batch/rc_bt_0004.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +174,26 @@ static int run_rc_bt_0004(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { + /* 매입금액 < 수수료 → 순액 음수 → 반송/취소 */ + if (h_amount < h_fee) { strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + return_cnt++; + return_sum += (h_fee - h_amount); - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + rc = rc_rec_update_status(h_key, h_new_status); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[rc_bt_0004] 반송전이 실패 key=%s rc=%d", + h_key, rc); + errcnt++; + } + } else { + /* 정상 통과 - 상태 변경 없음 */ + skip_cnt++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0004", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0004.pgc" +#line 88 "app/batch/rc_bt_0004.pgc" if (errcnt == 0) @@ -196,16 +202,17 @@ static int run_rc_bt_0004(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0004] 요약 date=%s 대상=%ld 반송=%ld 통과=%ld", + biz_date, total, return_cnt, skip_cnt); - printf("========== 대사 배치 결과 [rc_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 반송 대사 배치 [rc_bt_0004] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 반송처리(U) : %ld\n", return_cnt); + printf(" 정상통과 : %ld\n", skip_cnt); + printf(" 반송차액 합계 : %ld\n", return_sum); + printf(" 오류 건수 : %ld\n", errcnt); + printf("========================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0004.pgc b/app/batch/rc_bt_0004.pgc index f6b2a11..65c0ae4 100644 --- a/app/batch/rc_bt_0004.pgc +++ b/app/batch/rc_bt_0004.pgc @@ -1,9 +1,12 @@ /* @tier medium @module rc @transform reconciliation-batch */ /* - * rc_bt_0004.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=medium] + * rc_bt_0004.pgc - 반송(return) 대사 배치 (RC_BT_0004) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수('R') 건을 커서로 순회하며 반송/취소 대사를 수행한다. + * 업무규칙: 매입금액(amount) 이 수수료(fee) 보다 작으면 순액이 음수가 되어 + * 반송/취소 건으로 간주 → 불일치('U') 로 전이하고 반송차액 + * (fee - amount) 을 누계한다. + * 그 외 정상 건은 상태를 변경하지 않고 통과시킨다. * * 실행: rc_bt_0004 [db@host] */ @@ -23,19 +26,20 @@ static int run_rc_bt_0004(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; + long h_fee; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, return_cnt = 0, skip_cnt = 0, errcnt = 0; + long return_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_rc_bt_0004 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount, fee FROM rc_ledger WHERE biz_date = :h_biz_date AND status = 'R' @@ -51,7 +55,7 @@ static int run_rc_bt_0004(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0004] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_rc_bt_0004 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_rc_bt_0004 INTO :h_key, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,19 +67,21 @@ static int run_rc_bt_0004(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { + /* 매입금액 < 수수료 → 순액 음수 → 반송/취소 */ + if (h_amount < h_fee) { strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + return_cnt++; + return_sum += (h_fee - h_amount); - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + rc = rc_rec_update_status(h_key, h_new_status); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[rc_bt_0004] 반송전이 실패 key=%s rc=%d", + h_key, rc); + errcnt++; + } + } else { + /* 정상 통과 - 상태 변경 없음 */ + skip_cnt++; } } @@ -87,16 +93,17 @@ static int run_rc_bt_0004(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0004] 요약 date=%s 대상=%ld 반송=%ld 통과=%ld", + biz_date, total, return_cnt, skip_cnt); - printf("========== 대사 배치 결과 [rc_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 반송 대사 배치 [rc_bt_0004] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 반송처리(U) : %ld\n", return_cnt); + printf(" 정상통과 : %ld\n", skip_cnt); + printf(" 반송차액 합계 : %ld\n", return_sum); + printf(" 오류 건수 : %ld\n", errcnt); + printf("========================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0005.c b/app/batch/rc_bt_0005.c index 1799294..3c6560c 100644 --- a/app/batch/rc_bt_0005.c +++ b/app/batch/rc_bt_0005.c @@ -8,10 +8,14 @@ #line 1 "app/batch/rc_bt_0005.pgc" /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0005.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0005.pgc - 승인-매입 금액불일치 검출 배치 (RC_BT_0005) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수('R') 건을 커서로 순회하며 수수료 정합성을 검증한다. + * 업무규칙: 표준 수수료율 2.5% 로 역산한 추정수수료 + * exp_fee = amount * 25 / 1000 과 실제 수수료(fee) 의 절대차가 + * 허용오차 RC_TOL_ABS(원) 를 초과하면 금액불일치 → 불일치('U'), + * 허용범위 이내면 매입확정('M') 으로 전이한다. + * 불일치 차액을 누계하여 리포트한다. * * 실행: rc_bt_0005 [db@host] */ @@ -93,9 +97,12 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0005.pgc" +#line 23 "app/batch/rc_bt_0005.pgc" +/* 수수료 절대 허용오차 (원) */ +#define RC_TOL_ABS 100 + static int run_rc_bt_0005(const char *biz_date) { /* exec sql begin declare section */ @@ -105,41 +112,42 @@ static int run_rc_bt_0005(const char *biz_date) -#line 24 "app/batch/rc_bt_0005.pgc" +#line 31 "app/batch/rc_bt_0005.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/rc_bt_0005.pgc" +#line 32 "app/batch/rc_bt_0005.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/rc_bt_0005.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/rc_bt_0005.pgc" +#line 33 "app/batch/rc_bt_0005.pgc" long h_amount ; -#line 28 "app/batch/rc_bt_0005.pgc" +#line 34 "app/batch/rc_bt_0005.pgc" + long h_fee ; + +#line 35 "app/batch/rc_bt_0005.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0005.pgc" +#line 36 "app/batch/rc_bt_0005.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, match = 0, mismatch = 0, errcnt = 0; + long total_diff = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0005 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0005.pgc" + /* declare cur_rc_bt_0005 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'R' order by key */ +#line 50 "app/batch/rc_bt_0005.pgc" -#line 42 "app/batch/rc_bt_0005.pgc" +#line 50 "app/batch/rc_bt_0005.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0005 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0005 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'R' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0005.pgc" +#line 52 "app/batch/rc_bt_0005.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0005"); @@ -150,14 +158,16 @@ static int run_rc_bt_0005(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0005] tx_begin 경고"); for (;;) { + long exp_fee, feediff, absd; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0005", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0005.pgc" +#line 64 "app/batch/rc_bt_0005.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +180,30 @@ static int run_rc_bt_0005(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { + /* 추정수수료 = 매입금액 * 2.5% */ + exp_fee = h_amount * 25 / 1000; + feediff = h_fee - exp_fee; + absd = labs(feediff); + + if (absd > RC_TOL_ABS) { strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + mismatch++; + total_diff += absd; + } else { + strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); + match++; } rc = rc_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[rc_bt_0005] 상태전이 실패 key=%s rc=%d", + h_key, rc); errcnt++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0005", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0005.pgc" +#line 98 "app/batch/rc_bt_0005.pgc" if (errcnt == 0) @@ -196,16 +212,18 @@ static int run_rc_bt_0005(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0005] 요약 date=%s 대상=%ld 일치=%ld 불일치=%ld", + biz_date, total, match, mismatch); - printf("========== 대사 배치 결과 [rc_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("==== 금액불일치 검출 배치 [rc_bt_0005] ====\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 일치(M) : %ld\n", match); + printf(" 불일치(U) : %ld\n", mismatch); + printf(" 차액 누계 : %ld\n", total_diff); + printf(" 허용오차(원) : %d\n", RC_TOL_ABS); + printf(" 오류 건수 : %ld\n", errcnt); + printf("========================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0005.pgc b/app/batch/rc_bt_0005.pgc index 43ae332..29a9a14 100644 --- a/app/batch/rc_bt_0005.pgc +++ b/app/batch/rc_bt_0005.pgc @@ -1,9 +1,13 @@ /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0005.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0005.pgc - 승인-매입 금액불일치 검출 배치 (RC_BT_0005) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수('R') 건을 커서로 순회하며 수수료 정합성을 검증한다. + * 업무규칙: 표준 수수료율 2.5% 로 역산한 추정수수료 + * exp_fee = amount * 25 / 1000 과 실제 수수료(fee) 의 절대차가 + * 허용오차 RC_TOL_ABS(원) 를 초과하면 금액불일치 → 불일치('U'), + * 허용범위 이내면 매입확정('M') 으로 전이한다. + * 불일치 차액을 누계하여 리포트한다. * * 실행: rc_bt_0005 [db@host] */ @@ -18,24 +22,28 @@ EXEC SQL INCLUDE sqlca; +/* 수수료 절대 허용오차 (원) */ +#define RC_TOL_ABS 100 + static int run_rc_bt_0005(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; + long h_fee; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, match = 0, mismatch = 0, errcnt = 0; + long total_diff = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_rc_bt_0005 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount, fee FROM rc_ledger WHERE biz_date = :h_biz_date AND status = 'R' @@ -51,7 +59,9 @@ static int run_rc_bt_0005(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0005] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_rc_bt_0005 INTO :h_key, :h_merch_id, :h_amount; + long exp_fee, feediff, absd; + + EXEC SQL FETCH cur_rc_bt_0005 INTO :h_key, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,18 +73,24 @@ static int run_rc_bt_0005(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { + /* 추정수수료 = 매입금액 * 2.5% */ + exp_fee = h_amount * 25 / 1000; + feediff = h_fee - exp_fee; + absd = labs(feediff); + + if (absd > RC_TOL_ABS) { strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + mismatch++; + total_diff += absd; + } else { + strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); + match++; } rc = rc_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[rc_bt_0005] 상태전이 실패 key=%s rc=%d", + h_key, rc); errcnt++; } } @@ -87,16 +103,18 @@ static int run_rc_bt_0005(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0005] 요약 date=%s 대상=%ld 일치=%ld 불일치=%ld", + biz_date, total, match, mismatch); - printf("========== 대사 배치 결과 [rc_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("==== 금액불일치 검출 배치 [rc_bt_0005] ====\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 일치(M) : %ld\n", match); + printf(" 불일치(U) : %ld\n", mismatch); + printf(" 차액 누계 : %ld\n", total_diff); + printf(" 허용오차(원) : %d\n", RC_TOL_ABS); + printf(" 오류 건수 : %ld\n", errcnt); + printf("========================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0006.c b/app/batch/rc_bt_0006.c index 4f4d33f..c93405b 100644 --- a/app/batch/rc_bt_0006.c +++ b/app/batch/rc_bt_0006.c @@ -8,10 +8,13 @@ #line 1 "app/batch/rc_bt_0006.pgc" /* @tier hard @module rc @transform reconciliation-batch */ /* - * rc_bt_0006.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=hard] + * rc_bt_0006.pgc - 건수 불일치 검출 배치 (RC_BT_0006) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 상태별 건수를 집계하여 대사 정합성을 점검하는 + * 읽기전용(리포트 전용) 배치이다. 커서/갱신을 사용하지 않고 표준 + * DBIO API rc_rec_count() 만으로 상태별 건수를 조회한다. + * 업무규칙: 접수('R') 잔량이 남아 있으면 아직 대사가 완료되지 않은 + * 것으로 보아 '미처리' 로 판정한다. * * 실행: rc_bt_0006 [db@host] */ @@ -93,121 +96,77 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0006.pgc" +#line 22 "app/batch/rc_bt_0006.pgc" static int run_rc_bt_0006(const char *biz_date) { - /* exec sql begin declare section */ - - - - - - -#line 24 "app/batch/rc_bt_0006.pgc" - char h_biz_date [ 9 ] ; - -#line 25 "app/batch/rc_bt_0006.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/rc_bt_0006.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/rc_bt_0006.pgc" - long h_amount ; - -#line 28 "app/batch/rc_bt_0006.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0006.pgc" + long cntR, cntM, cntU, cntS; + long total, processed; + long proc_rate, settle_rate, fail_rate; + int mismatch; + /* 상태별 건수 조회 (읽기전용) */ + cntR = rc_rec_count(biz_date, RC_ST_RECV); + cntM = rc_rec_count(biz_date, RC_ST_DONE); + cntU = rc_rec_count(biz_date, RC_ST_FAIL); + cntS = rc_rec_count(biz_date, RC_ST_SETTLED); - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; - - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0006 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0006.pgc" - -#line 42 "app/batch/rc_bt_0006.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0006 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0006.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0006"); + if (cntR < 0) { + tx_log(TX_LOG_ERROR, "[rc_bt_0006] 접수(R) 건수 조회 실패 date=%s", + biz_date); + return TX_EDB; + } + if (cntM < 0) { + tx_log(TX_LOG_ERROR, "[rc_bt_0006] 매입확정(M) 건수 조회 실패 date=%s", + biz_date); + return TX_EDB; + } + if (cntU < 0) { + tx_log(TX_LOG_ERROR, "[rc_bt_0006] 불일치(U) 건수 조회 실패 date=%s", + biz_date); + return TX_EDB; + } + if (cntS < 0) { + tx_log(TX_LOG_ERROR, "[rc_bt_0006] 정산(S) 건수 조회 실패 date=%s", + biz_date); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[rc_bt_0006] tx_begin 경고"); + total = cntR + cntM + cntU + cntS; + processed = cntM + cntU + cntS; + mismatch = (cntR > 0) ? 1 : 0; - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0006", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0006.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_rc_bt_0006"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 처리율/정산율/불일치율 (백분율, 정수 반올림) */ + if (total > 0) { + proc_rate = (processed * 100) / total; + settle_rate = (cntS * 100) / total; + fail_rate = (cntU * 100) / total; + } else { + proc_rate = settle_rate = fail_rate = 0; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0006", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0006.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[rc_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0006] 요약 date=%s 합계=%ld 처리=%ld 미처리여부=%d", + biz_date, total, processed, mismatch); - printf("========== 대사 배치 결과 [rc_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 건수 불일치 검출 배치 [rc_bt_0006] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" ----------------------------------------\n"); + printf(" 접수(R) : %ld\n", cntR); + printf(" 매입확정(M) : %ld\n", cntM); + printf(" 불일치(U) : %ld\n", cntU); + printf(" 정산(S) : %ld\n", cntS); + printf(" ----------------------------------------\n"); + printf(" 합계 : %ld\n", total); + printf(" 처리 완료 : %ld\n", processed); + printf(" 처리율(%%) : %ld\n", proc_rate); + printf(" 정산율(%%) : %ld\n", settle_rate); + printf(" 불일치율(%%) : %ld\n", fail_rate); + printf(" 미처리 여부 : %s\n", mismatch ? "미처리(접수 잔량)" : "완료"); + printf("===============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/rc_bt_0006.pgc b/app/batch/rc_bt_0006.pgc index b6cce54..21a4e98 100644 --- a/app/batch/rc_bt_0006.pgc +++ b/app/batch/rc_bt_0006.pgc @@ -1,9 +1,12 @@ /* @tier hard @module rc @transform reconciliation-batch */ /* - * rc_bt_0006.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=hard] + * rc_bt_0006.pgc - 건수 불일치 검출 배치 (RC_BT_0006) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 상태별 건수를 집계하여 대사 정합성을 점검하는 + * 읽기전용(리포트 전용) 배치이다. 커서/갱신을 사용하지 않고 표준 + * DBIO API rc_rec_count() 만으로 상태별 건수를 조회한다. + * 업무규칙: 접수('R') 잔량이 남아 있으면 아직 대사가 완료되지 않은 + * 것으로 보아 '미처리' 로 판정한다. * * 실행: rc_bt_0006 [db@host] */ @@ -20,85 +23,72 @@ EXEC SQL INCLUDE sqlca; static int run_rc_bt_0006(const char *biz_date) { - EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; - EXEC SQL END DECLARE SECTION; + long cntR, cntM, cntU, cntS; + long total, processed; + long proc_rate, settle_rate, fail_rate; + int mismatch; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + /* 상태별 건수 조회 (읽기전용) */ + cntR = rc_rec_count(biz_date, RC_ST_RECV); + cntM = rc_rec_count(biz_date, RC_ST_DONE); + cntU = rc_rec_count(biz_date, RC_ST_FAIL); + cntS = rc_rec_count(biz_date, RC_ST_SETTLED); - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - - EXEC SQL DECLARE cur_rc_bt_0006 CURSOR FOR - SELECT key, merch_id, amount - FROM rc_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_rc_bt_0006; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0006"); + if (cntR < 0) { + tx_log(TX_LOG_ERROR, "[rc_bt_0006] 접수(R) 건수 조회 실패 date=%s", + biz_date); + return TX_EDB; + } + if (cntM < 0) { + tx_log(TX_LOG_ERROR, "[rc_bt_0006] 매입확정(M) 건수 조회 실패 date=%s", + biz_date); + return TX_EDB; + } + if (cntU < 0) { + tx_log(TX_LOG_ERROR, "[rc_bt_0006] 불일치(U) 건수 조회 실패 date=%s", + biz_date); + return TX_EDB; + } + if (cntS < 0) { + tx_log(TX_LOG_ERROR, "[rc_bt_0006] 정산(S) 건수 조회 실패 date=%s", + biz_date); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[rc_bt_0006] tx_begin 경고"); + total = cntR + cntM + cntU + cntS; + processed = cntM + cntU + cntS; + mismatch = (cntR > 0) ? 1 : 0; - for (;;) { - EXEC SQL FETCH cur_rc_bt_0006 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_rc_bt_0006"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 처리율/정산율/불일치율 (백분율, 정수 반올림) */ + if (total > 0) { + proc_rate = (processed * 100) / total; + settle_rate = (cntS * 100) / total; + fail_rate = (cntU * 100) / total; + } else { + proc_rate = settle_rate = fail_rate = 0; } - EXEC SQL CLOSE cur_rc_bt_0006; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[rc_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0006] 요약 date=%s 합계=%ld 처리=%ld 미처리여부=%d", + biz_date, total, processed, mismatch); - printf("========== 대사 배치 결과 [rc_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 건수 불일치 검출 배치 [rc_bt_0006] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" ----------------------------------------\n"); + printf(" 접수(R) : %ld\n", cntR); + printf(" 매입확정(M) : %ld\n", cntM); + printf(" 불일치(U) : %ld\n", cntU); + printf(" 정산(S) : %ld\n", cntS); + printf(" ----------------------------------------\n"); + printf(" 합계 : %ld\n", total); + printf(" 처리 완료 : %ld\n", processed); + printf(" 처리율(%%) : %ld\n", proc_rate); + printf(" 정산율(%%) : %ld\n", settle_rate); + printf(" 불일치율(%%) : %ld\n", fail_rate); + printf(" 미처리 여부 : %s\n", mismatch ? "미처리(접수 잔량)" : "완료"); + printf("===============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/rc_bt_0007.c b/app/batch/rc_bt_0007.c index 4d675f3..50a9e89 100644 --- a/app/batch/rc_bt_0007.c +++ b/app/batch/rc_bt_0007.c @@ -8,10 +8,12 @@ #line 1 "app/batch/rc_bt_0007.pgc" /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0007.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0007.pgc - 대사 불일치 예외처리 배치 (RC_BT_0007) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 불일치('U') 건을 커서로 순회하며 예외 재평가를 수행한다. + * 업무규칙: 매입금액(amount) 이 유효금액이고 수수료(fee) 가 음수가 아니면 + * 정상 데이터로 판단 → 예외해소 → 매입확정('M') 으로 전이한다. + * 그 외에는 상태를 유지('U') 하고 미해소로 집계한다. * * 실행: rc_bt_0007 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0007.pgc" +#line 21 "app/batch/rc_bt_0007.pgc" static int run_rc_bt_0007(const char *biz_date) @@ -105,41 +107,41 @@ static int run_rc_bt_0007(const char *biz_date) -#line 24 "app/batch/rc_bt_0007.pgc" +#line 26 "app/batch/rc_bt_0007.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/rc_bt_0007.pgc" +#line 27 "app/batch/rc_bt_0007.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/rc_bt_0007.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/rc_bt_0007.pgc" +#line 28 "app/batch/rc_bt_0007.pgc" long h_amount ; -#line 28 "app/batch/rc_bt_0007.pgc" +#line 29 "app/batch/rc_bt_0007.pgc" + long h_fee ; + +#line 30 "app/batch/rc_bt_0007.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0007.pgc" +#line 31 "app/batch/rc_bt_0007.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, resolved = 0, unresolved = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0007 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0007.pgc" + /* declare cur_rc_bt_0007 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'U' order by key */ +#line 44 "app/batch/rc_bt_0007.pgc" -#line 42 "app/batch/rc_bt_0007.pgc" +#line 44 "app/batch/rc_bt_0007.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0007 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0007 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'U' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0007.pgc" +#line 46 "app/batch/rc_bt_0007.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0007"); @@ -153,11 +155,11 @@ static int run_rc_bt_0007(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0007", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0007.pgc" +#line 56 "app/batch/rc_bt_0007.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +172,25 @@ static int run_rc_bt_0007(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + /* 재평가: 금액 유효 + 수수료 정상 → 예외해소 */ + if (amount_is_valid(h_amount) && h_fee >= 0) { strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + resolved++; - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + rc = rc_rec_update_status(h_key, h_new_status); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[rc_bt_0007] 해소전이 실패 key=%s rc=%d", + h_key, rc); + errcnt++; + } + } else { + /* 미해소 - 상태 유지('U') */ + unresolved++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0007", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0007.pgc" +#line 85 "app/batch/rc_bt_0007.pgc" if (errcnt == 0) @@ -196,15 +199,15 @@ static int run_rc_bt_0007(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0007] 요약 date=%s 대상=%ld 해소=%ld 미해소=%ld", + biz_date, total, resolved, unresolved); - printf("========== 대사 배치 결과 [rc_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("==== 대사 불일치 예외처리 배치 [rc_bt_0007] ====\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 해소(M) : %ld\n", resolved); + printf(" 미해소(U) : %ld\n", unresolved); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/rc_bt_0007.pgc b/app/batch/rc_bt_0007.pgc index 9aec9b7..8fa9563 100644 --- a/app/batch/rc_bt_0007.pgc +++ b/app/batch/rc_bt_0007.pgc @@ -1,9 +1,11 @@ /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0007.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0007.pgc - 대사 불일치 예외처리 배치 (RC_BT_0007) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 불일치('U') 건을 커서로 순회하며 예외 재평가를 수행한다. + * 업무규칙: 매입금액(amount) 이 유효금액이고 수수료(fee) 가 음수가 아니면 + * 정상 데이터로 판단 → 예외해소 → 매입확정('M') 으로 전이한다. + * 그 외에는 상태를 유지('U') 하고 미해소로 집계한다. * * 실행: rc_bt_0007 [db@host] */ @@ -23,22 +25,22 @@ static int run_rc_bt_0007(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; + long h_fee; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, resolved = 0, unresolved = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_rc_bt_0007 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount, fee FROM rc_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'U' ORDER BY key; EXEC SQL OPEN cur_rc_bt_0007; @@ -51,7 +53,7 @@ static int run_rc_bt_0007(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0007] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_rc_bt_0007 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_rc_bt_0007 INTO :h_key, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,19 +65,20 @@ static int run_rc_bt_0007(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + /* 재평가: 금액 유효 + 수수료 정상 → 예외해소 */ + if (amount_is_valid(h_amount) && h_fee >= 0) { strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + resolved++; - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + rc = rc_rec_update_status(h_key, h_new_status); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[rc_bt_0007] 해소전이 실패 key=%s rc=%d", + h_key, rc); + errcnt++; + } + } else { + /* 미해소 - 상태 유지('U') */ + unresolved++; } } @@ -87,15 +90,15 @@ static int run_rc_bt_0007(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0007] 요약 date=%s 대상=%ld 해소=%ld 미해소=%ld", + biz_date, total, resolved, unresolved); - printf("========== 대사 배치 결과 [rc_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("==== 대사 불일치 예외처리 배치 [rc_bt_0007] ====\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 해소(M) : %ld\n", resolved); + printf(" 미해소(U) : %ld\n", unresolved); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/rc_bt_0008.c b/app/batch/rc_bt_0008.c index 2f8dff6..fb42716 100644 --- a/app/batch/rc_bt_0008.c +++ b/app/batch/rc_bt_0008.c @@ -8,10 +8,11 @@ #line 1 "app/batch/rc_bt_0008.pgc" /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0008.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0008.pgc - 재대사 배치 / 일괄 초기화 (RC_BT_0008) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 불일치('U') 건을 커서로 순회하며 상태를 접수('R') 로 + * 되돌려 다음 대사 사이클에서 재처리되도록 준비한다. + * 업무규칙: 조건 없이 조회된 모든 불일치 건을 재대사 대기 상태로 전환한다. * * 실행: rc_bt_0008 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0008.pgc" +#line 20 "app/batch/rc_bt_0008.pgc" static int run_rc_bt_0008(const char *biz_date) @@ -102,44 +103,36 @@ static int run_rc_bt_0008(const char *biz_date) - - -#line 24 "app/batch/rc_bt_0008.pgc" +#line 25 "app/batch/rc_bt_0008.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/rc_bt_0008.pgc" +#line 26 "app/batch/rc_bt_0008.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/rc_bt_0008.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/rc_bt_0008.pgc" - long h_amount ; - -#line 28 "app/batch/rc_bt_0008.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0008.pgc" +#line 28 "app/batch/rc_bt_0008.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, reset_cnt = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0008 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0008.pgc" + /* declare cur_rc_bt_0008 cursor for select key from rc_ledger where biz_date = $1 and status = 'U' order by key */ +#line 41 "app/batch/rc_bt_0008.pgc" -#line 42 "app/batch/rc_bt_0008.pgc" +#line 41 "app/batch/rc_bt_0008.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0008 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0008 cursor for select key from rc_ledger where biz_date = $1 and status = 'U' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0008.pgc" +#line 43 "app/batch/rc_bt_0008.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0008"); @@ -149,15 +142,13 @@ static int run_rc_bt_0008(const char *biz_date) if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[rc_bt_0008] tx_begin 경고"); + strncpy(h_new_status, RC_ST_RECV, sizeof(h_new_status)); + for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0008", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0008.pgc" +#line 55 "app/batch/rc_bt_0008.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +161,19 @@ static int run_rc_bt_0008(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - + /* 불일치 → 접수(재대사 대기) 로 일괄 초기화 */ rc = rc_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[rc_bt_0008] 재대사전환 실패 key=%s rc=%d", + h_key, rc); errcnt++; + } else { + reset_cnt++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0008", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0008.pgc" +#line 78 "app/batch/rc_bt_0008.pgc" if (errcnt == 0) @@ -196,16 +182,15 @@ static int run_rc_bt_0008(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0008] 요약 date=%s 대상=%ld 재대사전환=%ld", + biz_date, total, reset_cnt); - printf("========== 대사 배치 결과 [rc_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========= 재대사 배치 [rc_bt_0008] =========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 재대사대기전환(R) : %ld\n", reset_cnt); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==========================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0008.pgc b/app/batch/rc_bt_0008.pgc index 5d60702..4b35c07 100644 --- a/app/batch/rc_bt_0008.pgc +++ b/app/batch/rc_bt_0008.pgc @@ -1,9 +1,10 @@ /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0008.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0008.pgc - 재대사 배치 / 일괄 초기화 (RC_BT_0008) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 불일치('U') 건을 커서로 순회하며 상태를 접수('R') 로 + * 되돌려 다음 대사 사이클에서 재처리되도록 준비한다. + * 업무규칙: 조건 없이 조회된 모든 불일치 건을 재대사 대기 상태로 전환한다. * * 실행: rc_bt_0008 [db@host] */ @@ -23,22 +24,20 @@ static int run_rc_bt_0008(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, reset_cnt = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_rc_bt_0008 CURSOR FOR - SELECT key, merch_id, amount + SELECT key FROM rc_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'U' ORDER BY key; EXEC SQL OPEN cur_rc_bt_0008; @@ -50,8 +49,10 @@ static int run_rc_bt_0008(const char *biz_date) if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[rc_bt_0008] tx_begin 경고"); + strncpy(h_new_status, RC_ST_RECV, sizeof(h_new_status)); + for (;;) { - EXEC SQL FETCH cur_rc_bt_0008 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_rc_bt_0008 INTO :h_key; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,19 +64,14 @@ static int run_rc_bt_0008(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - + /* 불일치 → 접수(재대사 대기) 로 일괄 초기화 */ rc = rc_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[rc_bt_0008] 재대사전환 실패 key=%s rc=%d", + h_key, rc); errcnt++; + } else { + reset_cnt++; } } @@ -87,16 +83,15 @@ static int run_rc_bt_0008(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0008] 요약 date=%s 대상=%ld 재대사전환=%ld", + biz_date, total, reset_cnt); - printf("========== 대사 배치 결과 [rc_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========= 재대사 배치 [rc_bt_0008] =========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 재대사대기전환(R) : %ld\n", reset_cnt); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==========================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0009.c b/app/batch/rc_bt_0009.c index 002febb..bf334e7 100644 --- a/app/batch/rc_bt_0009.c +++ b/app/batch/rc_bt_0009.c @@ -8,10 +8,13 @@ #line 1 "app/batch/rc_bt_0009.pgc" /* @tier medium @module rc @transform reconciliation-batch */ /* - * rc_bt_0009.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=medium] + * rc_bt_0009.pgc - 카드사별 대사 배치 (RC_BT_0009) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 전체 원장을 카드번호 순으로 커서 순회하며 대사를 수행하고, + * 카드번호 앞 2자리(BIN 상위) 를 카드사 식별자로 삼아 카드사별 건수/금액을 + * 집계한다. + * 업무규칙: 매입금액(amount) 이 유효금액이면 매입확정('M'), 아니면 불일치('U'). + * 카드사별 버킷(최대 16개) 에 건수/금액을 누적하여 리포트한다. * * 실행: rc_bt_0009 [db@host] */ @@ -93,9 +96,11 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0009.pgc" +#line 22 "app/batch/rc_bt_0009.pgc" +#define RC_MAX_ISSUER 16 + static int run_rc_bt_0009(const char *biz_date) { /* exec sql begin declare section */ @@ -105,41 +110,46 @@ static int run_rc_bt_0009(const char *biz_date) -#line 24 "app/batch/rc_bt_0009.pgc" +#line 29 "app/batch/rc_bt_0009.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/rc_bt_0009.pgc" +#line 30 "app/batch/rc_bt_0009.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/rc_bt_0009.pgc" - char h_merch_id [ 16 ] ; +#line 31 "app/batch/rc_bt_0009.pgc" + char h_card_no [ 20 ] ; -#line 27 "app/batch/rc_bt_0009.pgc" +#line 32 "app/batch/rc_bt_0009.pgc" long h_amount ; -#line 28 "app/batch/rc_bt_0009.pgc" +#line 33 "app/batch/rc_bt_0009.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0009.pgc" +#line 34 "app/batch/rc_bt_0009.pgc" + char issuer[RC_MAX_ISSUER][3]; + long icnt[RC_MAX_ISSUER]; + long isum[RC_MAX_ISSUER]; + int nissuer = 0; + long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + int rc, i; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0009 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0009.pgc" + /* declare cur_rc_bt_0009 cursor for select key , card_no , amount from rc_ledger where biz_date = $1 order by card_no */ +#line 51 "app/batch/rc_bt_0009.pgc" -#line 42 "app/batch/rc_bt_0009.pgc" +#line 51 "app/batch/rc_bt_0009.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0009 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0009 cursor for select key , card_no , amount from rc_ledger where biz_date = $1 order by card_no", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0009.pgc" +#line 53 "app/batch/rc_bt_0009.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0009"); @@ -150,14 +160,17 @@ static int run_rc_bt_0009(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0009] tx_begin 경고"); for (;;) { + char bin2[3]; + int found; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0009", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0009.pgc" +#line 66 "app/batch/rc_bt_0009.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,7 +183,7 @@ static int run_rc_bt_0009(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ + /* 대사 판정 */ if (amount_is_valid(h_amount)) { strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); done++; @@ -181,13 +194,40 @@ static int run_rc_bt_0009(const char *biz_date) rc = rc_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[rc_bt_0009] 상태전이 실패 key=%s rc=%d", + h_key, rc); errcnt++; } + + /* 카드사 BIN 상위 2자리 추출 */ + bin2[0] = h_card_no[0]; + bin2[1] = h_card_no[1]; + bin2[2] = '\0'; + + /* 카드사 버킷 검색/삽입 */ + found = 0; + for (i = 0; i < nissuer; i++) { + if (strcmp(issuer[i], bin2) == 0) { + found = 1; + break; + } + } + if (!found && nissuer < RC_MAX_ISSUER) { + i = nissuer++; + strncpy(issuer[i], bin2, sizeof(issuer[i])); + issuer[i][2] = '\0'; + icnt[i] = 0; + isum[i] = 0; + found = 1; + } + if (found && i < RC_MAX_ISSUER) { + icnt[i]++; + isum[i] += h_amount; + } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0009", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0009.pgc" +#line 121 "app/batch/rc_bt_0009.pgc" if (errcnt == 0) @@ -196,15 +236,21 @@ static int run_rc_bt_0009(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0009] 요약 date=%s 대상=%ld 확정=%ld 불일치=%ld 카드사=%d", + biz_date, total, done, unmatch, nissuer); - printf("========== 대사 배치 결과 [rc_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("======= 카드사별 대사 배치 [rc_bt_0009] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" ------------------------------------------\n"); + printf(" 카드사BIN 건수 금액\n"); + for (i = 0; i < nissuer; i++) { + printf(" %-2s %6ld %14ld\n", issuer[i], icnt[i], isum[i]); + } + printf(" ------------------------------------------\n"); + printf(" 확정(M) : %ld\n", done); + printf(" 불일치(U) : %ld\n", unmatch); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/rc_bt_0009.pgc b/app/batch/rc_bt_0009.pgc index d8af220..805e3c3 100644 --- a/app/batch/rc_bt_0009.pgc +++ b/app/batch/rc_bt_0009.pgc @@ -1,9 +1,12 @@ /* @tier medium @module rc @transform reconciliation-batch */ /* - * rc_bt_0009.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=medium] + * rc_bt_0009.pgc - 카드사별 대사 배치 (RC_BT_0009) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 전체 원장을 카드번호 순으로 커서 순회하며 대사를 수행하고, + * 카드번호 앞 2자리(BIN 상위) 를 카드사 식별자로 삼아 카드사별 건수/금액을 + * 집계한다. + * 업무규칙: 매입금액(amount) 이 유효금액이면 매입확정('M'), 아니면 불일치('U'). + * 카드사별 버킷(최대 16개) 에 건수/금액을 누적하여 리포트한다. * * 실행: rc_bt_0009 [db@host] */ @@ -18,28 +21,34 @@ EXEC SQL INCLUDE sqlca; +#define RC_MAX_ISSUER 16 + static int run_rc_bt_0009(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; + char h_card_no[20]; long h_amount; char h_new_status[2]; EXEC SQL END DECLARE SECTION; + char issuer[RC_MAX_ISSUER][3]; + long icnt[RC_MAX_ISSUER]; + long isum[RC_MAX_ISSUER]; + int nissuer = 0; + long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + int rc, i; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_rc_bt_0009 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, card_no, amount FROM rc_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + ORDER BY card_no; EXEC SQL OPEN cur_rc_bt_0009; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +60,10 @@ static int run_rc_bt_0009(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0009] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_rc_bt_0009 INTO :h_key, :h_merch_id, :h_amount; + char bin2[3]; + int found; + + EXEC SQL FETCH cur_rc_bt_0009 INTO :h_key, :h_card_no, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,7 +75,7 @@ static int run_rc_bt_0009(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ + /* 대사 판정 */ if (amount_is_valid(h_amount)) { strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); done++; @@ -74,9 +86,36 @@ static int run_rc_bt_0009(const char *biz_date) rc = rc_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[rc_bt_0009] 상태전이 실패 key=%s rc=%d", + h_key, rc); errcnt++; } + + /* 카드사 BIN 상위 2자리 추출 */ + bin2[0] = h_card_no[0]; + bin2[1] = h_card_no[1]; + bin2[2] = '\0'; + + /* 카드사 버킷 검색/삽입 */ + found = 0; + for (i = 0; i < nissuer; i++) { + if (strcmp(issuer[i], bin2) == 0) { + found = 1; + break; + } + } + if (!found && nissuer < RC_MAX_ISSUER) { + i = nissuer++; + strncpy(issuer[i], bin2, sizeof(issuer[i])); + issuer[i][2] = '\0'; + icnt[i] = 0; + isum[i] = 0; + found = 1; + } + if (found && i < RC_MAX_ISSUER) { + icnt[i]++; + isum[i] += h_amount; + } } EXEC SQL CLOSE cur_rc_bt_0009; @@ -87,15 +126,21 @@ static int run_rc_bt_0009(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0009] 요약 date=%s 대상=%ld 확정=%ld 불일치=%ld 카드사=%d", + biz_date, total, done, unmatch, nissuer); - printf("========== 대사 배치 결과 [rc_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("======= 카드사별 대사 배치 [rc_bt_0009] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" ------------------------------------------\n"); + printf(" 카드사BIN 건수 금액\n"); + for (i = 0; i < nissuer; i++) { + printf(" %-2s %6ld %14ld\n", issuer[i], icnt[i], isum[i]); + } + printf(" ------------------------------------------\n"); + printf(" 확정(M) : %ld\n", done); + printf(" 불일치(U) : %ld\n", unmatch); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/rc_bt_0010.c b/app/batch/rc_bt_0010.c index b204b14..9b6208a 100644 --- a/app/batch/rc_bt_0010.c +++ b/app/batch/rc_bt_0010.c @@ -8,10 +8,14 @@ #line 1 "app/batch/rc_bt_0010.pgc" /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0010.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0010.pgc - 가맹점별 대사 집계 배치 (RC_BT_0010) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 원장을 가맹점번호(merch_id) 순으로 커서 순회하며 + * 가맹점 단위 소계를 집계한다. (읽기/집계 전용, 상태변경 없음) + * 업무규칙: 가맹점이 바뀔 때마다 직전 가맹점 그룹을 평가하여 + * 건수/금액/불일치건(status='U') 소계를 출력한다. + * 그룹 내에 하나라도 'U' 가 있으면 불일치 가맹점으로 집계한다. + * 총가맹점/불일치가맹점/총금액을 리포트한다. * * 실행: rc_bt_0010 [db@host] */ @@ -93,7 +97,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0010.pgc" +#line 23 "app/batch/rc_bt_0010.pgc" static int run_rc_bt_0010(const char *biz_date) @@ -103,61 +107,59 @@ static int run_rc_bt_0010(const char *biz_date) - -#line 24 "app/batch/rc_bt_0010.pgc" +#line 28 "app/batch/rc_bt_0010.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/rc_bt_0010.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/rc_bt_0010.pgc" +#line 29 "app/batch/rc_bt_0010.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/rc_bt_0010.pgc" +#line 30 "app/batch/rc_bt_0010.pgc" long h_amount ; -#line 28 "app/batch/rc_bt_0010.pgc" - char h_new_status [ 2 ] ; +#line 31 "app/batch/rc_bt_0010.pgc" + char h_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0010.pgc" +#line 32 "app/batch/rc_bt_0010.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0; + long total_sum = 0, grand_sum = 0; + long merchant_cnt = 0, imbalanced_cnt = 0; + long grp_cnt = 0, grp_sum = 0, grp_bad = 0; + char prev_merch[16]; + int have_group = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + prev_merch[0] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0010 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0010.pgc" + /* declare cur_rc_bt_0010 cursor for select merch_id , amount , status from rc_ledger where biz_date = $1 order by merch_id */ +#line 49 "app/batch/rc_bt_0010.pgc" -#line 42 "app/batch/rc_bt_0010.pgc" +#line 49 "app/batch/rc_bt_0010.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0010 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0010 cursor for select merch_id , amount , status from rc_ledger where biz_date = $1 order by merch_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0010.pgc" +#line 51 "app/batch/rc_bt_0010.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0010"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[rc_bt_0010] tx_begin 경고"); - for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0010", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0010.pgc" +#line 58 "app/batch/rc_bt_0010.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,45 +170,59 @@ static int run_rc_bt_0010(const char *biz_date) break; } + /* 가맹점 변경 감지 -> 직전 그룹 소계 평가/출력 */ + if (have_group && strcmp(prev_merch, h_merch_id) != 0) { + merchant_cnt++; + grand_sum += grp_sum; + if (grp_bad > 0) + imbalanced_cnt++; + printf(" [소계] 가맹점=%s 건수=%ld 금액=%ld 불일치=%ld\n", + prev_merch, grp_cnt, grp_sum, grp_bad); + grp_cnt = 0; + grp_sum = 0; + grp_bad = 0; + } + + strncpy(prev_merch, h_merch_id, sizeof(prev_merch) - 1); + prev_merch[sizeof(prev_merch) - 1] = '\0'; + have_group = 1; + total++; + total_sum += h_amount; + grp_cnt++; + grp_sum += h_amount; + if (strcmp(h_status, RC_ST_FAIL) == 0) + grp_bad++; + } - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 마지막 가맹점 그룹 처리 */ + if (have_group) { + merchant_cnt++; + grand_sum += grp_sum; + if (grp_bad > 0) + imbalanced_cnt++; + printf(" [소계] 가맹점=%s 건수=%ld 금액=%ld 불일치=%ld\n", + prev_merch, grp_cnt, grp_sum, grp_bad); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0010", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0010.pgc" +#line 103 "app/batch/rc_bt_0010.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[rc_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0010] 요약 date=%s 가맹점=%ld 불일치가맹점=%ld 총금액=%ld", + biz_date, merchant_cnt, imbalanced_cnt, grand_sum); - printf("========== 대사 배치 결과 [rc_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 가맹점별 대사 집계 배치 [rc_bt_0010] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 총 건수 : %ld\n", total); + printf(" 총 가맹점 : %ld\n", merchant_cnt); + printf(" 불일치 가맹점 : %ld\n", imbalanced_cnt); + printf(" 총 금액 : %ld\n", grand_sum); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); + (void)total_sum; return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0010.pgc b/app/batch/rc_bt_0010.pgc index 1116596..59a24ef 100644 --- a/app/batch/rc_bt_0010.pgc +++ b/app/batch/rc_bt_0010.pgc @@ -1,9 +1,13 @@ /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0010.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0010.pgc - 가맹점별 대사 집계 배치 (RC_BT_0010) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 원장을 가맹점번호(merch_id) 순으로 커서 순회하며 + * 가맹점 단위 소계를 집계한다. (읽기/집계 전용, 상태변경 없음) + * 업무규칙: 가맹점이 바뀔 때마다 직전 가맹점 그룹을 평가하여 + * 건수/금액/불일치건(status='U') 소계를 출력한다. + * 그룹 내에 하나라도 'U' 가 있으면 불일치 가맹점으로 집계한다. + * 총가맹점/불일치가맹점/총금액을 리포트한다. * * 실행: rc_bt_0010 [db@host] */ @@ -22,24 +26,27 @@ static int run_rc_bt_0010(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; char h_merch_id[16]; long h_amount; - char h_new_status[2]; + char h_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0; + long total_sum = 0, grand_sum = 0; + long merchant_cnt = 0, imbalanced_cnt = 0; + long grp_cnt = 0, grp_sum = 0, grp_bad = 0; + char prev_merch[16]; + int have_group = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + prev_merch[0] = '\0'; EXEC SQL DECLARE cur_rc_bt_0010 CURSOR FOR - SELECT key, merch_id, amount + SELECT merch_id, amount, status FROM rc_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + ORDER BY merch_id; EXEC SQL OPEN cur_rc_bt_0010; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,11 +54,8 @@ static int run_rc_bt_0010(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[rc_bt_0010] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_rc_bt_0010 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_rc_bt_0010 INTO :h_merch_id, :h_amount, :h_status; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,43 +65,57 @@ static int run_rc_bt_0010(const char *biz_date) break; } + /* 가맹점 변경 감지 -> 직전 그룹 소계 평가/출력 */ + if (have_group && strcmp(prev_merch, h_merch_id) != 0) { + merchant_cnt++; + grand_sum += grp_sum; + if (grp_bad > 0) + imbalanced_cnt++; + printf(" [소계] 가맹점=%s 건수=%ld 금액=%ld 불일치=%ld\n", + prev_merch, grp_cnt, grp_sum, grp_bad); + grp_cnt = 0; + grp_sum = 0; + grp_bad = 0; + } + + strncpy(prev_merch, h_merch_id, sizeof(prev_merch) - 1); + prev_merch[sizeof(prev_merch) - 1] = '\0'; + have_group = 1; + total++; + total_sum += h_amount; + grp_cnt++; + grp_sum += h_amount; + if (strcmp(h_status, RC_ST_FAIL) == 0) + grp_bad++; + } - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 마지막 가맹점 그룹 처리 */ + if (have_group) { + merchant_cnt++; + grand_sum += grp_sum; + if (grp_bad > 0) + imbalanced_cnt++; + printf(" [소계] 가맹점=%s 건수=%ld 금액=%ld 불일치=%ld\n", + prev_merch, grp_cnt, grp_sum, grp_bad); } EXEC SQL CLOSE cur_rc_bt_0010; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[rc_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0010] 요약 date=%s 가맹점=%ld 불일치가맹점=%ld 총금액=%ld", + biz_date, merchant_cnt, imbalanced_cnt, grand_sum); - printf("========== 대사 배치 결과 [rc_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 가맹점별 대사 집계 배치 [rc_bt_0010] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 총 건수 : %ld\n", total); + printf(" 총 가맹점 : %ld\n", merchant_cnt); + printf(" 불일치 가맹점 : %ld\n", imbalanced_cnt); + printf(" 총 금액 : %ld\n", grand_sum); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); + (void)total_sum; return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0011.c b/app/batch/rc_bt_0011.c index 36e1eb9..63f1083 100644 --- a/app/batch/rc_bt_0011.c +++ b/app/batch/rc_bt_0011.c @@ -8,10 +8,13 @@ #line 1 "app/batch/rc_bt_0011.pgc" /* @tier medium @module rc @transform reconciliation-batch */ /* - * rc_bt_0011.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=medium] + * rc_bt_0011.pgc - 대사 tolerance 비교 배치 (상대 오차) (RC_BT_0011) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 접수('R') 건을 커서로 순회하며 상대 오차 허용범위 대사를 수행한다. + * 업무규칙: 승인 추정금액 = amount - fee 이며, 승인/매입 차액은 |fee| 이다. + * 허용오차 allow = amount * RC_TOL_BP / 10000 (bp 단위, 3% = 300bp). + * |fee| <= allow 이면 매입확정('M'), 아니면 불일치('U') 로 전이하고 + * 초과분(|fee| - allow) 을 누계한다. * * 실행: rc_bt_0011 [db@host] */ @@ -93,9 +96,11 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0011.pgc" +#line 22 "app/batch/rc_bt_0011.pgc" +#define RC_TOL_BP 300 /* 상대 허용오차 (basis point, 300bp = 3%) */ + static int run_rc_bt_0011(const char *biz_date) { /* exec sql begin declare section */ @@ -105,41 +110,42 @@ static int run_rc_bt_0011(const char *biz_date) -#line 24 "app/batch/rc_bt_0011.pgc" +#line 29 "app/batch/rc_bt_0011.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/rc_bt_0011.pgc" +#line 30 "app/batch/rc_bt_0011.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/rc_bt_0011.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/rc_bt_0011.pgc" +#line 31 "app/batch/rc_bt_0011.pgc" long h_amount ; -#line 28 "app/batch/rc_bt_0011.pgc" +#line 32 "app/batch/rc_bt_0011.pgc" + long h_fee ; + +#line 33 "app/batch/rc_bt_0011.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0011.pgc" +#line 34 "app/batch/rc_bt_0011.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, match = 0, unmatch = 0, errcnt = 0; + long over_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0011 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0011.pgc" + /* declare cur_rc_bt_0011 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'R' order by key */ +#line 48 "app/batch/rc_bt_0011.pgc" -#line 42 "app/batch/rc_bt_0011.pgc" +#line 48 "app/batch/rc_bt_0011.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0011 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0011 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'R' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0011.pgc" +#line 50 "app/batch/rc_bt_0011.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0011"); @@ -150,14 +156,16 @@ static int run_rc_bt_0011(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0011] tx_begin 경고"); for (;;) { + long appr_est, diff, allow; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0011", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0011.pgc" +#line 62 "app/batch/rc_bt_0011.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +178,29 @@ static int run_rc_bt_0011(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + appr_est = h_amount - h_fee; + diff = labs(h_amount - appr_est); /* = |fee| */ + allow = h_amount * RC_TOL_BP / 10000; /* 상대 허용오차 */ + + if (diff <= allow) { strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; + match++; } else { strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); unmatch++; + over_sum += (diff - allow); } rc = rc_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[rc_bt_0011] 상태전이 실패 key=%s rc=%d", + h_key, rc); errcnt++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0011", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0011.pgc" +#line 95 "app/batch/rc_bt_0011.pgc" if (errcnt == 0) @@ -196,16 +209,18 @@ static int run_rc_bt_0011(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0011] 요약 date=%s 대상=%ld 허용내=%ld 초과=%ld 초과합=%ld", + biz_date, total, match, unmatch, over_sum); - printf("========== 대사 배치 결과 [rc_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 대사 tolerance 비교 배치 [rc_bt_0011] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 허용내(M) : %ld\n", match); + printf(" 허용초과(U) : %ld\n", unmatch); + printf(" 초과 차액합계 : %ld\n", over_sum); + printf(" 허용율 : %d bp\n", RC_TOL_BP); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0011.pgc b/app/batch/rc_bt_0011.pgc index 123cc80..136b439 100644 --- a/app/batch/rc_bt_0011.pgc +++ b/app/batch/rc_bt_0011.pgc @@ -1,9 +1,12 @@ /* @tier medium @module rc @transform reconciliation-batch */ /* - * rc_bt_0011.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=medium] + * rc_bt_0011.pgc - 대사 tolerance 비교 배치 (상대 오차) (RC_BT_0011) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 접수('R') 건을 커서로 순회하며 상대 오차 허용범위 대사를 수행한다. + * 업무규칙: 승인 추정금액 = amount - fee 이며, 승인/매입 차액은 |fee| 이다. + * 허용오차 allow = amount * RC_TOL_BP / 10000 (bp 단위, 3% = 300bp). + * |fee| <= allow 이면 매입확정('M'), 아니면 불일치('U') 로 전이하고 + * 초과분(|fee| - allow) 을 누계한다. * * 실행: rc_bt_0011 [db@host] */ @@ -18,24 +21,27 @@ EXEC SQL INCLUDE sqlca; +#define RC_TOL_BP 300 /* 상대 허용오차 (basis point, 300bp = 3%) */ + static int run_rc_bt_0011(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; + long h_fee; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, match = 0, unmatch = 0, errcnt = 0; + long over_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_rc_bt_0011 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount, fee FROM rc_ledger WHERE biz_date = :h_biz_date AND status = 'R' @@ -51,7 +57,9 @@ static int run_rc_bt_0011(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0011] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_rc_bt_0011 INTO :h_key, :h_merch_id, :h_amount; + long appr_est, diff, allow; + + EXEC SQL FETCH cur_rc_bt_0011 INTO :h_key, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,18 +71,23 @@ static int run_rc_bt_0011(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + appr_est = h_amount - h_fee; + diff = labs(h_amount - appr_est); /* = |fee| */ + allow = h_amount * RC_TOL_BP / 10000; /* 상대 허용오차 */ + + if (diff <= allow) { strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; + match++; } else { strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); unmatch++; + over_sum += (diff - allow); } rc = rc_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[rc_bt_0011] 상태전이 실패 key=%s rc=%d", + h_key, rc); errcnt++; } } @@ -87,16 +100,18 @@ static int run_rc_bt_0011(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0011] 요약 date=%s 대상=%ld 허용내=%ld 초과=%ld 초과합=%ld", + biz_date, total, match, unmatch, over_sum); - printf("========== 대사 배치 결과 [rc_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 대사 tolerance 비교 배치 [rc_bt_0011] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 허용내(M) : %ld\n", match); + printf(" 허용초과(U) : %ld\n", unmatch); + printf(" 초과 차액합계 : %ld\n", over_sum); + printf(" 허용율 : %d bp\n", RC_TOL_BP); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0012.c b/app/batch/rc_bt_0012.c index adc47e3..1aa05aa 100644 --- a/app/batch/rc_bt_0012.c +++ b/app/batch/rc_bt_0012.c @@ -8,10 +8,13 @@ #line 1 "app/batch/rc_bt_0012.pgc" /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0012.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0012.pgc - 대사 결과 집계(원장반영) 배치 (RC_BT_0012) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 매입확정('M') 건을 커서로 순회하며 원장 반영액을 산정하고 정산 전이한다. + * 업무규칙: 원장 반영액 gl_amt = amount - fee. + * gl_amt > 0 이면 정산완료('S') 로 전이하고 반영합계/수수료합계를 + * 누계한다. gl_amt <= 0 이면 반영 스킵(상태 유지). + * GL 반영 요약을 리포트한다. * * 실행: rc_bt_0012 [db@host] */ @@ -93,7 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0012.pgc" +#line 22 "app/batch/rc_bt_0012.pgc" static int run_rc_bt_0012(const char *biz_date) @@ -105,41 +108,42 @@ static int run_rc_bt_0012(const char *biz_date) -#line 24 "app/batch/rc_bt_0012.pgc" +#line 27 "app/batch/rc_bt_0012.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/rc_bt_0012.pgc" +#line 28 "app/batch/rc_bt_0012.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/rc_bt_0012.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/rc_bt_0012.pgc" +#line 29 "app/batch/rc_bt_0012.pgc" long h_amount ; -#line 28 "app/batch/rc_bt_0012.pgc" +#line 30 "app/batch/rc_bt_0012.pgc" + long h_fee ; + +#line 31 "app/batch/rc_bt_0012.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0012.pgc" +#line 32 "app/batch/rc_bt_0012.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, posted = 0, skipped = 0, errcnt = 0; + long gl_total = 0, fee_total = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0012 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0012.pgc" + /* declare cur_rc_bt_0012 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'M' order by key */ +#line 46 "app/batch/rc_bt_0012.pgc" -#line 42 "app/batch/rc_bt_0012.pgc" +#line 46 "app/batch/rc_bt_0012.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0012 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0012 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0012.pgc" +#line 48 "app/batch/rc_bt_0012.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0012"); @@ -150,14 +154,16 @@ static int run_rc_bt_0012(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0012] tx_begin 경고"); for (;;) { + long gl_amt; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0012", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0012.pgc" +#line 60 "app/batch/rc_bt_0012.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +176,27 @@ static int run_rc_bt_0012(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; + /* 원장 반영액 = 매입금액 - 수수료 */ + gl_amt = h_amount - h_fee; + if (gl_amt > 0) { + strncpy(h_new_status, RC_ST_SETTLED, sizeof(h_new_status)); + rc = rc_rec_update_status(h_key, h_new_status); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[rc_bt_0012] 정산전이 실패 key=%s rc=%d", + h_key, rc); + errcnt++; + continue; + } + posted++; + gl_total += gl_amt; + fee_total += h_fee; } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + skipped++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0012", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0012.pgc" +#line 91 "app/batch/rc_bt_0012.pgc" if (errcnt == 0) @@ -196,16 +205,18 @@ static int run_rc_bt_0012(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0012] 요약 date=%s 대상=%ld 반영=%ld 스킵=%ld 반영합=%ld", + biz_date, total, posted, skipped, gl_total); - printf("========== 대사 배치 결과 [rc_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 대사 결과 집계(원장반영) 배치 [rc_bt_0012] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 원장반영(S) : %ld\n", posted); + printf(" 반영 스킵 : %ld\n", skipped); + printf(" 원장반영 합계 : %ld\n", gl_total); + printf(" 수수료 합계 : %ld\n", fee_total); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0012.pgc b/app/batch/rc_bt_0012.pgc index b9b2e7a..4f4c769 100644 --- a/app/batch/rc_bt_0012.pgc +++ b/app/batch/rc_bt_0012.pgc @@ -1,9 +1,12 @@ /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0012.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0012.pgc - 대사 결과 집계(원장반영) 배치 (RC_BT_0012) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 매입확정('M') 건을 커서로 순회하며 원장 반영액을 산정하고 정산 전이한다. + * 업무규칙: 원장 반영액 gl_amt = amount - fee. + * gl_amt > 0 이면 정산완료('S') 로 전이하고 반영합계/수수료합계를 + * 누계한다. gl_amt <= 0 이면 반영 스킵(상태 유지). + * GL 반영 요약을 리포트한다. * * 실행: rc_bt_0012 [db@host] */ @@ -23,22 +26,23 @@ static int run_rc_bt_0012(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; + long h_fee; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, posted = 0, skipped = 0, errcnt = 0; + long gl_total = 0, fee_total = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_rc_bt_0012 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount, fee FROM rc_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'M' ORDER BY key; EXEC SQL OPEN cur_rc_bt_0012; @@ -51,7 +55,9 @@ static int run_rc_bt_0012(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0012] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_rc_bt_0012 INTO :h_key, :h_merch_id, :h_amount; + long gl_amt; + + EXEC SQL FETCH cur_rc_bt_0012 INTO :h_key, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,19 +69,22 @@ static int run_rc_bt_0012(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; + /* 원장 반영액 = 매입금액 - 수수료 */ + gl_amt = h_amount - h_fee; + if (gl_amt > 0) { + strncpy(h_new_status, RC_ST_SETTLED, sizeof(h_new_status)); + rc = rc_rec_update_status(h_key, h_new_status); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[rc_bt_0012] 정산전이 실패 key=%s rc=%d", + h_key, rc); + errcnt++; + continue; + } + posted++; + gl_total += gl_amt; + fee_total += h_fee; } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + skipped++; } } @@ -87,16 +96,18 @@ static int run_rc_bt_0012(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0012] 요약 date=%s 대상=%ld 반영=%ld 스킵=%ld 반영합=%ld", + biz_date, total, posted, skipped, gl_total); - printf("========== 대사 배치 결과 [rc_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 대사 결과 집계(원장반영) 배치 [rc_bt_0012] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 원장반영(S) : %ld\n", posted); + printf(" 반영 스킵 : %ld\n", skipped); + printf(" 원장반영 합계 : %ld\n", gl_total); + printf(" 수수료 합계 : %ld\n", fee_total); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0013.c b/app/batch/rc_bt_0013.c index f7838ef..dd515f7 100644 --- a/app/batch/rc_bt_0013.c +++ b/app/batch/rc_bt_0013.c @@ -8,12 +8,16 @@ #line 1 "app/batch/rc_bt_0013.pgc" /* @tier medium @module rc @transform reconciliation-batch */ /* - * rc_bt_0013.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=medium] + * rc_bt_0013.pgc - 지연대사 배치 (RC_BT_0013) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 인자로 받은 영업일을 마감기준일(cutoff) 로 사용하여, cutoff 이전의 + * 미접수 대사('R') 건을 영업일 순으로 커서 순회하며 지연 경과일로 분류한다. + * 업무규칙: 경과일 age = atol(cutoff) - atol(biz_date) (YYYYMMDD 수치 근사차). + * age <= 3 : 지연1 (계수만) + * age <= 7 : 지연2 (계수만) + * age > 7 : 장기지연 -> 불일치('U') 강제 전이 (aged_fail). * - * 실행: rc_bt_0013 [db@host] + * 실행: rc_bt_0013 [db@host] */ #include #include @@ -93,10 +97,10 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0013.pgc" +#line 23 "app/batch/rc_bt_0013.pgc" -static int run_rc_bt_0013(const char *biz_date) +static int run_rc_bt_0013(const char *cutoff) { /* exec sql begin declare section */ @@ -105,41 +109,44 @@ static int run_rc_bt_0013(const char *biz_date) -#line 24 "app/batch/rc_bt_0013.pgc" - char h_biz_date [ 9 ] ; +#line 28 "app/batch/rc_bt_0013.pgc" + char h_cutoff [ 9 ] ; -#line 25 "app/batch/rc_bt_0013.pgc" +#line 29 "app/batch/rc_bt_0013.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/rc_bt_0013.pgc" - char h_merch_id [ 16 ] ; +#line 30 "app/batch/rc_bt_0013.pgc" + char h_biz_date [ 9 ] ; -#line 27 "app/batch/rc_bt_0013.pgc" +#line 31 "app/batch/rc_bt_0013.pgc" long h_amount ; -#line 28 "app/batch/rc_bt_0013.pgc" +#line 32 "app/batch/rc_bt_0013.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0013.pgc" +#line 33 "app/batch/rc_bt_0013.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, errcnt = 0; + long b1 = 0, b2 = 0, b3 = 0, aged_fail = 0; + long cutoff_num; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_cutoff, cutoff, sizeof(h_cutoff) - 1); + h_cutoff[sizeof(h_cutoff) - 1] = '\0'; + cutoff_num = atol(h_cutoff); - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0013 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0013.pgc" + ECPGset_var( 0, ( h_cutoff ), __LINE__);\ + /* declare cur_rc_bt_0013 cursor for select key , biz_date , amount from rc_ledger where status = 'R' and biz_date < $1 order by biz_date */ +#line 49 "app/batch/rc_bt_0013.pgc" -#line 42 "app/batch/rc_bt_0013.pgc" +#line 49 "app/batch/rc_bt_0013.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0013 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0013 cursor for select key , biz_date , amount from rc_ledger where status = 'R' and biz_date < $1 order by biz_date", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0013.pgc" +#line 51 "app/batch/rc_bt_0013.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0013"); @@ -150,14 +157,16 @@ static int run_rc_bt_0013(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0013] tx_begin 경고"); for (;;) { + long age; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0013", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0013.pgc" +#line 63 "app/batch/rc_bt_0013.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +179,30 @@ static int run_rc_bt_0013(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* YYYYMMDD 수치 근사 경과일 */ + age = labs(cutoff_num - atol(h_biz_date)); - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (age <= 3) { + b1++; + } else if (age <= 7) { + b2++; + } else { + b3++; + strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); + rc = rc_rec_update_status(h_key, h_new_status); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, + "[rc_bt_0013] 장기지연 전이 실패 key=%s rc=%d", + h_key, rc); + errcnt++; + continue; + } + aged_fail++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0013", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0013.pgc" +#line 97 "app/batch/rc_bt_0013.pgc" if (errcnt == 0) @@ -196,16 +211,18 @@ static int run_rc_bt_0013(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0013] 요약 cutoff=%s 대상=%ld 지연1=%ld 지연2=%ld 장기=%ld", + cutoff, total, b1, b2, b3); - printf("========== 대사 배치 결과 [rc_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 지연대사 배치 [rc_bt_0013] =======\n"); + printf(" 마감기준일(cutoff) : %s\n", cutoff); + printf(" 대상 건수 : %ld\n", total); + printf(" 지연1(<=3일) : %ld\n", b1); + printf(" 지연2(<=7일) : %ld\n", b2); + printf(" 장기(>7일, U처리) : %ld\n", b3); + printf(" 장기 불일치 전이 : %ld\n", aged_fail); + printf(" 오류 건수 : %ld\n", errcnt); + printf("========================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -230,7 +247,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[rc_bt_0013] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[rc_bt_0013] 배치 시작 cutoff=%s", biz_date); rc = rc_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/rc_bt_0013.pgc b/app/batch/rc_bt_0013.pgc index 8ec2842..9869225 100644 --- a/app/batch/rc_bt_0013.pgc +++ b/app/batch/rc_bt_0013.pgc @@ -1,11 +1,15 @@ /* @tier medium @module rc @transform reconciliation-batch */ /* - * rc_bt_0013.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=medium] + * rc_bt_0013.pgc - 지연대사 배치 (RC_BT_0013) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 인자로 받은 영업일을 마감기준일(cutoff) 로 사용하여, cutoff 이전의 + * 미접수 대사('R') 건을 영업일 순으로 커서 순회하며 지연 경과일로 분류한다. + * 업무규칙: 경과일 age = atol(cutoff) - atol(biz_date) (YYYYMMDD 수치 근사차). + * age <= 3 : 지연1 (계수만) + * age <= 7 : 지연2 (계수만) + * age > 7 : 장기지연 -> 불일치('U') 강제 전이 (aged_fail). * - * 실행: rc_bt_0013 [db@host] + * 실행: rc_bt_0013 [db@host] */ #include #include @@ -18,28 +22,31 @@ EXEC SQL INCLUDE sqlca; -static int run_rc_bt_0013(const char *biz_date) +static int run_rc_bt_0013(const char *cutoff) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; + char h_cutoff[9]; char h_key[16]; - char h_merch_id[16]; + char h_biz_date[9]; long h_amount; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, errcnt = 0; + long b1 = 0, b2 = 0, b3 = 0, aged_fail = 0; + long cutoff_num; int rc; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_cutoff, cutoff, sizeof(h_cutoff) - 1); + h_cutoff[sizeof(h_cutoff) - 1] = '\0'; + cutoff_num = atol(h_cutoff); EXEC SQL DECLARE cur_rc_bt_0013 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, biz_date, amount FROM rc_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + WHERE status = 'R' + AND biz_date < :h_cutoff + ORDER BY biz_date; EXEC SQL OPEN cur_rc_bt_0013; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +58,9 @@ static int run_rc_bt_0013(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0013] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_rc_bt_0013 INTO :h_key, :h_merch_id, :h_amount; + long age; + + EXEC SQL FETCH cur_rc_bt_0013 INTO :h_key, :h_biz_date, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,19 +72,25 @@ static int run_rc_bt_0013(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* YYYYMMDD 수치 근사 경과일 */ + age = labs(cutoff_num - atol(h_biz_date)); - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (age <= 3) { + b1++; + } else if (age <= 7) { + b2++; + } else { + b3++; + strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); + rc = rc_rec_update_status(h_key, h_new_status); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, + "[rc_bt_0013] 장기지연 전이 실패 key=%s rc=%d", + h_key, rc); + errcnt++; + continue; + } + aged_fail++; } } @@ -87,16 +102,18 @@ static int run_rc_bt_0013(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0013] 요약 cutoff=%s 대상=%ld 지연1=%ld 지연2=%ld 장기=%ld", + cutoff, total, b1, b2, b3); - printf("========== 대사 배치 결과 [rc_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 지연대사 배치 [rc_bt_0013] =======\n"); + printf(" 마감기준일(cutoff) : %s\n", cutoff); + printf(" 대상 건수 : %ld\n", total); + printf(" 지연1(<=3일) : %ld\n", b1); + printf(" 지연2(<=7일) : %ld\n", b2); + printf(" 장기(>7일, U처리) : %ld\n", b3); + printf(" 장기 불일치 전이 : %ld\n", aged_fail); + printf(" 오류 건수 : %ld\n", errcnt); + printf("========================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } @@ -121,7 +138,7 @@ int main(int argc, char **argv) return 2; } - tx_log(TX_LOG_INFO, "[rc_bt_0013] 배치 시작 date=%s", biz_date); + tx_log(TX_LOG_INFO, "[rc_bt_0013] 배치 시작 cutoff=%s", biz_date); rc = rc_dbio_connect(target); if (rc != TX_OK) { diff --git a/app/batch/rc_bt_0014.c b/app/batch/rc_bt_0014.c index 2ced64e..be297f3 100644 --- a/app/batch/rc_bt_0014.c +++ b/app/batch/rc_bt_0014.c @@ -8,10 +8,13 @@ #line 1 "app/batch/rc_bt_0014.pgc" /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0014.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0014.pgc - 대사 통계 배치 (RC_BT_0014) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 원장 전건을 커서로 순회하며 금액 통계를 산출한다. + * (리포트 전용 - 상태변경/트랜잭션 없음, 접속/해제만 수행) + * 업무규칙: 건수/최소금액/최대금액/합계금액을 누적하고, 매입확정('M') 및 + * 정산완료('S') 건을 매입건으로 계수한다. + * 평균 = 합계/건수, 매입율 = 매입건*100/건수 (0 나눗셈 방어). * * 실행: rc_bt_0014 [db@host] */ @@ -93,7 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0014.pgc" +#line 22 "app/batch/rc_bt_0014.pgc" static int run_rc_bt_0014(const char *biz_date) @@ -102,62 +105,51 @@ static int run_rc_bt_0014(const char *biz_date) - - -#line 24 "app/batch/rc_bt_0014.pgc" +#line 27 "app/batch/rc_bt_0014.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/rc_bt_0014.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/rc_bt_0014.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/rc_bt_0014.pgc" +#line 28 "app/batch/rc_bt_0014.pgc" long h_amount ; -#line 28 "app/batch/rc_bt_0014.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ #line 29 "app/batch/rc_bt_0014.pgc" + char h_status [ 2 ] ; +/* exec sql end declare section */ +#line 30 "app/batch/rc_bt_0014.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long cnt = 0, errcnt = 0; + long minamt = 0, maxamt = 0, sumamt = 0, matched = 0; + long avg = 0, matchrate = 0; + int first = 1; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0014 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0014.pgc" + /* declare cur_rc_bt_0014 cursor for select amount , status from rc_ledger where biz_date = $1 order by key */ +#line 44 "app/batch/rc_bt_0014.pgc" -#line 42 "app/batch/rc_bt_0014.pgc" +#line 44 "app/batch/rc_bt_0014.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0014 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0014 cursor for select amount , status from rc_ledger where biz_date = $1 order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0014.pgc" +#line 46 "app/batch/rc_bt_0014.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0014"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[rc_bt_0014] tx_begin 경고"); - for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0014", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0014.pgc" +#line 53 "app/batch/rc_bt_0014.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,44 +160,46 @@ static int run_rc_bt_0014(const char *biz_date) break; } - total++; + cnt++; + sumamt += h_amount; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; + if (first) { + minamt = h_amount; + maxamt = h_amount; + first = 0; } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (h_amount < minamt) minamt = h_amount; + if (h_amount > maxamt) maxamt = h_amount; } - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (strcmp(h_status, RC_ST_DONE) == 0 || + strcmp(h_status, RC_ST_SETTLED) == 0) + matched++; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0014", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0014.pgc" +#line 80 "app/batch/rc_bt_0014.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + if (cnt > 0) { + avg = sumamt / cnt; + matchrate = matched * 100 / cnt; + } tx_log(TX_LOG_INFO, - "[rc_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0014] 요약 date=%s 건수=%ld 합계=%ld 평균=%ld 매입율=%ld%%", + biz_date, cnt, sumamt, avg, matchrate); - printf("========== 대사 배치 결과 [rc_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 대사 통계 배치 [rc_bt_0014] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 건수 : %ld\n", cnt); + printf(" 최소 금액 : %ld\n", minamt); + printf(" 최대 금액 : %ld\n", maxamt); + printf(" 평균 금액 : %ld\n", avg); + printf(" 합계 금액 : %ld\n", sumamt); + printf(" 매입율 : %ld%%\n", matchrate); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=========================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0014.pgc b/app/batch/rc_bt_0014.pgc index 722f2f3..532562f 100644 --- a/app/batch/rc_bt_0014.pgc +++ b/app/batch/rc_bt_0014.pgc @@ -1,9 +1,12 @@ /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0014.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0014.pgc - 대사 통계 배치 (RC_BT_0014) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 원장 전건을 커서로 순회하며 금액 통계를 산출한다. + * (리포트 전용 - 상태변경/트랜잭션 없음, 접속/해제만 수행) + * 업무규칙: 건수/최소금액/최대금액/합계금액을 누적하고, 매입확정('M') 및 + * 정산완료('S') 건을 매입건으로 계수한다. + * 평균 = 합계/건수, 매입율 = 매입건*100/건수 (0 나눗셈 방어). * * 실행: rc_bt_0014 [db@host] */ @@ -22,23 +25,22 @@ static int run_rc_bt_0014(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; + char h_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long cnt = 0, errcnt = 0; + long minamt = 0, maxamt = 0, sumamt = 0, matched = 0; + long avg = 0, matchrate = 0; + int first = 1; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_rc_bt_0014 CURSOR FOR - SELECT key, merch_id, amount + SELECT amount, status FROM rc_ledger WHERE biz_date = :h_biz_date - AND status = 'R' ORDER BY key; EXEC SQL OPEN cur_rc_bt_0014; @@ -47,11 +49,8 @@ static int run_rc_bt_0014(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[rc_bt_0014] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_rc_bt_0014 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_rc_bt_0014 INTO :h_amount, :h_status; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,42 +60,44 @@ static int run_rc_bt_0014(const char *biz_date) break; } - total++; + cnt++; + sumamt += h_amount; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; + if (first) { + minamt = h_amount; + maxamt = h_amount; + first = 0; } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (h_amount < minamt) minamt = h_amount; + if (h_amount > maxamt) maxamt = h_amount; } - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (strcmp(h_status, RC_ST_DONE) == 0 || + strcmp(h_status, RC_ST_SETTLED) == 0) + matched++; } EXEC SQL CLOSE cur_rc_bt_0014; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + if (cnt > 0) { + avg = sumamt / cnt; + matchrate = matched * 100 / cnt; + } tx_log(TX_LOG_INFO, - "[rc_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0014] 요약 date=%s 건수=%ld 합계=%ld 평균=%ld 매입율=%ld%%", + biz_date, cnt, sumamt, avg, matchrate); - printf("========== 대사 배치 결과 [rc_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 대사 통계 배치 [rc_bt_0014] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 건수 : %ld\n", cnt); + printf(" 최소 금액 : %ld\n", minamt); + printf(" 최대 금액 : %ld\n", maxamt); + printf(" 평균 금액 : %ld\n", avg); + printf(" 합계 금액 : %ld\n", sumamt); + printf(" 매입율 : %ld%%\n", matchrate); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=========================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0015.c b/app/batch/rc_bt_0015.c index 1d14eed..657cde2 100644 --- a/app/batch/rc_bt_0015.c +++ b/app/batch/rc_bt_0015.c @@ -8,10 +8,13 @@ #line 1 "app/batch/rc_bt_0015.pgc" /* @tier hard @module rc @transform reconciliation-batch */ /* - * rc_bt_0015.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=hard] + * rc_bt_0015.pgc - 일괄 대사마감 배치 (2-phase) (RC_BT_0015) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에 대해 두 단계의 커서로 대사 마감을 일괄 수행한다. + * 업무규칙: + * [Phase1] 매입확정('M') 건 -> 정산완료('S') 로 전이 (마감정산). + * [Phase2] 미처리 접수('R') 건 -> 불일치('U') 로 전이 (마감불일치). + * 두 단계를 단일 트랜잭션으로 묶어 errcnt==0 일 때만 커밋한다. * * 실행: rc_bt_0015 [db@host] */ @@ -93,7 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0015.pgc" +#line 22 "app/batch/rc_bt_0015.pgc" static int run_rc_bt_0015(const char *biz_date) @@ -102,109 +105,139 @@ static int run_rc_bt_0015(const char *biz_date) - - -#line 24 "app/batch/rc_bt_0015.pgc" +#line 27 "app/batch/rc_bt_0015.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/rc_bt_0015.pgc" +#line 28 "app/batch/rc_bt_0015.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/rc_bt_0015.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/rc_bt_0015.pgc" - long h_amount ; - -#line 28 "app/batch/rc_bt_0015.pgc" +#line 29 "app/batch/rc_bt_0015.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0015.pgc" +#line 30 "app/batch/rc_bt_0015.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long settle_cnt = 0, close_unmatch = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0015 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0015.pgc" + /* declare cur_rc_bt_0015_a cursor for select key from rc_ledger where biz_date = $1 and status = 'M' order by key */ +#line 43 "app/batch/rc_bt_0015.pgc" -#line 42 "app/batch/rc_bt_0015.pgc" +#line 43 "app/batch/rc_bt_0015.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0015 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0015.pgc" + ECPGset_var( 1, ( h_biz_date ), __LINE__);\ + /* declare cur_rc_bt_0015_b cursor for select key from rc_ledger where biz_date = $1 and status = 'R' order by key */ +#line 50 "app/batch/rc_bt_0015.pgc" + +#line 50 "app/batch/rc_bt_0015.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0015"); - return TX_EDB; - } if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[rc_bt_0015] tx_begin 경고"); + /* ---- Phase1: 매입확정('M') -> 정산완료('S') ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0015_a cursor for select key from rc_ledger where biz_date = $1 and status = 'M' order by key", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 56 "app/batch/rc_bt_0015.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0015_a"); + tx_abort(); + return TX_EDB; + } + for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0015", ECPGt_EOIT, + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0015_a", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0015.pgc" +#line 64 "app/batch/rc_bt_0015.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_rc_bt_0015"); + TX_DBIO_LOG_ERR("FETCH cur_rc_bt_0015_a"); errcnt++; break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - + strncpy(h_new_status, RC_ST_SETTLED, sizeof(h_new_status)); rc = rc_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[rc_bt_0015] 마감정산 실패 key=%s rc=%d", + h_key, rc); errcnt++; + continue; } + settle_cnt++; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0015", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0015.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0015_a", ECPGt_EOIT, ECPGt_EORT);} +#line 85 "app/batch/rc_bt_0015.pgc" + /* ---- Phase2: 미처리 접수('R') -> 불일치('U') ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0015_b cursor for select key from rc_ledger where biz_date = $1 and status = 'R' order by key", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 88 "app/batch/rc_bt_0015.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0015_b"); + errcnt++; + } else { + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0015_b", ECPGt_EOIT, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 94 "app/batch/rc_bt_0015.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_bt_0015_b"); + errcnt++; + break; + } + + strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); + rc = rc_rec_update_status(h_key, h_new_status); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[rc_bt_0015] 마감불일치 실패 key=%s rc=%d", + h_key, rc); + errcnt++; + continue; + } + close_unmatch++; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0015_b", ECPGt_EOIT, ECPGt_EORT);} +#line 115 "app/batch/rc_bt_0015.pgc" + + } + if (errcnt == 0) tx_commit(); else tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0015] 요약 date=%s 마감정산=%ld 마감불일치=%ld 오류=%ld", + biz_date, settle_cnt, close_unmatch, errcnt); - printf("========== 대사 배치 결과 [rc_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("======= 일괄 대사마감 배치 [rc_bt_0015] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 마감정산(S) : %ld\n", settle_cnt); + printf(" 마감불일치(U) : %ld\n", close_unmatch); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/rc_bt_0015.pgc b/app/batch/rc_bt_0015.pgc index e3b94f3..a63cd45 100644 --- a/app/batch/rc_bt_0015.pgc +++ b/app/batch/rc_bt_0015.pgc @@ -1,9 +1,12 @@ /* @tier hard @module rc @transform reconciliation-batch */ /* - * rc_bt_0015.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=hard] + * rc_bt_0015.pgc - 일괄 대사마감 배치 (2-phase) (RC_BT_0015) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일에 대해 두 단계의 커서로 대사 마감을 일괄 수행한다. + * 업무규칙: + * [Phase1] 매입확정('M') 건 -> 정산완료('S') 로 전이 (마감정산). + * [Phase2] 미처리 접수('R') 건 -> 불일치('U') 로 전이 (마감불일치). + * 두 단계를 단일 트랜잭션으로 묶어 errcnt==0 일 때만 커밋한다. * * 실행: rc_bt_0015 [db@host] */ @@ -23,63 +26,94 @@ static int run_rc_bt_0015(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long settle_cnt = 0, close_unmatch = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - EXEC SQL DECLARE cur_rc_bt_0015 CURSOR FOR - SELECT key, merch_id, amount + EXEC SQL DECLARE cur_rc_bt_0015_a CURSOR FOR + SELECT key + FROM rc_ledger + WHERE biz_date = :h_biz_date + AND status = 'M' + ORDER BY key; + + EXEC SQL DECLARE cur_rc_bt_0015_b CURSOR FOR + SELECT key FROM rc_ledger WHERE biz_date = :h_biz_date AND status = 'R' ORDER BY key; - EXEC SQL OPEN cur_rc_bt_0015; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0015"); - return TX_EDB; - } - if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[rc_bt_0015] tx_begin 경고"); + /* ---- Phase1: 매입확정('M') -> 정산완료('S') ---- */ + EXEC SQL OPEN cur_rc_bt_0015_a; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0015_a"); + tx_abort(); + return TX_EDB; + } + for (;;) { - EXEC SQL FETCH cur_rc_bt_0015 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_rc_bt_0015_a INTO :h_key; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_rc_bt_0015"); + TX_DBIO_LOG_ERR("FETCH cur_rc_bt_0015_a"); errcnt++; break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - + strncpy(h_new_status, RC_ST_SETTLED, sizeof(h_new_status)); rc = rc_rec_update_status(h_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[rc_bt_0015] 마감정산 실패 key=%s rc=%d", + h_key, rc); errcnt++; + continue; } + settle_cnt++; } - EXEC SQL CLOSE cur_rc_bt_0015; + EXEC SQL CLOSE cur_rc_bt_0015_a; + + /* ---- Phase2: 미처리 접수('R') -> 불일치('U') ---- */ + EXEC SQL OPEN cur_rc_bt_0015_b; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0015_b"); + errcnt++; + } else { + for (;;) { + EXEC SQL FETCH cur_rc_bt_0015_b INTO :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_bt_0015_b"); + errcnt++; + break; + } + + strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); + rc = rc_rec_update_status(h_key, h_new_status); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[rc_bt_0015] 마감불일치 실패 key=%s rc=%d", + h_key, rc); + errcnt++; + continue; + } + close_unmatch++; + } + + EXEC SQL CLOSE cur_rc_bt_0015_b; + } if (errcnt == 0) tx_commit(); @@ -87,15 +121,14 @@ static int run_rc_bt_0015(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0015] 요약 date=%s 마감정산=%ld 마감불일치=%ld 오류=%ld", + biz_date, settle_cnt, close_unmatch, errcnt); - printf("========== 대사 배치 결과 [rc_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("======= 일괄 대사마감 배치 [rc_bt_0015] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 마감정산(S) : %ld\n", settle_cnt); + printf(" 마감불일치(U) : %ld\n", close_unmatch); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/rc_bt_0016.c b/app/batch/rc_bt_0016.c index 80288e5..7c1b0db 100644 --- a/app/batch/rc_bt_0016.c +++ b/app/batch/rc_bt_0016.c @@ -8,10 +8,13 @@ #line 1 "app/batch/rc_bt_0016.pgc" /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0016.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0016.pgc - 대사 diff 리포트 배치 (RC_BT_0016) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 원장을 가맹점번호(merch_id) 순으로 커서 순회하며 가맹점별 + * 승인측/매입측 금액 차이를 리포트한다. (읽기 전용 - 상태변경 없음) + * 업무규칙: 승인측 = status='R' 금액 합계, 매입측 = status in('M','S') 금액 합계. + * 가맹점이 바뀔 때 차액(승인측-매입측) 라인을 출력하고, 차액이 0 이 + * 아닌 가맹점을 불일치 가맹점으로 계수한다. * * 실행: rc_bt_0016 [db@host] */ @@ -93,7 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0016.pgc" +#line 22 "app/batch/rc_bt_0016.pgc" static int run_rc_bt_0016(const char *biz_date) @@ -103,61 +106,58 @@ static int run_rc_bt_0016(const char *biz_date) - -#line 24 "app/batch/rc_bt_0016.pgc" +#line 27 "app/batch/rc_bt_0016.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/rc_bt_0016.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/rc_bt_0016.pgc" +#line 28 "app/batch/rc_bt_0016.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/rc_bt_0016.pgc" +#line 29 "app/batch/rc_bt_0016.pgc" long h_amount ; -#line 28 "app/batch/rc_bt_0016.pgc" - char h_new_status [ 2 ] ; +#line 30 "app/batch/rc_bt_0016.pgc" + char h_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0016.pgc" +#line 31 "app/batch/rc_bt_0016.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0; + long merchant_cnt = 0, diff_merch = 0, grand_diff = 0; + long appr_side = 0, cap_side = 0; + char prev_merch[16]; + int have_group = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + prev_merch[0] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0016 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0016.pgc" + /* declare cur_rc_bt_0016 cursor for select merch_id , amount , status from rc_ledger where biz_date = $1 order by merch_id */ +#line 47 "app/batch/rc_bt_0016.pgc" -#line 42 "app/batch/rc_bt_0016.pgc" +#line 47 "app/batch/rc_bt_0016.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0016 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0016 cursor for select merch_id , amount , status from rc_ledger where biz_date = $1 order by merch_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0016.pgc" +#line 49 "app/batch/rc_bt_0016.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0016"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[rc_bt_0016] tx_begin 경고"); - for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0016", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0016.pgc" +#line 56 "app/batch/rc_bt_0016.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,44 +168,59 @@ static int run_rc_bt_0016(const char *biz_date) break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 가맹점 변경 -> 직전 가맹점 diff 출력 */ + if (have_group && strcmp(prev_merch, h_merch_id) != 0) { + long d = appr_side - cap_side; + merchant_cnt++; + grand_diff += d; + if (d != 0) + diff_merch++; + printf(" [diff] 가맹점=%s 승인측=%ld 매입측=%ld 차액=%ld\n", + prev_merch, appr_side, cap_side, d); + appr_side = 0; + cap_side = 0; } - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + strncpy(prev_merch, h_merch_id, sizeof(prev_merch) - 1); + prev_merch[sizeof(prev_merch) - 1] = '\0'; + have_group = 1; + + total++; + if (strcmp(h_status, RC_ST_RECV) == 0) { + appr_side += h_amount; + } else if (strcmp(h_status, RC_ST_DONE) == 0 || + strcmp(h_status, RC_ST_SETTLED) == 0) { + cap_side += h_amount; } } + /* 마지막 가맹점 diff 출력 */ + if (have_group) { + long d = appr_side - cap_side; + merchant_cnt++; + grand_diff += d; + if (d != 0) + diff_merch++; + printf(" [diff] 가맹점=%s 승인측=%ld 매입측=%ld 차액=%ld\n", + prev_merch, appr_side, cap_side, d); + } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0016", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0016.pgc" +#line 103 "app/batch/rc_bt_0016.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[rc_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0016] 요약 date=%s 가맹점=%ld 불일치=%ld 총차액=%ld", + biz_date, merchant_cnt, diff_merch, grand_diff); - printf("========== 대사 배치 결과 [rc_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 대사 diff 리포트 배치 [rc_bt_0016] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 총 건수 : %ld\n", total); + printf(" 총 가맹점 : %ld\n", merchant_cnt); + printf(" 불일치 가맹점 : %ld\n", diff_merch); + printf(" 총 차액 : %ld\n", grand_diff); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0016.pgc b/app/batch/rc_bt_0016.pgc index 1d92013..c6986ea 100644 --- a/app/batch/rc_bt_0016.pgc +++ b/app/batch/rc_bt_0016.pgc @@ -1,9 +1,12 @@ /* @tier easy @module rc @transform reconciliation-batch */ /* - * rc_bt_0016.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=easy] + * rc_bt_0016.pgc - 대사 diff 리포트 배치 (RC_BT_0016) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 원장을 가맹점번호(merch_id) 순으로 커서 순회하며 가맹점별 + * 승인측/매입측 금액 차이를 리포트한다. (읽기 전용 - 상태변경 없음) + * 업무규칙: 승인측 = status='R' 금액 합계, 매입측 = status in('M','S') 금액 합계. + * 가맹점이 바뀔 때 차액(승인측-매입측) 라인을 출력하고, 차액이 0 이 + * 아닌 가맹점을 불일치 가맹점으로 계수한다. * * 실행: rc_bt_0016 [db@host] */ @@ -22,24 +25,26 @@ static int run_rc_bt_0016(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; char h_merch_id[16]; long h_amount; - char h_new_status[2]; + char h_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, errcnt = 0; + long merchant_cnt = 0, diff_merch = 0, grand_diff = 0; + long appr_side = 0, cap_side = 0; + char prev_merch[16]; + int have_group = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + prev_merch[0] = '\0'; EXEC SQL DECLARE cur_rc_bt_0016 CURSOR FOR - SELECT key, merch_id, amount + SELECT merch_id, amount, status FROM rc_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + ORDER BY merch_id; EXEC SQL OPEN cur_rc_bt_0016; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,11 +52,8 @@ static int run_rc_bt_0016(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[rc_bt_0016] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_rc_bt_0016 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_rc_bt_0016 INTO :h_merch_id, :h_amount, :h_status; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,42 +63,57 @@ static int run_rc_bt_0016(const char *biz_date) break; } + /* 가맹점 변경 -> 직전 가맹점 diff 출력 */ + if (have_group && strcmp(prev_merch, h_merch_id) != 0) { + long d = appr_side - cap_side; + merchant_cnt++; + grand_diff += d; + if (d != 0) + diff_merch++; + printf(" [diff] 가맹점=%s 승인측=%ld 매입측=%ld 차액=%ld\n", + prev_merch, appr_side, cap_side, d); + appr_side = 0; + cap_side = 0; + } + + strncpy(prev_merch, h_merch_id, sizeof(prev_merch) - 1); + prev_merch[sizeof(prev_merch) - 1] = '\0'; + have_group = 1; + total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (strcmp(h_status, RC_ST_RECV) == 0) { + appr_side += h_amount; + } else if (strcmp(h_status, RC_ST_DONE) == 0 || + strcmp(h_status, RC_ST_SETTLED) == 0) { + cap_side += h_amount; } + } - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 마지막 가맹점 diff 출력 */ + if (have_group) { + long d = appr_side - cap_side; + merchant_cnt++; + grand_diff += d; + if (d != 0) + diff_merch++; + printf(" [diff] 가맹점=%s 승인측=%ld 매입측=%ld 차액=%ld\n", + prev_merch, appr_side, cap_side, d); } EXEC SQL CLOSE cur_rc_bt_0016; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - tx_log(TX_LOG_INFO, - "[rc_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0016] 요약 date=%s 가맹점=%ld 불일치=%ld 총차액=%ld", + biz_date, merchant_cnt, diff_merch, grand_diff); - printf("========== 대사 배치 결과 [rc_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("======= 대사 diff 리포트 배치 [rc_bt_0016] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 총 건수 : %ld\n", total); + printf(" 총 가맹점 : %ld\n", merchant_cnt); + printf(" 불일치 가맹점 : %ld\n", diff_merch); + printf(" 총 차액 : %ld\n", grand_diff); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/rc_bt_0017.c b/app/batch/rc_bt_0017.c index db4915b..cc3bb8b 100644 --- a/app/batch/rc_bt_0017.c +++ b/app/batch/rc_bt_0017.c @@ -8,10 +8,14 @@ #line 1 "app/batch/rc_bt_0017.pgc" /* @tier hard @module rc @transform reconciliation-batch */ /* - * rc_bt_0017.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=hard] + * rc_bt_0017.pgc - 대사 자동보정 배치 (RC_BT_0017) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 불일치('U') 건을 커서로 순회하며 수수료 오차를 재검증하여 + * 허용범위 내면 자동보정(매입확정)하고, 초과면 수기대상으로 남긴다. + * 업무규칙: 예상수수료 exp_fee = amount * 25 / 1000 (2.5%). + * 수수료 오차 feediff = |fee - exp_fee|. + * feediff <= RC_TOL_ABS 이면 자동보정 -> 매입확정('M') (corrected), + * 아니면 상태 유지('U') -> 수기대상(manual). * * 실행: rc_bt_0017 [db@host] */ @@ -93,9 +97,11 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/rc_bt_0017.pgc" +#line 23 "app/batch/rc_bt_0017.pgc" +#define RC_TOL_ABS 50 /* 자동보정 절대 허용오차 (원) */ + static int run_rc_bt_0017(const char *biz_date) { /* exec sql begin declare section */ @@ -105,41 +111,42 @@ static int run_rc_bt_0017(const char *biz_date) -#line 24 "app/batch/rc_bt_0017.pgc" +#line 30 "app/batch/rc_bt_0017.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/rc_bt_0017.pgc" +#line 31 "app/batch/rc_bt_0017.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/rc_bt_0017.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/rc_bt_0017.pgc" +#line 32 "app/batch/rc_bt_0017.pgc" long h_amount ; -#line 28 "app/batch/rc_bt_0017.pgc" +#line 33 "app/batch/rc_bt_0017.pgc" + long h_fee ; + +#line 34 "app/batch/rc_bt_0017.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/rc_bt_0017.pgc" +#line 35 "app/batch/rc_bt_0017.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, corrected = 0, manual = 0, errcnt = 0; + long corrected_amt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_rc_bt_0017 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/rc_bt_0017.pgc" + /* declare cur_rc_bt_0017 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'U' order by key */ +#line 49 "app/batch/rc_bt_0017.pgc" -#line 42 "app/batch/rc_bt_0017.pgc" +#line 49 "app/batch/rc_bt_0017.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0017 cursor for select key , merch_id , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_bt_0017 cursor for select key , amount , fee from rc_ledger where biz_date = $1 and status = 'U' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/rc_bt_0017.pgc" +#line 51 "app/batch/rc_bt_0017.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_rc_bt_0017"); @@ -150,14 +157,16 @@ static int run_rc_bt_0017(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0017] tx_begin 경고"); for (;;) { + long exp_fee, feediff; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_bt_0017", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/rc_bt_0017.pgc" +#line 63 "app/batch/rc_bt_0017.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +179,28 @@ static int run_rc_bt_0017(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* 예상수수료 대비 오차 검증 */ + exp_fee = h_amount * 25 / 1000; + feediff = labs(h_fee - exp_fee); - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0017] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (feediff <= RC_TOL_ABS) { + strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); + rc = rc_rec_update_status(h_key, h_new_status); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[rc_bt_0017] 자동보정 실패 key=%s rc=%d", + h_key, rc); + errcnt++; + continue; + } + corrected++; + corrected_amt += h_amount; + } else { + manual++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_bt_0017", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/rc_bt_0017.pgc" +#line 95 "app/batch/rc_bt_0017.pgc" if (errcnt == 0) @@ -196,15 +209,17 @@ static int run_rc_bt_0017(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0017] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0017] 요약 date=%s 대상=%ld 자동보정=%ld 수기=%ld 오류=%ld", + biz_date, total, corrected, manual, errcnt); - printf("========== 대사 배치 결과 [rc_bt_0017] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("======= 대사 자동보정 배치 [rc_bt_0017] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 자동보정(M) : %ld\n", corrected); + printf(" 수기대상(U) : %ld\n", manual); + printf(" 보정금액 합계 : %ld\n", corrected_amt); + printf(" 허용오차 : %d\n", RC_TOL_ABS); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/rc_bt_0017.pgc b/app/batch/rc_bt_0017.pgc index cbca078..be1a3ef 100644 --- a/app/batch/rc_bt_0017.pgc +++ b/app/batch/rc_bt_0017.pgc @@ -1,9 +1,13 @@ /* @tier hard @module rc @transform reconciliation-batch */ /* - * rc_bt_0017.pgc - 대사 배치 (커서 순회 + 상태전이) [tier=hard] + * rc_bt_0017.pgc - 대사 자동보정 배치 (RC_BT_0017) * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 불일치('U') 건을 커서로 순회하며 수수료 오차를 재검증하여 + * 허용범위 내면 자동보정(매입확정)하고, 초과면 수기대상으로 남긴다. + * 업무규칙: 예상수수료 exp_fee = amount * 25 / 1000 (2.5%). + * 수수료 오차 feediff = |fee - exp_fee|. + * feediff <= RC_TOL_ABS 이면 자동보정 -> 매입확정('M') (corrected), + * 아니면 상태 유지('U') -> 수기대상(manual). * * 실행: rc_bt_0017 [db@host] */ @@ -18,27 +22,30 @@ EXEC SQL INCLUDE sqlca; +#define RC_TOL_ABS 50 /* 자동보정 절대 허용오차 (원) */ + static int run_rc_bt_0017(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; + long h_fee; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, corrected = 0, manual = 0, errcnt = 0; + long corrected_amt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_rc_bt_0017 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount, fee FROM rc_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'U' ORDER BY key; EXEC SQL OPEN cur_rc_bt_0017; @@ -51,7 +58,9 @@ static int run_rc_bt_0017(const char *biz_date) tx_log(TX_LOG_WARN, "[rc_bt_0017] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_rc_bt_0017 INTO :h_key, :h_merch_id, :h_amount; + long exp_fee, feediff; + + EXEC SQL FETCH cur_rc_bt_0017 INTO :h_key, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,19 +72,23 @@ static int run_rc_bt_0017(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, RC_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* 예상수수료 대비 오차 검증 */ + exp_fee = h_amount * 25 / 1000; + feediff = labs(h_fee - exp_fee); - rc = rc_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[rc_bt_0017] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (feediff <= RC_TOL_ABS) { + strncpy(h_new_status, RC_ST_DONE, sizeof(h_new_status)); + rc = rc_rec_update_status(h_key, h_new_status); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[rc_bt_0017] 자동보정 실패 key=%s rc=%d", + h_key, rc); + errcnt++; + continue; + } + corrected++; + corrected_amt += h_amount; + } else { + manual++; } } @@ -87,15 +100,17 @@ static int run_rc_bt_0017(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[rc_bt_0017] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[rc_bt_0017] 요약 date=%s 대상=%ld 자동보정=%ld 수기=%ld 오류=%ld", + biz_date, total, corrected, manual, errcnt); - printf("========== 대사 배치 결과 [rc_bt_0017] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("======= 대사 자동보정 배치 [rc_bt_0017] =======\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 자동보정(M) : %ld\n", corrected); + printf(" 수기대상(U) : %ld\n", manual); + printf(" 보정금액 합계 : %ld\n", corrected_amt); + printf(" 허용오차 : %d\n", RC_TOL_ABS); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/st_bt_0002.c b/app/batch/st_bt_0002.c index 5f7b249..1bf8aff 100644 --- a/app/batch/st_bt_0002.c +++ b/app/batch/st_bt_0002.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0002.pgc" /* @tier medium @module st @transform settlement-batch */ /* - * st_bt_0002.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=medium] + * st_bt_0002.pgc - 일일 정산대상 집계 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 상태별(R/M/S/U) 건수와 거래금액을 집계하여 일일 정산 + * 대상 현황을 리포트한다. * * 실행: st_bt_0002 [db@host] */ @@ -96,6 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0002.pgc" + static int run_st_bt_0002(const char *biz_date) { /* exec sql begin declare section */ @@ -103,111 +104,64 @@ static int run_st_bt_0002(const char *biz_date) - -#line 24 "app/batch/st_bt_0002.pgc" +#line 25 "app/batch/st_bt_0002.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/st_bt_0002.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/st_bt_0002.pgc" - char h_merch_id [ 16 ] ; + char h_status [ 2 ] ; #line 27 "app/batch/st_bt_0002.pgc" - long h_amount ; + long h_cnt ; #line 28 "app/batch/st_bt_0002.pgc" - char h_new_status [ 2 ] ; + long h_sum ; /* exec sql end declare section */ #line 29 "app/batch/st_bt_0002.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const char *codes[4] = { "R", "M", "S", "U" }; + long total_cnt = 0, total_sum = 0; + int i; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0002 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/st_bt_0002.pgc" + printf("========== 일일 정산대상 집계 [st_bt_0002] ==========\n"); + printf(" 영업일 : %s\n", biz_date); -#line 42 "app/batch/st_bt_0002.pgc" + for (i = 0; i < 4; i++) { + strncpy(h_status, codes[i], sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0002 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from st_ledger where biz_date = $1 and status = $2 ", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/st_bt_0002.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_st_bt_0002"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0002] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0002", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/st_bt_0002.pgc" +#line 49 "app/batch/st_bt_0002.pgc" - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_st_bt_0002"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT agg st_bt_0002"); + return TX_EDB; } + printf(" [%s] 건수=%8ld 금액=%15ld\n", codes[i], h_cnt, h_sum); + total_cnt += h_cnt; + total_sum += h_sum; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0002", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0002.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" ----------------------------------------\n"); + printf(" 합계 건수=%8ld 금액=%15ld\n", total_cnt, total_sum); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + tx_log(TX_LOG_INFO, "[st_bt_0002] 집계완료 date=%s 건수=%ld 금액=%ld", + biz_date, total_cnt, total_sum); + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/st_bt_0002.pgc b/app/batch/st_bt_0002.pgc index 535e2ad..c9d6473 100644 --- a/app/batch/st_bt_0002.pgc +++ b/app/batch/st_bt_0002.pgc @@ -1,9 +1,9 @@ /* @tier medium @module st @transform settlement-batch */ /* - * st_bt_0002.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=medium] + * st_bt_0002.pgc - 일일 정산대상 집계 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 상태별(R/M/S/U) 건수와 거래금액을 집계하여 일일 정산 + * 대상 현황을 리포트한다. * * 실행: st_bt_0002 [db@host] */ @@ -18,87 +18,52 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0002(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_status[2]; + long h_cnt; + long h_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const char *codes[4] = { "R", "M", "S", "U" }; + long total_cnt = 0, total_sum = 0; + int i; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - EXEC SQL DECLARE cur_st_bt_0002 CURSOR FOR - SELECT key, merch_id, amount - FROM st_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + printf("========== 일일 정산대상 집계 [st_bt_0002] ==========\n"); + printf(" 영업일 : %s\n", biz_date); - EXEC SQL OPEN cur_st_bt_0002; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_st_bt_0002"); - return TX_EDB; + for (i = 0; i < 4; i++) { + strncpy(h_status, codes[i], sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_cnt, :h_sum + FROM st_ledger + WHERE biz_date = :h_biz_date + AND status = :h_status; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT agg st_bt_0002"); + return TX_EDB; + } + printf(" [%s] 건수=%8ld 금액=%15ld\n", codes[i], h_cnt, h_sum); + total_cnt += h_cnt; + total_sum += h_sum; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0002] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_st_bt_0002 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_st_bt_0002"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0002] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_st_bt_0002; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" ----------------------------------------\n"); + printf(" 합계 건수=%8ld 금액=%15ld\n", total_cnt, total_sum); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + tx_log(TX_LOG_INFO, "[st_bt_0002] 집계완료 date=%s 건수=%ld 금액=%ld", + biz_date, total_cnt, total_sum); + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/st_bt_0003.c b/app/batch/st_bt_0003.c index 6618c89..c5cfbd6 100644 --- a/app/batch/st_bt_0003.c +++ b/app/batch/st_bt_0003.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0003.pgc" /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0003.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0003.pgc - MDR 수수료 일괄계산 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 접수(R) 건을 커서로 순회하며 MDR 요율(1.80%)로 수수료를 원단위 반올림 + * 계산하여 원장 fee 컬럼을 일괄 갱신한다. * * 실행: st_bt_0003 [db@host] */ @@ -96,6 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0003.pgc" + static int run_st_bt_0003(const char *biz_date) { /* exec sql begin declare section */ @@ -103,40 +104,36 @@ static int run_st_bt_0003(const char *biz_date) - -#line 24 "app/batch/st_bt_0003.pgc" +#line 25 "app/batch/st_bt_0003.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/st_bt_0003.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/st_bt_0003.pgc" - char h_merch_id [ 16 ] ; + char h_key [ 16 ] ; #line 27 "app/batch/st_bt_0003.pgc" long h_amount ; #line 28 "app/batch/st_bt_0003.pgc" - char h_new_status [ 2 ] ; + long h_fee ; /* exec sql end declare section */ #line 29 "app/batch/st_bt_0003.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const long mdr_bp = 180; /* 1.80% */ + long total = 0, upd = 0, feesum = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0003 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_st_bt_0003 cursor for select key , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ #line 42 "app/batch/st_bt_0003.pgc" #line 42 "app/batch/st_bt_0003.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0003 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0003 cursor for select key , amount from st_ledger where biz_date = $1 and status = 'R' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/st_bt_0003.pgc" @@ -153,13 +150,10 @@ static int run_st_bt_0003(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0003", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 54 "app/batch/st_bt_0003.pgc" - if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -167,46 +161,40 @@ static int run_st_bt_0003(const char *biz_date) errcnt++; break; } - total++; + h_fee = (h_amount * mdr_bp + 5000) / 10000; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update st_ledger set fee = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 67 "app/batch/st_bt_0003.pgc" - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE fee st_bt_0003"); errcnt++; + } else { + upd++; + feesum += h_fee; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0003", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0003.pgc" +#line 77 "app/batch/st_bt_0003.pgc" + if (errcnt == 0) tx_commit(); else tx_abort(); - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== MDR 수수료 일괄계산 [st_bt_0003] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 갱신 건수 : %ld\n", upd); + printf(" 수수료 합계: %ld\n", feesum); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0003] MDR계산 date=%s 갱신=%ld 수수료합=%ld", + biz_date, upd, feesum); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0003.pgc b/app/batch/st_bt_0003.pgc index ea8a6fb..8e40b09 100644 --- a/app/batch/st_bt_0003.pgc +++ b/app/batch/st_bt_0003.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0003.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0003.pgc - MDR 수수료 일괄계산 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 접수(R) 건을 커서로 순회하며 MDR 요율(1.80%)로 수수료를 원단위 반올림 + * 계산하여 원장 fee 컬럼을 일괄 갱신한다. * * 실행: st_bt_0003 [db@host] */ @@ -18,24 +18,24 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0003(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const long mdr_bp = 180; /* 1.80% */ + long total = 0, upd = 0, feesum = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_st_bt_0003 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount FROM st_ledger WHERE biz_date = :h_biz_date AND status = 'R' @@ -51,8 +51,7 @@ static int run_st_bt_0003(const char *biz_date) tx_log(TX_LOG_WARN, "[st_bt_0003] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_st_bt_0003 INTO :h_key, :h_merch_id, :h_amount; - + EXEC SQL FETCH cur_st_bt_0003 INTO :h_key, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -60,44 +59,34 @@ static int run_st_bt_0003(const char *biz_date) errcnt++; break; } - total++; + h_fee = (h_amount * mdr_bp + 5000) / 10000; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL UPDATE st_ledger + SET fee = :h_fee, upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE fee st_bt_0003"); errcnt++; + } else { + upd++; + feesum += h_fee; } } EXEC SQL CLOSE cur_st_bt_0003; + if (errcnt == 0) tx_commit(); else tx_abort(); - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== MDR 수수료 일괄계산 [st_bt_0003] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 갱신 건수 : %ld\n", upd); + printf(" 수수료 합계: %ld\n", feesum); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0003] MDR계산 date=%s 갱신=%ld 수수료합=%ld", + biz_date, upd, feesum); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0004.c b/app/batch/st_bt_0004.c index ef54c52..29fe057 100644 --- a/app/batch/st_bt_0004.c +++ b/app/batch/st_bt_0004.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0004.pgc" /* @tier hard @module st @transform settlement-batch */ /* - * st_bt_0004.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=hard] + * st_bt_0004.pgc - VAN 수수료 일괄부과 배치 [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 접수(R) 건을 커서로 순회하며 건당 정액 VAN 수수료(120원)를 기존 + * 수수료에 가산하여 원장에 부과한다. * * 실행: st_bt_0004 [db@host] */ @@ -96,50 +96,43 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0004.pgc" + static int run_st_bt_0004(const char *biz_date) { /* exec sql begin declare section */ - - -#line 24 "app/batch/st_bt_0004.pgc" +#line 25 "app/batch/st_bt_0004.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/st_bt_0004.pgc" +#line 26 "app/batch/st_bt_0004.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/st_bt_0004.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/st_bt_0004.pgc" - long h_amount ; - -#line 28 "app/batch/st_bt_0004.pgc" - char h_new_status [ 2 ] ; + long h_fee ; /* exec sql end declare section */ -#line 29 "app/batch/st_bt_0004.pgc" +#line 28 "app/batch/st_bt_0004.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const long van_flat = 120; /* VAN 건당 정액 */ + long total = 0, upd = 0, vansum = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0004 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/st_bt_0004.pgc" + /* declare cur_st_bt_0004 cursor for select key , fee from st_ledger where biz_date = $1 and status = 'R' order by key */ +#line 41 "app/batch/st_bt_0004.pgc" -#line 42 "app/batch/st_bt_0004.pgc" +#line 41 "app/batch/st_bt_0004.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0004 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0004 cursor for select key , fee from st_ledger where biz_date = $1 and status = 'R' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/st_bt_0004.pgc" +#line 43 "app/batch/st_bt_0004.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_st_bt_0004"); @@ -153,12 +146,9 @@ static int run_st_bt_0004(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0004", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/st_bt_0004.pgc" - +#line 53 "app/batch/st_bt_0004.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -167,46 +157,40 @@ static int run_st_bt_0004(const char *biz_date) errcnt++; break; } - total++; + h_fee = h_fee + van_flat; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update st_ledger set fee = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 66 "app/batch/st_bt_0004.pgc" - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE fee st_bt_0004"); errcnt++; + } else { + upd++; + vansum += van_flat; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0004", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0004.pgc" +#line 76 "app/batch/st_bt_0004.pgc" + if (errcnt == 0) tx_commit(); else tx_abort(); - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0004] ==========\n"); + printf("========== VAN 수수료 일괄부과 [st_bt_0004] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 부과 건수 : %ld\n", upd); + printf(" VAN 수수료계: %ld\n", vansum); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0004] VAN부과 date=%s 부과=%ld 합계=%ld", + biz_date, upd, vansum); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0004.pgc b/app/batch/st_bt_0004.pgc index 9bd7077..37f1221 100644 --- a/app/batch/st_bt_0004.pgc +++ b/app/batch/st_bt_0004.pgc @@ -1,9 +1,9 @@ /* @tier hard @module st @transform settlement-batch */ /* - * st_bt_0004.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=hard] + * st_bt_0004.pgc - VAN 수수료 일괄부과 배치 [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 접수(R) 건을 커서로 순회하며 건당 정액 VAN 수수료(120원)를 기존 + * 수수료에 가산하여 원장에 부과한다. * * 실행: st_bt_0004 [db@host] */ @@ -18,24 +18,23 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0004(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const long van_flat = 120; /* VAN 건당 정액 */ + long total = 0, upd = 0, vansum = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_st_bt_0004 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, fee FROM st_ledger WHERE biz_date = :h_biz_date AND status = 'R' @@ -51,8 +50,7 @@ static int run_st_bt_0004(const char *biz_date) tx_log(TX_LOG_WARN, "[st_bt_0004] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_st_bt_0004 INTO :h_key, :h_merch_id, :h_amount; - + EXEC SQL FETCH cur_st_bt_0004 INTO :h_key, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -60,44 +58,34 @@ static int run_st_bt_0004(const char *biz_date) errcnt++; break; } - total++; + h_fee = h_fee + van_flat; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0004] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL UPDATE st_ledger + SET fee = :h_fee, upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE fee st_bt_0004"); errcnt++; + } else { + upd++; + vansum += van_flat; } } EXEC SQL CLOSE cur_st_bt_0004; + if (errcnt == 0) tx_commit(); else tx_abort(); - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0004] ==========\n"); + printf("========== VAN 수수료 일괄부과 [st_bt_0004] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 부과 건수 : %ld\n", upd); + printf(" VAN 수수료계: %ld\n", vansum); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0004] VAN부과 date=%s 부과=%ld 합계=%ld", + biz_date, upd, vansum); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0005.c b/app/batch/st_bt_0005.c index 7d86bce..409a65d 100644 --- a/app/batch/st_bt_0005.c +++ b/app/batch/st_bt_0005.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0005.pgc" /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0005.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0005.pgc - 가맹점별 정산금 netting 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 정산완료(M/S) 건을 가맹점별로 그룹 집계하여 매출합에서 수수료합을 + * 차감한 정산순액(netting)을 산출한다. * * 실행: st_bt_0005 [db@host] */ @@ -96,6 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0005.pgc" + static int run_st_bt_0005(const char *biz_date) { /* exec sql begin declare section */ @@ -103,40 +104,35 @@ static int run_st_bt_0005(const char *biz_date) - -#line 24 "app/batch/st_bt_0005.pgc" +#line 25 "app/batch/st_bt_0005.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/st_bt_0005.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/st_bt_0005.pgc" - char h_merch_id [ 16 ] ; + char h_merch [ 16 ] ; #line 27 "app/batch/st_bt_0005.pgc" - long h_amount ; + long h_gross ; #line 28 "app/batch/st_bt_0005.pgc" - char h_new_status [ 2 ] ; + long h_fee ; /* exec sql end declare section */ #line 29 "app/batch/st_bt_0005.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merch_cnt = 0, net_total = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0005 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_st_bt_0005 cursor for select merch_id , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from st_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by merch_id order by merch_id */ #line 42 "app/batch/st_bt_0005.pgc" #line 42 "app/batch/st_bt_0005.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0005 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0005 cursor for select merch_id , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from st_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by merch_id order by merch_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/st_bt_0005.pgc" @@ -146,20 +142,19 @@ static int run_st_bt_0005(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0005] tx_begin 경고"); + printf("========== 가맹점별 정산 netting [st_bt_0005] ==========\n"); + printf(" 영업일 : %s\n", biz_date); for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0005", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_gross),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 54 "app/batch/st_bt_0005.pgc" - if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -167,46 +162,23 @@ static int run_st_bt_0005(const char *biz_date) errcnt++; break; } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + merch_cnt++; + long net = h_gross - h_fee; + net_total += net; + printf(" 가맹점 %-12s 매출=%13ld 수수료=%11ld 정산=%13ld\n", + h_merch, h_gross, h_fee, net); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0005", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0005.pgc" +#line 69 "app/batch/st_bt_0005.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" ----------------------------------------\n"); + printf(" 가맹점 수 : %ld 정산순액 합계 : %ld\n", merch_cnt, net_total); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0005] netting date=%s 가맹점=%ld 순액합=%ld", + biz_date, merch_cnt, net_total); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0005.pgc b/app/batch/st_bt_0005.pgc index 64a98f9..e746b2f 100644 --- a/app/batch/st_bt_0005.pgc +++ b/app/batch/st_bt_0005.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0005.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0005.pgc - 가맹점별 정산금 netting 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 정산완료(M/S) 건을 가맹점별로 그룹 집계하여 매출합에서 수수료합을 + * 차감한 정산순액(netting)을 산출한다. * * 실행: st_bt_0005 [db@host] */ @@ -18,28 +18,28 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0005(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_merch[16]; + long h_gross; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merch_cnt = 0, net_total = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_st_bt_0005 CURSOR FOR - SELECT key, merch_id, amount + SELECT merch_id, coalesce(sum(amount), 0), coalesce(sum(fee), 0) FROM st_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status IN ('M', 'S') + GROUP BY merch_id + ORDER BY merch_id; EXEC SQL OPEN cur_st_bt_0005; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,12 +47,11 @@ static int run_st_bt_0005(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0005] tx_begin 경고"); + printf("========== 가맹점별 정산 netting [st_bt_0005] ==========\n"); + printf(" 영업일 : %s\n", biz_date); for (;;) { - EXEC SQL FETCH cur_st_bt_0005 INTO :h_key, :h_merch_id, :h_amount; - + EXEC SQL FETCH cur_st_bt_0005 INTO :h_merch, :h_gross, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -60,44 +59,21 @@ static int run_st_bt_0005(const char *biz_date) errcnt++; break; } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0005] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + merch_cnt++; + long net = h_gross - h_fee; + net_total += net; + printf(" 가맹점 %-12s 매출=%13ld 수수료=%11ld 정산=%13ld\n", + h_merch, h_gross, h_fee, net); } EXEC SQL CLOSE cur_st_bt_0005; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" ----------------------------------------\n"); + printf(" 가맹점 수 : %ld 정산순액 합계 : %ld\n", merch_cnt, net_total); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0005] netting date=%s 가맹점=%ld 순액합=%ld", + biz_date, merch_cnt, net_total); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0006.c b/app/batch/st_bt_0006.c index cfbcfec..6670310 100644 --- a/app/batch/st_bt_0006.c +++ b/app/batch/st_bt_0006.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0006.pgc" /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0006.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0006.pgc - 정산 부가세(VAT) 집계 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 수수료 총액을 공급가액으로 하여 부가세 10% 를 산출한다. + * 공급가/세액/합계를 원단위 반올림으로 집계한다. * * 실행: st_bt_0006 [db@host] */ @@ -96,118 +96,60 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0006.pgc" + static int run_st_bt_0006(const char *biz_date) { /* exec sql begin declare section */ - - -#line 24 "app/batch/st_bt_0006.pgc" +#line 25 "app/batch/st_bt_0006.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/st_bt_0006.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/st_bt_0006.pgc" - char h_merch_id [ 16 ] ; + long h_feesum ; #line 27 "app/batch/st_bt_0006.pgc" - long h_amount ; - -#line 28 "app/batch/st_bt_0006.pgc" - char h_new_status [ 2 ] ; + long h_cnt ; /* exec sql end declare section */ -#line 29 "app/batch/st_bt_0006.pgc" +#line 28 "app/batch/st_bt_0006.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long vat, total; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0006 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/st_bt_0006.pgc" - -#line 42 "app/batch/st_bt_0006.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0006 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( fee ) , 0 ) from st_ledger where biz_date = $1 and status in ( 'M' , 'S' )", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/st_bt_0006.pgc" + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_feesum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 39 "app/batch/st_bt_0006.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_st_bt_0006"); + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT vat st_bt_0006"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0006] tx_begin 경고"); + vat = (h_feesum * 1000 + 5000) / 10000; /* 10% 원단위 반올림 */ + total = h_feesum + vat; - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0006", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/st_bt_0006.pgc" - - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_st_bt_0006"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0006", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0006.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 정산 부가세 집계 [st_bt_0006] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", h_cnt); + printf(" 공급가(수수료): %ld\n", h_feesum); + printf(" 부가세(10%%): %ld\n", vat); + printf(" 합계 : %ld\n", total); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + tx_log(TX_LOG_INFO, "[st_bt_0006] VAT집계 date=%s 공급가=%ld 세액=%ld", + biz_date, h_feesum, vat); + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/st_bt_0006.pgc b/app/batch/st_bt_0006.pgc index 3265d25..55a7987 100644 --- a/app/batch/st_bt_0006.pgc +++ b/app/batch/st_bt_0006.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0006.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0006.pgc - 정산 부가세(VAT) 집계 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 수수료 총액을 공급가액으로 하여 부가세 10% 를 산출한다. + * 공급가/세액/합계를 원단위 반올림으로 집계한다. * * 실행: st_bt_0006 [db@host] */ @@ -18,87 +18,45 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0006(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_feesum; + long h_cnt; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long vat, total; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - EXEC SQL DECLARE cur_st_bt_0006 CURSOR FOR - SELECT key, merch_id, amount - FROM st_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + EXEC SQL SELECT count(*), coalesce(sum(fee), 0) + INTO :h_cnt, :h_feesum + FROM st_ledger + WHERE biz_date = :h_biz_date + AND status IN ('M', 'S'); - EXEC SQL OPEN cur_st_bt_0006; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_st_bt_0006"); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT vat st_bt_0006"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0006] tx_begin 경고"); + vat = (h_feesum * 1000 + 5000) / 10000; /* 10% 원단위 반올림 */ + total = h_feesum + vat; - for (;;) { - EXEC SQL FETCH cur_st_bt_0006 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_st_bt_0006"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0006] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_st_bt_0006; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 정산 부가세 집계 [st_bt_0006] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", h_cnt); + printf(" 공급가(수수료): %ld\n", h_feesum); + printf(" 부가세(10%%): %ld\n", vat); + printf(" 합계 : %ld\n", total); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + tx_log(TX_LOG_INFO, "[st_bt_0006] VAT집계 date=%s 공급가=%ld 세액=%ld", + biz_date, h_feesum, vat); + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/st_bt_0007.c b/app/batch/st_bt_0007.c index e64972f..10f50f4 100644 --- a/app/batch/st_bt_0007.c +++ b/app/batch/st_bt_0007.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0007.pgc" /* @tier medium @module st @transform settlement-batch */ /* - * st_bt_0007.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=medium] + * st_bt_0007.pgc - 가맹점별 원천징수 집계 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 가맹점별 정산순액을 그룹 집계하고 사업소득 원천징수 3.3% + * (소득세 3% + 지방세 0.3%)를 산출한다. * * 실행: st_bt_0007 [db@host] */ @@ -96,69 +96,58 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0007.pgc" + static int run_st_bt_0007(const char *biz_date) { /* exec sql begin declare section */ - - -#line 24 "app/batch/st_bt_0007.pgc" +#line 25 "app/batch/st_bt_0007.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/st_bt_0007.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/st_bt_0007.pgc" - char h_merch_id [ 16 ] ; + char h_merch [ 16 ] ; #line 27 "app/batch/st_bt_0007.pgc" - long h_amount ; - -#line 28 "app/batch/st_bt_0007.pgc" - char h_new_status [ 2 ] ; + long h_base ; /* exec sql end declare section */ -#line 29 "app/batch/st_bt_0007.pgc" +#line 28 "app/batch/st_bt_0007.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merch_cnt = 0, wh_total = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0007 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/st_bt_0007.pgc" + /* declare cur_st_bt_0007 cursor for select merch_id , coalesce ( sum ( amount - fee ) , 0 ) from st_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by merch_id order by merch_id */ +#line 41 "app/batch/st_bt_0007.pgc" -#line 42 "app/batch/st_bt_0007.pgc" +#line 41 "app/batch/st_bt_0007.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0007 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0007 cursor for select merch_id , coalesce ( sum ( amount - fee ) , 0 ) from st_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by merch_id order by merch_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/st_bt_0007.pgc" +#line 43 "app/batch/st_bt_0007.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_st_bt_0007"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0007] tx_begin 경고"); + printf("========== 가맹점별 원천징수 집계 [st_bt_0007] ==========\n"); + printf(" 영업일 : %s\n", biz_date); for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0007", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_base),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/st_bt_0007.pgc" - +#line 53 "app/batch/st_bt_0007.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -167,46 +156,25 @@ static int run_st_bt_0007(const char *biz_date) errcnt++; break; } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + merch_cnt++; + long income_tax = (h_base * 300 + 5000) / 10000; /* 3.0% */ + long local_tax = (income_tax * 1000 + 5000) / 10000; /* 소득세의 10% */ + long withhold = income_tax + local_tax; + wh_total += withhold; + printf(" 가맹점 %-12s 지급기준=%13ld 원천징수=%11ld\n", + h_merch, h_base, withhold); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0007", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0007.pgc" +#line 70 "app/batch/st_bt_0007.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" ----------------------------------------\n"); + printf(" 가맹점 수 : %ld 원천징수 합계 : %ld\n", merch_cnt, wh_total); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0007] 원천징수 date=%s 가맹점=%ld 세액합=%ld", + biz_date, merch_cnt, wh_total); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0007.pgc b/app/batch/st_bt_0007.pgc index 408da85..6501f00 100644 --- a/app/batch/st_bt_0007.pgc +++ b/app/batch/st_bt_0007.pgc @@ -1,9 +1,9 @@ /* @tier medium @module st @transform settlement-batch */ /* - * st_bt_0007.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=medium] + * st_bt_0007.pgc - 가맹점별 원천징수 집계 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 가맹점별 정산순액을 그룹 집계하고 사업소득 원천징수 3.3% + * (소득세 3% + 지방세 0.3%)를 산출한다. * * 실행: st_bt_0007 [db@host] */ @@ -18,28 +18,27 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0007(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_merch[16]; + long h_base; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merch_cnt = 0, wh_total = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_st_bt_0007 CURSOR FOR - SELECT key, merch_id, amount + SELECT merch_id, coalesce(sum(amount - fee), 0) FROM st_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status IN ('M', 'S') + GROUP BY merch_id + ORDER BY merch_id; EXEC SQL OPEN cur_st_bt_0007; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,12 +46,11 @@ static int run_st_bt_0007(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0007] tx_begin 경고"); + printf("========== 가맹점별 원천징수 집계 [st_bt_0007] ==========\n"); + printf(" 영업일 : %s\n", biz_date); for (;;) { - EXEC SQL FETCH cur_st_bt_0007 INTO :h_key, :h_merch_id, :h_amount; - + EXEC SQL FETCH cur_st_bt_0007 INTO :h_merch, :h_base; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -60,44 +58,23 @@ static int run_st_bt_0007(const char *biz_date) errcnt++; break; } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + merch_cnt++; + long income_tax = (h_base * 300 + 5000) / 10000; /* 3.0% */ + long local_tax = (income_tax * 1000 + 5000) / 10000; /* 소득세의 10% */ + long withhold = income_tax + local_tax; + wh_total += withhold; + printf(" 가맹점 %-12s 지급기준=%13ld 원천징수=%11ld\n", + h_merch, h_base, withhold); } EXEC SQL CLOSE cur_st_bt_0007; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" ----------------------------------------\n"); + printf(" 가맹점 수 : %ld 원천징수 합계 : %ld\n", merch_cnt, wh_total); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0007] 원천징수 date=%s 가맹점=%ld 세액합=%ld", + biz_date, merch_cnt, wh_total); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0008.c b/app/batch/st_bt_0008.c index 865fde7..53429a2 100644 --- a/app/batch/st_bt_0008.c +++ b/app/batch/st_bt_0008.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0008.pgc" /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0008.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0008.pgc - 지연정산 이자 산출 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 처리완료(M) 상태로 지급 대기 중인 건에 대해 지연이자(연 15%, 1일 + * 지연 가정)를 원단위 반올림 일할 계산하여 집계한다. * * 실행: st_bt_0008 [db@host] */ @@ -96,47 +96,41 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0008.pgc" + static int run_st_bt_0008(const char *biz_date) { /* exec sql begin declare section */ - - -#line 24 "app/batch/st_bt_0008.pgc" +#line 25 "app/batch/st_bt_0008.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/st_bt_0008.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/st_bt_0008.pgc" - char h_merch_id [ 16 ] ; + char h_key [ 16 ] ; #line 27 "app/batch/st_bt_0008.pgc" long h_amount ; - -#line 28 "app/batch/st_bt_0008.pgc" - char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/st_bt_0008.pgc" +#line 28 "app/batch/st_bt_0008.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const long annual_bp = 1500; /* 연 15% */ + const long delay_days = 1; + long total = 0, int_total = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0008 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_st_bt_0008 cursor for select key , amount from st_ledger where biz_date = $1 and status = 'M' order by key */ #line 42 "app/batch/st_bt_0008.pgc" #line 42 "app/batch/st_bt_0008.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0008 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0008 cursor for select key , amount from st_ledger where biz_date = $1 and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/st_bt_0008.pgc" @@ -146,19 +140,13 @@ static int run_st_bt_0008(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0008] tx_begin 경고"); - for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0008", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/st_bt_0008.pgc" - +#line 51 "app/batch/st_bt_0008.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -167,46 +155,25 @@ static int run_st_bt_0008(const char *biz_date) errcnt++; break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + long yearly = (h_amount * annual_bp + 5000) / 10000; + long interest = yearly * delay_days / 365; + int_total += interest; } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0008", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0008.pgc" +#line 65 "app/batch/st_bt_0008.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0008] ==========\n"); + printf("========== 지연정산 이자 산출 [st_bt_0008] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 지연일수 : %ld\n", delay_days); + printf(" 지연이자 합 : %ld\n", int_total); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0008] 지연이자 date=%s 건수=%ld 이자합=%ld", + biz_date, total, int_total); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0008.pgc b/app/batch/st_bt_0008.pgc index d1644a6..40c4c26 100644 --- a/app/batch/st_bt_0008.pgc +++ b/app/batch/st_bt_0008.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0008.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0008.pgc - 지연정산 이자 산출 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 처리완료(M) 상태로 지급 대기 중인 건에 대해 지연이자(연 15%, 1일 + * 지연 가정)를 원단위 반올림 일할 계산하여 집계한다. * * 실행: st_bt_0008 [db@host] */ @@ -18,27 +18,27 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0008(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const long annual_bp = 1500; /* 연 15% */ + const long delay_days = 1; + long total = 0, int_total = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_st_bt_0008 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount FROM st_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'M' ORDER BY key; EXEC SQL OPEN cur_st_bt_0008; @@ -47,12 +47,8 @@ static int run_st_bt_0008(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0008] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_st_bt_0008 INTO :h_key, :h_merch_id, :h_amount; - + EXEC SQL FETCH cur_st_bt_0008 INTO :h_key, :h_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -60,44 +56,23 @@ static int run_st_bt_0008(const char *biz_date) errcnt++; break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + long yearly = (h_amount * annual_bp + 5000) / 10000; + long interest = yearly * delay_days / 365; + int_total += interest; } EXEC SQL CLOSE cur_st_bt_0008; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0008] ==========\n"); + printf("========== 지연정산 이자 산출 [st_bt_0008] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 지연일수 : %ld\n", delay_days); + printf(" 지연이자 합 : %ld\n", int_total); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0008] 지연이자 date=%s 건수=%ld 이자합=%ld", + biz_date, total, int_total); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0009.c b/app/batch/st_bt_0009.c index 7db603d..ab4382c 100644 --- a/app/batch/st_bt_0009.c +++ b/app/batch/st_bt_0009.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0009.pgc" /* @tier medium @module st @transform settlement-batch */ /* - * st_bt_0009.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=medium] + * st_bt_0009.pgc - 정산 배치마감(확정) 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 처리완료(M) 건을 커서로 순회하며 정산완료(S)로 확정 전이하여 당일 + * 정산을 마감한다. 오류 시 롤백한다. * * 실행: st_bt_0009 [db@host] */ @@ -96,6 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0009.pgc" + static int run_st_bt_0009(const char *biz_date) { /* exec sql begin declare section */ @@ -103,43 +104,38 @@ static int run_st_bt_0009(const char *biz_date) - -#line 24 "app/batch/st_bt_0009.pgc" +#line 25 "app/batch/st_bt_0009.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/st_bt_0009.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/st_bt_0009.pgc" - char h_merch_id [ 16 ] ; + char h_key [ 16 ] ; #line 27 "app/batch/st_bt_0009.pgc" long h_amount ; #line 28 "app/batch/st_bt_0009.pgc" - char h_new_status [ 2 ] ; + long h_fee ; /* exec sql end declare section */ #line 29 "app/batch/st_bt_0009.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, settled = 0, net_total = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0009 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/st_bt_0009.pgc" + /* declare cur_st_bt_0009 cursor for select key , amount , fee from st_ledger where biz_date = $1 and status = 'M' order by key */ +#line 41 "app/batch/st_bt_0009.pgc" -#line 42 "app/batch/st_bt_0009.pgc" +#line 41 "app/batch/st_bt_0009.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0009 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0009 cursor for select key , amount , fee from st_ledger where biz_date = $1 and status = 'M' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/st_bt_0009.pgc" +#line 43 "app/batch/st_bt_0009.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_st_bt_0009"); @@ -153,12 +149,11 @@ static int run_st_bt_0009(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0009", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/st_bt_0009.pgc" - +#line 53 "app/batch/st_bt_0009.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -167,46 +162,37 @@ static int run_st_bt_0009(const char *biz_date) errcnt++; break; } - total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update st_ledger set status = 'S' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 65 "app/batch/st_bt_0009.pgc" - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE settle st_bt_0009"); errcnt++; + } else { + settled++; + net_total += (h_amount - h_fee); } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0009", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0009.pgc" +#line 75 "app/batch/st_bt_0009.pgc" + if (errcnt == 0) tx_commit(); else tx_abort(); - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0009] ==========\n"); + printf("========== 정산 배치마감 [st_bt_0009] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 확정(S) : %ld\n", settled); + printf(" 정산순액 합 : %ld\n", net_total); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0009] 마감 date=%s 확정=%ld 순액합=%ld", + biz_date, settled, net_total); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0009.pgc b/app/batch/st_bt_0009.pgc index d6a7563..27c3348 100644 --- a/app/batch/st_bt_0009.pgc +++ b/app/batch/st_bt_0009.pgc @@ -1,9 +1,9 @@ /* @tier medium @module st @transform settlement-batch */ /* - * st_bt_0009.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=medium] + * st_bt_0009.pgc - 정산 배치마감(확정) 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 처리완료(M) 건을 커서로 순회하며 정산완료(S)로 확정 전이하여 당일 + * 정산을 마감한다. 오류 시 롤백한다. * * 실행: st_bt_0009 [db@host] */ @@ -18,27 +18,26 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0009(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, settled = 0, net_total = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_st_bt_0009 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount, fee FROM st_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status = 'M' ORDER BY key; EXEC SQL OPEN cur_st_bt_0009; @@ -51,8 +50,7 @@ static int run_st_bt_0009(const char *biz_date) tx_log(TX_LOG_WARN, "[st_bt_0009] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_st_bt_0009 INTO :h_key, :h_merch_id, :h_amount; - + EXEC SQL FETCH cur_st_bt_0009 INTO :h_key, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -60,44 +58,33 @@ static int run_st_bt_0009(const char *biz_date) errcnt++; break; } - total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0009] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL UPDATE st_ledger + SET status = 'S', upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE settle st_bt_0009"); errcnt++; + } else { + settled++; + net_total += (h_amount - h_fee); } } EXEC SQL CLOSE cur_st_bt_0009; + if (errcnt == 0) tx_commit(); else tx_abort(); - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0009] ==========\n"); + printf("========== 정산 배치마감 [st_bt_0009] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 확정(S) : %ld\n", settled); + printf(" 정산순액 합 : %ld\n", net_total); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0009] 마감 date=%s 확정=%ld 순액합=%ld", + biz_date, settled, net_total); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0010.c b/app/batch/st_bt_0010.c index 999cfad..4c615e4 100644 --- a/app/batch/st_bt_0010.c +++ b/app/batch/st_bt_0010.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0010.pgc" /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0010.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0010.pgc - 월정산 집계 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일이 속한 월(YYYYMM) 전체의 상태별 건수/거래금액/수수료를 + * 집계하여 월 정산 마감 현황을 리포트한다. * * 실행: st_bt_0010 [db@host] */ @@ -96,6 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0010.pgc" + static int run_st_bt_0010(const char *biz_date) { /* exec sql begin declare section */ @@ -105,109 +106,71 @@ static int run_st_bt_0010(const char *biz_date) -#line 24 "app/batch/st_bt_0010.pgc" - char h_biz_date [ 9 ] ; - #line 25 "app/batch/st_bt_0010.pgc" - char h_key [ 16 ] ; + char h_ym [ 7 ] ; #line 26 "app/batch/st_bt_0010.pgc" - char h_merch_id [ 16 ] ; + char h_status [ 2 ] ; #line 27 "app/batch/st_bt_0010.pgc" - long h_amount ; + long h_cnt ; #line 28 "app/batch/st_bt_0010.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_sum ; + #line 29 "app/batch/st_bt_0010.pgc" + long h_fee ; +/* exec sql end declare section */ +#line 30 "app/batch/st_bt_0010.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const char *codes[3] = { "M", "S", "U" }; + long total_cnt = 0, total_sum = 0, total_fee = 0; + int i; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_ym, biz_date, 6); /* YYYYMM */ + h_ym[6] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0010 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/st_bt_0010.pgc" + printf("========== 월정산 집계 [st_bt_0010] ==========\n"); + printf(" 정산월 : %s\n", h_ym); -#line 42 "app/batch/st_bt_0010.pgc" + for (i = 0; i < 3; i++) { + strncpy(h_status, codes[i], sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0010 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/st_bt_0010.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_st_bt_0010"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0010] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0010", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from st_ledger where substr ( biz_date , 1 , 6 ) = $1 and status = $2 ", + ECPGt_char,(h_ym),(long)7,(long)1,(7)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/st_bt_0010.pgc" +#line 50 "app/batch/st_bt_0010.pgc" - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_st_bt_0010"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT month st_bt_0010"); + return TX_EDB; } + printf(" [%s] 건수=%8ld 매출=%15ld 수수료=%13ld\n", + codes[i], h_cnt, h_sum, h_fee); + total_cnt += h_cnt; + total_sum += h_sum; + total_fee += h_fee; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0010", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0010.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" ----------------------------------------\n"); + printf(" 합계 건수=%8ld 매출=%15ld 수수료=%13ld\n", + total_cnt, total_sum, total_fee); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + tx_log(TX_LOG_INFO, "[st_bt_0010] 월정산 ym=%s 건수=%ld 매출=%ld", + h_ym, total_cnt, total_sum); + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/st_bt_0010.pgc b/app/batch/st_bt_0010.pgc index b01aee0..cb9da53 100644 --- a/app/batch/st_bt_0010.pgc +++ b/app/batch/st_bt_0010.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0010.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0010.pgc - 월정산 집계 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일이 속한 월(YYYYMM) 전체의 상태별 건수/거래금액/수수료를 + * 집계하여 월 정산 마감 현황을 리포트한다. * * 실행: st_bt_0010 [db@host] */ @@ -18,87 +18,56 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0010(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; - char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_ym[7]; + char h_status[2]; + long h_cnt; + long h_sum; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const char *codes[3] = { "M", "S", "U" }; + long total_cnt = 0, total_sum = 0, total_fee = 0; + int i; - strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); - h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_ym, biz_date, 6); /* YYYYMM */ + h_ym[6] = '\0'; - EXEC SQL DECLARE cur_st_bt_0010 CURSOR FOR - SELECT key, merch_id, amount - FROM st_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + printf("========== 월정산 집계 [st_bt_0010] ==========\n"); + printf(" 정산월 : %s\n", h_ym); - EXEC SQL OPEN cur_st_bt_0010; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_st_bt_0010"); - return TX_EDB; + for (i = 0; i < 3; i++) { + strncpy(h_status, codes[i], sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + EXEC SQL SELECT count(*), coalesce(sum(amount), 0), coalesce(sum(fee), 0) + INTO :h_cnt, :h_sum, :h_fee + FROM st_ledger + WHERE substr(biz_date, 1, 6) = :h_ym + AND status = :h_status; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT month st_bt_0010"); + return TX_EDB; + } + printf(" [%s] 건수=%8ld 매출=%15ld 수수료=%13ld\n", + codes[i], h_cnt, h_sum, h_fee); + total_cnt += h_cnt; + total_sum += h_sum; + total_fee += h_fee; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0010] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_st_bt_0010 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_st_bt_0010"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_st_bt_0010; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" ----------------------------------------\n"); + printf(" 합계 건수=%8ld 매출=%15ld 수수료=%13ld\n", + total_cnt, total_sum, total_fee); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + tx_log(TX_LOG_INFO, "[st_bt_0010] 월정산 ym=%s 건수=%ld 매출=%ld", + h_ym, total_cnt, total_sum); + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/st_bt_0011.c b/app/batch/st_bt_0011.c index 9ec1d63..d8464c0 100644 --- a/app/batch/st_bt_0011.c +++ b/app/batch/st_bt_0011.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0011.pgc" /* @tier medium @module st @transform settlement-batch */ /* - * st_bt_0011.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=medium] + * st_bt_0011.pgc - 가맹점별 정산 집계 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 정산 원장을 가맹점별로 그룹 집계하여 건수/매출/수수료/정산순액을 + * 산출하는 가맹점 정산명세 배치. * * 실행: st_bt_0011 [db@host] */ @@ -96,6 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0011.pgc" + static int run_st_bt_0011(const char *biz_date) { /* exec sql begin declare section */ @@ -105,38 +106,37 @@ static int run_st_bt_0011(const char *biz_date) -#line 24 "app/batch/st_bt_0011.pgc" +#line 25 "app/batch/st_bt_0011.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/st_bt_0011.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/st_bt_0011.pgc" - char h_merch_id [ 16 ] ; + char h_merch [ 16 ] ; #line 27 "app/batch/st_bt_0011.pgc" - long h_amount ; + long h_cnt ; #line 28 "app/batch/st_bt_0011.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_sum ; + #line 29 "app/batch/st_bt_0011.pgc" + long h_fee ; +/* exec sql end declare section */ +#line 30 "app/batch/st_bt_0011.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merch_cnt = 0, grand_net = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0011 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_st_bt_0011 cursor for select merch_id , count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from st_ledger where biz_date = $1 group by merch_id order by merch_id */ #line 42 "app/batch/st_bt_0011.pgc" #line 42 "app/batch/st_bt_0011.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0011 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0011 cursor for select merch_id , count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from st_ledger where biz_date = $1 group by merch_id order by merch_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/st_bt_0011.pgc" @@ -146,20 +146,21 @@ static int run_st_bt_0011(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0011] tx_begin 경고"); + printf("========== 가맹점별 정산 집계 [st_bt_0011] ==========\n"); + printf(" 영업일 : %s\n", biz_date); for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0011", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 54 "app/batch/st_bt_0011.pgc" - if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -167,46 +168,23 @@ static int run_st_bt_0011(const char *biz_date) errcnt++; break; } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + merch_cnt++; + long net = h_sum - h_fee; + grand_net += net; + printf(" %-12s 건=%6ld 매출=%13ld 수수료=%11ld 순액=%13ld\n", + h_merch, h_cnt, h_sum, h_fee, net); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0011", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0011.pgc" +#line 69 "app/batch/st_bt_0011.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" ----------------------------------------\n"); + printf(" 가맹점 수 : %ld 정산순액 총계 : %ld\n", merch_cnt, grand_net); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0011] 가맹점집계 date=%s 가맹점=%ld 순액=%ld", + biz_date, merch_cnt, grand_net); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0011.pgc b/app/batch/st_bt_0011.pgc index a5e9862..406e18c 100644 --- a/app/batch/st_bt_0011.pgc +++ b/app/batch/st_bt_0011.pgc @@ -1,9 +1,9 @@ /* @tier medium @module st @transform settlement-batch */ /* - * st_bt_0011.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=medium] + * st_bt_0011.pgc - 가맹점별 정산 집계 배치 [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 정산 원장을 가맹점별로 그룹 집계하여 건수/매출/수수료/정산순액을 + * 산출하는 가맹점 정산명세 배치. * * 실행: st_bt_0011 [db@host] */ @@ -18,28 +18,28 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0011(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_merch[16]; + long h_cnt; + long h_sum; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long merch_cnt = 0, grand_net = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_st_bt_0011 CURSOR FOR - SELECT key, merch_id, amount + SELECT merch_id, count(*), coalesce(sum(amount), 0), coalesce(sum(fee), 0) FROM st_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + GROUP BY merch_id + ORDER BY merch_id; EXEC SQL OPEN cur_st_bt_0011; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,12 +47,11 @@ static int run_st_bt_0011(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0011] tx_begin 경고"); + printf("========== 가맹점별 정산 집계 [st_bt_0011] ==========\n"); + printf(" 영업일 : %s\n", biz_date); for (;;) { - EXEC SQL FETCH cur_st_bt_0011 INTO :h_key, :h_merch_id, :h_amount; - + EXEC SQL FETCH cur_st_bt_0011 INTO :h_merch, :h_cnt, :h_sum, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -60,44 +59,21 @@ static int run_st_bt_0011(const char *biz_date) errcnt++; break; } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + merch_cnt++; + long net = h_sum - h_fee; + grand_net += net; + printf(" %-12s 건=%6ld 매출=%13ld 수수료=%11ld 순액=%13ld\n", + h_merch, h_cnt, h_sum, h_fee, net); } EXEC SQL CLOSE cur_st_bt_0011; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" ----------------------------------------\n"); + printf(" 가맹점 수 : %ld 정산순액 총계 : %ld\n", merch_cnt, grand_net); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0011] 가맹점집계 date=%s 가맹점=%ld 순액=%ld", + biz_date, merch_cnt, grand_net); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0012.c b/app/batch/st_bt_0012.c index 42d2636..9ba3a52 100644 --- a/app/batch/st_bt_0012.c +++ b/app/batch/st_bt_0012.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0012.pgc" /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0012.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0012.pgc - 정산 diff 검증 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 원장에 기록된 실제 수수료와 표준요율(1.80%) 재산출 수수료를 커서로 + * 대조하여 불일치 건을 검출/집계한다. * * 실행: st_bt_0012 [db@host] */ @@ -96,6 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0012.pgc" + static int run_st_bt_0012(const char *biz_date) { /* exec sql begin declare section */ @@ -103,62 +104,54 @@ static int run_st_bt_0012(const char *biz_date) - -#line 24 "app/batch/st_bt_0012.pgc" +#line 25 "app/batch/st_bt_0012.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/st_bt_0012.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/st_bt_0012.pgc" - char h_merch_id [ 16 ] ; + char h_key [ 16 ] ; #line 27 "app/batch/st_bt_0012.pgc" long h_amount ; #line 28 "app/batch/st_bt_0012.pgc" - char h_new_status [ 2 ] ; + long h_fee ; /* exec sql end declare section */ #line 29 "app/batch/st_bt_0012.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const long std_bp = 180; + long total = 0, mismatch = 0, diff_abs_sum = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0012 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/st_bt_0012.pgc" + /* declare cur_st_bt_0012 cursor for select key , amount , fee from st_ledger where biz_date = $1 order by key */ +#line 41 "app/batch/st_bt_0012.pgc" -#line 42 "app/batch/st_bt_0012.pgc" +#line 41 "app/batch/st_bt_0012.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0012 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0012 cursor for select key , amount , fee from st_ledger where biz_date = $1 order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/st_bt_0012.pgc" +#line 43 "app/batch/st_bt_0012.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_st_bt_0012"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0012] tx_begin 경고"); - for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0012", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/st_bt_0012.pgc" - +#line 50 "app/batch/st_bt_0012.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -167,47 +160,31 @@ static int run_st_bt_0012(const char *biz_date) errcnt++; break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + long expected = (h_amount * std_bp + 5000) / 10000; + long diff = h_fee - expected; + if (diff != 0) { + mismatch++; + diff_abs_sum += (diff < 0) ? -diff : diff; + tx_log(TX_LOG_WARN, "[st_bt_0012] 불일치 key=%s 실제=%ld 예상=%ld", + h_key, h_fee, expected); } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0012", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0012.pgc" +#line 69 "app/batch/st_bt_0012.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0012] ==========\n"); + printf("========== 정산 diff 검증 [st_bt_0012] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 검증 건수 : %ld\n", total); + printf(" 불일치 건수 : %ld\n", mismatch); + printf(" 차이 절대합 : %ld\n", diff_abs_sum); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + tx_log(TX_LOG_INFO, "[st_bt_0012] diff검증 date=%s 검증=%ld 불일치=%ld", + biz_date, total, mismatch); + return (errcnt == 0 && mismatch == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) diff --git a/app/batch/st_bt_0012.pgc b/app/batch/st_bt_0012.pgc index cda3a2b..622f984 100644 --- a/app/batch/st_bt_0012.pgc +++ b/app/batch/st_bt_0012.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0012.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0012.pgc - 정산 diff 검증 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 원장에 기록된 실제 수수료와 표준요율(1.80%) 재산출 수수료를 커서로 + * 대조하여 불일치 건을 검출/집계한다. * * 실행: st_bt_0012 [db@host] */ @@ -18,27 +18,26 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0012(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const long std_bp = 180; + long total = 0, mismatch = 0, diff_abs_sum = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_st_bt_0012 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount, fee FROM st_ledger WHERE biz_date = :h_biz_date - AND status = 'R' ORDER BY key; EXEC SQL OPEN cur_st_bt_0012; @@ -47,12 +46,8 @@ static int run_st_bt_0012(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0012] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_st_bt_0012 INTO :h_key, :h_merch_id, :h_amount; - + EXEC SQL FETCH cur_st_bt_0012 INTO :h_key, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -60,45 +55,29 @@ static int run_st_bt_0012(const char *biz_date) errcnt++; break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0012] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + long expected = (h_amount * std_bp + 5000) / 10000; + long diff = h_fee - expected; + if (diff != 0) { + mismatch++; + diff_abs_sum += (diff < 0) ? -diff : diff; + tx_log(TX_LOG_WARN, "[st_bt_0012] 불일치 key=%s 실제=%ld 예상=%ld", + h_key, h_fee, expected); } } EXEC SQL CLOSE cur_st_bt_0012; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0012] ==========\n"); + printf("========== 정산 diff 검증 [st_bt_0012] ==========\n"); printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" 검증 건수 : %ld\n", total); + printf(" 불일치 건수 : %ld\n", mismatch); + printf(" 차이 절대합 : %ld\n", diff_abs_sum); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + tx_log(TX_LOG_INFO, "[st_bt_0012] diff검증 date=%s 검증=%ld 불일치=%ld", + biz_date, total, mismatch); + return (errcnt == 0 && mismatch == 0) ? TX_OK : TX_FAIL; } int main(int argc, char **argv) diff --git a/app/batch/st_bt_0013.c b/app/batch/st_bt_0013.c index 14f4ff9..65f2bd9 100644 --- a/app/batch/st_bt_0013.c +++ b/app/batch/st_bt_0013.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0013.pgc" /* @tier hard @module st @transform settlement-batch */ /* - * st_bt_0013.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=hard] + * st_bt_0013.pgc - 정산 수수료 재계산 배치 [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 접수/완료 건을 커서로 순회하며 표준요율(1.80%)로 수수료를 재계산하고, + * 기존 값과 다른 건만 원장을 갱신하며 총 변동분(delta)을 집계한다. * * 실행: st_bt_0013 [db@host] */ @@ -96,6 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0013.pgc" + static int run_st_bt_0013(const char *biz_date) { /* exec sql begin declare section */ @@ -105,41 +106,41 @@ static int run_st_bt_0013(const char *biz_date) -#line 24 "app/batch/st_bt_0013.pgc" +#line 25 "app/batch/st_bt_0013.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/st_bt_0013.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/st_bt_0013.pgc" - char h_merch_id [ 16 ] ; + char h_key [ 16 ] ; #line 27 "app/batch/st_bt_0013.pgc" long h_amount ; #line 28 "app/batch/st_bt_0013.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_fee ; + #line 29 "app/batch/st_bt_0013.pgc" + long h_newfee ; +/* exec sql end declare section */ +#line 30 "app/batch/st_bt_0013.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const long std_bp = 180; + long total = 0, changed = 0, delta_sum = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0013 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/st_bt_0013.pgc" + /* declare cur_st_bt_0013 cursor for select key , amount , fee from st_ledger where biz_date = $1 and status in ( 'R' , 'M' ) order by key */ +#line 43 "app/batch/st_bt_0013.pgc" -#line 42 "app/batch/st_bt_0013.pgc" +#line 43 "app/batch/st_bt_0013.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0013 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0013 cursor for select key , amount , fee from st_ledger where biz_date = $1 and status in ( 'R' , 'M' ) order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/st_bt_0013.pgc" +#line 45 "app/batch/st_bt_0013.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_st_bt_0013"); @@ -153,12 +154,11 @@ static int run_st_bt_0013(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0013", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/st_bt_0013.pgc" - +#line 55 "app/batch/st_bt_0013.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -167,46 +167,42 @@ static int run_st_bt_0013(const char *biz_date) errcnt++; break; } - total++; + h_newfee = (h_amount * std_bp + 5000) / 10000; + if (h_newfee == h_fee) + continue; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update st_ledger set fee = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_long,&(h_newfee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 70 "app/batch/st_bt_0013.pgc" - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE recalc st_bt_0013"); errcnt++; + } else { + changed++; + delta_sum += (h_newfee - h_fee); } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0013", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0013.pgc" +#line 80 "app/batch/st_bt_0013.pgc" + if (errcnt == 0) tx_commit(); else tx_abort(); - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0013] ==========\n"); + printf("========== 정산 수수료 재계산 [st_bt_0013] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 갱신 건수 : %ld\n", changed); + printf(" 수수료 변동 : %ld\n", delta_sum); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0013] 재계산 date=%s 갱신=%ld 변동=%ld", + biz_date, changed, delta_sum); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0013.pgc b/app/batch/st_bt_0013.pgc index ad37136..3c93b02 100644 --- a/app/batch/st_bt_0013.pgc +++ b/app/batch/st_bt_0013.pgc @@ -1,9 +1,9 @@ /* @tier hard @module st @transform settlement-batch */ /* - * st_bt_0013.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=hard] + * st_bt_0013.pgc - 정산 수수료 재계산 배치 [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 접수/완료 건을 커서로 순회하며 표준요율(1.80%)로 수수료를 재계산하고, + * 기존 값과 다른 건만 원장을 갱신하며 총 변동분(delta)을 집계한다. * * 실행: st_bt_0013 [db@host] */ @@ -18,27 +18,28 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0013(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; - char h_new_status[2]; + long h_fee; + long h_newfee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + const long std_bp = 180; + long total = 0, changed = 0, delta_sum = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_st_bt_0013 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, amount, fee FROM st_ledger WHERE biz_date = :h_biz_date - AND status = 'R' + AND status IN ('R', 'M') ORDER BY key; EXEC SQL OPEN cur_st_bt_0013; @@ -51,8 +52,7 @@ static int run_st_bt_0013(const char *biz_date) tx_log(TX_LOG_WARN, "[st_bt_0013] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_st_bt_0013 INTO :h_key, :h_merch_id, :h_amount; - + EXEC SQL FETCH cur_st_bt_0013 INTO :h_key, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -60,44 +60,36 @@ static int run_st_bt_0013(const char *biz_date) errcnt++; break; } - total++; + h_newfee = (h_amount * std_bp + 5000) / 10000; + if (h_newfee == h_fee) + continue; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); + EXEC SQL UPDATE st_ledger + SET fee = :h_newfee, upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE recalc st_bt_0013"); errcnt++; + } else { + changed++; + delta_sum += (h_newfee - h_fee); } } EXEC SQL CLOSE cur_st_bt_0013; + if (errcnt == 0) tx_commit(); else tx_abort(); - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0013] ==========\n"); + printf("========== 정산 수수료 재계산 [st_bt_0013] ==========\n"); printf(" 영업일 : %s\n", biz_date); printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); + printf(" 갱신 건수 : %ld\n", changed); + printf(" 수수료 변동 : %ld\n", delta_sum); printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0013] 재계산 date=%s 갱신=%ld 변동=%ld", + biz_date, changed, delta_sum); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0014.c b/app/batch/st_bt_0014.c index f471808..05a1783 100644 --- a/app/batch/st_bt_0014.c +++ b/app/batch/st_bt_0014.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0014.pgc" /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0014.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0014.pgc - 카드사(BIN) 정산 집계 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 카드번호 앞 2자리(발급사 BIN)별로 매출/수수료를 그룹 집계하여 + * 발급 카드사 정산 대사용 순액을 산출한다. * * 실행: st_bt_0014 [db@host] */ @@ -96,6 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0014.pgc" + static int run_st_bt_0014(const char *biz_date) { /* exec sql begin declare section */ @@ -105,60 +106,60 @@ static int run_st_bt_0014(const char *biz_date) -#line 24 "app/batch/st_bt_0014.pgc" +#line 25 "app/batch/st_bt_0014.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/st_bt_0014.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/st_bt_0014.pgc" - char h_merch_id [ 16 ] ; + char h_bin [ 3 ] ; #line 27 "app/batch/st_bt_0014.pgc" - long h_amount ; + long h_cnt ; #line 28 "app/batch/st_bt_0014.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_sum ; + #line 29 "app/batch/st_bt_0014.pgc" + long h_fee ; +/* exec sql end declare section */ +#line 30 "app/batch/st_bt_0014.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long bin_cnt = 0, net_total = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0014 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/st_bt_0014.pgc" + /* declare cur_st_bt_0014 cursor for select substr ( card_no , 1 , 2 ) , count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from st_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by substr ( card_no , 1 , 2 ) order by 1 */ +#line 44 "app/batch/st_bt_0014.pgc" -#line 42 "app/batch/st_bt_0014.pgc" +#line 44 "app/batch/st_bt_0014.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0014 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0014 cursor for select substr ( card_no , 1 , 2 ) , count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from st_ledger where biz_date = $1 and status in ( 'M' , 'S' ) group by substr ( card_no , 1 , 2 ) order by 1", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/st_bt_0014.pgc" +#line 46 "app/batch/st_bt_0014.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_st_bt_0014"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0014] tx_begin 경고"); + printf("========== 카드사(BIN) 정산 집계 [st_bt_0014] ==========\n"); + printf(" 영업일 : %s\n", biz_date); for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0014", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_bin),(long)3,(long)1,(3)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/st_bt_0014.pgc" - +#line 56 "app/batch/st_bt_0014.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -167,46 +168,23 @@ static int run_st_bt_0014(const char *biz_date) errcnt++; break; } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + bin_cnt++; + long net = h_sum - h_fee; + net_total += net; + printf(" BIN %-2s 건=%7ld 매출=%15ld 수수료=%13ld 순액=%15ld\n", + h_bin, h_cnt, h_sum, h_fee, net); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0014", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0014.pgc" +#line 71 "app/batch/st_bt_0014.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" ----------------------------------------\n"); + printf(" BIN 수 : %ld 카드사 정산순액 합계 : %ld\n", bin_cnt, net_total); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0014] 카드사정산 date=%s BIN=%ld 순액=%ld", + biz_date, bin_cnt, net_total); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0014.pgc b/app/batch/st_bt_0014.pgc index 20de443..7b26529 100644 --- a/app/batch/st_bt_0014.pgc +++ b/app/batch/st_bt_0014.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0014.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0014.pgc - 카드사(BIN) 정산 집계 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 카드번호 앞 2자리(발급사 BIN)별로 매출/수수료를 그룹 집계하여 + * 발급 카드사 정산 대사용 순액을 산출한다. * * 실행: st_bt_0014 [db@host] */ @@ -18,28 +18,30 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0014(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_bin[3]; + long h_cnt; + long h_sum; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long bin_cnt = 0, net_total = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_st_bt_0014 CURSOR FOR - SELECT key, merch_id, amount + SELECT substr(card_no, 1, 2), count(*), + coalesce(sum(amount), 0), coalesce(sum(fee), 0) FROM st_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status IN ('M', 'S') + GROUP BY substr(card_no, 1, 2) + ORDER BY 1; EXEC SQL OPEN cur_st_bt_0014; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,12 +49,11 @@ static int run_st_bt_0014(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0014] tx_begin 경고"); + printf("========== 카드사(BIN) 정산 집계 [st_bt_0014] ==========\n"); + printf(" 영업일 : %s\n", biz_date); for (;;) { - EXEC SQL FETCH cur_st_bt_0014 INTO :h_key, :h_merch_id, :h_amount; - + EXEC SQL FETCH cur_st_bt_0014 INTO :h_bin, :h_cnt, :h_sum, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -60,44 +61,21 @@ static int run_st_bt_0014(const char *biz_date) errcnt++; break; } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + bin_cnt++; + long net = h_sum - h_fee; + net_total += net; + printf(" BIN %-2s 건=%7ld 매출=%15ld 수수료=%13ld 순액=%15ld\n", + h_bin, h_cnt, h_sum, h_fee, net); } EXEC SQL CLOSE cur_st_bt_0014; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" ----------------------------------------\n"); + printf(" BIN 수 : %ld 카드사 정산순액 합계 : %ld\n", bin_cnt, net_total); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0014] 카드사정산 date=%s BIN=%ld 순액=%ld", + biz_date, bin_cnt, net_total); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0015.c b/app/batch/st_bt_0015.c index dea2490..0185258 100644 --- a/app/batch/st_bt_0015.c +++ b/app/batch/st_bt_0015.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0015.pgc" /* @tier hard @module st @transform settlement-batch */ /* - * st_bt_0015.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=hard] + * st_bt_0015.pgc - 정산 리포트 배치 [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 상태별 건수/금액과 정산완료율, 총수수료, 정산순액을 + * 종합하여 일일 정산 리포트를 출력한다. * * 실행: st_bt_0015 [db@host] */ @@ -96,6 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0015.pgc" + static int run_st_bt_0015(const char *biz_date) { /* exec sql begin declare section */ @@ -105,109 +106,77 @@ static int run_st_bt_0015(const char *biz_date) -#line 24 "app/batch/st_bt_0015.pgc" +#line 25 "app/batch/st_bt_0015.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/st_bt_0015.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/st_bt_0015.pgc" - char h_merch_id [ 16 ] ; + long h_total_cnt ; #line 27 "app/batch/st_bt_0015.pgc" - long h_amount ; + long h_settled_cnt ; #line 28 "app/batch/st_bt_0015.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_gross ; + #line 29 "app/batch/st_bt_0015.pgc" + long h_feesum ; +/* exec sql end declare section */ +#line 30 "app/batch/st_bt_0015.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long net, rate_bp = 0, avg = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0015 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/st_bt_0015.pgc" - -#line 42 "app/batch/st_bt_0015.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0015 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from st_ledger where biz_date = $1 ", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/st_bt_0015.pgc" + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_total_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_gross),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_feesum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 40 "app/batch/st_bt_0015.pgc" - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_st_bt_0015"); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT total st_bt_0015"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0015] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0015", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from st_ledger where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settled_cnt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/st_bt_0015.pgc" +#line 50 "app/batch/st_bt_0015.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_st_bt_0015"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT settled st_bt_0015"); + return TX_EDB; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0015", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0015.pgc" + net = h_gross - h_feesum; + if (h_total_cnt > 0) { + rate_bp = h_settled_cnt * 10000 / h_total_cnt; + avg = h_gross / h_total_cnt; + } - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 정산 리포트 [st_bt_0015] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 총 건수 : %ld\n", h_total_cnt); + printf(" 정산완료 건수 : %ld\n", h_settled_cnt); + printf(" 정산완료율 : %ld.%02ld%%\n", rate_bp / 100, rate_bp % 100); + printf(" 총 매출 : %ld\n", h_gross); + printf(" 총 수수료 : %ld\n", h_feesum); + printf(" 정산순액 : %ld\n", net); + printf(" 건당 평균매출 : %ld\n", avg); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + tx_log(TX_LOG_INFO, "[st_bt_0015] 리포트 date=%s 건수=%ld 정산율bp=%ld 순액=%ld", + biz_date, h_total_cnt, rate_bp, net); + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/st_bt_0015.pgc b/app/batch/st_bt_0015.pgc index 28f2f68..1dc7422 100644 --- a/app/batch/st_bt_0015.pgc +++ b/app/batch/st_bt_0015.pgc @@ -1,9 +1,9 @@ /* @tier hard @module st @transform settlement-batch */ /* - * st_bt_0015.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=hard] + * st_bt_0015.pgc - 정산 리포트 배치 [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 상태별 건수/금액과 정산완료율, 총수수료, 정산순액을 + * 종합하여 일일 정산 리포트를 출력한다. * * 실행: st_bt_0015 [db@host] */ @@ -18,87 +18,61 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0015(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_total_cnt; + long h_settled_cnt; + long h_gross; + long h_feesum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long net, rate_bp = 0, avg = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - EXEC SQL DECLARE cur_st_bt_0015 CURSOR FOR - SELECT key, merch_id, amount - FROM st_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; - - EXEC SQL OPEN cur_st_bt_0015; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_st_bt_0015"); + EXEC SQL SELECT count(*), coalesce(sum(amount), 0), coalesce(sum(fee), 0) + INTO :h_total_cnt, :h_gross, :h_feesum + FROM st_ledger + WHERE biz_date = :h_biz_date; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT total st_bt_0015"); return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0015] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_st_bt_0015 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_st_bt_0015"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + EXEC SQL SELECT count(*) + INTO :h_settled_cnt + FROM st_ledger + WHERE biz_date = :h_biz_date + AND status = 'S'; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT settled st_bt_0015"); + return TX_EDB; } - EXEC SQL CLOSE cur_st_bt_0015; + net = h_gross - h_feesum; + if (h_total_cnt > 0) { + rate_bp = h_settled_cnt * 10000 / h_total_cnt; + avg = h_gross / h_total_cnt; + } - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 정산 리포트 [st_bt_0015] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 총 건수 : %ld\n", h_total_cnt); + printf(" 정산완료 건수 : %ld\n", h_settled_cnt); + printf(" 정산완료율 : %ld.%02ld%%\n", rate_bp / 100, rate_bp % 100); + printf(" 총 매출 : %ld\n", h_gross); + printf(" 총 수수료 : %ld\n", h_feesum); + printf(" 정산순액 : %ld\n", net); + printf(" 건당 평균매출 : %ld\n", avg); printf("=============================================\n"); - return (errcnt == 0) ? TX_OK : TX_FAIL; + tx_log(TX_LOG_INFO, "[st_bt_0015] 리포트 date=%s 건수=%ld 정산율bp=%ld 순액=%ld", + biz_date, h_total_cnt, rate_bp, net); + return TX_OK; } int main(int argc, char **argv) diff --git a/app/batch/st_bt_0016.c b/app/batch/st_bt_0016.c index 2e5b350..dc0dd25 100644 --- a/app/batch/st_bt_0016.c +++ b/app/batch/st_bt_0016.c @@ -8,10 +8,10 @@ #line 1 "app/batch/st_bt_0016.pgc" /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0016.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0016.pgc - 정산 이체연계 추출 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 정산완료(S) 건을 커서로 추출하여 가맹점별 이체 정산순액을 산출하고, + * 펌뱅킹 이체연계 전송 대상 매니페스트(건수/총액)를 구성한다. * * 실행: st_bt_0016 [db@host] */ @@ -96,6 +96,7 @@ struct sqlca_t *ECPGget_sqlca(void); #line 19 "app/batch/st_bt_0016.pgc" + static int run_st_bt_0016(const char *biz_date) { /* exec sql begin declare section */ @@ -105,38 +106,37 @@ static int run_st_bt_0016(const char *biz_date) -#line 24 "app/batch/st_bt_0016.pgc" +#line 25 "app/batch/st_bt_0016.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/st_bt_0016.pgc" +#line 26 "app/batch/st_bt_0016.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/st_bt_0016.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/st_bt_0016.pgc" - long h_amount ; + char h_merch [ 16 ] ; #line 28 "app/batch/st_bt_0016.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_amount ; + #line 29 "app/batch/st_bt_0016.pgc" + long h_fee ; +/* exec sql end declare section */ +#line 30 "app/batch/st_bt_0016.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, xfer_total = 0, skipped = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_st_bt_0016 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key */ + /* declare cur_st_bt_0016 cursor for select key , merch_id , amount , fee from st_ledger where biz_date = $1 and status = 'S' order by merch_id , key */ #line 42 "app/batch/st_bt_0016.pgc" #line 42 "app/batch/st_bt_0016.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0016 cursor for select key , merch_id , amount from st_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_st_bt_0016 cursor for select key , merch_id , amount , fee from st_ledger where biz_date = $1 and status = 'S' order by merch_id , key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} #line 44 "app/batch/st_bt_0016.pgc" @@ -146,20 +146,21 @@ static int run_st_bt_0016(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0016] tx_begin 경고"); + printf("========== 정산 이체연계 추출 [st_bt_0016] ==========\n"); + printf(" 영업일 : %s\n", biz_date); for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_st_bt_0016", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 54 "app/batch/st_bt_0016.pgc" - if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -167,46 +168,28 @@ static int run_st_bt_0016(const char *biz_date) errcnt++; break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + long net = h_amount - h_fee; + if (net <= 0) { /* 이체 대상 아님(순액 0 이하) */ + skipped++; + continue; } + xfer_total += net; + printf(" 전송 key=%-12s 가맹점=%-12s 이체액=%13ld\n", + h_key, h_merch, net); } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_st_bt_0016", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/st_bt_0016.pgc" +#line 73 "app/batch/st_bt_0016.pgc" - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" ----------------------------------------\n"); + printf(" 대상 건수 : %ld 제외 : %ld 이체 총액 : %ld\n", + total, skipped, xfer_total); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0016] 이체연계 date=%s 건수=%ld 총액=%ld", + biz_date, total - skipped, xfer_total); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/st_bt_0016.pgc b/app/batch/st_bt_0016.pgc index add6f6f..551df0c 100644 --- a/app/batch/st_bt_0016.pgc +++ b/app/batch/st_bt_0016.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-batch */ /* - * st_bt_0016.pgc - 정산수수료 배치 (커서 순회 + 상태전이) [tier=easy] + * st_bt_0016.pgc - 정산 이체연계 추출 배치 [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 정산완료(S) 건을 커서로 추출하여 가맹점별 이체 정산순액을 산출하고, + * 펌뱅킹 이체연계 전송 대상 매니페스트(건수/총액)를 구성한다. * * 실행: st_bt_0016 [db@host] */ @@ -18,28 +18,28 @@ EXEC SQL INCLUDE sqlca; + static int run_st_bt_0016(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; + char h_merch[16]; long h_amount; - char h_new_status[2]; + long h_fee; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + long total = 0, xfer_total = 0, skipped = 0, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_st_bt_0016 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, merch_id, amount, fee FROM st_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status = 'S' + ORDER BY merch_id, key; EXEC SQL OPEN cur_st_bt_0016; if (sqlca.sqlcode != TX_SQL_OK) { @@ -47,12 +47,11 @@ static int run_st_bt_0016(const char *biz_date) return TX_EDB; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[st_bt_0016] tx_begin 경고"); + printf("========== 정산 이체연계 추출 [st_bt_0016] ==========\n"); + printf(" 영업일 : %s\n", biz_date); for (;;) { - EXEC SQL FETCH cur_st_bt_0016 INTO :h_key, :h_merch_id, :h_amount; - + EXEC SQL FETCH cur_st_bt_0016 INTO :h_key, :h_merch, :h_amount, :h_fee; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; if (sqlca.sqlcode != TX_SQL_OK) { @@ -60,44 +59,26 @@ static int run_st_bt_0016(const char *biz_date) errcnt++; break; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, ST_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, ST_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = st_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[st_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + long net = h_amount - h_fee; + if (net <= 0) { /* 이체 대상 아님(순액 0 이하) */ + skipped++; + continue; } + xfer_total += net; + printf(" 전송 key=%-12s 가맹점=%-12s 이체액=%13ld\n", + h_key, h_merch, net); } EXEC SQL CLOSE cur_st_bt_0016; - if (errcnt == 0) - tx_commit(); - else - tx_abort(); - - tx_log(TX_LOG_INFO, - "[st_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); - - printf("========== 정산수수료 배치 결과 [st_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf(" ----------------------------------------\n"); + printf(" 대상 건수 : %ld 제외 : %ld 이체 총액 : %ld\n", + total, skipped, xfer_total); printf("=============================================\n"); + tx_log(TX_LOG_INFO, "[st_bt_0016] 이체연계 date=%s 건수=%ld 총액=%ld", + biz_date, total - skipped, xfer_total); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0002.c b/app/batch/vl_bt_0002.c index 32ae614..b5d9ae4 100644 --- a/app/batch/vl_bt_0002.c +++ b/app/batch/vl_bt_0002.c @@ -8,10 +8,11 @@ #line 1 "app/batch/vl_bt_0002.pgc" /* @tier medium @module vl @transform validation-batch */ /* - * vl_bt_0002.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=medium] + * vl_bt_0002.pgc - 매출 정합성 일괄검증 배치 (VL_BT_0002) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수건을 원장(vl_ledger) 과 매출원천(vl_sale_source) 을 + * 조인하여 금액 대사한다. 정합이면 완료('M'), 불일치이면 실패('U') 로 + * 상태를 전이하고 불일치 내역을 집계한다. 배치 main 엔트리 포함. * * 실행: vl_bt_0002 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0002.pgc" +#line 20 "app/batch/vl_bt_0002.pgc" static int run_vl_bt_0002(const char *biz_date) @@ -105,41 +106,42 @@ static int run_vl_bt_0002(const char *biz_date) -#line 24 "app/batch/vl_bt_0002.pgc" +#line 25 "app/batch/vl_bt_0002.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0002.pgc" +#line 26 "app/batch/vl_bt_0002.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/vl_bt_0002.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/vl_bt_0002.pgc" - long h_amount ; + long h_ledger_amt ; #line 28 "app/batch/vl_bt_0002.pgc" + long h_sale_amt ; + +#line 29 "app/batch/vl_bt_0002.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/vl_bt_0002.pgc" +#line 30 "app/batch/vl_bt_0002.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, matched = 0, mismatch = 0, errcnt = 0; + long diff_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0002 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0002.pgc" + /* declare cur_vl_bt_0002 cursor for select l . key , l . amount , s . sale_amount from vl_ledger l join vl_sale_source s on s . txn_key = l . key where l . biz_date = $1 and l . status = 'R' order by l . key */ +#line 45 "app/batch/vl_bt_0002.pgc" -#line 42 "app/batch/vl_bt_0002.pgc" +#line 45 "app/batch/vl_bt_0002.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0002 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0002 cursor for select l . key , l . amount , s . sale_amount from vl_ledger l join vl_sale_source s on s . txn_key = l . key where l . biz_date = $1 and l . status = 'R' order by l . key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0002.pgc" +#line 47 "app/batch/vl_bt_0002.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0002"); @@ -153,11 +155,11 @@ static int run_vl_bt_0002(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_vl_bt_0002", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_ledger_amt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sale_amt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0002.pgc" +#line 57 "app/batch/vl_bt_0002.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,13 +172,13 @@ static int run_vl_bt_0002(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + if (h_ledger_amt == h_sale_amt) { strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; + matched++; } else { strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + diff_sum += labs(h_ledger_amt - h_sale_amt); + mismatch++; } rc = vl_rec_update_status(h_key, h_new_status); @@ -187,7 +189,7 @@ static int run_vl_bt_0002(const char *biz_date) } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0002", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0002.pgc" +#line 85 "app/batch/vl_bt_0002.pgc" if (errcnt == 0) @@ -196,16 +198,17 @@ static int run_vl_bt_0002(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0002] 요약 date=%s 대상=%ld 정합=%ld 불일치=%ld 오류=%ld", + biz_date, total, matched, mismatch, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 매출 정합성 일괄검증 [vl_bt_0002] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정합(M) : %ld\n", matched); + printf(" 불일치(U) : %ld\n", mismatch); + printf(" 불일치 금액합: %ld\n", diff_sum); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0002.pgc b/app/batch/vl_bt_0002.pgc index 21a9398..89654db 100644 --- a/app/batch/vl_bt_0002.pgc +++ b/app/batch/vl_bt_0002.pgc @@ -1,9 +1,10 @@ /* @tier medium @module vl @transform validation-batch */ /* - * vl_bt_0002.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=medium] + * vl_bt_0002.pgc - 매출 정합성 일괄검증 배치 (VL_BT_0002) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 접수건을 원장(vl_ledger) 과 매출원천(vl_sale_source) 을 + * 조인하여 금액 대사한다. 정합이면 완료('M'), 불일치이면 실패('U') 로 + * 상태를 전이하고 불일치 내역을 집계한다. 배치 main 엔트리 포함. * * 실행: vl_bt_0002 [db@host] */ @@ -23,23 +24,25 @@ static int run_vl_bt_0002(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; + long h_ledger_amt; + long h_sale_amt; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, matched = 0, mismatch = 0, errcnt = 0; + long diff_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_vl_bt_0002 CURSOR FOR - SELECT key, merch_id, amount - FROM vl_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT l.key, l.amount, s.sale_amount + FROM vl_ledger l + JOIN vl_sale_source s ON s.txn_key = l.key + WHERE l.biz_date = :h_biz_date + AND l.status = 'R' + ORDER BY l.key; EXEC SQL OPEN cur_vl_bt_0002; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +54,7 @@ static int run_vl_bt_0002(const char *biz_date) tx_log(TX_LOG_WARN, "[vl_bt_0002] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_vl_bt_0002 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_vl_bt_0002 INTO :h_key, :h_ledger_amt, :h_sale_amt; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,13 +66,13 @@ static int run_vl_bt_0002(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + if (h_ledger_amt == h_sale_amt) { strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; + matched++; } else { strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + diff_sum += labs(h_ledger_amt - h_sale_amt); + mismatch++; } rc = vl_rec_update_status(h_key, h_new_status); @@ -87,16 +90,17 @@ static int run_vl_bt_0002(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0002] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0002] 요약 date=%s 대상=%ld 정합=%ld 불일치=%ld 오류=%ld", + biz_date, total, matched, mismatch, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0002] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 매출 정합성 일괄검증 [vl_bt_0002] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정합(M) : %ld\n", matched); + printf(" 불일치(U) : %ld\n", mismatch); + printf(" 불일치 금액합: %ld\n", diff_sum); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0003.c b/app/batch/vl_bt_0003.c index 77ac4a5..31c2cf6 100644 --- a/app/batch/vl_bt_0003.c +++ b/app/batch/vl_bt_0003.c @@ -8,10 +8,11 @@ #line 1 "app/batch/vl_bt_0003.pgc" /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0003.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0003.pgc - 중복거래 배치검출 (VL_BT_0003) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 에서 (카드번호, 금액, 거래일) 이 동일한 + * 중복 그룹을 커서로 순회한다. 중복 그룹에 속한 건은 불일치('U') 로 + * 전이하고, 중복 그룹 수와 중복 건수를 집계한다. 배치 main 엔트리 포함. * * 실행: vl_bt_0003 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0003.pgc" +#line 20 "app/batch/vl_bt_0003.pgc" static int run_vl_bt_0003(const char *biz_date) @@ -104,42 +105,52 @@ static int run_vl_bt_0003(const char *biz_date) + -#line 24 "app/batch/vl_bt_0003.pgc" +#line 25 "app/batch/vl_bt_0003.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0003.pgc" +#line 26 "app/batch/vl_bt_0003.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/vl_bt_0003.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/vl_bt_0003.pgc" - long h_amount ; + char h_card_no [ 20 ] ; #line 28 "app/batch/vl_bt_0003.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_amount ; + #line 29 "app/batch/vl_bt_0003.pgc" + char h_txn_date [ 9 ] ; + +#line 30 "app/batch/vl_bt_0003.pgc" + long h_dup_cnt ; +/* exec sql end declare section */ +#line 31 "app/batch/vl_bt_0003.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, dup_rows = 0, groups = 0, errcnt = 0; + char cur_sig[64], prev_sig[64]; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + prev_sig[0] = '\0'; + /* (카드번호,금액,거래일) 그룹 중 2건 이상인 중복 그룹만 조인 */ ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0003 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0003.pgc" + ECPGset_var( 1, ( h_biz_date ), __LINE__);\ + /* declare cur_vl_bt_0003 cursor for select l . key , l . card_no , l . amount , l . txn_date , g . cnt from vl_ledger l join ( select card_no , amount , txn_date , count ( * ) as cnt from vl_ledger where biz_date = $1 and status = 'R' group by card_no , amount , txn_date having count ( * ) > 1 ) g on g . card_no = l . card_no and g . amount = l . amount and g . txn_date = l . txn_date where l . biz_date = $2 and l . status = 'R' order by l . card_no , l . amount , l . txn_date , l . key */ +#line 56 "app/batch/vl_bt_0003.pgc" -#line 42 "app/batch/vl_bt_0003.pgc" +#line 56 "app/batch/vl_bt_0003.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0003 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0003 cursor for select l . key , l . card_no , l . amount , l . txn_date , g . cnt from vl_ledger l join ( select card_no , amount , txn_date , count ( * ) as cnt from vl_ledger where biz_date = $1 and status = 'R' group by card_no , amount , txn_date having count ( * ) > 1 ) g on g . card_no = l . card_no and g . amount = l . amount and g . txn_date = l . txn_date where l . biz_date = $2 and l . status = 'R' order by l . card_no , l . amount , l . txn_date , l . key", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0003.pgc" +#line 58 "app/batch/vl_bt_0003.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0003"); @@ -153,11 +164,15 @@ static int run_vl_bt_0003(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_vl_bt_0003", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_txn_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_dup_cnt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0003.pgc" +#line 69 "app/batch/vl_bt_0003.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,25 +184,30 @@ static int run_vl_bt_0003(const char *biz_date) } total++; + dup_rows++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 그룹 경계 판별: 서명이 바뀌면 새 중복 그룹 */ + snprintf(cur_sig, sizeof(cur_sig), "%s|%ld|%s", + h_card_no, h_amount, h_txn_date); + if (strcmp(cur_sig, prev_sig) != 0) { + groups++; + strncpy(prev_sig, cur_sig, sizeof(prev_sig) - 1); + prev_sig[sizeof(prev_sig) - 1] = '\0'; } - rc = vl_rec_update_status(h_key, h_new_status); + /* 중복건은 불일치('U') 로 전이하여 후속 정산에서 제외 */ + rc = vl_rec_update_status(h_key, VL_ST_FAIL); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[vl_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); errcnt++; + } else { + tx_log(TX_LOG_DEBUG, "[vl_bt_0003] 중복 key=%s card=%s cnt=%ld", + h_key, h_card_no, h_dup_cnt); } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0003", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0003.pgc" +#line 102 "app/batch/vl_bt_0003.pgc" if (errcnt == 0) @@ -196,16 +216,16 @@ static int run_vl_bt_0003(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0003] 요약 date=%s 중복건=%ld 그룹=%ld 오류=%ld", + biz_date, dup_rows, groups, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 중복거래 배치검출 [vl_bt_0003] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 중복 건수 : %ld\n", dup_rows); + printf(" 중복 그룹 수 : %ld\n", groups); + printf(" U 전이 건수 : %ld\n", dup_rows - errcnt); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0003.pgc b/app/batch/vl_bt_0003.pgc index c06d737..32c5f2d 100644 --- a/app/batch/vl_bt_0003.pgc +++ b/app/batch/vl_bt_0003.pgc @@ -1,9 +1,10 @@ /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0003.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0003.pgc - 중복거래 배치검출 (VL_BT_0003) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 에서 (카드번호, 금액, 거래일) 이 동일한 + * 중복 그룹을 커서로 순회한다. 중복 그룹에 속한 건은 불일치('U') 로 + * 전이하고, 중복 그룹 수와 중복 건수를 집계한다. 배치 main 엔트리 포함. * * 실행: vl_bt_0003 [db@host] */ @@ -23,23 +24,36 @@ static int run_vl_bt_0003(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; + char h_card_no[20]; long h_amount; - char h_new_status[2]; + char h_txn_date[9]; + long h_dup_cnt; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, dup_rows = 0, groups = 0, errcnt = 0; + char cur_sig[64], prev_sig[64]; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + prev_sig[0] = '\0'; + /* (카드번호,금액,거래일) 그룹 중 2건 이상인 중복 그룹만 조인 */ EXEC SQL DECLARE cur_vl_bt_0003 CURSOR FOR - SELECT key, merch_id, amount - FROM vl_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT l.key, l.card_no, l.amount, l.txn_date, g.cnt + FROM vl_ledger l + JOIN ( SELECT card_no, amount, txn_date, COUNT(*) AS cnt + FROM vl_ledger + WHERE biz_date = :h_biz_date + AND status = 'R' + GROUP BY card_no, amount, txn_date + HAVING COUNT(*) > 1 ) g + ON g.card_no = l.card_no + AND g.amount = l.amount + AND g.txn_date = l.txn_date + WHERE l.biz_date = :h_biz_date + AND l.status = 'R' + ORDER BY l.card_no, l.amount, l.txn_date, l.key; EXEC SQL OPEN cur_vl_bt_0003; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +65,8 @@ static int run_vl_bt_0003(const char *biz_date) tx_log(TX_LOG_WARN, "[vl_bt_0003] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_vl_bt_0003 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_vl_bt_0003 + INTO :h_key, :h_card_no, :h_amount, :h_txn_date, :h_dup_cnt; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,20 +77,25 @@ static int run_vl_bt_0003(const char *biz_date) } total++; + dup_rows++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 그룹 경계 판별: 서명이 바뀌면 새 중복 그룹 */ + snprintf(cur_sig, sizeof(cur_sig), "%s|%ld|%s", + h_card_no, h_amount, h_txn_date); + if (strcmp(cur_sig, prev_sig) != 0) { + groups++; + strncpy(prev_sig, cur_sig, sizeof(prev_sig) - 1); + prev_sig[sizeof(prev_sig) - 1] = '\0'; } - rc = vl_rec_update_status(h_key, h_new_status); + /* 중복건은 불일치('U') 로 전이하여 후속 정산에서 제외 */ + rc = vl_rec_update_status(h_key, VL_ST_FAIL); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[vl_bt_0003] 상태전이 실패 key=%s rc=%d", h_key, rc); errcnt++; + } else { + tx_log(TX_LOG_DEBUG, "[vl_bt_0003] 중복 key=%s card=%s cnt=%ld", + h_key, h_card_no, h_dup_cnt); } } @@ -87,16 +107,16 @@ static int run_vl_bt_0003(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0003] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0003] 요약 date=%s 중복건=%ld 그룹=%ld 오류=%ld", + biz_date, dup_rows, groups, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0003] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 중복거래 배치검출 [vl_bt_0003] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 중복 건수 : %ld\n", dup_rows); + printf(" 중복 그룹 수 : %ld\n", groups); + printf(" U 전이 건수 : %ld\n", dup_rows - errcnt); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0004.c b/app/batch/vl_bt_0004.c index 6600257..13c5c15 100644 --- a/app/batch/vl_bt_0004.c +++ b/app/batch/vl_bt_0004.c @@ -8,10 +8,11 @@ #line 1 "app/batch/vl_bt_0004.pgc" /* @tier hard @module vl @transform validation-batch */ /* - * vl_bt_0004.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=hard] + * vl_bt_0004.pgc - 가맹점 유효성 배치검증 (VL_BT_0004) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 원장 접수(status='R') 건을 가맹점원장(mm_merchant) 과 + * 조인하여 가맹점 상태를 확인한다. 정상('A') 가맹점 건은 완료('M') 로, + * 해지/정지 등 비정상 가맹점 건은 불일치('U') 로 전이한다. main 포함. * * 실행: vl_bt_0004 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0004.pgc" +#line 20 "app/batch/vl_bt_0004.pgc" static int run_vl_bt_0004(const char *biz_date) @@ -105,41 +106,42 @@ static int run_vl_bt_0004(const char *biz_date) -#line 24 "app/batch/vl_bt_0004.pgc" +#line 25 "app/batch/vl_bt_0004.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0004.pgc" +#line 26 "app/batch/vl_bt_0004.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/vl_bt_0004.pgc" +#line 27 "app/batch/vl_bt_0004.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/vl_bt_0004.pgc" - long h_amount ; - #line 28 "app/batch/vl_bt_0004.pgc" + char h_mrc_status [ 2 ] ; + +#line 29 "app/batch/vl_bt_0004.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/vl_bt_0004.pgc" +#line 30 "app/batch/vl_bt_0004.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, valid = 0, invalid = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* LEFT JOIN + COALESCE 로 미등록 가맹점은 'X'(부재) 로 판정 */ ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0004 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0004.pgc" + /* declare cur_vl_bt_0004 cursor for select l . key , l . merch_id , coalesce ( m . mrc_status , 'X' ) from vl_ledger l left join mm_merchant m on m . merch_id = l . merch_id where l . biz_date = $1 and l . status = 'R' order by l . key */ +#line 45 "app/batch/vl_bt_0004.pgc" -#line 42 "app/batch/vl_bt_0004.pgc" +#line 45 "app/batch/vl_bt_0004.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0004 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0004 cursor for select l . key , l . merch_id , coalesce ( m . mrc_status , 'X' ) from vl_ledger l left join mm_merchant m on m . merch_id = l . merch_id where l . biz_date = $1 and l . status = 'R' order by l . key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0004.pgc" +#line 47 "app/batch/vl_bt_0004.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0004"); @@ -155,9 +157,9 @@ static int run_vl_bt_0004(const char *biz_date) ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_mrc_status),(long)2,(long)1,(2)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0004.pgc" +#line 57 "app/batch/vl_bt_0004.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,13 +172,15 @@ static int run_vl_bt_0004(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + /* 업무규칙: 가맹점 상태 'A'(정상) 만 완료, 그 외는 불일치 */ + if (strcmp(h_mrc_status, "A") == 0) { strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; + valid++; } else { strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + invalid++; + tx_log(TX_LOG_WARN, "[vl_bt_0004] 비정상 가맹점 key=%s merch=%s st=%s", + h_key, h_merch_id, h_mrc_status); } rc = vl_rec_update_status(h_key, h_new_status); @@ -187,7 +191,7 @@ static int run_vl_bt_0004(const char *biz_date) } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0004", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0004.pgc" +#line 87 "app/batch/vl_bt_0004.pgc" if (errcnt == 0) @@ -196,16 +200,16 @@ static int run_vl_bt_0004(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0004] 요약 date=%s 대상=%ld 정상=%ld 비정상=%ld 오류=%ld", + biz_date, total, valid, invalid, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 가맹점 유효성 배치검증 [vl_bt_0004] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정상 가맹점(M): %ld\n", valid); + printf(" 비정상(U) : %ld\n", invalid); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0004.pgc b/app/batch/vl_bt_0004.pgc index 019b28f..a769154 100644 --- a/app/batch/vl_bt_0004.pgc +++ b/app/batch/vl_bt_0004.pgc @@ -1,9 +1,10 @@ /* @tier hard @module vl @transform validation-batch */ /* - * vl_bt_0004.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=hard] + * vl_bt_0004.pgc - 가맹점 유효성 배치검증 (VL_BT_0004) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 원장 접수(status='R') 건을 가맹점원장(mm_merchant) 과 + * 조인하여 가맹점 상태를 확인한다. 정상('A') 가맹점 건은 완료('M') 로, + * 해지/정지 등 비정상 가맹점 건은 불일치('U') 로 전이한다. main 포함. * * 실행: vl_bt_0004 [db@host] */ @@ -24,22 +25,24 @@ static int run_vl_bt_0004(const char *biz_date) char h_biz_date[9]; char h_key[16]; char h_merch_id[16]; - long h_amount; + char h_mrc_status[2]; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, valid = 0, invalid = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* LEFT JOIN + COALESCE 로 미등록 가맹점은 'X'(부재) 로 판정 */ EXEC SQL DECLARE cur_vl_bt_0004 CURSOR FOR - SELECT key, merch_id, amount - FROM vl_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT l.key, l.merch_id, COALESCE(m.mrc_status, 'X') + FROM vl_ledger l + LEFT JOIN mm_merchant m ON m.merch_id = l.merch_id + WHERE l.biz_date = :h_biz_date + AND l.status = 'R' + ORDER BY l.key; EXEC SQL OPEN cur_vl_bt_0004; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +54,7 @@ static int run_vl_bt_0004(const char *biz_date) tx_log(TX_LOG_WARN, "[vl_bt_0004] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_vl_bt_0004 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_vl_bt_0004 INTO :h_key, :h_merch_id, :h_mrc_status; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,13 +66,15 @@ static int run_vl_bt_0004(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + /* 업무규칙: 가맹점 상태 'A'(정상) 만 완료, 그 외는 불일치 */ + if (strcmp(h_mrc_status, "A") == 0) { strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; + valid++; } else { strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + invalid++; + tx_log(TX_LOG_WARN, "[vl_bt_0004] 비정상 가맹점 key=%s merch=%s st=%s", + h_key, h_merch_id, h_mrc_status); } rc = vl_rec_update_status(h_key, h_new_status); @@ -87,16 +92,16 @@ static int run_vl_bt_0004(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0004] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0004] 요약 date=%s 대상=%ld 정상=%ld 비정상=%ld 오류=%ld", + biz_date, total, valid, invalid, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0004] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 가맹점 유효성 배치검증 [vl_bt_0004] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정상 가맹점(M): %ld\n", valid); + printf(" 비정상(U) : %ld\n", invalid); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0005.c b/app/batch/vl_bt_0005.c index 7bb1fd0..bc2e9ff 100644 --- a/app/batch/vl_bt_0005.c +++ b/app/batch/vl_bt_0005.c @@ -8,10 +8,11 @@ #line 1 "app/batch/vl_bt_0005.pgc" /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0005.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0005.pgc - 금액 허용오차 정합검증 배치 (VL_BT_0005) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 과 승인원장(vl_approval) 을 조인하여 + * |원장금액 - 승인금액| 을 산출한다. 편차가 허용오차(TOLERANCE) 이내이면 + * 완료('M'), 초과이면 불일치('U') 로 전이한다. 배치 main 엔트리 포함. * * 실행: vl_bt_0005 [db@host] */ @@ -93,9 +94,11 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0005.pgc" +#line 20 "app/batch/vl_bt_0005.pgc" +#define VL_BT_0005_TOLERANCE 100L /* 허용오차: 100원 */ + static int run_vl_bt_0005(const char *biz_date) { /* exec sql begin declare section */ @@ -105,41 +108,42 @@ static int run_vl_bt_0005(const char *biz_date) -#line 24 "app/batch/vl_bt_0005.pgc" +#line 27 "app/batch/vl_bt_0005.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0005.pgc" +#line 28 "app/batch/vl_bt_0005.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/vl_bt_0005.pgc" - char h_merch_id [ 16 ] ; +#line 29 "app/batch/vl_bt_0005.pgc" + long h_ledger_amt ; -#line 27 "app/batch/vl_bt_0005.pgc" - long h_amount ; +#line 30 "app/batch/vl_bt_0005.pgc" + long h_appr_amt ; -#line 28 "app/batch/vl_bt_0005.pgc" +#line 31 "app/batch/vl_bt_0005.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/vl_bt_0005.pgc" +#line 32 "app/batch/vl_bt_0005.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, within = 0, over = 0, errcnt = 0; + long diff, diff_sum = 0, max_diff = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0005 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0005.pgc" + /* declare cur_vl_bt_0005 cursor for select l . key , l . amount , a . appr_amount from vl_ledger l join vl_approval a on a . appr_no = l . key where l . biz_date = $1 and l . status = 'R' order by l . key */ +#line 47 "app/batch/vl_bt_0005.pgc" -#line 42 "app/batch/vl_bt_0005.pgc" +#line 47 "app/batch/vl_bt_0005.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0005 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0005 cursor for select l . key , l . amount , a . appr_amount from vl_ledger l join vl_approval a on a . appr_no = l . key where l . biz_date = $1 and l . status = 'R' order by l . key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0005.pgc" +#line 49 "app/batch/vl_bt_0005.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0005"); @@ -153,11 +157,11 @@ static int run_vl_bt_0005(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_vl_bt_0005", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_ledger_amt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_appr_amt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0005.pgc" +#line 59 "app/batch/vl_bt_0005.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,14 +173,17 @@ static int run_vl_bt_0005(const char *biz_date) } total++; + diff = labs(h_ledger_amt - h_appr_amt); - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + if (diff <= VL_BT_0005_TOLERANCE) { strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; + within++; } else { strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + over++; + diff_sum += diff; + if (diff > max_diff) + max_diff = diff; } rc = vl_rec_update_status(h_key, h_new_status); @@ -187,7 +194,7 @@ static int run_vl_bt_0005(const char *biz_date) } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0005", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0005.pgc" +#line 90 "app/batch/vl_bt_0005.pgc" if (errcnt == 0) @@ -196,16 +203,19 @@ static int run_vl_bt_0005(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0005] 요약 date=%s 대상=%ld 허용내=%ld 초과=%ld 오류=%ld", + biz_date, total, within, over, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 금액 허용오차 정합검증 [vl_bt_0005] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 허용오차(원) : %ld\n", VL_BT_0005_TOLERANCE); + printf(" 대상 건수 : %ld\n", total); + printf(" 허용내(M) : %ld\n", within); + printf(" 초과(U) : %ld\n", over); + printf(" 편차 합계 : %ld\n", diff_sum); + printf(" 최대 편차 : %ld\n", max_diff); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0005.pgc b/app/batch/vl_bt_0005.pgc index 49fdeaa..3ff1d1c 100644 --- a/app/batch/vl_bt_0005.pgc +++ b/app/batch/vl_bt_0005.pgc @@ -1,9 +1,10 @@ /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0005.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0005.pgc - 금액 허용오차 정합검증 배치 (VL_BT_0005) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 과 승인원장(vl_approval) 을 조인하여 + * |원장금액 - 승인금액| 을 산출한다. 편차가 허용오차(TOLERANCE) 이내이면 + * 완료('M'), 초과이면 불일치('U') 로 전이한다. 배치 main 엔트리 포함. * * 실행: vl_bt_0005 [db@host] */ @@ -18,28 +19,32 @@ EXEC SQL INCLUDE sqlca; +#define VL_BT_0005_TOLERANCE 100L /* 허용오차: 100원 */ + static int run_vl_bt_0005(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; + long h_ledger_amt; + long h_appr_amt; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, within = 0, over = 0, errcnt = 0; + long diff, diff_sum = 0, max_diff = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_vl_bt_0005 CURSOR FOR - SELECT key, merch_id, amount - FROM vl_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT l.key, l.amount, a.appr_amount + FROM vl_ledger l + JOIN vl_approval a ON a.appr_no = l.key + WHERE l.biz_date = :h_biz_date + AND l.status = 'R' + ORDER BY l.key; EXEC SQL OPEN cur_vl_bt_0005; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +56,7 @@ static int run_vl_bt_0005(const char *biz_date) tx_log(TX_LOG_WARN, "[vl_bt_0005] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_vl_bt_0005 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_vl_bt_0005 INTO :h_key, :h_ledger_amt, :h_appr_amt; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,14 +67,17 @@ static int run_vl_bt_0005(const char *biz_date) } total++; + diff = labs(h_ledger_amt - h_appr_amt); - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + if (diff <= VL_BT_0005_TOLERANCE) { strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; + within++; } else { strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + over++; + diff_sum += diff; + if (diff > max_diff) + max_diff = diff; } rc = vl_rec_update_status(h_key, h_new_status); @@ -87,16 +95,19 @@ static int run_vl_bt_0005(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0005] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0005] 요약 date=%s 대상=%ld 허용내=%ld 초과=%ld 오류=%ld", + biz_date, total, within, over, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0005] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 금액 허용오차 정합검증 [vl_bt_0005] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 허용오차(원) : %ld\n", VL_BT_0005_TOLERANCE); + printf(" 대상 건수 : %ld\n", total); + printf(" 허용내(M) : %ld\n", within); + printf(" 초과(U) : %ld\n", over); + printf(" 편차 합계 : %ld\n", diff_sum); + printf(" 최대 편차 : %ld\n", max_diff); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=====================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0006.c b/app/batch/vl_bt_0006.c index 783b8f3..24457c3 100644 --- a/app/batch/vl_bt_0006.c +++ b/app/batch/vl_bt_0006.c @@ -8,10 +8,12 @@ #line 1 "app/batch/vl_bt_0006.pgc" /* @tier hard @module vl @transform validation-batch */ /* - * vl_bt_0006.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=hard] + * vl_bt_0006.pgc - 일자 정합성 배치검증 (VL_BT_0006) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 의 거래일(txn_date) 과 정산예정일 + * (settle_date) 을 커서로 조회하여 일자 정합을 검증한다. 정산예정일이 + * 거래일보다 앞서거나(순서오류), 정산예정일이 주말(영업일 아님) 이면 + * 불일치('U'), 정상이면 완료('M') 로 전이한다. 배치 main 엔트리 포함. * * 실행: vl_bt_0006 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0006.pgc" +#line 21 "app/batch/vl_bt_0006.pgc" static int run_vl_bt_0006(const char *biz_date) @@ -105,41 +107,42 @@ static int run_vl_bt_0006(const char *biz_date) -#line 24 "app/batch/vl_bt_0006.pgc" +#line 26 "app/batch/vl_bt_0006.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0006.pgc" +#line 27 "app/batch/vl_bt_0006.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/vl_bt_0006.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/vl_bt_0006.pgc" - long h_amount ; - #line 28 "app/batch/vl_bt_0006.pgc" + char h_txn_date [ 9 ] ; + +#line 29 "app/batch/vl_bt_0006.pgc" + char h_settle_date [ 9 ] ; + +#line 30 "app/batch/vl_bt_0006.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/vl_bt_0006.pgc" +#line 31 "app/batch/vl_bt_0006.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, ok = 0, order_err = 0, holiday_err = 0, errcnt = 0; + int bad; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0006 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0006.pgc" + /* declare cur_vl_bt_0006 cursor for select key , txn_date , settle_date from vl_ledger where biz_date = $1 and status = 'R' order by key */ +#line 45 "app/batch/vl_bt_0006.pgc" -#line 42 "app/batch/vl_bt_0006.pgc" +#line 45 "app/batch/vl_bt_0006.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0006 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0006 cursor for select key , txn_date , settle_date from vl_ledger where biz_date = $1 and status = 'R' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0006.pgc" +#line 47 "app/batch/vl_bt_0006.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0006"); @@ -153,11 +156,11 @@ static int run_vl_bt_0006(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_vl_bt_0006", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_txn_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_settle_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0006.pgc" +#line 57 "app/batch/vl_bt_0006.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,14 +172,28 @@ static int run_vl_bt_0006(const char *biz_date) } total++; + bad = 0; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { + /* 순서 검증: 정산예정일 < 거래일 이면 오류 (YYYYMMDD 문자열 비교) */ + if (strcmp(h_settle_date, h_txn_date) < 0) { + order_err++; + bad = 1; + tx_log(TX_LOG_WARN, "[vl_bt_0006] 일자역전 key=%s txn=%s settle=%s", + h_key, h_txn_date, h_settle_date); + } + /* 영업일 검증: 정산예정일이 주말이면 오류 */ + else if (date_is_weekend(h_settle_date)) { + holiday_err++; + bad = 1; + tx_log(TX_LOG_WARN, "[vl_bt_0006] 정산일주말 key=%s settle=%s", + h_key, h_settle_date); + } + + if (bad) { strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + } else { + strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); + ok++; } rc = vl_rec_update_status(h_key, h_new_status); @@ -187,7 +204,7 @@ static int run_vl_bt_0006(const char *biz_date) } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0006", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0006.pgc" +#line 99 "app/batch/vl_bt_0006.pgc" if (errcnt == 0) @@ -196,16 +213,17 @@ static int run_vl_bt_0006(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0006] 요약 date=%s 대상=%ld 정상=%ld 역전=%ld 주말=%ld 오류=%ld", + biz_date, total, ok, order_err, holiday_err, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 일자 정합성 배치검증 [vl_bt_0006] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정상(M) : %ld\n", ok); + printf(" 일자역전(U) : %ld\n", order_err); + printf(" 정산일주말(U) : %ld\n", holiday_err); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0006.pgc b/app/batch/vl_bt_0006.pgc index cbbecee..9018bf8 100644 --- a/app/batch/vl_bt_0006.pgc +++ b/app/batch/vl_bt_0006.pgc @@ -1,9 +1,11 @@ /* @tier hard @module vl @transform validation-batch */ /* - * vl_bt_0006.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=hard] + * vl_bt_0006.pgc - 일자 정합성 배치검증 (VL_BT_0006) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 의 거래일(txn_date) 과 정산예정일 + * (settle_date) 을 커서로 조회하여 일자 정합을 검증한다. 정산예정일이 + * 거래일보다 앞서거나(순서오류), 정산예정일이 주말(영업일 아님) 이면 + * 불일치('U'), 정상이면 완료('M') 로 전이한다. 배치 main 엔트리 포함. * * 실행: vl_bt_0006 [db@host] */ @@ -23,19 +25,20 @@ static int run_vl_bt_0006(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; + char h_txn_date[9]; + char h_settle_date[9]; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, ok = 0, order_err = 0, holiday_err = 0, errcnt = 0; + int bad; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_vl_bt_0006 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, txn_date, settle_date FROM vl_ledger WHERE biz_date = :h_biz_date AND status = 'R' @@ -51,7 +54,7 @@ static int run_vl_bt_0006(const char *biz_date) tx_log(TX_LOG_WARN, "[vl_bt_0006] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_vl_bt_0006 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_vl_bt_0006 INTO :h_key, :h_txn_date, :h_settle_date; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,14 +65,28 @@ static int run_vl_bt_0006(const char *biz_date) } total++; + bad = 0; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { + /* 순서 검증: 정산예정일 < 거래일 이면 오류 (YYYYMMDD 문자열 비교) */ + if (strcmp(h_settle_date, h_txn_date) < 0) { + order_err++; + bad = 1; + tx_log(TX_LOG_WARN, "[vl_bt_0006] 일자역전 key=%s txn=%s settle=%s", + h_key, h_txn_date, h_settle_date); + } + /* 영업일 검증: 정산예정일이 주말이면 오류 */ + else if (date_is_weekend(h_settle_date)) { + holiday_err++; + bad = 1; + tx_log(TX_LOG_WARN, "[vl_bt_0006] 정산일주말 key=%s settle=%s", + h_key, h_settle_date); + } + + if (bad) { strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + } else { + strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); + ok++; } rc = vl_rec_update_status(h_key, h_new_status); @@ -87,16 +104,17 @@ static int run_vl_bt_0006(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0006] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0006] 요약 date=%s 대상=%ld 정상=%ld 역전=%ld 주말=%ld 오류=%ld", + biz_date, total, ok, order_err, holiday_err, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0006] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 일자 정합성 배치검증 [vl_bt_0006] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정상(M) : %ld\n", ok); + printf(" 일자역전(U) : %ld\n", order_err); + printf(" 정산일주말(U) : %ld\n", holiday_err); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0007.c b/app/batch/vl_bt_0007.c index 81cda0a..0ddf290 100644 --- a/app/batch/vl_bt_0007.c +++ b/app/batch/vl_bt_0007.c @@ -8,10 +8,12 @@ #line 1 "app/batch/vl_bt_0007.pgc" /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0007.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0007.pgc - 승인-매입 대사 정합검증 배치 (VL_BT_0007) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 승인원장(vl_approval) 건을 커서로 순회하며, 대응하는 + * 매입건(vl_purchase) 을 승인번호로 조회·대사한다. 매입 미발생건과 + * 금액불일치건은 불일치('U'), 정상 대사건은 완료('M') 로 전이한다. + * 배치 main 엔트리 포함. * * 실행: vl_bt_0007 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0007.pgc" +#line 21 "app/batch/vl_bt_0007.pgc" static int run_vl_bt_0007(const char *biz_date) @@ -105,41 +107,41 @@ static int run_vl_bt_0007(const char *biz_date) -#line 24 "app/batch/vl_bt_0007.pgc" +#line 26 "app/batch/vl_bt_0007.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0007.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/vl_bt_0007.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/vl_bt_0007.pgc" - long h_amount ; + char h_appr_no [ 16 ] ; #line 28 "app/batch/vl_bt_0007.pgc" + long h_appr_amt ; + +#line 29 "app/batch/vl_bt_0007.pgc" + long h_pur_amt ; + +#line 30 "app/batch/vl_bt_0007.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/vl_bt_0007.pgc" +#line 31 "app/batch/vl_bt_0007.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, matched = 0, no_purchase = 0, amt_diff = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0007 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0007.pgc" + /* declare cur_vl_bt_0007 cursor for select appr_no , appr_amount from vl_approval where biz_date = $1 and status = 'R' order by appr_no */ +#line 44 "app/batch/vl_bt_0007.pgc" -#line 42 "app/batch/vl_bt_0007.pgc" +#line 44 "app/batch/vl_bt_0007.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0007 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0007 cursor for select appr_no , appr_amount from vl_approval where biz_date = $1 and status = 'R' order by appr_no", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0007.pgc" +#line 46 "app/batch/vl_bt_0007.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0007"); @@ -151,13 +153,11 @@ static int run_vl_bt_0007(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_vl_bt_0007", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_appr_no),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_appr_amt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0007.pgc" +#line 56 "app/batch/vl_bt_0007.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +170,45 @@ static int run_vl_bt_0007(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { + /* 대응 매입건 단건 조회 (승인번호 기준 대사) */ + h_pur_amt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select pur_amount from vl_purchase where appr_no = $1 ", + ECPGt_char,(h_appr_no),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_pur_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 72 "app/batch/vl_bt_0007.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + /* 미매입: 승인 후 매입 미발생 */ strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + no_purchase++; + tx_log(TX_LOG_WARN, "[vl_bt_0007] 미매입 appr=%s", h_appr_no); + } else if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_purchase"); + errcnt++; + continue; + } else if (h_pur_amt != h_appr_amt) { + /* 금액 불일치 */ + strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); + amt_diff++; + tx_log(TX_LOG_WARN, "[vl_bt_0007] 금액불일치 appr=%s 승인=%ld 매입=%ld", + h_appr_no, h_appr_amt, h_pur_amt); + } else { + strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); + matched++; } - rc = vl_rec_update_status(h_key, h_new_status); + rc = vl_rec_update_status(h_appr_no, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[vl_bt_0007] 상태전이 실패 appr=%s rc=%d", h_appr_no, rc); errcnt++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0007", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0007.pgc" +#line 101 "app/batch/vl_bt_0007.pgc" if (errcnt == 0) @@ -196,16 +217,17 @@ static int run_vl_bt_0007(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0007] 요약 date=%s 대상=%ld 정상=%ld 미매입=%ld 금액불일치=%ld 오류=%ld", + biz_date, total, matched, no_purchase, amt_diff, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 승인-매입 대사 정합검증 [vl_bt_0007] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 승인 건수 : %ld\n", total); + printf(" 정상 대사(M) : %ld\n", matched); + printf(" 미매입(U) : %ld\n", no_purchase); + printf(" 금액불일치(U) : %ld\n", amt_diff); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0007.pgc b/app/batch/vl_bt_0007.pgc index 9f0d391..2a9fcce 100644 --- a/app/batch/vl_bt_0007.pgc +++ b/app/batch/vl_bt_0007.pgc @@ -1,9 +1,11 @@ /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0007.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0007.pgc - 승인-매입 대사 정합검증 배치 (VL_BT_0007) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 승인원장(vl_approval) 건을 커서로 순회하며, 대응하는 + * 매입건(vl_purchase) 을 승인번호로 조회·대사한다. 매입 미발생건과 + * 금액불일치건은 불일치('U'), 정상 대사건은 완료('M') 로 전이한다. + * 배치 main 엔트리 포함. * * 실행: vl_bt_0007 [db@host] */ @@ -22,24 +24,24 @@ static int run_vl_bt_0007(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; + char h_appr_no[16]; + long h_appr_amt; + long h_pur_amt; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, matched = 0, no_purchase = 0, amt_diff = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_vl_bt_0007 CURSOR FOR - SELECT key, merch_id, amount - FROM vl_ledger + SELECT appr_no, appr_amount + FROM vl_approval WHERE biz_date = :h_biz_date AND status = 'R' - ORDER BY key; + ORDER BY appr_no; EXEC SQL OPEN cur_vl_bt_0007; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +53,7 @@ static int run_vl_bt_0007(const char *biz_date) tx_log(TX_LOG_WARN, "[vl_bt_0007] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_vl_bt_0007 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_vl_bt_0007 INTO :h_appr_no, :h_appr_amt; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,18 +65,35 @@ static int run_vl_bt_0007(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { + /* 대응 매입건 단건 조회 (승인번호 기준 대사) */ + h_pur_amt = 0; + EXEC SQL SELECT pur_amount INTO :h_pur_amt + FROM vl_purchase + WHERE appr_no = :h_appr_no; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + /* 미매입: 승인 후 매입 미발생 */ strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + no_purchase++; + tx_log(TX_LOG_WARN, "[vl_bt_0007] 미매입 appr=%s", h_appr_no); + } else if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_purchase"); + errcnt++; + continue; + } else if (h_pur_amt != h_appr_amt) { + /* 금액 불일치 */ + strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); + amt_diff++; + tx_log(TX_LOG_WARN, "[vl_bt_0007] 금액불일치 appr=%s 승인=%ld 매입=%ld", + h_appr_no, h_appr_amt, h_pur_amt); + } else { + strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); + matched++; } - rc = vl_rec_update_status(h_key, h_new_status); + rc = vl_rec_update_status(h_appr_no, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0007] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[vl_bt_0007] 상태전이 실패 appr=%s rc=%d", h_appr_no, rc); errcnt++; } } @@ -87,16 +106,17 @@ static int run_vl_bt_0007(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0007] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0007] 요약 date=%s 대상=%ld 정상=%ld 미매입=%ld 금액불일치=%ld 오류=%ld", + biz_date, total, matched, no_purchase, amt_diff, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0007] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 승인-매입 대사 정합검증 [vl_bt_0007] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 승인 건수 : %ld\n", total); + printf(" 정상 대사(M) : %ld\n", matched); + printf(" 미매입(U) : %ld\n", no_purchase); + printf(" 금액불일치(U) : %ld\n", amt_diff); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0008.c b/app/batch/vl_bt_0008.c index 746c4f8..b1cd623 100644 --- a/app/batch/vl_bt_0008.c +++ b/app/batch/vl_bt_0008.c @@ -8,10 +8,12 @@ #line 1 "app/batch/vl_bt_0008.pgc" /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0008.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0008.pgc - 가맹점 한도 정합검증 배치 (VL_BT_0008) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 을 가맹점별로 집계(SUM) 한 당일 매출을 + * 커서로 순회하며, 가맹점 한도원장(vl_limit) 의 일한도와 대사한다. + * 당일 매출이 한도를 초과한 가맹점을 로그·집계한다(상태전이 없음, 감시용). + * 배치 main 엔트리 포함. * * 실행: vl_bt_0008 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0008.pgc" +#line 21 "app/batch/vl_bt_0008.pgc" static int run_vl_bt_0008(const char *biz_date) @@ -103,43 +105,40 @@ static int run_vl_bt_0008(const char *biz_date) - -#line 24 "app/batch/vl_bt_0008.pgc" +#line 26 "app/batch/vl_bt_0008.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0008.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/vl_bt_0008.pgc" +#line 27 "app/batch/vl_bt_0008.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/vl_bt_0008.pgc" - long h_amount ; - #line 28 "app/batch/vl_bt_0008.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_day_sum ; + #line 29 "app/batch/vl_bt_0008.pgc" + long h_limit_amt ; +/* exec sql end declare section */ +#line 30 "app/batch/vl_bt_0008.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long merch_cnt = 0, over_cnt = 0, no_limit = 0, errcnt = 0; + long over_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0008 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0008.pgc" + /* declare cur_vl_bt_0008 cursor for select merch_id , SUM ( amount ) from vl_ledger where biz_date = $1 and status in ( 'R' , 'M' ) group by merch_id order by merch_id */ +#line 45 "app/batch/vl_bt_0008.pgc" -#line 42 "app/batch/vl_bt_0008.pgc" +#line 45 "app/batch/vl_bt_0008.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0008 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0008 cursor for select merch_id , SUM ( amount ) from vl_ledger where biz_date = $1 and status in ( 'R' , 'M' ) group by merch_id order by merch_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0008.pgc" +#line 47 "app/batch/vl_bt_0008.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0008"); @@ -151,13 +150,11 @@ static int run_vl_bt_0008(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_vl_bt_0008", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_day_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0008.pgc" +#line 57 "app/batch/vl_bt_0008.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,26 +165,41 @@ static int run_vl_bt_0008(const char *biz_date) break; } - total++; + merch_cnt++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 가맹점 일한도 조회 */ + h_limit_amt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select day_limit from vl_limit where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_limit_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 73 "app/batch/vl_bt_0008.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + /* 한도 미등록 가맹점: 감시 목록에만 기록 */ + no_limit++; + tx_log(TX_LOG_WARN, "[vl_bt_0008] 한도미등록 merch=%s 당일=%ld", + h_merch_id, h_day_sum); + continue; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_limit"); + errcnt++; + continue; } - rc = vl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (h_day_sum > h_limit_amt) { + over_cnt++; + over_sum += (h_day_sum - h_limit_amt); + tx_log(TX_LOG_WARN, "[vl_bt_0008] 한도초과 merch=%s 당일=%ld 한도=%ld 초과=%ld", + h_merch_id, h_day_sum, h_limit_amt, h_day_sum - h_limit_amt); } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0008", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0008.pgc" +#line 96 "app/batch/vl_bt_0008.pgc" if (errcnt == 0) @@ -196,16 +208,17 @@ static int run_vl_bt_0008(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0008] 요약 date=%s 가맹점=%ld 초과=%ld 미등록=%ld 오류=%ld", + biz_date, merch_cnt, over_cnt, no_limit, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 가맹점 한도 정합검증 [vl_bt_0008] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 집계 가맹점 수 : %ld\n", merch_cnt); + printf(" 한도초과 가맹점 : %ld\n", over_cnt); + printf(" 초과금액 합계 : %ld\n", over_sum); + printf(" 한도미등록 : %ld\n", no_limit); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0008.pgc b/app/batch/vl_bt_0008.pgc index a73ca66..92428ab 100644 --- a/app/batch/vl_bt_0008.pgc +++ b/app/batch/vl_bt_0008.pgc @@ -1,9 +1,11 @@ /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0008.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0008.pgc - 가맹점 한도 정합검증 배치 (VL_BT_0008) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 을 가맹점별로 집계(SUM) 한 당일 매출을 + * 커서로 순회하며, 가맹점 한도원장(vl_limit) 의 일한도와 대사한다. + * 당일 매출이 한도를 초과한 가맹점을 로그·집계한다(상태전이 없음, 감시용). + * 배치 main 엔트리 포함. * * 실행: vl_bt_0008 [db@host] */ @@ -22,24 +24,25 @@ static int run_vl_bt_0008(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_day_sum; + long h_limit_amt; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long merch_cnt = 0, over_cnt = 0, no_limit = 0, errcnt = 0; + long over_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_vl_bt_0008 CURSOR FOR - SELECT key, merch_id, amount + SELECT merch_id, SUM(amount) FROM vl_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + AND status IN ('R', 'M') + GROUP BY merch_id + ORDER BY merch_id; EXEC SQL OPEN cur_vl_bt_0008; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +54,7 @@ static int run_vl_bt_0008(const char *biz_date) tx_log(TX_LOG_WARN, "[vl_bt_0008] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_vl_bt_0008 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_vl_bt_0008 INTO :h_merch_id, :h_day_sum; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,21 +64,32 @@ static int run_vl_bt_0008(const char *biz_date) break; } - total++; + merch_cnt++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 가맹점 일한도 조회 */ + h_limit_amt = 0; + EXEC SQL SELECT day_limit INTO :h_limit_amt + FROM vl_limit + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + /* 한도 미등록 가맹점: 감시 목록에만 기록 */ + no_limit++; + tx_log(TX_LOG_WARN, "[vl_bt_0008] 한도미등록 merch=%s 당일=%ld", + h_merch_id, h_day_sum); + continue; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_limit"); + errcnt++; + continue; } - rc = vl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0008] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (h_day_sum > h_limit_amt) { + over_cnt++; + over_sum += (h_day_sum - h_limit_amt); + tx_log(TX_LOG_WARN, "[vl_bt_0008] 한도초과 merch=%s 당일=%ld 한도=%ld 초과=%ld", + h_merch_id, h_day_sum, h_limit_amt, h_day_sum - h_limit_amt); } } @@ -87,16 +101,17 @@ static int run_vl_bt_0008(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0008] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0008] 요약 date=%s 가맹점=%ld 초과=%ld 미등록=%ld 오류=%ld", + biz_date, merch_cnt, over_cnt, no_limit, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0008] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 가맹점 한도 정합검증 [vl_bt_0008] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 집계 가맹점 수 : %ld\n", merch_cnt); + printf(" 한도초과 가맹점 : %ld\n", over_cnt); + printf(" 초과금액 합계 : %ld\n", over_sum); + printf(" 한도미등록 : %ld\n", no_limit); + printf(" 오류 건수 : %ld\n", errcnt); + printf("===================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0009.c b/app/batch/vl_bt_0009.c index 61c09b5..219a0e7 100644 --- a/app/batch/vl_bt_0009.c +++ b/app/batch/vl_bt_0009.c @@ -8,10 +8,12 @@ #line 1 "app/batch/vl_bt_0009.pgc" /* @tier medium @module vl @transform validation-batch */ /* - * vl_bt_0009.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=medium] + * vl_bt_0009.pgc - 수수료 정합검증 배치 (VL_BT_0009) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 을 수수료율원장(vl_fee_rate) 과 조인하여 + * 계산수수료(amount * fee_rate / 10000) 와 원장 수수료(fee) 를 대사한다. + * 반올림 오차(1원) 를 초과하면 불일치('U'), 이내면 완료('M') 로 전이한다. + * 배치 main 엔트리 포함. * * 실행: vl_bt_0009 [db@host] */ @@ -93,9 +95,11 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0009.pgc" +#line 21 "app/batch/vl_bt_0009.pgc" +#define VL_BT_0009_ROUND_TOL 1L /* 반올림 허용오차: 1원 */ + static int run_vl_bt_0009(const char *biz_date) { /* exec sql begin declare section */ @@ -103,43 +107,48 @@ static int run_vl_bt_0009(const char *biz_date) + /* bp(만분율): 예) 250 = 2.5% */ -#line 24 "app/batch/vl_bt_0009.pgc" +#line 28 "app/batch/vl_bt_0009.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0009.pgc" +#line 29 "app/batch/vl_bt_0009.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/vl_bt_0009.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/vl_bt_0009.pgc" +#line 30 "app/batch/vl_bt_0009.pgc" long h_amount ; -#line 28 "app/batch/vl_bt_0009.pgc" +#line 31 "app/batch/vl_bt_0009.pgc" + long h_fee ; + +#line 32 "app/batch/vl_bt_0009.pgc" + long h_fee_rate ; + +#line 33 "app/batch/vl_bt_0009.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/vl_bt_0009.pgc" +#line 34 "app/batch/vl_bt_0009.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, matched = 0, mismatch = 0, errcnt = 0; + long calc_fee, diff, diff_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0009 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0009.pgc" + /* declare cur_vl_bt_0009 cursor for select l . key , l . amount , l . fee , r . fee_rate from vl_ledger l join vl_fee_rate r on r . merch_id = l . merch_id where l . biz_date = $1 and l . status = 'R' order by l . key */ +#line 49 "app/batch/vl_bt_0009.pgc" -#line 42 "app/batch/vl_bt_0009.pgc" +#line 49 "app/batch/vl_bt_0009.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0009 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0009 cursor for select l . key , l . amount , l . fee , r . fee_rate from vl_ledger l join vl_fee_rate r on r . merch_id = l . merch_id where l . biz_date = $1 and l . status = 'R' order by l . key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0009.pgc" +#line 51 "app/batch/vl_bt_0009.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0009"); @@ -153,11 +162,13 @@ static int run_vl_bt_0009(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_vl_bt_0009", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee_rate),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0009.pgc" +#line 62 "app/batch/vl_bt_0009.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,13 +181,20 @@ static int run_vl_bt_0009(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + /* 계산수수료 = 거래금액 * 요율(bp) / 10000, 반올림 */ + calc_fee = (h_amount * h_fee_rate + 5000L) / 10000L; + diff = labs(calc_fee - h_fee); + + if (diff <= VL_BT_0009_ROUND_TOL) { strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; + matched++; } else { strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + mismatch++; + diff_sum += diff; + tx_log(TX_LOG_WARN, + "[vl_bt_0009] 수수료불일치 key=%s 계산=%ld 원장=%ld rate=%ld", + h_key, calc_fee, h_fee, h_fee_rate); } rc = vl_rec_update_status(h_key, h_new_status); @@ -187,7 +205,7 @@ static int run_vl_bt_0009(const char *biz_date) } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0009", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0009.pgc" +#line 97 "app/batch/vl_bt_0009.pgc" if (errcnt == 0) @@ -196,15 +214,16 @@ static int run_vl_bt_0009(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0009] 요약 date=%s 대상=%ld 정합=%ld 불일치=%ld 오류=%ld", + biz_date, total, matched, mismatch, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 수수료 정합검증 [vl_bt_0009] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정합(M) : %ld\n", matched); + printf(" 불일치(U) : %ld\n", mismatch); + printf(" 수수료 편차합 : %ld\n", diff_sum); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/vl_bt_0009.pgc b/app/batch/vl_bt_0009.pgc index 8e917ef..e5a8da8 100644 --- a/app/batch/vl_bt_0009.pgc +++ b/app/batch/vl_bt_0009.pgc @@ -1,9 +1,11 @@ /* @tier medium @module vl @transform validation-batch */ /* - * vl_bt_0009.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=medium] + * vl_bt_0009.pgc - 수수료 정합검증 배치 (VL_BT_0009) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 을 수수료율원장(vl_fee_rate) 과 조인하여 + * 계산수수료(amount * fee_rate / 10000) 와 원장 수수료(fee) 를 대사한다. + * 반올림 오차(1원) 를 초과하면 불일치('U'), 이내면 완료('M') 로 전이한다. + * 배치 main 엔트리 포함. * * 실행: vl_bt_0009 [db@host] */ @@ -18,28 +20,33 @@ EXEC SQL INCLUDE sqlca; +#define VL_BT_0009_ROUND_TOL 1L /* 반올림 허용오차: 1원 */ + static int run_vl_bt_0009(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; long h_amount; + long h_fee; + long h_fee_rate; /* bp(만분율): 예) 250 = 2.5% */ char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, matched = 0, mismatch = 0, errcnt = 0; + long calc_fee, diff, diff_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_vl_bt_0009 CURSOR FOR - SELECT key, merch_id, amount - FROM vl_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT l.key, l.amount, l.fee, r.fee_rate + FROM vl_ledger l + JOIN vl_fee_rate r ON r.merch_id = l.merch_id + WHERE l.biz_date = :h_biz_date + AND l.status = 'R' + ORDER BY l.key; EXEC SQL OPEN cur_vl_bt_0009; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +58,8 @@ static int run_vl_bt_0009(const char *biz_date) tx_log(TX_LOG_WARN, "[vl_bt_0009] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_vl_bt_0009 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_vl_bt_0009 + INTO :h_key, :h_amount, :h_fee, :h_fee_rate; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,13 +71,20 @@ static int run_vl_bt_0009(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { + /* 계산수수료 = 거래금액 * 요율(bp) / 10000, 반올림 */ + calc_fee = (h_amount * h_fee_rate + 5000L) / 10000L; + diff = labs(calc_fee - h_fee); + + if (diff <= VL_BT_0009_ROUND_TOL) { strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; + matched++; } else { strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + mismatch++; + diff_sum += diff; + tx_log(TX_LOG_WARN, + "[vl_bt_0009] 수수료불일치 key=%s 계산=%ld 원장=%ld rate=%ld", + h_key, calc_fee, h_fee, h_fee_rate); } rc = vl_rec_update_status(h_key, h_new_status); @@ -87,15 +102,16 @@ static int run_vl_bt_0009(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0009] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0009] 요약 date=%s 대상=%ld 정합=%ld 불일치=%ld 오류=%ld", + biz_date, total, matched, mismatch, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0009] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); + printf("========== 수수료 정합검증 [vl_bt_0009] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정합(M) : %ld\n", matched); + printf(" 불일치(U) : %ld\n", mismatch); + printf(" 수수료 편차합 : %ld\n", diff_sum); + printf(" 오류 건수 : %ld\n", errcnt); printf("=============================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; diff --git a/app/batch/vl_bt_0010.c b/app/batch/vl_bt_0010.c index 81c7cb9..e5dfdd6 100644 --- a/app/batch/vl_bt_0010.c +++ b/app/batch/vl_bt_0010.c @@ -8,10 +8,12 @@ #line 1 "app/batch/vl_bt_0010.pgc" /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0010.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0010.pgc - 정산금액 정합검증 배치 (VL_BT_0010) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 정산원장(vl_settle) 을 커서로 순회하며, 정산 항등식 + * (매입금액 - 수수료 = 정산액) 을 검증한다. 항등식이 성립하지 않는 + * 건은 불일치('U'), 성립건은 정산완료('S') 로 전이하고 집계한다. + * 배치 main 엔트리 포함. * * 실행: vl_bt_0010 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0010.pgc" +#line 21 "app/batch/vl_bt_0010.pgc" static int run_vl_bt_0010(const char *biz_date) @@ -104,42 +106,47 @@ static int run_vl_bt_0010(const char *biz_date) + -#line 24 "app/batch/vl_bt_0010.pgc" +#line 26 "app/batch/vl_bt_0010.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0010.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/vl_bt_0010.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/vl_bt_0010.pgc" - long h_amount ; + char h_settle_key [ 16 ] ; #line 28 "app/batch/vl_bt_0010.pgc" + long h_pur_amt ; + +#line 29 "app/batch/vl_bt_0010.pgc" + long h_fee_amt ; + +#line 30 "app/batch/vl_bt_0010.pgc" + long h_settle_amt ; + +#line 31 "app/batch/vl_bt_0010.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/vl_bt_0010.pgc" +#line 32 "app/batch/vl_bt_0010.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, ok = 0, mismatch = 0, errcnt = 0; + long expected, diff, settle_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0010 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0010.pgc" + /* declare cur_vl_bt_0010 cursor for select s . settle_key , s . pur_amount , s . fee_amount , s . settle_amount from vl_settle s where s . biz_date = $1 and s . status = 'R' order by s . settle_key */ +#line 46 "app/batch/vl_bt_0010.pgc" -#line 42 "app/batch/vl_bt_0010.pgc" +#line 46 "app/batch/vl_bt_0010.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0010 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0010 cursor for select s . settle_key , s . pur_amount , s . fee_amount , s . settle_amount from vl_settle s where s . biz_date = $1 and s . status = 'R' order by s . settle_key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0010.pgc" +#line 48 "app/batch/vl_bt_0010.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0010"); @@ -151,13 +158,15 @@ static int run_vl_bt_0010(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_vl_bt_0010", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_settle_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_pur_amt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_fee_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_settle_amt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0010.pgc" +#line 59 "app/batch/vl_bt_0010.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +179,32 @@ static int run_vl_bt_0010(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; + /* 정산 항등식: 매입 - 수수료 = 정산액 */ + expected = h_pur_amt - h_fee_amt; + diff = labs(expected - h_settle_amt); + + if (diff == 0) { + strncpy(h_new_status, VL_ST_SETTLED, sizeof(h_new_status)); + ok++; + settle_sum += h_settle_amt; } else { strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + mismatch++; + tx_log(TX_LOG_WARN, + "[vl_bt_0010] 정산불일치 key=%s 기대=%ld 실제=%ld 차=%ld", + h_settle_key, expected, h_settle_amt, diff); } - rc = vl_rec_update_status(h_key, h_new_status); + rc = vl_rec_update_status(h_settle_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[vl_bt_0010] 상태전이 실패 key=%s rc=%d", + h_settle_key, rc); errcnt++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0010", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0010.pgc" +#line 95 "app/batch/vl_bt_0010.pgc" if (errcnt == 0) @@ -196,16 +213,17 @@ static int run_vl_bt_0010(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0010] 요약 date=%s 대상=%ld 정산완료=%ld 불일치=%ld 오류=%ld", + biz_date, total, ok, mismatch, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 정산금액 정합검증 [vl_bt_0010] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정산완료(S) : %ld\n", ok); + printf(" 불일치(U) : %ld\n", mismatch); + printf(" 정산금액 합계 : %ld\n", settle_sum); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0010.pgc b/app/batch/vl_bt_0010.pgc index d52e113..0b520c7 100644 --- a/app/batch/vl_bt_0010.pgc +++ b/app/batch/vl_bt_0010.pgc @@ -1,9 +1,11 @@ /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0010.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0010.pgc - 정산금액 정합검증 배치 (VL_BT_0010) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 정산원장(vl_settle) 을 커서로 순회하며, 정산 항등식 + * (매입금액 - 수수료 = 정산액) 을 검증한다. 항등식이 성립하지 않는 + * 건은 불일치('U'), 성립건은 정산완료('S') 로 전이하고 집계한다. + * 배치 main 엔트리 포함. * * 실행: vl_bt_0010 [db@host] */ @@ -22,24 +24,26 @@ static int run_vl_bt_0010(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; + char h_settle_key[16]; + long h_pur_amt; + long h_fee_amt; + long h_settle_amt; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, ok = 0, mismatch = 0, errcnt = 0; + long expected, diff, settle_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_vl_bt_0010 CURSOR FOR - SELECT key, merch_id, amount - FROM vl_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT s.settle_key, s.pur_amount, s.fee_amount, s.settle_amount + FROM vl_settle s + WHERE s.biz_date = :h_biz_date + AND s.status = 'R' + ORDER BY s.settle_key; EXEC SQL OPEN cur_vl_bt_0010; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +55,8 @@ static int run_vl_bt_0010(const char *biz_date) tx_log(TX_LOG_WARN, "[vl_bt_0010] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_vl_bt_0010 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_vl_bt_0010 + INTO :h_settle_key, :h_pur_amt, :h_fee_amt, :h_settle_amt; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,18 +68,26 @@ static int run_vl_bt_0010(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; + /* 정산 항등식: 매입 - 수수료 = 정산액 */ + expected = h_pur_amt - h_fee_amt; + diff = labs(expected - h_settle_amt); + + if (diff == 0) { + strncpy(h_new_status, VL_ST_SETTLED, sizeof(h_new_status)); + ok++; + settle_sum += h_settle_amt; } else { strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + mismatch++; + tx_log(TX_LOG_WARN, + "[vl_bt_0010] 정산불일치 key=%s 기대=%ld 실제=%ld 차=%ld", + h_settle_key, expected, h_settle_amt, diff); } - rc = vl_rec_update_status(h_key, h_new_status); + rc = vl_rec_update_status(h_settle_key, h_new_status); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0010] 상태전이 실패 key=%s rc=%d", h_key, rc); + tx_log(TX_LOG_ERROR, "[vl_bt_0010] 상태전이 실패 key=%s rc=%d", + h_settle_key, rc); errcnt++; } } @@ -87,16 +100,17 @@ static int run_vl_bt_0010(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0010] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0010] 요약 date=%s 대상=%ld 정산완료=%ld 불일치=%ld 오류=%ld", + biz_date, total, ok, mismatch, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0010] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 정산금액 정합검증 [vl_bt_0010] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정산완료(S) : %ld\n", ok); + printf(" 불일치(U) : %ld\n", mismatch); + printf(" 정산금액 합계 : %ld\n", settle_sum); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0011.c b/app/batch/vl_bt_0011.c index 0399fba..c0f3c14 100644 --- a/app/batch/vl_bt_0011.c +++ b/app/batch/vl_bt_0011.c @@ -8,10 +8,12 @@ #line 1 "app/batch/vl_bt_0011.pgc" /* @tier medium @module vl @transform validation-batch */ /* - * vl_bt_0011.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=medium] + * vl_bt_0011.pgc - 원장-집계 반영 정합검증 배치 (VL_BT_0011) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 을 가맹점별로 집계한 값과, 일집계 테이블 + * (vl_daily_agg) 의 사전 집계값을 대사한다. 건수/금액이 일치하지 않는 + * 가맹점(미반영/부분반영) 을 재집계 대상으로 로그·집계한다. + * 상태전이는 하지 않으며 재집계 워크리스트만 산출한다. main 포함. * * 실행: vl_bt_0011 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0011.pgc" +#line 21 "app/batch/vl_bt_0011.pgc" static int run_vl_bt_0011(const char *biz_date) @@ -104,42 +106,47 @@ static int run_vl_bt_0011(const char *biz_date) + -#line 24 "app/batch/vl_bt_0011.pgc" +#line 26 "app/batch/vl_bt_0011.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0011.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/vl_bt_0011.pgc" +#line 27 "app/batch/vl_bt_0011.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/vl_bt_0011.pgc" - long h_amount ; - #line 28 "app/batch/vl_bt_0011.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_led_cnt ; + #line 29 "app/batch/vl_bt_0011.pgc" + long h_led_sum ; + +#line 30 "app/batch/vl_bt_0011.pgc" + long h_agg_cnt ; + +#line 31 "app/batch/vl_bt_0011.pgc" + long h_agg_sum ; +/* exec sql end declare section */ +#line 32 "app/batch/vl_bt_0011.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long merch_cnt = 0, unreflected = 0, cnt_diff = 0, sum_diff = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 원장 실집계를 가맹점별로 커서 순회 */ ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0011 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0011.pgc" + /* declare cur_vl_bt_0011 cursor for select merch_id , count ( * ) , coalesce ( SUM ( amount ) , 0 ) from vl_ledger where biz_date = $1 group by merch_id order by merch_id */ +#line 46 "app/batch/vl_bt_0011.pgc" -#line 42 "app/batch/vl_bt_0011.pgc" +#line 46 "app/batch/vl_bt_0011.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0011 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0011 cursor for select merch_id , count ( * ) , coalesce ( SUM ( amount ) , 0 ) from vl_ledger where biz_date = $1 group by merch_id order by merch_id", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0011.pgc" +#line 48 "app/batch/vl_bt_0011.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0011"); @@ -151,13 +158,13 @@ static int run_vl_bt_0011(const char *biz_date) for (;;) { { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_vl_bt_0011", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_led_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_led_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0011.pgc" +#line 59 "app/batch/vl_bt_0011.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -168,26 +175,52 @@ static int run_vl_bt_0011(const char *biz_date) break; } - total++; + merch_cnt++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 사전 집계 조회 (미반영이면 NOTFOUND) */ + h_agg_cnt = 0; + h_agg_sum = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select agg_cnt , agg_sum from vl_daily_agg where merch_id = $1 and agg_date = $2 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_agg_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_agg_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 77 "app/batch/vl_bt_0011.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + unreflected++; + tx_log(TX_LOG_WARN, + "[vl_bt_0011] 집계미반영 merch=%s 원장건수=%ld 원장금액=%ld", + h_merch_id, h_led_cnt, h_led_sum); + continue; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_daily_agg"); + errcnt++; + continue; } - rc = vl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (h_agg_cnt != h_led_cnt) { + cnt_diff++; + tx_log(TX_LOG_WARN, + "[vl_bt_0011] 건수불일치 merch=%s 원장=%ld 집계=%ld", + h_merch_id, h_led_cnt, h_agg_cnt); + } + if (h_agg_sum != h_led_sum) { + sum_diff++; + tx_log(TX_LOG_WARN, + "[vl_bt_0011] 금액불일치 merch=%s 원장=%ld 집계=%ld", + h_merch_id, h_led_sum, h_agg_sum); } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0011", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0011.pgc" +#line 106 "app/batch/vl_bt_0011.pgc" if (errcnt == 0) @@ -196,16 +229,18 @@ static int run_vl_bt_0011(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0011] 요약 date=%s 가맹점=%ld 미반영=%ld 건수차=%ld 금액차=%ld 오류=%ld", + biz_date, merch_cnt, unreflected, cnt_diff, sum_diff, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 원장-집계 반영 정합검증 [vl_bt_0011] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 집계 가맹점 수 : %ld\n", merch_cnt); + printf(" 집계미반영 : %ld\n", unreflected); + printf(" 건수 불일치 : %ld\n", cnt_diff); + printf(" 금액 불일치 : %ld\n", sum_diff); + printf(" 재집계 대상 : %ld\n", unreflected + cnt_diff + sum_diff); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0011.pgc b/app/batch/vl_bt_0011.pgc index ebcff9f..d754ddf 100644 --- a/app/batch/vl_bt_0011.pgc +++ b/app/batch/vl_bt_0011.pgc @@ -1,9 +1,11 @@ /* @tier medium @module vl @transform validation-batch */ /* - * vl_bt_0011.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=medium] + * vl_bt_0011.pgc - 원장-집계 반영 정합검증 배치 (VL_BT_0011) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 을 가맹점별로 집계한 값과, 일집계 테이블 + * (vl_daily_agg) 의 사전 집계값을 대사한다. 건수/금액이 일치하지 않는 + * 가맹점(미반영/부분반영) 을 재집계 대상으로 로그·집계한다. + * 상태전이는 하지 않으며 재집계 워크리스트만 산출한다. main 포함. * * 실행: vl_bt_0011 [db@host] */ @@ -22,24 +24,26 @@ static int run_vl_bt_0011(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_led_cnt; + long h_led_sum; + long h_agg_cnt; + long h_agg_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long merch_cnt = 0, unreflected = 0, cnt_diff = 0, sum_diff = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 원장 실집계를 가맹점별로 커서 순회 */ EXEC SQL DECLARE cur_vl_bt_0011 CURSOR FOR - SELECT key, merch_id, amount + SELECT merch_id, COUNT(*), COALESCE(SUM(amount), 0) FROM vl_ledger WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + GROUP BY merch_id + ORDER BY merch_id; EXEC SQL OPEN cur_vl_bt_0011; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +55,8 @@ static int run_vl_bt_0011(const char *biz_date) tx_log(TX_LOG_WARN, "[vl_bt_0011] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_vl_bt_0011 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_vl_bt_0011 + INTO :h_merch_id, :h_led_cnt, :h_led_sum; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -61,21 +66,40 @@ static int run_vl_bt_0011(const char *biz_date) break; } - total++; + merch_cnt++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + /* 사전 집계 조회 (미반영이면 NOTFOUND) */ + h_agg_cnt = 0; + h_agg_sum = 0; + EXEC SQL SELECT agg_cnt, agg_sum INTO :h_agg_cnt, :h_agg_sum + FROM vl_daily_agg + WHERE merch_id = :h_merch_id + AND agg_date = :h_biz_date; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + unreflected++; + tx_log(TX_LOG_WARN, + "[vl_bt_0011] 집계미반영 merch=%s 원장건수=%ld 원장금액=%ld", + h_merch_id, h_led_cnt, h_led_sum); + continue; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_daily_agg"); + errcnt++; + continue; } - rc = vl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0011] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (h_agg_cnt != h_led_cnt) { + cnt_diff++; + tx_log(TX_LOG_WARN, + "[vl_bt_0011] 건수불일치 merch=%s 원장=%ld 집계=%ld", + h_merch_id, h_led_cnt, h_agg_cnt); + } + if (h_agg_sum != h_led_sum) { + sum_diff++; + tx_log(TX_LOG_WARN, + "[vl_bt_0011] 금액불일치 merch=%s 원장=%ld 집계=%ld", + h_merch_id, h_led_sum, h_agg_sum); } } @@ -87,16 +111,18 @@ static int run_vl_bt_0011(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0011] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0011] 요약 date=%s 가맹점=%ld 미반영=%ld 건수차=%ld 금액차=%ld 오류=%ld", + biz_date, merch_cnt, unreflected, cnt_diff, sum_diff, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0011] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 원장-집계 반영 정합검증 [vl_bt_0011] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 집계 가맹점 수 : %ld\n", merch_cnt); + printf(" 집계미반영 : %ld\n", unreflected); + printf(" 건수 불일치 : %ld\n", cnt_diff); + printf(" 금액 불일치 : %ld\n", sum_diff); + printf(" 재집계 대상 : %ld\n", unreflected + cnt_diff + sum_diff); + printf(" 오류 건수 : %ld\n", errcnt); + printf("======================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0012.c b/app/batch/vl_bt_0012.c index 68f9b6e..e755e30 100644 --- a/app/batch/vl_bt_0012.c +++ b/app/batch/vl_bt_0012.c @@ -8,10 +8,12 @@ #line 1 "app/batch/vl_bt_0012.pgc" /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0012.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0012.pgc - 참조무결성 배치검증 (VL_BT_0012) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 을 커서로 순회하며, 참조 대상인 가맹점 + * (mm_merchant) 과 카드사(mm_issuer, 카드 BIN 6자리 기준) 의 존재 여부를 + * 확인한다. 참조키가 부재하면 무결성 위반으로 불일치('U'), 모두 존재하면 + * 완료('M') 로 전이한다. 배치 main 엔트리 포함. * * 실행: vl_bt_0012 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0012.pgc" +#line 21 "app/batch/vl_bt_0012.pgc" static int run_vl_bt_0012(const char *biz_date) @@ -104,42 +106,52 @@ static int run_vl_bt_0012(const char *biz_date) + + -#line 24 "app/batch/vl_bt_0012.pgc" +#line 26 "app/batch/vl_bt_0012.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0012.pgc" +#line 27 "app/batch/vl_bt_0012.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/vl_bt_0012.pgc" +#line 28 "app/batch/vl_bt_0012.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/vl_bt_0012.pgc" - long h_amount ; +#line 29 "app/batch/vl_bt_0012.pgc" + char h_bin [ 7 ] ; -#line 28 "app/batch/vl_bt_0012.pgc" +#line 30 "app/batch/vl_bt_0012.pgc" + long h_merch_exist ; + +#line 31 "app/batch/vl_bt_0012.pgc" + long h_issuer_exist ; + +#line 32 "app/batch/vl_bt_0012.pgc" char h_new_status [ 2 ] ; /* exec sql end declare section */ -#line 29 "app/batch/vl_bt_0012.pgc" +#line 33 "app/batch/vl_bt_0012.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, ok = 0, merch_miss = 0, issuer_miss = 0, errcnt = 0; + int bad; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 카드번호 앞 6자리(BIN) 를 SQL 에서 추출하여 조회 */ ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0012 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0012.pgc" + /* declare cur_vl_bt_0012 cursor for select key , merch_id , SUBSTR ( card_no , 1 , 6 ) from vl_ledger where biz_date = $1 and status = 'R' order by key */ +#line 48 "app/batch/vl_bt_0012.pgc" -#line 42 "app/batch/vl_bt_0012.pgc" +#line 48 "app/batch/vl_bt_0012.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0012 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0012 cursor for select key , merch_id , SUBSTR ( card_no , 1 , 6 ) from vl_ledger where biz_date = $1 and status = 'R' order by key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0012.pgc" +#line 50 "app/batch/vl_bt_0012.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0012"); @@ -155,9 +167,9 @@ static int run_vl_bt_0012(const char *biz_date) ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_char,(h_bin),(long)7,(long)1,(7)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0012.pgc" +#line 60 "app/batch/vl_bt_0012.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -169,14 +181,55 @@ static int run_vl_bt_0012(const char *biz_date) } total++; + bad = 0; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { + /* FK 1: 가맹점 존재 여부 */ + h_merch_exist = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_merchant where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_merch_exist),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 77 "app/batch/vl_bt_0012.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_merchant"); + errcnt++; + continue; + } + if (h_merch_exist == 0) { + merch_miss++; + bad = 1; + tx_log(TX_LOG_WARN, "[vl_bt_0012] 가맹점FK부재 key=%s merch=%s", + h_key, h_merch_id); + } + + /* FK 2: 카드사(BIN) 존재 여부 */ + h_issuer_exist = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_issuer where bin = $1 ", + ECPGt_char,(h_bin),(long)7,(long)1,(7)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_issuer_exist),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 94 "app/batch/vl_bt_0012.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_issuer"); + errcnt++; + continue; + } + if (h_issuer_exist == 0) { + issuer_miss++; + bad = 1; + tx_log(TX_LOG_WARN, "[vl_bt_0012] 카드사FK부재 key=%s bin=%s", + h_key, h_bin); + } + + if (bad) { strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + } else { + strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); + ok++; } rc = vl_rec_update_status(h_key, h_new_status); @@ -187,7 +240,7 @@ static int run_vl_bt_0012(const char *biz_date) } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0012", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0012.pgc" +#line 121 "app/batch/vl_bt_0012.pgc" if (errcnt == 0) @@ -196,16 +249,17 @@ static int run_vl_bt_0012(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0012] 요약 date=%s 대상=%ld 정상=%ld 가맹점부재=%ld 카드사부재=%ld 오류=%ld", + biz_date, total, ok, merch_miss, issuer_miss, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 참조무결성 배치검증 [vl_bt_0012] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정상(M) : %ld\n", ok); + printf(" 가맹점FK부재(U) : %ld\n", merch_miss); + printf(" 카드사FK부재(U) : %ld\n", issuer_miss); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0012.pgc b/app/batch/vl_bt_0012.pgc index a8d10a8..ac65d42 100644 --- a/app/batch/vl_bt_0012.pgc +++ b/app/batch/vl_bt_0012.pgc @@ -1,9 +1,11 @@ /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0012.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0012.pgc - 참조무결성 배치검증 (VL_BT_0012) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 을 커서로 순회하며, 참조 대상인 가맹점 + * (mm_merchant) 과 카드사(mm_issuer, 카드 BIN 6자리 기준) 의 존재 여부를 + * 확인한다. 참조키가 부재하면 무결성 위반으로 불일치('U'), 모두 존재하면 + * 완료('M') 로 전이한다. 배치 main 엔트리 포함. * * 실행: vl_bt_0012 [db@host] */ @@ -24,18 +26,22 @@ static int run_vl_bt_0012(const char *biz_date) char h_biz_date[9]; char h_key[16]; char h_merch_id[16]; - long h_amount; + char h_bin[7]; + long h_merch_exist; + long h_issuer_exist; char h_new_status[2]; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, ok = 0, merch_miss = 0, issuer_miss = 0, errcnt = 0; + int bad; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 카드번호 앞 6자리(BIN) 를 SQL 에서 추출하여 조회 */ EXEC SQL DECLARE cur_vl_bt_0012 CURSOR FOR - SELECT key, merch_id, amount + SELECT key, merch_id, SUBSTR(card_no, 1, 6) FROM vl_ledger WHERE biz_date = :h_biz_date AND status = 'R' @@ -51,7 +57,7 @@ static int run_vl_bt_0012(const char *biz_date) tx_log(TX_LOG_WARN, "[vl_bt_0012] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_vl_bt_0012 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_vl_bt_0012 INTO :h_key, :h_merch_id, :h_bin; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -62,14 +68,47 @@ static int run_vl_bt_0012(const char *biz_date) } total++; + bad = 0; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { + /* FK 1: 가맹점 존재 여부 */ + h_merch_exist = 0; + EXEC SQL SELECT COUNT(*) INTO :h_merch_exist + FROM mm_merchant + WHERE merch_id = :h_merch_id; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_merchant"); + errcnt++; + continue; + } + if (h_merch_exist == 0) { + merch_miss++; + bad = 1; + tx_log(TX_LOG_WARN, "[vl_bt_0012] 가맹점FK부재 key=%s merch=%s", + h_key, h_merch_id); + } + + /* FK 2: 카드사(BIN) 존재 여부 */ + h_issuer_exist = 0; + EXEC SQL SELECT COUNT(*) INTO :h_issuer_exist + FROM mm_issuer + WHERE bin = :h_bin; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_issuer"); + errcnt++; + continue; + } + if (h_issuer_exist == 0) { + issuer_miss++; + bad = 1; + tx_log(TX_LOG_WARN, "[vl_bt_0012] 카드사FK부재 key=%s bin=%s", + h_key, h_bin); + } + + if (bad) { strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + } else { + strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); + ok++; } rc = vl_rec_update_status(h_key, h_new_status); @@ -87,16 +126,17 @@ static int run_vl_bt_0012(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0012] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0012] 요약 date=%s 대상=%ld 정상=%ld 가맹점부재=%ld 카드사부재=%ld 오류=%ld", + biz_date, total, ok, merch_miss, issuer_miss, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0012] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 참조무결성 배치검증 [vl_bt_0012] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 대상 건수 : %ld\n", total); + printf(" 정상(M) : %ld\n", ok); + printf(" 가맹점FK부재(U) : %ld\n", merch_miss); + printf(" 카드사FK부재(U) : %ld\n", issuer_miss); + printf(" 오류 건수 : %ld\n", errcnt); + printf("==================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0013.c b/app/batch/vl_bt_0013.c index fbece86..0ca5b2b 100644 --- a/app/batch/vl_bt_0013.c +++ b/app/batch/vl_bt_0013.c @@ -8,10 +8,12 @@ #line 1 "app/batch/vl_bt_0013.pgc" /* @tier medium @module vl @transform validation-batch */ /* - * vl_bt_0013.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=medium] + * vl_bt_0013.pgc - 이상금액 탐지 배치 (VL_BT_0013) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 을 가맹점 평균거래액(vl_merch_avg) 과 + * 조인하여, 거래금액이 가맹점 평균의 임계배수(THRESHOLD) 를 초과하는 + * 이상거래를 커서로 탐지한다. 의심건은 의심거래 테이블(vl_suspect) 에 + * INSERT 하고 집계한다(상태전이 없음, FDS 후속검토용). main 포함. * * 실행: vl_bt_0013 [db@host] */ @@ -93,9 +95,11 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0013.pgc" +#line 21 "app/batch/vl_bt_0013.pgc" +#define VL_BT_0013_THRESHOLD 5L /* 평균 대비 임계배수 */ + static int run_vl_bt_0013(const char *biz_date) { /* exec sql begin declare section */ @@ -104,42 +108,47 @@ static int run_vl_bt_0013(const char *biz_date) + -#line 24 "app/batch/vl_bt_0013.pgc" +#line 28 "app/batch/vl_bt_0013.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0013.pgc" +#line 29 "app/batch/vl_bt_0013.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/vl_bt_0013.pgc" +#line 30 "app/batch/vl_bt_0013.pgc" char h_merch_id [ 16 ] ; -#line 27 "app/batch/vl_bt_0013.pgc" +#line 31 "app/batch/vl_bt_0013.pgc" long h_amount ; -#line 28 "app/batch/vl_bt_0013.pgc" - char h_new_status [ 2 ] ; +#line 32 "app/batch/vl_bt_0013.pgc" + long h_avg_amount ; + +#line 33 "app/batch/vl_bt_0013.pgc" + long h_ratio ; /* exec sql end declare section */ -#line 29 "app/batch/vl_bt_0013.pgc" +#line 34 "app/batch/vl_bt_0013.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, suspect = 0, inserted = 0, errcnt = 0; + long suspect_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0013 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0013.pgc" + /* declare cur_vl_bt_0013 cursor for select l . key , l . merch_id , l . amount , a . avg_amount from vl_ledger l join vl_merch_avg a on a . merch_id = l . merch_id where l . biz_date = $1 and l . status = 'R' and a . avg_amount > 0 order by l . key */ +#line 50 "app/batch/vl_bt_0013.pgc" -#line 42 "app/batch/vl_bt_0013.pgc" +#line 50 "app/batch/vl_bt_0013.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0013 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0013 cursor for select l . key , l . merch_id , l . amount , a . avg_amount from vl_ledger l join vl_merch_avg a on a . merch_id = l . merch_id where l . biz_date = $1 and l . status = 'R' and a . avg_amount > 0 order by l . key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0013.pgc" +#line 52 "app/batch/vl_bt_0013.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0013"); @@ -156,8 +165,10 @@ static int run_vl_bt_0013(const char *biz_date) ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_avg_amount),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0013.pgc" +#line 63 "app/batch/vl_bt_0013.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +181,45 @@ static int run_vl_bt_0013(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* 임계 판정: 거래금액이 평균의 THRESHOLD 배 초과이면 이상거래 */ + if (h_amount <= h_avg_amount * VL_BT_0013_THRESHOLD) + continue; - rc = vl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); + suspect++; + suspect_sum += h_amount; + h_ratio = h_amount / h_avg_amount; /* 평균 대비 배수(정수) */ + + tx_log(TX_LOG_WARN, + "[vl_bt_0013] 이상금액 key=%s merch=%s 금액=%ld 평균=%ld 배수=%ld", + h_key, h_merch_id, h_amount, h_avg_amount, h_ratio); + + /* 의심거래 등록 (FDS 후속검토 대상) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into vl_suspect ( susp_key , merch_id , amount , avg_amount , ratio , biz_date , susp_reason ) values ( $1 , $2 , $3 , $4 , $5 , $6 , 'AMOUNT_OUTLIER' )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_avg_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_ratio),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 92 "app/batch/vl_bt_0013.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT vl_suspect"); errcnt++; + } else { + inserted++; } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0013", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0013.pgc" +#line 102 "app/batch/vl_bt_0013.pgc" if (errcnt == 0) @@ -196,16 +228,18 @@ static int run_vl_bt_0013(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0013] 요약 date=%s 대상=%ld 의심=%ld 등록=%ld 오류=%ld", + biz_date, total, suspect, inserted, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 이상금액 탐지 배치 [vl_bt_0013] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 임계배수 : %ld\n", VL_BT_0013_THRESHOLD); + printf(" 검사 건수 : %ld\n", total); + printf(" 의심 건수 : %ld\n", suspect); + printf(" 의심금액 합계 : %ld\n", suspect_sum); + printf(" vl_suspect 등록: %ld\n", inserted); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0013.pgc b/app/batch/vl_bt_0013.pgc index ac5bde0..7fd72d7 100644 --- a/app/batch/vl_bt_0013.pgc +++ b/app/batch/vl_bt_0013.pgc @@ -1,9 +1,11 @@ /* @tier medium @module vl @transform validation-batch */ /* - * vl_bt_0013.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=medium] + * vl_bt_0013.pgc - 이상금액 탐지 배치 (VL_BT_0013) [tier=medium] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 을 가맹점 평균거래액(vl_merch_avg) 과 + * 조인하여, 거래금액이 가맹점 평균의 임계배수(THRESHOLD) 를 초과하는 + * 이상거래를 커서로 탐지한다. 의심건은 의심거래 테이블(vl_suspect) 에 + * INSERT 하고 집계한다(상태전이 없음, FDS 후속검토용). main 포함. * * 실행: vl_bt_0013 [db@host] */ @@ -18,6 +20,8 @@ EXEC SQL INCLUDE sqlca; +#define VL_BT_0013_THRESHOLD 5L /* 평균 대비 임계배수 */ + static int run_vl_bt_0013(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; @@ -25,21 +29,25 @@ static int run_vl_bt_0013(const char *biz_date) char h_key[16]; char h_merch_id[16]; long h_amount; - char h_new_status[2]; + long h_avg_amount; + long h_ratio; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, suspect = 0, inserted = 0, errcnt = 0; + long suspect_sum = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; EXEC SQL DECLARE cur_vl_bt_0013 CURSOR FOR - SELECT key, merch_id, amount - FROM vl_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT l.key, l.merch_id, l.amount, a.avg_amount + FROM vl_ledger l + JOIN vl_merch_avg a ON a.merch_id = l.merch_id + WHERE l.biz_date = :h_biz_date + AND l.status = 'R' + AND a.avg_amount > 0 + ORDER BY l.key; EXEC SQL OPEN cur_vl_bt_0013; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +59,8 @@ static int run_vl_bt_0013(const char *biz_date) tx_log(TX_LOG_WARN, "[vl_bt_0013] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_vl_bt_0013 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_vl_bt_0013 + INTO :h_key, :h_merch_id, :h_amount, :h_avg_amount; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,19 +72,30 @@ static int run_vl_bt_0013(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } + /* 임계 판정: 거래금액이 평균의 THRESHOLD 배 초과이면 이상거래 */ + if (h_amount <= h_avg_amount * VL_BT_0013_THRESHOLD) + continue; - rc = vl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0013] 상태전이 실패 key=%s rc=%d", h_key, rc); + suspect++; + suspect_sum += h_amount; + h_ratio = h_amount / h_avg_amount; /* 평균 대비 배수(정수) */ + + tx_log(TX_LOG_WARN, + "[vl_bt_0013] 이상금액 key=%s merch=%s 금액=%ld 평균=%ld 배수=%ld", + h_key, h_merch_id, h_amount, h_avg_amount, h_ratio); + + /* 의심거래 등록 (FDS 후속검토 대상) */ + EXEC SQL INSERT INTO vl_suspect + (susp_key, merch_id, amount, avg_amount, ratio, biz_date, susp_reason) + VALUES + (:h_key, :h_merch_id, :h_amount, :h_avg_amount, :h_ratio, + :h_biz_date, 'AMOUNT_OUTLIER'); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT vl_suspect"); errcnt++; + } else { + inserted++; } } @@ -87,16 +107,18 @@ static int run_vl_bt_0013(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0013] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0013] 요약 date=%s 대상=%ld 의심=%ld 등록=%ld 오류=%ld", + biz_date, total, suspect, inserted, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0013] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 이상금액 탐지 배치 [vl_bt_0013] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 임계배수 : %ld\n", VL_BT_0013_THRESHOLD); + printf(" 검사 건수 : %ld\n", total); + printf(" 의심 건수 : %ld\n", suspect); + printf(" 의심금액 합계 : %ld\n", suspect_sum); + printf(" vl_suspect 등록: %ld\n", inserted); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0014.c b/app/batch/vl_bt_0014.c index 1a48f07..9640320 100644 --- a/app/batch/vl_bt_0014.c +++ b/app/batch/vl_bt_0014.c @@ -8,10 +8,11 @@ #line 1 "app/batch/vl_bt_0014.pgc" /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0014.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0014.pgc - 정합성 현황 리포트 배치 (VL_BT_0014) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 의 상태코드별 건수/금액을 집계 SELECT 로 + * 산출하여 정합성 현황 리포트를 출력한다. 커서 없이 상태코드 배열을 + * 순회하며 상태별 단건 집계를 수행하고, 정합률을 계산한다. main 포함. * * 실행: vl_bt_0014 [db@host] */ @@ -93,7 +94,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0014.pgc" +#line 20 "app/batch/vl_bt_0014.pgc" static int run_vl_bt_0014(const char *biz_date) @@ -103,109 +104,83 @@ static int run_vl_bt_0014(const char *biz_date) - -#line 24 "app/batch/vl_bt_0014.pgc" +#line 25 "app/batch/vl_bt_0014.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0014.pgc" - char h_key [ 16 ] ; - #line 26 "app/batch/vl_bt_0014.pgc" - char h_merch_id [ 16 ] ; + char h_status [ 2 ] ; #line 27 "app/batch/vl_bt_0014.pgc" - long h_amount ; + long h_cnt ; #line 28 "app/batch/vl_bt_0014.pgc" - char h_new_status [ 2 ] ; + long h_sum ; /* exec sql end declare section */ #line 29 "app/batch/vl_bt_0014.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + static const char *st_codes[] = { "R", "M", "U", "S" }; + static const char *st_labels[] = { "접수(R)", "완료(M)", "불일치(U)", "정산완료(S)" }; + long cnts[4] = {0,0,0,0}; + long sums[4] = {0,0,0,0}; + long grand_cnt = 0, grand_sum = 0; + long done_cnt, fail_cnt; + int i, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0014 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0014.pgc" + for (i = 0; i < 4; i++) { + strncpy(h_status, st_codes[i], sizeof(h_status)); -#line 42 "app/batch/vl_bt_0014.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0014 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( SUM ( amount ) , 0 ) from vl_ledger where biz_date = $1 and status = $2 ", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0014.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0014"); - return TX_EDB; - } - - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[vl_bt_0014] tx_begin 경고"); - - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_vl_bt_0014", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0014.pgc" +#line 49 "app/batch/vl_bt_0014.pgc" - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_vl_bt_0014"); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT vl_ledger 집계"); errcnt++; - break; + continue; } - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = vl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + cnts[i] = h_cnt; + sums[i] = h_sum; + grand_cnt += h_cnt; + grand_sum += h_sum; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0014", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0014.pgc" - - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + /* 정합률 = 완료 / (완료 + 불일치) * 100 */ + done_cnt = cnts[1]; + fail_cnt = cnts[2]; tx_log(TX_LOG_INFO, - "[vl_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0014] 요약 date=%s 총건수=%ld 완료=%ld 불일치=%ld 오류=%ld", + biz_date, grand_cnt, done_cnt, fail_cnt, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 정합성 현황 리포트 [vl_bt_0014] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" ---------------------------------------------\n"); + printf(" 상태 건수 금액\n"); + for (i = 0; i < 4; i++) + printf(" %-12s %8ld %14ld\n", st_labels[i], cnts[i], sums[i]); + printf(" ---------------------------------------------\n"); + printf(" 합계 %8ld %14ld\n", grand_cnt, grand_sum); + if (done_cnt + fail_cnt > 0) + printf(" 정합률 : %ld%%\n", + done_cnt * 100 / (done_cnt + fail_cnt)); + else + printf(" 정합률 : N/A (판정대상 없음)\n"); + printf(" 오류 건수 : %d\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0014.pgc b/app/batch/vl_bt_0014.pgc index f844886..6d7781f 100644 --- a/app/batch/vl_bt_0014.pgc +++ b/app/batch/vl_bt_0014.pgc @@ -1,9 +1,10 @@ /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0014.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0014.pgc - 정합성 현황 리포트 배치 (VL_BT_0014) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일 원장(vl_ledger) 의 상태코드별 건수/금액을 집계 SELECT 로 + * 산출하여 정합성 현황 리포트를 출력한다. 커서 없이 상태코드 배열을 + * 순회하며 상태별 단건 집계를 수행하고, 정합률을 계산한다. main 포함. * * 실행: vl_bt_0014 [db@host] */ @@ -22,81 +23,66 @@ static int run_vl_bt_0014(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + char h_status[2]; + long h_cnt; + long h_sum; EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; - int rc; + static const char *st_codes[] = { "R", "M", "U", "S" }; + static const char *st_labels[] = { "접수(R)", "완료(M)", "불일치(U)", "정산완료(S)" }; + long cnts[4] = {0,0,0,0}; + long sums[4] = {0,0,0,0}; + long grand_cnt = 0, grand_sum = 0; + long done_cnt, fail_cnt; + int i, errcnt = 0; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - EXEC SQL DECLARE cur_vl_bt_0014 CURSOR FOR - SELECT key, merch_id, amount - FROM vl_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + for (i = 0; i < 4; i++) { + strncpy(h_status, st_codes[i], sizeof(h_status)); - EXEC SQL OPEN cur_vl_bt_0014; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0014"); - return TX_EDB; + EXEC SQL SELECT COUNT(*), COALESCE(SUM(amount), 0) + INTO :h_cnt, :h_sum + FROM vl_ledger + WHERE biz_date = :h_biz_date + AND status = :h_status; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT vl_ledger 집계"); + errcnt++; + continue; + } + + cnts[i] = h_cnt; + sums[i] = h_sum; + grand_cnt += h_cnt; + grand_sum += h_sum; } - if (tx_begin() != TX_OK) - tx_log(TX_LOG_WARN, "[vl_bt_0014] tx_begin 경고"); - - for (;;) { - EXEC SQL FETCH cur_vl_bt_0014 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_vl_bt_0014"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = vl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0014] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } - } - - EXEC SQL CLOSE cur_vl_bt_0014; - - if (errcnt == 0) - tx_commit(); - else - tx_abort(); + /* 정합률 = 완료 / (완료 + 불일치) * 100 */ + done_cnt = cnts[1]; + fail_cnt = cnts[2]; tx_log(TX_LOG_INFO, - "[vl_bt_0014] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0014] 요약 date=%s 총건수=%ld 완료=%ld 불일치=%ld 오류=%ld", + biz_date, grand_cnt, done_cnt, fail_cnt, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0014] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 정합성 현황 리포트 [vl_bt_0014] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" ---------------------------------------------\n"); + printf(" 상태 건수 금액\n"); + for (i = 0; i < 4; i++) + printf(" %-12s %8ld %14ld\n", st_labels[i], cnts[i], sums[i]); + printf(" ---------------------------------------------\n"); + printf(" 합계 %8ld %14ld\n", grand_cnt, grand_sum); + if (done_cnt + fail_cnt > 0) + printf(" 정합률 : %ld%%\n", + done_cnt * 100 / (done_cnt + fail_cnt)); + else + printf(" 정합률 : N/A (판정대상 없음)\n"); + printf(" 오류 건수 : %d\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0015.c b/app/batch/vl_bt_0015.c index 7a3e1a6..f14b585 100644 --- a/app/batch/vl_bt_0015.c +++ b/app/batch/vl_bt_0015.c @@ -8,10 +8,12 @@ #line 1 "app/batch/vl_bt_0015.pgc" /* @tier hard @module vl @transform validation-batch */ /* - * vl_bt_0015.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=hard] + * vl_bt_0015.pgc - 미처리(불일치) 재처리 배치 (VL_BT_0015) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 앞선 배치에서 불일치('U') 로 분류된 건을 커서로 순회하며, 최신 매출 + * 원천(vl_sale_source) 을 다시 조인·재판정한다. 원천이 보정되어 금액이 + * 일치하면 완료('M') 로 회복 전이하고, 여전히 불일치이면 잔여 불일치로 + * 유지·집계한다. 배치 main 엔트리 포함. * * 실행: vl_bt_0015 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0015.pgc" +#line 21 "app/batch/vl_bt_0015.pgc" static int run_vl_bt_0015(const char *biz_date) @@ -103,43 +105,44 @@ static int run_vl_bt_0015(const char *biz_date) - + /* 원천 존재 여부(1/0) */ -#line 24 "app/batch/vl_bt_0015.pgc" +#line 26 "app/batch/vl_bt_0015.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0015.pgc" +#line 27 "app/batch/vl_bt_0015.pgc" char h_key [ 16 ] ; -#line 26 "app/batch/vl_bt_0015.pgc" - char h_merch_id [ 16 ] ; - -#line 27 "app/batch/vl_bt_0015.pgc" - long h_amount ; - #line 28 "app/batch/vl_bt_0015.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_ledger_amt ; + #line 29 "app/batch/vl_bt_0015.pgc" + long h_sale_amt ; + +#line 30 "app/batch/vl_bt_0015.pgc" + long h_src_flag ; +/* exec sql end declare section */ +#line 31 "app/batch/vl_bt_0015.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, recovered = 0, residual = 0, no_source = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 불일치('U') 건 + 최신 원천 존재 플래그를 LEFT JOIN 으로 조회 */ ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0015 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0015.pgc" + /* declare cur_vl_bt_0015 cursor for select l . key , l . amount , coalesce ( s . sale_amount , 0 ) , case when s . txn_key is null then 0 else 1 end from vl_ledger l left join vl_sale_source s on s . txn_key = l . key where l . biz_date = $1 and l . status = 'U' order by l . key */ +#line 48 "app/batch/vl_bt_0015.pgc" -#line 42 "app/batch/vl_bt_0015.pgc" +#line 48 "app/batch/vl_bt_0015.pgc" - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0015 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0015 cursor for select l . key , l . amount , coalesce ( s . sale_amount , 0 ) , case when s . txn_key is null then 0 else 1 end from vl_ledger l left join vl_sale_source s on s . txn_key = l . key where l . biz_date = $1 and l . status = 'U' order by l . key", ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0015.pgc" +#line 50 "app/batch/vl_bt_0015.pgc" if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0015"); @@ -153,11 +156,13 @@ static int run_vl_bt_0015(const char *biz_date) { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_vl_bt_0015", ECPGt_EOIT, ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_long,&(h_ledger_amt),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_long,&(h_sale_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_src_flag),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0015.pgc" +#line 61 "app/batch/vl_bt_0015.pgc" if (sqlca.sqlcode == TX_SQL_NOTFOUND) @@ -170,24 +175,34 @@ static int run_vl_bt_0015(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (h_src_flag == 0) { + /* 원천 여전히 부재 → 재처리 불가, 잔여 불일치 유지 */ + no_source++; + residual++; + tx_log(TX_LOG_WARN, "[vl_bt_0015] 원천부재 key=%s 유지(U)", h_key); + continue; } - rc = vl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (h_ledger_amt == h_sale_amt) { + /* 원천 보정 후 정합 → 완료로 회복 */ + rc = vl_rec_update_status(h_key, VL_ST_DONE); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[vl_bt_0015] 회복전이 실패 key=%s rc=%d", h_key, rc); + errcnt++; + } else { + recovered++; + tx_log(TX_LOG_INFO, "[vl_bt_0015] 회복 key=%s 금액=%ld", h_key, h_ledger_amt); + } + } else { + /* 여전히 불일치 → 잔여로 집계(상태 유지) */ + residual++; + tx_log(TX_LOG_DEBUG, "[vl_bt_0015] 잔여불일치 key=%s 원장=%ld 원천=%ld", + h_key, h_ledger_amt, h_sale_amt); } } { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0015", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0015.pgc" +#line 99 "app/batch/vl_bt_0015.pgc" if (errcnt == 0) @@ -196,16 +211,17 @@ static int run_vl_bt_0015(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0015] 요약 date=%s 대상=%ld 회복=%ld 잔여=%ld 원천부재=%ld 오류=%ld", + biz_date, total, recovered, residual, no_source, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 미처리 재처리 배치 [vl_bt_0015] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 재처리 대상 : %ld\n", total); + printf(" 회복(U→M) : %ld\n", recovered); + printf(" 잔여 불일치 : %ld\n", residual); + printf(" 원천 부재 : %ld\n", no_source); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0015.pgc b/app/batch/vl_bt_0015.pgc index de4a899..8154f8f 100644 --- a/app/batch/vl_bt_0015.pgc +++ b/app/batch/vl_bt_0015.pgc @@ -1,9 +1,11 @@ /* @tier hard @module vl @transform validation-batch */ /* - * vl_bt_0015.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=hard] + * vl_bt_0015.pgc - 미처리(불일치) 재처리 배치 (VL_BT_0015) [tier=hard] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 앞선 배치에서 불일치('U') 로 분류된 건을 커서로 순회하며, 최신 매출 + * 원천(vl_sale_source) 을 다시 조인·재판정한다. 원천이 보정되어 금액이 + * 일치하면 완료('M') 로 회복 전이하고, 여전히 불일치이면 잔여 불일치로 + * 유지·집계한다. 배치 main 엔트리 포함. * * 실행: vl_bt_0015 [db@host] */ @@ -23,23 +25,27 @@ static int run_vl_bt_0015(const char *biz_date) EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_ledger_amt; + long h_sale_amt; + long h_src_flag; /* 원천 존재 여부(1/0) */ EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long total = 0, recovered = 0, residual = 0, no_source = 0, errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 불일치('U') 건 + 최신 원천 존재 플래그를 LEFT JOIN 으로 조회 */ EXEC SQL DECLARE cur_vl_bt_0015 CURSOR FOR - SELECT key, merch_id, amount - FROM vl_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + SELECT l.key, l.amount, + COALESCE(s.sale_amount, 0), + CASE WHEN s.txn_key IS NULL THEN 0 ELSE 1 END + FROM vl_ledger l + LEFT JOIN vl_sale_source s ON s.txn_key = l.key + WHERE l.biz_date = :h_biz_date + AND l.status = 'U' + ORDER BY l.key; EXEC SQL OPEN cur_vl_bt_0015; if (sqlca.sqlcode != TX_SQL_OK) { @@ -51,7 +57,8 @@ static int run_vl_bt_0015(const char *biz_date) tx_log(TX_LOG_WARN, "[vl_bt_0015] tx_begin 경고"); for (;;) { - EXEC SQL FETCH cur_vl_bt_0015 INTO :h_key, :h_merch_id, :h_amount; + EXEC SQL FETCH cur_vl_bt_0015 + INTO :h_key, :h_ledger_amt, :h_sale_amt, :h_src_flag; if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; @@ -63,19 +70,29 @@ static int run_vl_bt_0015(const char *biz_date) total++; - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; + if (h_src_flag == 0) { + /* 원천 여전히 부재 → 재처리 불가, 잔여 불일치 유지 */ + no_source++; + residual++; + tx_log(TX_LOG_WARN, "[vl_bt_0015] 원천부재 key=%s 유지(U)", h_key); + continue; } - rc = vl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0015] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; + if (h_ledger_amt == h_sale_amt) { + /* 원천 보정 후 정합 → 완료로 회복 */ + rc = vl_rec_update_status(h_key, VL_ST_DONE); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[vl_bt_0015] 회복전이 실패 key=%s rc=%d", h_key, rc); + errcnt++; + } else { + recovered++; + tx_log(TX_LOG_INFO, "[vl_bt_0015] 회복 key=%s 금액=%ld", h_key, h_ledger_amt); + } + } else { + /* 여전히 불일치 → 잔여로 집계(상태 유지) */ + residual++; + tx_log(TX_LOG_DEBUG, "[vl_bt_0015] 잔여불일치 key=%s 원장=%ld 원천=%ld", + h_key, h_ledger_amt, h_sale_amt); } } @@ -87,16 +104,17 @@ static int run_vl_bt_0015(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0015] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0015] 요약 date=%s 대상=%ld 회복=%ld 잔여=%ld 원천부재=%ld 오류=%ld", + biz_date, total, recovered, residual, no_source, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0015] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 미처리 재처리 배치 [vl_bt_0015] ==========\n"); + printf(" 영업일 : %s\n", biz_date); + printf(" 재처리 대상 : %ld\n", total); + printf(" 회복(U→M) : %ld\n", recovered); + printf(" 잔여 불일치 : %ld\n", residual); + printf(" 원천 부재 : %ld\n", no_source); + printf(" 오류 건수 : %ld\n", errcnt); + printf("=================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0016.c b/app/batch/vl_bt_0016.c index 8a89c5a..e219cd3 100644 --- a/app/batch/vl_bt_0016.c +++ b/app/batch/vl_bt_0016.c @@ -8,10 +8,12 @@ #line 1 "app/batch/vl_bt_0016.pgc" /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0016.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0016.pgc - 정합성 통계 집계 배치 (VL_BT_0016) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 정합성 처리 결과를 표준 DBIO 집계 API(vl_rec_count/ + * vl_rec_sum) 로 산출하여, 일별 통계 테이블(vl_stat_daily) 에 대상/정합/ + * 불일치/거래금액과 영업일 구분(영업일/휴일) 을 INSERT 한다. 기존 통계가 + * 있으면 재적재를 위해 선삭제(UPSERT) 한다. 배치 main 엔트리 포함. * * 실행: vl_bt_0016 [db@host] */ @@ -93,7 +95,7 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 19 "app/batch/vl_bt_0016.pgc" +#line 21 "app/batch/vl_bt_0016.pgc" static int run_vl_bt_0016(const char *biz_date) @@ -104,91 +106,95 @@ static int run_vl_bt_0016(const char *biz_date) + + /* 'B'=영업일, 'H'=휴일 */ -#line 24 "app/batch/vl_bt_0016.pgc" +#line 26 "app/batch/vl_bt_0016.pgc" char h_biz_date [ 9 ] ; -#line 25 "app/batch/vl_bt_0016.pgc" - char h_key [ 16 ] ; - -#line 26 "app/batch/vl_bt_0016.pgc" - char h_merch_id [ 16 ] ; - #line 27 "app/batch/vl_bt_0016.pgc" - long h_amount ; + long h_target ; #line 28 "app/batch/vl_bt_0016.pgc" - char h_new_status [ 2 ] ; -/* exec sql end declare section */ + long h_matched ; + #line 29 "app/batch/vl_bt_0016.pgc" + long h_mismatch ; + +#line 30 "app/batch/vl_bt_0016.pgc" + long h_settled ; + +#line 31 "app/batch/vl_bt_0016.pgc" + long h_amount_sum ; + +#line 32 "app/batch/vl_bt_0016.pgc" + char h_day_type [ 2 ] ; +/* exec sql end declare section */ +#line 33 "app/batch/vl_bt_0016.pgc" - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long recv; + int errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - ECPGset_var( 0, ( h_biz_date ), __LINE__);\ - /* declare cur_vl_bt_0016 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key */ -#line 42 "app/batch/vl_bt_0016.pgc" + /* 표준 집계 API 로 상태별 건수/금액 산출 */ + recv = vl_rec_count(biz_date, VL_ST_RECV); + h_matched = vl_rec_count(biz_date, VL_ST_DONE); + h_mismatch = vl_rec_count(biz_date, VL_ST_FAIL); + h_settled = vl_rec_count(biz_date, VL_ST_SETTLED); + h_target = recv + h_matched + h_mismatch + h_settled; -#line 42 "app/batch/vl_bt_0016.pgc" - - - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_vl_bt_0016 cursor for select key , merch_id , amount from vl_ledger where biz_date = $1 and status = 'R' order by key", - ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} -#line 44 "app/batch/vl_bt_0016.pgc" - - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0016"); - return TX_EDB; + h_amount_sum = 0; + rc = vl_rec_sum(biz_date, &h_amount_sum); + if (rc != TX_OK) { + tx_log(TX_LOG_WARN, "[vl_bt_0016] 금액합 조회 경고 rc=%d", rc); + h_amount_sum = 0; } + /* 영업일 구분 */ + if (date_is_weekend(biz_date)) + strncpy(h_day_type, "H", sizeof(h_day_type)); + else + strncpy(h_day_type, "B", sizeof(h_day_type)); + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[vl_bt_0016] tx_begin 경고"); - for (;;) { - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_vl_bt_0016", ECPGt_EOIT, - ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, - ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), - ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} -#line 54 "app/batch/vl_bt_0016.pgc" + /* 재적재: 기존 통계 선삭제 후 INSERT */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "delete from vl_stat_daily where stat_date = $1 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 66 "app/batch/vl_bt_0016.pgc" - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_vl_bt_0016"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = vl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("DELETE vl_stat_daily"); + errcnt++; } - { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_vl_bt_0016", ECPGt_EOIT, ECPGt_EORT);} -#line 82 "app/batch/vl_bt_0016.pgc" + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into vl_stat_daily ( stat_date , day_type , target_cnt , matched_cnt , mismatch_cnt , settled_cnt , amount_sum ) values ( $1 , $2 , $3 , $4 , $5 , $6 , $7 )", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_day_type),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_target),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_matched),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_mismatch),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_settled),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 77 "app/batch/vl_bt_0016.pgc" + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT vl_stat_daily"); + errcnt++; + } if (errcnt == 0) tx_commit(); @@ -196,16 +202,20 @@ static int run_vl_bt_0016(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0016] 요약 date=%s 대상=%ld 정합=%ld 불일치=%ld 금액=%ld 오류=%d", + biz_date, h_target, h_matched, h_mismatch, h_amount_sum, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 정합성 통계 집계 [vl_bt_0016] ==========\n"); + printf(" 영업일 : %s (%s)\n", biz_date, + (h_day_type[0] == 'H') ? "휴일" : "영업일"); + printf(" 대상 건수 : %ld\n", h_target); + printf(" 정합(M) : %ld\n", h_matched); + printf(" 불일치(U) : %ld\n", h_mismatch); + printf(" 정산완료(S) : %ld\n", h_settled); + printf(" 거래금액 합계 : %ld\n", h_amount_sum); + printf(" vl_stat_daily : 적재 %s\n", (errcnt == 0) ? "성공" : "실패"); + printf(" 오류 건수 : %d\n", errcnt); + printf("================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/batch/vl_bt_0016.pgc b/app/batch/vl_bt_0016.pgc index dca306f..dc337c1 100644 --- a/app/batch/vl_bt_0016.pgc +++ b/app/batch/vl_bt_0016.pgc @@ -1,9 +1,11 @@ /* @tier easy @module vl @transform validation-batch */ /* - * vl_bt_0016.pgc - 정합성 배치 (커서 순회 + 상태전이) [tier=easy] + * vl_bt_0016.pgc - 정합성 통계 집계 배치 (VL_BT_0016) [tier=easy] * - * 지정 영업일의 접수(status='R') 건을 커서로 순회하며 업무규칙에 따라 - * 완료('M')/불일치('U') 로 상태를 전이한다. 배치 main 엔트리 포함. + * 지정 영업일의 정합성 처리 결과를 표준 DBIO 집계 API(vl_rec_count/ + * vl_rec_sum) 로 산출하여, 일별 통계 테이블(vl_stat_daily) 에 대상/정합/ + * 불일치/거래금액과 영업일 구분(영업일/휴일) 을 INSERT 한다. 기존 통계가 + * 있으면 재적재를 위해 선삭제(UPSERT) 한다. 배치 main 엔트리 포함. * * 실행: vl_bt_0016 [db@host] */ @@ -22,64 +24,61 @@ static int run_vl_bt_0016(const char *biz_date) { EXEC SQL BEGIN DECLARE SECTION; char h_biz_date[9]; - char h_key[16]; - char h_merch_id[16]; - long h_amount; - char h_new_status[2]; + long h_target; + long h_matched; + long h_mismatch; + long h_settled; + long h_amount_sum; + char h_day_type[2]; /* 'B'=영업일, 'H'=휴일 */ EXEC SQL END DECLARE SECTION; - long total = 0, done = 0, unmatch = 0, errcnt = 0; + long recv; + int errcnt = 0; int rc; strncpy(h_biz_date, biz_date, sizeof(h_biz_date) - 1); h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - EXEC SQL DECLARE cur_vl_bt_0016 CURSOR FOR - SELECT key, merch_id, amount - FROM vl_ledger - WHERE biz_date = :h_biz_date - AND status = 'R' - ORDER BY key; + /* 표준 집계 API 로 상태별 건수/금액 산출 */ + recv = vl_rec_count(biz_date, VL_ST_RECV); + h_matched = vl_rec_count(biz_date, VL_ST_DONE); + h_mismatch = vl_rec_count(biz_date, VL_ST_FAIL); + h_settled = vl_rec_count(biz_date, VL_ST_SETTLED); + h_target = recv + h_matched + h_mismatch + h_settled; - EXEC SQL OPEN cur_vl_bt_0016; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("OPEN cur_vl_bt_0016"); - return TX_EDB; + h_amount_sum = 0; + rc = vl_rec_sum(biz_date, &h_amount_sum); + if (rc != TX_OK) { + tx_log(TX_LOG_WARN, "[vl_bt_0016] 금액합 조회 경고 rc=%d", rc); + h_amount_sum = 0; } + /* 영업일 구분 */ + if (date_is_weekend(biz_date)) + strncpy(h_day_type, "H", sizeof(h_day_type)); + else + strncpy(h_day_type, "B", sizeof(h_day_type)); + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[vl_bt_0016] tx_begin 경고"); - for (;;) { - EXEC SQL FETCH cur_vl_bt_0016 INTO :h_key, :h_merch_id, :h_amount; - - if (sqlca.sqlcode == TX_SQL_NOTFOUND) - break; - if (sqlca.sqlcode != TX_SQL_OK) { - TX_DBIO_LOG_ERR("FETCH cur_vl_bt_0016"); - errcnt++; - break; - } - - total++; - - /* 업무규칙: 금액 유효성으로 완료/불일치 판정 */ - if (amount_is_valid(h_amount)) { - strncpy(h_new_status, VL_ST_DONE, sizeof(h_new_status)); - done++; - } else { - strncpy(h_new_status, VL_ST_FAIL, sizeof(h_new_status)); - unmatch++; - } - - rc = vl_rec_update_status(h_key, h_new_status); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[vl_bt_0016] 상태전이 실패 key=%s rc=%d", h_key, rc); - errcnt++; - } + /* 재적재: 기존 통계 선삭제 후 INSERT */ + EXEC SQL DELETE FROM vl_stat_daily WHERE stat_date = :h_biz_date; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("DELETE vl_stat_daily"); + errcnt++; } - EXEC SQL CLOSE cur_vl_bt_0016; + EXEC SQL INSERT INTO vl_stat_daily + (stat_date, day_type, target_cnt, matched_cnt, mismatch_cnt, + settled_cnt, amount_sum) + VALUES + (:h_biz_date, :h_day_type, :h_target, :h_matched, :h_mismatch, + :h_settled, :h_amount_sum); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT vl_stat_daily"); + errcnt++; + } if (errcnt == 0) tx_commit(); @@ -87,16 +86,20 @@ static int run_vl_bt_0016(const char *biz_date) tx_abort(); tx_log(TX_LOG_INFO, - "[vl_bt_0016] 요약 date=%s 대상=%ld 완료=%ld 불일치=%ld 오류=%ld", - biz_date, total, done, unmatch, errcnt); + "[vl_bt_0016] 요약 date=%s 대상=%ld 정합=%ld 불일치=%ld 금액=%ld 오류=%d", + biz_date, h_target, h_matched, h_mismatch, h_amount_sum, errcnt); - printf("========== 정합성 배치 결과 [vl_bt_0016] ==========\n"); - printf(" 영업일 : %s\n", biz_date); - printf(" 대상 건수 : %ld\n", total); - printf(" 완료(M) : %ld\n", done); - printf(" 불일치(U) : %ld\n", unmatch); - printf(" 오류 건수 : %ld\n", errcnt); - printf("=============================================\n"); + printf("========== 정합성 통계 집계 [vl_bt_0016] ==========\n"); + printf(" 영업일 : %s (%s)\n", biz_date, + (h_day_type[0] == 'H') ? "휴일" : "영업일"); + printf(" 대상 건수 : %ld\n", h_target); + printf(" 정합(M) : %ld\n", h_matched); + printf(" 불일치(U) : %ld\n", h_mismatch); + printf(" 정산완료(S) : %ld\n", h_settled); + printf(" 거래금액 합계 : %ld\n", h_amount_sum); + printf(" vl_stat_daily : 적재 %s\n", (errcnt == 0) ? "성공" : "실패"); + printf(" 오류 건수 : %d\n", errcnt); + printf("================================================\n"); return (errcnt == 0) ? TX_OK : TX_FAIL; } diff --git a/app/online/ac_ol_0001.c b/app/online/ac_ol_0001.c index 1b14960..caf1391 100644 --- a/app/online/ac_ol_0001.c +++ b/app/online/ac_ol_0001.c @@ -8,15 +8,16 @@ #line 1 "app/online/ac_ol_0001.pgc" /* @tier easy @module ac @transform acquire-online */ /* - * ac_ol_0001.pgc - 매입 온라인 서비스 (AC_OL_0001) [tier=easy] + * ac_ol_0001.pgc - 매입 상태조회 서비스 (AC_OL_0001) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리키(KEY)로 ac_ledger 를 단건조회하여 상태/금액/수수료/가맹점번호를 + * 반환한다. 상태코드는 화면 표시용 한글 라벨로 매핑하여 함께 내려준다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,16 +90,53 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0001.pgc" +#line 16 "app/online/ac_ol_0001.pgc" TX_SERVICE(AC_OL_0001, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + + +#line 21 "app/online/ac_ol_0001.pgc" + char h_key [ 16 ] ; + +#line 22 "app/online/ac_ol_0001.pgc" + char h_status [ 2 ] ; + +#line 23 "app/online/ac_ol_0001.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/ac_ol_0001.pgc" + char h_card_no [ 20 ] ; + +#line 25 "app/online/ac_ol_0001.pgc" + char h_biz_date [ 9 ] ; + +#line 26 "app/online/ac_ol_0001.pgc" + long h_amount ; + +#line 27 "app/online/ac_ol_0001.pgc" + long h_fee ; +/* exec sql end declare section */ +#line 28 "app/online/ac_ol_0001.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0001] 매입 단건조회 서비스 진입"); + + char key[16]; + char status_nm[16]; + char amount_won[32]; + char card_masked[20]; + char settle_due[9]; + size_t clen; + long peer_cnt; + + tx_log(TX_LOG_INFO, "[AC_OL_0001] 매입 상태조회 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0001] KEY 누락"); @@ -106,20 +144,92 @@ TX_SERVICE(AC_OL_0001, ctx) return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0001] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select status , amount , fee , merch_id , card_no , biz_date from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 53 "app/online/ac_ol_0001.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0001] 대상 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (strcmp(h_status, AC_ST_RECV) == 0) { + strncpy(status_nm, "접수", sizeof(status_nm) - 1); + } else if (strcmp(h_status, AC_ST_DONE) == 0) { + strncpy(status_nm, "처리완료", sizeof(status_nm) - 1); + } else if (strcmp(h_status, AC_ST_FAIL) == 0) { + strncpy(status_nm, "불일치", sizeof(status_nm) - 1); + } else if (strcmp(h_status, AC_ST_SETTLED) == 0) { + strncpy(status_nm, "정산완료", sizeof(status_nm) - 1); + } else { + strncpy(status_nm, "알수없음", sizeof(status_nm) - 1); + } + status_nm[sizeof(status_nm) - 1] = '\0'; + + /* 카드번호 뒷자리 마스킹 (앞 6자리만 노출) */ + clen = strlen(h_card_no); + strncpy(card_masked, h_card_no, sizeof(card_masked) - 1); + card_masked[sizeof(card_masked) - 1] = '\0'; + if (clen > 6) { + size_t j; + for (j = 6; j < clen && j < sizeof(card_masked) - 1; j++) + card_masked[j] = '*'; + } + + /* 접수 상태면 정산예정일(다음 영업일)을 함께 안내 */ + settle_due[0] = '\0'; + if (strcmp(h_status, AC_ST_RECV) == 0) { + if (date_next_business(h_biz_date, settle_due) != 0) + settle_due[0] = '\0'; + } + + amount_format_won(h_amount, amount_won, sizeof(amount_won)); + + /* 참고정보: 동일 영업일/동일 상태 건수 (표준 DBIO 재사용) */ + peer_cnt = ac_rec_count(h_biz_date, h_status); + if (peer_cnt < 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0001] 동일상태 건수조회 실패 rc=%ld", peer_cnt); + peer_cnt = 0; + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0001] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "MERCHID", h_merch_id); + tx_buf_sets(ctx->out, "CARDNO", card_masked); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_sets(ctx->out, "AMOUNTWON", amount_won); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "STATUSNM", status_nm); + tx_buf_sets(ctx->out, "BIZDATE", h_biz_date); + tx_buf_setlong(ctx->out, "PEERCNT", peer_cnt); + if (settle_due[0] != '\0') + tx_buf_sets(ctx->out, "SETTLEDUE", settle_due); + tx_log(TX_LOG_INFO, + "[AC_OL_0001] 조회완료 key=%s status=%s(%s) amount=%ld 동일상태건수=%ld", + key, h_status, status_nm, h_amount, peer_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0001.pgc b/app/online/ac_ol_0001.pgc index 6a14e7c..af81839 100644 --- a/app/online/ac_ol_0001.pgc +++ b/app/online/ac_ol_0001.pgc @@ -1,14 +1,15 @@ /* @tier easy @module ac @transform acquire-online */ /* - * ac_ol_0001.pgc - 매입 온라인 서비스 (AC_OL_0001) [tier=easy] + * ac_ol_0001.pgc - 매입 상태조회 서비스 (AC_OL_0001) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리키(KEY)로 ac_ledger 를 단건조회하여 상태/금액/수수료/가맹점번호를 + * 반환한다. 상태코드는 화면 표시용 한글 라벨로 매핑하여 함께 내려준다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,11 +17,25 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0001, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_status[2]; + char h_merch_id[16]; + char h_card_no[20]; + char h_biz_date[9]; + long h_amount; + long h_fee; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0001] 매입 단건조회 서비스 진입"); + char key[16]; + char status_nm[16]; + char amount_won[32]; + char card_masked[20]; + char settle_due[9]; + size_t clen; + long peer_cnt; + + tx_log(TX_LOG_INFO, "[AC_OL_0001] 매입 상태조회 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0001] KEY 누락"); @@ -28,20 +43,80 @@ TX_SERVICE(AC_OL_0001, ctx) return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0001] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + EXEC SQL + SELECT status, amount, fee, merch_id, card_no, biz_date + INTO :h_status, :h_amount, :h_fee, :h_merch_id, :h_card_no, :h_biz_date + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0001] 대상 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (strcmp(h_status, AC_ST_RECV) == 0) { + strncpy(status_nm, "접수", sizeof(status_nm) - 1); + } else if (strcmp(h_status, AC_ST_DONE) == 0) { + strncpy(status_nm, "처리완료", sizeof(status_nm) - 1); + } else if (strcmp(h_status, AC_ST_FAIL) == 0) { + strncpy(status_nm, "불일치", sizeof(status_nm) - 1); + } else if (strcmp(h_status, AC_ST_SETTLED) == 0) { + strncpy(status_nm, "정산완료", sizeof(status_nm) - 1); + } else { + strncpy(status_nm, "알수없음", sizeof(status_nm) - 1); + } + status_nm[sizeof(status_nm) - 1] = '\0'; + + /* 카드번호 뒷자리 마스킹 (앞 6자리만 노출) */ + clen = strlen(h_card_no); + strncpy(card_masked, h_card_no, sizeof(card_masked) - 1); + card_masked[sizeof(card_masked) - 1] = '\0'; + if (clen > 6) { + size_t j; + for (j = 6; j < clen && j < sizeof(card_masked) - 1; j++) + card_masked[j] = '*'; + } + + /* 접수 상태면 정산예정일(다음 영업일)을 함께 안내 */ + settle_due[0] = '\0'; + if (strcmp(h_status, AC_ST_RECV) == 0) { + if (date_next_business(h_biz_date, settle_due) != 0) + settle_due[0] = '\0'; + } + + amount_format_won(h_amount, amount_won, sizeof(amount_won)); + + /* 참고정보: 동일 영업일/동일 상태 건수 (표준 DBIO 재사용) */ + peer_cnt = ac_rec_count(h_biz_date, h_status); + if (peer_cnt < 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0001] 동일상태 건수조회 실패 rc=%ld", peer_cnt); + peer_cnt = 0; + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0001] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "MERCHID", h_merch_id); + tx_buf_sets(ctx->out, "CARDNO", card_masked); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_sets(ctx->out, "AMOUNTWON", amount_won); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "STATUSNM", status_nm); + tx_buf_sets(ctx->out, "BIZDATE", h_biz_date); + tx_buf_setlong(ctx->out, "PEERCNT", peer_cnt); + if (settle_due[0] != '\0') + tx_buf_sets(ctx->out, "SETTLEDUE", settle_due); + tx_log(TX_LOG_INFO, + "[AC_OL_0001] 조회완료 key=%s status=%s(%s) amount=%ld 동일상태건수=%ld", + key, h_status, status_nm, h_amount, peer_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0002.c b/app/online/ac_ol_0002.c index 33550d9..50a5018 100644 --- a/app/online/ac_ol_0002.c +++ b/app/online/ac_ol_0002.c @@ -8,15 +8,17 @@ #line 1 "app/online/ac_ol_0002.pgc" /* @tier medium @module ac @transform acquire-online */ /* - * ac_ol_0002.pgc - 매입 온라인 서비스 (AC_OL_0002) [tier=medium] + * ac_ol_0002.pgc - 매입 접수 서비스 (AC_OL_0002) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 수수료율(merchant.fee_rate)과 거래한도(limit_amt)를 조회하여 + * 수수료를 산정하고 한도초과/중복키를 반려한 뒤 접수(status='R') 상태로 + * ac_ledger 에 등록한다. 미등록/비활성 가맹점은 반려. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,22 +91,53 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0002.pgc" +#line 17 "app/online/ac_ol_0002.pgc" TX_SERVICE(AC_OL_0002, ctx) { + /* exec sql begin declare section */ + + + + + + + +#line 22 "app/online/ac_ol_0002.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/ac_ol_0002.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/ac_ol_0002.pgc" + long h_fee_rate ; + +#line 25 "app/online/ac_ol_0002.pgc" + long h_limit_amt ; + +#line 26 "app/online/ac_ol_0002.pgc" + char h_status [ 2 ] ; + +#line 27 "app/online/ac_ol_0002.pgc" + long h_dup ; +/* exec sql end declare section */ +#line 28 "app/online/ac_ol_0002.pgc" + + ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16], merch[16], card[20], bizdate[16]; + long amount = 0, fee, cnt; int rc; - tx_log(TX_LOG_INFO, "[AC_OL_0002] 매입 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[AC_OL_0002] 매입 접수 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0002] 필수 필드 누락"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -113,26 +146,92 @@ TX_SERVICE(AC_OL_0002, ctx) /* 업무검증: 금액 범위 + 영업일 형식 */ if (!amount_is_valid(amount)) { tx_log(TX_LOG_WARN, "[AC_OL_0002] 금액 범위 오류 amount=%ld", amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { tx_log(TX_LOG_WARN, "[AC_OL_0002] 영업일 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_status, "A", sizeof(h_status)); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select fee_rate , limit_amt from merchant where merch_id = $1 and status = $2 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_fee_rate),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_limit_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 72 "app/online/ac_ol_0002.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0002] 미등록/비활성 가맹점 merch=%s", merch); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT merchant.fee_rate"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (h_limit_amt > 0 && amount > h_limit_amt) { + tx_log(TX_LOG_WARN, "[AC_OL_0002] 거래한도 초과 merch=%s amount=%ld limit=%ld", + merch, amount, h_limit_amt); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_dup),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 98 "app/online/ac_ol_0002.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT dup ac_ledger"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0002] 중복 접수키 key=%s", key); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + fee = amount * h_fee_rate / 10000; + memset(&rec, 0, sizeof(rec)); strncpy(rec.key, key, sizeof(rec.key) - 1); strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); + strncpy(rec.card_no, card, sizeof(rec.card_no) - 1); strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + rec.fee = fee; rc = ac_rec_insert(&rec); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0002] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, rc, ctx->out); return; } @@ -142,7 +241,8 @@ TX_SERVICE(AC_OL_0002, ctx) tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "FEE", fee); tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0002] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_log(TX_LOG_INFO, "[AC_OL_0002] 접수완료 key=%s fee=%ld 당일건수=%ld", key, fee, cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0002.pgc b/app/online/ac_ol_0002.pgc index 5c496a8..cb1ec10 100644 --- a/app/online/ac_ol_0002.pgc +++ b/app/online/ac_ol_0002.pgc @@ -1,14 +1,16 @@ /* @tier medium @module ac @transform acquire-online */ /* - * ac_ol_0002.pgc - 매입 온라인 서비스 (AC_OL_0002) [tier=medium] + * ac_ol_0002.pgc - 매입 접수 서비스 (AC_OL_0002) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 수수료율(merchant.fee_rate)과 거래한도(limit_amt)를 조회하여 + * 수수료를 산정하고 한도초과/중복키를 반려한 뒤 접수(status='R') 상태로 + * ac_ledger 에 등록한다. 미등록/비활성 가맹점은 반려. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,17 +18,28 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0002, ctx) { + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_merch_id[16]; + long h_fee_rate; + long h_limit_amt; + char h_status[2]; + long h_dup; + EXEC SQL END DECLARE SECTION; + ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16], merch[16], card[20], bizdate[16]; + long amount = 0, fee, cnt; int rc; - tx_log(TX_LOG_INFO, "[AC_OL_0002] 매입 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[AC_OL_0002] 매입 접수 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0002] 필수 필드 누락"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -35,26 +48,83 @@ TX_SERVICE(AC_OL_0002, ctx) /* 업무검증: 금액 범위 + 영업일 형식 */ if (!amount_is_valid(amount)) { tx_log(TX_LOG_WARN, "[AC_OL_0002] 금액 범위 오류 amount=%ld", amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { tx_log(TX_LOG_WARN, "[AC_OL_0002] 영업일 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_status, "A", sizeof(h_status)); + + EXEC SQL + SELECT fee_rate, limit_amt INTO :h_fee_rate, :h_limit_amt + FROM merchant + WHERE merch_id = :h_merch_id + AND status = :h_status; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0002] 미등록/비활성 가맹점 merch=%s", merch); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT merchant.fee_rate"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (h_limit_amt > 0 && amount > h_limit_amt) { + tx_log(TX_LOG_WARN, "[AC_OL_0002] 거래한도 초과 merch=%s amount=%ld limit=%ld", + merch, amount, h_limit_amt); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL + SELECT count(*) INTO :h_dup + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT dup ac_ledger"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0002] 중복 접수키 key=%s", key); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + fee = amount * h_fee_rate / 10000; + memset(&rec, 0, sizeof(rec)); strncpy(rec.key, key, sizeof(rec.key) - 1); strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); + strncpy(rec.card_no, card, sizeof(rec.card_no) - 1); strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + rec.fee = fee; rc = ac_rec_insert(&rec); if (rc != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0002] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, rc, ctx->out); return; } @@ -64,7 +134,8 @@ TX_SERVICE(AC_OL_0002, ctx) tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "FEE", fee); tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0002] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_log(TX_LOG_INFO, "[AC_OL_0002] 접수완료 key=%s fee=%ld 당일건수=%ld", key, fee, cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0003.c b/app/online/ac_ol_0003.c index 589d639..b6d9787 100644 --- a/app/online/ac_ol_0003.c +++ b/app/online/ac_ol_0003.c @@ -8,15 +8,17 @@ #line 1 "app/online/ac_ol_0003.pgc" /* @tier easy @module ac @transform acquire-online */ /* - * ac_ol_0003.pgc - 매입 온라인 서비스 (AC_OL_0003) [tier=easy] + * ac_ol_0003.pgc - DDC 매입 서비스 (직불카드 즉시매입, AC_OL_0003) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 직불카드(DDC)는 승인과 동시에 매입이 확정되므로 접수 단계 없이 바로 + * 처리완료(status='M') 상태로 ac_ledger 에 적재한다. 가맹점 활성 여부와 + * 접수키 중복을 사전 검증한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +91,181 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0003.pgc" +#line 17 "app/online/ac_ol_0003.pgc" TX_SERVICE(AC_OL_0003, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + + + + + +#line 22 "app/online/ac_ol_0003.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/ac_ol_0003.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/ac_ol_0003.pgc" + char h_card_no [ 20 ] ; + +#line 25 "app/online/ac_ol_0003.pgc" + long h_amount ; + +#line 26 "app/online/ac_ol_0003.pgc" + char h_biz_date [ 9 ] ; + +#line 27 "app/online/ac_ol_0003.pgc" + char h_status [ 2 ] ; + +#line 28 "app/online/ac_ol_0003.pgc" + long h_fee ; + +#line 29 "app/online/ac_ol_0003.pgc" + char h_acq_type [ 4 ] ; + +#line 30 "app/online/ac_ol_0003.pgc" + long h_merch_active ; + +#line 31 "app/online/ac_ol_0003.pgc" + long h_dup ; +/* exec sql end declare section */ +#line 32 "app/online/ac_ol_0003.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0003] 매입 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0003] KEY 누락"); + char key[16], merch[16], card[20], bizdate[16]; + long amount = 0; + + tx_log(TX_LOG_INFO, "[AC_OL_0003] DDC 매입 서비스 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0003] 필수 필드 누락"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + if (!amount_is_valid(amount)) { + tx_log(TX_LOG_WARN, "[AC_OL_0003] 금액 범위 오류 amount=%ld", amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[AC_OL_0003] 영업일 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from merchant where merch_id = $1 and status = 'A'", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_merch_active),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 72 "app/online/ac_ol_0003.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT merchant.status(DDC)"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_merch_active <= 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0003] 미등록/비활성 가맹점 merch=%s", merch); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_dup),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 90 "app/online/ac_ol_0003.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT dup ac_ledger(DDC)"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0003] 중복 처리키 key=%s", key); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + strncpy(h_card_no, card, sizeof(h_card_no) - 1); + h_card_no[sizeof(h_card_no) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, AC_ST_DONE, sizeof(h_status)); + strncpy(h_acq_type, "DDC", sizeof(h_acq_type) - 1); + h_acq_type[sizeof(h_acq_type) - 1] = '\0'; + h_amount = amount; + h_fee = amount * 20 / 10000; /* 직불 수수료 0.20% */ + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[AC_OL_0003] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into ac_ledger ( key , merch_id , card_no , amount , biz_date , status , fee , acq_type ) values ( $1 , $2 , $3 , $4 , $5 , $6 , $7 , $8 )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_acq_type),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 123 "app/online/ac_ol_0003.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("INSERT ac_ledger(DDC)"); + tx_abort(); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_log(TX_LOG_INFO, "[AC_OL_0003] DDC 즉시매입 완료 key=%s amount=%ld fee=%ld", + key, amount, h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0003.pgc b/app/online/ac_ol_0003.pgc index 2bdcd73..012045c 100644 --- a/app/online/ac_ol_0003.pgc +++ b/app/online/ac_ol_0003.pgc @@ -1,14 +1,16 @@ /* @tier easy @module ac @transform acquire-online */ /* - * ac_ol_0003.pgc - 매입 온라인 서비스 (AC_OL_0003) [tier=easy] + * ac_ol_0003.pgc - DDC 매입 서비스 (직불카드 즉시매입, AC_OL_0003) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 직불카드(DDC)는 승인과 동시에 매입이 확정되므로 접수 단계 없이 바로 + * 처리완료(status='M') 상태로 ac_ledger 에 적재한다. 가맹점 활성 여부와 + * 접수키 중복을 사전 검증한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +18,126 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0003, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_merch_id[16]; + char h_card_no[20]; + long h_amount; + char h_biz_date[9]; + char h_status[2]; + long h_fee; + char h_acq_type[4]; + long h_merch_active; + long h_dup; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0003] 매입 단건조회 서비스 진입"); + char key[16], merch[16], card[20], bizdate[16]; + long amount = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0003] KEY 누락"); + tx_log(TX_LOG_INFO, "[AC_OL_0003] DDC 매입 서비스 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0003] 필수 필드 누락"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + if (!amount_is_valid(amount)) { + tx_log(TX_LOG_WARN, "[AC_OL_0003] 금액 범위 오류 amount=%ld", amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[AC_OL_0003] 영업일 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + + EXEC SQL + SELECT count(*) INTO :h_merch_active + FROM merchant + WHERE merch_id = :h_merch_id + AND status = 'A'; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT merchant.status(DDC)"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_merch_active <= 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0003] 미등록/비활성 가맹점 merch=%s", merch); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + EXEC SQL + SELECT count(*) INTO :h_dup + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT dup ac_ledger(DDC)"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0003] 중복 처리키 key=%s", key); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + strncpy(h_card_no, card, sizeof(h_card_no) - 1); + h_card_no[sizeof(h_card_no) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, AC_ST_DONE, sizeof(h_status)); + strncpy(h_acq_type, "DDC", sizeof(h_acq_type) - 1); + h_acq_type[sizeof(h_acq_type) - 1] = '\0'; + h_amount = amount; + h_fee = amount * 20 / 10000; /* 직불 수수료 0.20% */ + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[AC_OL_0003] tx_begin 경고"); + + EXEC SQL + INSERT INTO ac_ledger + (key, merch_id, card_no, amount, biz_date, status, fee, acq_type) + VALUES + (:h_key, :h_merch_id, :h_card_no, :h_amount, :h_biz_date, + :h_status, :h_fee, :h_acq_type); + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("INSERT ac_ledger(DDC)"); + tx_abort(); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_log(TX_LOG_INFO, "[AC_OL_0003] DDC 즉시매입 완료 key=%s amount=%ld fee=%ld", + key, amount, h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0004.c b/app/online/ac_ol_0004.c index 8d8f89c..c61c294 100644 --- a/app/online/ac_ol_0004.c +++ b/app/online/ac_ol_0004.c @@ -8,15 +8,16 @@ #line 1 "app/online/ac_ol_0004.pgc" /* @tier medium @module ac @transform acquire-online */ /* - * ac_ol_0004.pgc - 매입 온라인 서비스 (AC_OL_0004) [tier=medium] + * ac_ol_0004.pgc - EDC 매입 서비스 (전자매출전표 매입, AC_OL_0004) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인번호(APPROVALNO)로 approval 원장을 조회하여 승인완료 건만 매입 + * 처리하고, approval_no 를 연결키로 ac_ledger 에 acq_type='EDC' 로 적재한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,60 +90,169 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0004.pgc" +#line 16 "app/online/ac_ol_0004.pgc" TX_SERVICE(AC_OL_0004, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + + + + + + +#line 21 "app/online/ac_ol_0004.pgc" + char h_appr_no [ 9 ] ; + +#line 22 "app/online/ac_ol_0004.pgc" + long h_amount ; + +#line 23 "app/online/ac_ol_0004.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/ac_ol_0004.pgc" + char h_card_no [ 20 ] ; + +#line 25 "app/online/ac_ol_0004.pgc" + char h_appr_status [ 2 ] ; + +#line 26 "app/online/ac_ol_0004.pgc" + char h_key [ 16 ] ; + +#line 27 "app/online/ac_ol_0004.pgc" + char h_biz_date [ 9 ] ; + +#line 28 "app/online/ac_ol_0004.pgc" + char h_status [ 2 ] ; + +#line 29 "app/online/ac_ol_0004.pgc" + long h_fee ; + +#line 30 "app/online/ac_ol_0004.pgc" + char h_acq_type [ 4 ] ; +/* exec sql end declare section */ +#line 31 "app/online/ac_ol_0004.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0004] 매입 등록 서비스 진입"); + + char key[16], apprno[9], bizdate[16]; + + tx_log(TX_LOG_INFO, "[AC_OL_0004] EDC 매입 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "APPROVALNO", apprno, sizeof(apprno)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0004] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AC_OL_0004] 금액 범위 오류 amount=%ld", amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { tx_log(TX_LOG_WARN, "[AC_OL_0004] 영업일 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_appr_no, apprno, sizeof(h_appr_no) - 1); + h_appr_no[sizeof(h_appr_no) - 1] = '\0'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0004] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , merch_id , card_no , appr_status from approval where approval_no = $1 ", + ECPGt_char,(h_appr_no),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_appr_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 59 "app/online/ac_ol_0004.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0004] 승인전표 없음 approvalno=%s", apprno); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT approval"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); + if (strcmp(h_appr_status, "A") != 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0004] 승인 미완료 approvalno=%s status=%s", + apprno, h_appr_status); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + if (!amount_is_valid(h_amount)) { + tx_log(TX_LOG_WARN, "[AC_OL_0004] 승인금액 범위 오류 amount=%ld", h_amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, AC_ST_DONE, sizeof(h_status)); + strncpy(h_acq_type, "EDC", sizeof(h_acq_type) - 1); + h_acq_type[sizeof(h_acq_type) - 1] = '\0'; + h_fee = h_amount * 15 / 10000; /* EDC 수수료 0.15% */ + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[AC_OL_0004] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into ac_ledger ( key , merch_id , card_no , amount , biz_date , status , fee , acq_type , approval_no ) values ( $1 , $2 , $3 , $4 , $5 , $6 , $7 , $8 , $9 )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_acq_type),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_appr_no),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 106 "app/online/ac_ol_0004.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("INSERT ac_ledger(EDC)"); + tx_abort(); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0004] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_log(TX_LOG_INFO, "[AC_OL_0004] EDC 매입 완료 key=%s approvalno=%s fee=%ld", + key, apprno, h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0004.pgc b/app/online/ac_ol_0004.pgc index 64f4dfc..f446cf4 100644 --- a/app/online/ac_ol_0004.pgc +++ b/app/online/ac_ol_0004.pgc @@ -1,14 +1,15 @@ /* @tier medium @module ac @transform acquire-online */ /* - * ac_ol_0004.pgc - 매입 온라인 서비스 (AC_OL_0004) [tier=medium] + * ac_ol_0004.pgc - EDC 매입 서비스 (전자매출전표 매입, AC_OL_0004) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인번호(APPROVALNO)로 approval 원장을 조회하여 승인완료 건만 매입 + * 처리하고, approval_no 를 연결키로 ac_ledger 에 acq_type='EDC' 로 적재한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,55 +17,110 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0004, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_appr_no[9]; + long h_amount; + char h_merch_id[16]; + char h_card_no[20]; + char h_appr_status[2]; + char h_key[16]; + char h_biz_date[9]; + char h_status[2]; + long h_fee; + char h_acq_type[4]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0004] 매입 등록 서비스 진입"); + char key[16], apprno[9], bizdate[16]; + + tx_log(TX_LOG_INFO, "[AC_OL_0004] EDC 매입 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "APPROVALNO", apprno, sizeof(apprno)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0004] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AC_OL_0004] 금액 범위 오류 amount=%ld", amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { tx_log(TX_LOG_WARN, "[AC_OL_0004] 영업일 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_appr_no, apprno, sizeof(h_appr_no) - 1); + h_appr_no[sizeof(h_appr_no) - 1] = '\0'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0004] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL + SELECT amount, merch_id, card_no, appr_status + INTO :h_amount, :h_merch_id, :h_card_no, :h_appr_status + FROM approval + WHERE approval_no = :h_appr_no; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0004] 승인전표 없음 approvalno=%s", apprno); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT approval"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); + if (strcmp(h_appr_status, "A") != 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0004] 승인 미완료 approvalno=%s status=%s", + apprno, h_appr_status); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + if (!amount_is_valid(h_amount)) { + tx_log(TX_LOG_WARN, "[AC_OL_0004] 승인금액 범위 오류 amount=%ld", h_amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, AC_ST_DONE, sizeof(h_status)); + strncpy(h_acq_type, "EDC", sizeof(h_acq_type) - 1); + h_acq_type[sizeof(h_acq_type) - 1] = '\0'; + h_fee = h_amount * 15 / 10000; /* EDC 수수료 0.15% */ + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[AC_OL_0004] tx_begin 경고"); + + EXEC SQL + INSERT INTO ac_ledger + (key, merch_id, card_no, amount, biz_date, status, fee, + acq_type, approval_no) + VALUES + (:h_key, :h_merch_id, :h_card_no, :h_amount, :h_biz_date, + :h_status, :h_fee, :h_acq_type, :h_appr_no); + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("INSERT ac_ledger(EDC)"); + tx_abort(); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0004] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_log(TX_LOG_INFO, "[AC_OL_0004] EDC 매입 완료 key=%s approvalno=%s fee=%ld", + key, apprno, h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0005.c b/app/online/ac_ol_0005.c index d138a24..36618ae 100644 --- a/app/online/ac_ol_0005.c +++ b/app/online/ac_ol_0005.c @@ -8,15 +8,17 @@ #line 1 "app/online/ac_ol_0005.pgc" /* @tier easy @module ac @transform acquire-online */ /* - * ac_ol_0005.pgc - 매입 온라인 서비스 (AC_OL_0005) [tier=easy] + * ac_ol_0005.pgc - 매입 취소 서비스 (AC_OL_0005) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리키(KEY)로 현재 상태를 조회하여 이미 정산완료(S)/이미 취소(U)된 + * 건은 반려하고, 그 외는 취소(status='U')로 전이시킨다. 취소 사유코드는 + * 로그로 남기고, 처리 후 당일 취소건수를 함께 안내한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +91,133 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0005.pgc" +#line 17 "app/online/ac_ol_0005.pgc" TX_SERVICE(AC_OL_0005, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 22 "app/online/ac_ol_0005.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/ac_ol_0005.pgc" + char h_status [ 2 ] ; + +#line 24 "app/online/ac_ol_0005.pgc" + long h_amount ; + +#line 25 "app/online/ac_ol_0005.pgc" + char h_biz_date [ 9 ] ; + +#line 26 "app/online/ac_ol_0005.pgc" + char h_cancel_status [ 2 ] ; +/* exec sql end declare section */ +#line 27 "app/online/ac_ol_0005.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0005] 매입 단건조회 서비스 진입"); + + char key[16]; + char reason[32]; + long cancel_cnt; + + tx_log(TX_LOG_INFO, "[AC_OL_0005] 매입 취소 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0005] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK || + reason[0] == '\0') { + strncpy(reason, "미상", sizeof(reason) - 1); + reason[sizeof(reason) - 1] = '\0'; + tx_log(TX_LOG_DEBUG, "[AC_OL_0005] 취소사유 미지정 key=%s 기본값 적용", key); + } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0005] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select status , amount , biz_date from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 54 "app/online/ac_ol_0005.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0005] 대상 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT status ac_ledger"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (strcmp(h_status, AC_ST_SETTLED) == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0005] 이미 정산완료 취소불가 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "CANCELLED", "N"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + if (strcmp(h_status, AC_ST_FAIL) == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0005] 이미 취소된 건 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "CANCELLED", "N"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + strncpy(h_cancel_status, AC_ST_FAIL, sizeof(h_cancel_status)); + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[AC_OL_0005] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set status = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_char,(h_cancel_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 93 "app/online/ac_ol_0005.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger(취소)"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 참고정보: 당일 취소건수 (표준 DBIO 재사용) */ + cancel_cnt = ac_rec_count(h_biz_date, AC_ST_FAIL); + if (cancel_cnt < 0) + cancel_cnt = 0; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0005] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_cancel_status); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_sets(ctx->out, "CANCELLED", "Y"); + tx_buf_sets(ctx->out, "REASON", reason); + tx_buf_setlong(ctx->out, "DAYCANCELCNT", cancel_cnt); + tx_log(TX_LOG_INFO, + "[AC_OL_0005] 매입 취소완료 key=%s 이전상태=%s 사유=%s 당일취소건수=%ld", + key, h_status, reason, cancel_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0005.pgc b/app/online/ac_ol_0005.pgc index 19c2499..223382b 100644 --- a/app/online/ac_ol_0005.pgc +++ b/app/online/ac_ol_0005.pgc @@ -1,14 +1,16 @@ /* @tier easy @module ac @transform acquire-online */ /* - * ac_ol_0005.pgc - 매입 온라인 서비스 (AC_OL_0005) [tier=easy] + * ac_ol_0005.pgc - 매입 취소 서비스 (AC_OL_0005) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리키(KEY)로 현재 상태를 조회하여 이미 정산완료(S)/이미 취소(U)된 + * 건은 반려하고, 그 외는 취소(status='U')로 전이시킨다. 취소 사유코드는 + * 로그로 남기고, 처리 후 당일 취소건수를 함께 안내한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +18,103 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0005, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_status[2]; + long h_amount; + char h_biz_date[9]; + char h_cancel_status[2]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0005] 매입 단건조회 서비스 진입"); + char key[16]; + char reason[32]; + long cancel_cnt; + + tx_log(TX_LOG_INFO, "[AC_OL_0005] 매입 취소 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0005] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK || + reason[0] == '\0') { + strncpy(reason, "미상", sizeof(reason) - 1); + reason[sizeof(reason) - 1] = '\0'; + tx_log(TX_LOG_DEBUG, "[AC_OL_0005] 취소사유 미지정 key=%s 기본값 적용", key); + } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0005] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + EXEC SQL + SELECT status, amount, biz_date + INTO :h_status, :h_amount, :h_biz_date + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0005] 대상 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT status ac_ledger"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (strcmp(h_status, AC_ST_SETTLED) == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0005] 이미 정산완료 취소불가 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "CANCELLED", "N"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + if (strcmp(h_status, AC_ST_FAIL) == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0005] 이미 취소된 건 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "CANCELLED", "N"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + strncpy(h_cancel_status, AC_ST_FAIL, sizeof(h_cancel_status)); + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[AC_OL_0005] tx_begin 경고"); + + EXEC SQL + UPDATE ac_ledger + SET status = :h_cancel_status, + upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger(취소)"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 참고정보: 당일 취소건수 (표준 DBIO 재사용) */ + cancel_cnt = ac_rec_count(h_biz_date, AC_ST_FAIL); + if (cancel_cnt < 0) + cancel_cnt = 0; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0005] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_cancel_status); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_sets(ctx->out, "CANCELLED", "Y"); + tx_buf_sets(ctx->out, "REASON", reason); + tx_buf_setlong(ctx->out, "DAYCANCELCNT", cancel_cnt); + tx_log(TX_LOG_INFO, + "[AC_OL_0005] 매입 취소완료 key=%s 이전상태=%s 사유=%s 당일취소건수=%ld", + key, h_status, reason, cancel_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0006.c b/app/online/ac_ol_0006.c index ae1852c..f128c57 100644 --- a/app/online/ac_ol_0006.c +++ b/app/online/ac_ol_0006.c @@ -8,15 +8,17 @@ #line 1 "app/online/ac_ol_0006.pgc" /* @tier hard @module ac @transform acquire-online */ /* - * ac_ol_0006.pgc - 매입 온라인 서비스 (AC_OL_0006) [tier=hard] + * ac_ol_0006.pgc - 매입 정정 서비스 (AC_OL_0006) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원거래 금액/수수료를 조회하고 가맹점 수수료율로 신규 수수료를 재산정한 + * 뒤 ac_ledger 원장을 정정(UPDATE)하고, 정정 이력행(acq_type='CORR', + * orig_key 연결)을 별도로 적재한다. 전 과정을 하나의 트랜잭션으로 처리. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,88 +91,201 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0006.pgc" +#line 17 "app/online/ac_ol_0006.pgc" TX_SERVICE(AC_OL_0006, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + + + + + + + + +#line 22 "app/online/ac_ol_0006.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/ac_ol_0006.pgc" + long h_old_amount ; + +#line 24 "app/online/ac_ol_0006.pgc" + long h_old_fee ; + +#line 25 "app/online/ac_ol_0006.pgc" + char h_merch_id [ 16 ] ; + +#line 26 "app/online/ac_ol_0006.pgc" + char h_card_no [ 20 ] ; + +#line 27 "app/online/ac_ol_0006.pgc" + char h_biz_date [ 9 ] ; + +#line 28 "app/online/ac_ol_0006.pgc" + long h_new_amount ; + +#line 29 "app/online/ac_ol_0006.pgc" + long h_new_fee ; + +#line 30 "app/online/ac_ol_0006.pgc" + long h_fee_rate ; + +#line 31 "app/online/ac_ol_0006.pgc" + char h_corr_key [ 16 ] ; + +#line 32 "app/online/ac_ol_0006.pgc" + char h_status [ 2 ] ; +/* exec sql end declare section */ +#line 33 "app/online/ac_ol_0006.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0006] 매입 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0006] 필수 필드 누락"); + char key[16]; + long new_amount = 0; + size_t klen; + + tx_log(TX_LOG_INFO, "[AC_OL_0006] 매입 정정 서비스 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0006] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0006] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (tx_buf_getlong(ctx->in, "NEWAMOUNT", &new_amount) != TX_OK || + !amount_is_valid(new_amount)) { + tx_log(TX_LOG_WARN, "[AC_OL_0006] 정정금액 오류 amount=%ld", new_amount); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + h_new_amount = new_amount; + if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; + tx_log(TX_LOG_WARN, "[AC_OL_0006] tx_begin 경고"); } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , fee , merch_id , card_no , biz_date from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_old_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_old_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 65 "app/online/ac_ol_0006.pgc" - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0006] 원장반영 실패 rc=%d", rc); + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0006] 원거래 없음 key=%s", key); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger(정정대상)"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select fee_rate from merchant where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_fee_rate),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 83 "app/online/ac_ol_0006.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0006] 가맹점 없음 merch=%s", h_merch_id); + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT merchant.fee_rate(정정)"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AC_OL_0006] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); + h_new_fee = h_new_amount * h_fee_rate / 10000; - /* 교차 검증: 당일 합계 재계산 */ - if (ac_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set amount = $1 , fee = $2 , upd_ts = now ( ) where key = $3 ", + ECPGt_long,&(h_new_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_new_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 105 "app/online/ac_ol_0006.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger(정정)"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 정정 이력키 = 원키 앞부분 13자 + "-C" (16바이트 한도 준수) */ + klen = strlen(h_key); + if (klen > 13) + klen = 13; + memcpy(h_corr_key, h_key, klen); + h_corr_key[klen] = '\0'; + strcat(h_corr_key, "-C"); + strncpy(h_status, AC_ST_DONE, sizeof(h_status)); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into ac_ledger ( key , merch_id , card_no , amount , biz_date , status , fee , acq_type , orig_key ) values ( $1 , $2 , $3 , $4 , $5 , $6 , $7 , 'CORR' , $8 )", + ECPGt_char,(h_corr_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_new_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_new_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 129 "app/online/ac_ol_0006.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("INSERT ac_ledger(정정이력)"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AC_OL_0006] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_setlong(ctx->out, "OLDAMOUNT", h_old_amount); + tx_buf_setlong(ctx->out, "NEWAMOUNT", h_new_amount); + tx_buf_setlong(ctx->out, "OLDFEE", h_old_fee); + tx_buf_setlong(ctx->out, "NEWFEE", h_new_fee); + tx_log(TX_LOG_INFO, + "[AC_OL_0006] 정정완료 key=%s 구금액=%ld 신금액=%ld 구수수료=%ld 신수수료=%ld", + key, h_old_amount, h_new_amount, h_old_fee, h_new_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0006.pgc b/app/online/ac_ol_0006.pgc index 3df3999..a0a4dc1 100644 --- a/app/online/ac_ol_0006.pgc +++ b/app/online/ac_ol_0006.pgc @@ -1,14 +1,16 @@ /* @tier hard @module ac @transform acquire-online */ /* - * ac_ol_0006.pgc - 매입 온라인 서비스 (AC_OL_0006) [tier=hard] + * ac_ol_0006.pgc - 매입 정정 서비스 (AC_OL_0006) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원거래 금액/수수료를 조회하고 가맹점 수수료율로 신규 수수료를 재산정한 + * 뒤 ac_ledger 원장을 정정(UPDATE)하고, 정정 이력행(acq_type='CORR', + * orig_key 연결)을 별도로 적재한다. 전 과정을 하나의 트랜잭션으로 처리. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,83 +18,133 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0006, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_old_amount; + long h_old_fee; + char h_merch_id[16]; + char h_card_no[20]; + char h_biz_date[9]; + long h_new_amount; + long h_new_fee; + long h_fee_rate; + char h_corr_key[16]; + char h_status[2]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0006] 매입 트랜잭션 오케스트레이션 진입"); + char key[16]; + long new_amount = 0; + size_t klen; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0006] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[AC_OL_0006] 매입 정정 서비스 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0006] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0006] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (tx_buf_getlong(ctx->in, "NEWAMOUNT", &new_amount) != TX_OK || + !amount_is_valid(new_amount)) { + tx_log(TX_LOG_WARN, "[AC_OL_0006] 정정금액 오류 amount=%ld", new_amount); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + h_new_amount = new_amount; + if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; + tx_log(TX_LOG_WARN, "[AC_OL_0006] tx_begin 경고"); } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + EXEC SQL + SELECT amount, fee, merch_id, card_no, biz_date + INTO :h_old_amount, :h_old_fee, :h_merch_id, :h_card_no, :h_biz_date + FROM ac_ledger + WHERE key = :h_key; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0006] 원장반영 실패 rc=%d", rc); + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0006] 원거래 없음 key=%s", key); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger(정정대상)"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + EXEC SQL + SELECT fee_rate INTO :h_fee_rate + FROM merchant + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0006] 가맹점 없음 merch=%s", h_merch_id); + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT merchant.fee_rate(정정)"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AC_OL_0006] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); + h_new_fee = h_new_amount * h_fee_rate / 10000; - /* 교차 검증: 당일 합계 재계산 */ - if (ac_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; + EXEC SQL + UPDATE ac_ledger + SET amount = :h_new_amount, + fee = :h_new_fee, + upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger(정정)"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 정정 이력키 = 원키 앞부분 13자 + "-C" (16바이트 한도 준수) */ + klen = strlen(h_key); + if (klen > 13) + klen = 13; + memcpy(h_corr_key, h_key, klen); + h_corr_key[klen] = '\0'; + strcat(h_corr_key, "-C"); + strncpy(h_status, AC_ST_DONE, sizeof(h_status)); + + EXEC SQL + INSERT INTO ac_ledger + (key, merch_id, card_no, amount, biz_date, status, fee, + acq_type, orig_key) + VALUES + (:h_corr_key, :h_merch_id, :h_card_no, :h_new_amount, :h_biz_date, + :h_status, :h_new_fee, 'CORR', :h_key); + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("INSERT ac_ledger(정정이력)"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AC_OL_0006] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_setlong(ctx->out, "OLDAMOUNT", h_old_amount); + tx_buf_setlong(ctx->out, "NEWAMOUNT", h_new_amount); + tx_buf_setlong(ctx->out, "OLDFEE", h_old_fee); + tx_buf_setlong(ctx->out, "NEWFEE", h_new_fee); + tx_log(TX_LOG_INFO, + "[AC_OL_0006] 정정완료 key=%s 구금액=%ld 신금액=%ld 구수수료=%ld 신수수료=%ld", + key, h_old_amount, h_new_amount, h_old_fee, h_new_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0007.c b/app/online/ac_ol_0007.c index 8cac8d5..0d2c133 100644 --- a/app/online/ac_ol_0007.c +++ b/app/online/ac_ol_0007.c @@ -8,15 +8,17 @@ #line 1 "app/online/ac_ol_0007.pgc" /* @tier easy @module ac @transform acquire-online */ /* - * ac_ol_0007.pgc - 매입 온라인 서비스 (AC_OL_0007) [tier=easy] + * ac_ol_0007.pgc - 부분매입 서비스 (AC_OL_0007) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원거래 금액 중 일부(CAPAMT)만 매입 처리한다. 원장의 잔여금액을 차감 + * 갱신하고, 부분매입 내역행(acq_type='PART', orig_key 연결)을 별도 적재 + * 한 뒤 남은 잔여금액을 반환한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +91,166 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0007.pgc" +#line 17 "app/online/ac_ol_0007.pgc" TX_SERVICE(AC_OL_0007, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + + + + +#line 22 "app/online/ac_ol_0007.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/ac_ol_0007.pgc" + long h_orig_amount ; + +#line 24 "app/online/ac_ol_0007.pgc" + char h_merch_id [ 16 ] ; + +#line 25 "app/online/ac_ol_0007.pgc" + char h_card_no [ 20 ] ; + +#line 26 "app/online/ac_ol_0007.pgc" + char h_biz_date [ 9 ] ; + +#line 27 "app/online/ac_ol_0007.pgc" + long h_cap_amount ; + +#line 28 "app/online/ac_ol_0007.pgc" + long h_remaining ; + +#line 29 "app/online/ac_ol_0007.pgc" + char h_part_key [ 16 ] ; + +#line 30 "app/online/ac_ol_0007.pgc" + char h_status [ 2 ] ; +/* exec sql end declare section */ +#line 31 "app/online/ac_ol_0007.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0007] 매입 단건조회 서비스 진입"); + + char key[16]; + long capamt = 0; + size_t klen; + + tx_log(TX_LOG_INFO, "[AC_OL_0007] 부분매입 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0007] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0007] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (tx_buf_getlong(ctx->in, "CAPAMT", &capamt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0007] CAPAMT 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , merch_id , card_no , biz_date from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_orig_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 57 "app/online/ac_ol_0007.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0007] 원거래 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger(부분매입대상)"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (!amount_is_valid(capamt) || capamt > h_orig_amount) { + tx_log(TX_LOG_WARN, "[AC_OL_0007] 부분금액 오류 cap=%ld orig=%ld", + capamt, h_orig_amount); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + h_cap_amount = capamt; + h_remaining = h_orig_amount - capamt; + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[AC_OL_0007] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set amount = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_long,&(h_remaining),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 87 "app/online/ac_ol_0007.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger(부분매입)"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 부분매입 내역키 = 원키 앞부분 13자 + "-P" (16바이트 한도 준수) */ + klen = strlen(h_key); + if (klen > 13) + klen = 13; + memcpy(h_part_key, h_key, klen); + h_part_key[klen] = '\0'; + strcat(h_part_key, "-P"); + strncpy(h_status, AC_ST_DONE, sizeof(h_status)); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into ac_ledger ( key , merch_id , card_no , amount , biz_date , status , fee , acq_type , orig_key ) values ( $1 , $2 , $3 , $4 , $5 , $6 , 0 , 'PART' , $7 )", + ECPGt_char,(h_part_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cap_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 111 "app/online/ac_ol_0007.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("INSERT ac_ledger(부분매입내역)"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0007] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "CAPAMT", capamt); + tx_buf_setlong(ctx->out, "REMAINING", h_remaining); + tx_log(TX_LOG_INFO, "[AC_OL_0007] 부분매입 완료 key=%s cap=%ld 잔여=%ld", + key, capamt, h_remaining); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0007.pgc b/app/online/ac_ol_0007.pgc index 560d318..93c0732 100644 --- a/app/online/ac_ol_0007.pgc +++ b/app/online/ac_ol_0007.pgc @@ -1,14 +1,16 @@ /* @tier easy @module ac @transform acquire-online */ /* - * ac_ol_0007.pgc - 매입 온라인 서비스 (AC_OL_0007) [tier=easy] + * ac_ol_0007.pgc - 부분매입 서비스 (AC_OL_0007) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원거래 금액 중 일부(CAPAMT)만 매입 처리한다. 원장의 잔여금액을 차감 + * 갱신하고, 부분매입 내역행(acq_type='PART', orig_key 연결)을 별도 적재 + * 한 뒤 남은 잔여금액을 반환한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +18,112 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0007, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_orig_amount; + char h_merch_id[16]; + char h_card_no[20]; + char h_biz_date[9]; + long h_cap_amount; + long h_remaining; + char h_part_key[16]; + char h_status[2]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0007] 매입 단건조회 서비스 진입"); + char key[16]; + long capamt = 0; + size_t klen; + + tx_log(TX_LOG_INFO, "[AC_OL_0007] 부분매입 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0007] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0007] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (tx_buf_getlong(ctx->in, "CAPAMT", &capamt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0007] CAPAMT 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + EXEC SQL + SELECT amount, merch_id, card_no, biz_date + INTO :h_orig_amount, :h_merch_id, :h_card_no, :h_biz_date + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0007] 원거래 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger(부분매입대상)"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (!amount_is_valid(capamt) || capamt > h_orig_amount) { + tx_log(TX_LOG_WARN, "[AC_OL_0007] 부분금액 오류 cap=%ld orig=%ld", + capamt, h_orig_amount); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + h_cap_amount = capamt; + h_remaining = h_orig_amount - capamt; + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[AC_OL_0007] tx_begin 경고"); + + EXEC SQL + UPDATE ac_ledger + SET amount = :h_remaining, + upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger(부분매입)"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 부분매입 내역키 = 원키 앞부분 13자 + "-P" (16바이트 한도 준수) */ + klen = strlen(h_key); + if (klen > 13) + klen = 13; + memcpy(h_part_key, h_key, klen); + h_part_key[klen] = '\0'; + strcat(h_part_key, "-P"); + strncpy(h_status, AC_ST_DONE, sizeof(h_status)); + + EXEC SQL + INSERT INTO ac_ledger + (key, merch_id, card_no, amount, biz_date, status, fee, + acq_type, orig_key) + VALUES + (:h_part_key, :h_merch_id, :h_card_no, :h_cap_amount, :h_biz_date, + :h_status, 0, 'PART', :h_key); + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("INSERT ac_ledger(부분매입내역)"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0007] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "CAPAMT", capamt); + tx_buf_setlong(ctx->out, "REMAINING", h_remaining); + tx_log(TX_LOG_INFO, "[AC_OL_0007] 부분매입 완료 key=%s cap=%ld 잔여=%ld", + key, capamt, h_remaining); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0008.c b/app/online/ac_ol_0008.c index bef05f8..f4c6875 100644 --- a/app/online/ac_ol_0008.c +++ b/app/online/ac_ol_0008.c @@ -8,15 +8,17 @@ #line 1 "app/online/ac_ol_0008.pgc" /* @tier hard @module ac @transform acquire-online */ /* - * ac_ol_0008.pgc - 매입 온라인 서비스 (AC_OL_0008) [tier=hard] + * ac_ol_0008.pgc - 할부매입 서비스 (AC_OL_0008) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 총 매입금액을 개월수(MONTHS, install_month>=2)로 분할하여 월별 + * ac_ledger 행(키: KEY-01..KEY-NN, acq_type='INST')을 순차 INSERT 한다. + * 나머지 금액은 1회차에 몰아준다. 전 과정은 하나의 트랜잭션으로 처리. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,28 +91,71 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0008.pgc" +#line 17 "app/online/ac_ol_0008.pgc" TX_SERVICE(AC_OL_0008, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + + + + + + +#line 22 "app/online/ac_ol_0008.pgc" + char h_merch_id [ 16 ] ; + +#line 23 "app/online/ac_ol_0008.pgc" + char h_card_no [ 20 ] ; + +#line 24 "app/online/ac_ol_0008.pgc" + char h_biz_date [ 9 ] ; + +#line 25 "app/online/ac_ol_0008.pgc" + char h_status [ 2 ] ; + +#line 26 "app/online/ac_ol_0008.pgc" + char h_acq_type [ 4 ] ; + +#line 27 "app/online/ac_ol_0008.pgc" + char h_row_key [ 16 ] ; + +#line 28 "app/online/ac_ol_0008.pgc" + long h_row_amount ; + +#line 29 "app/online/ac_ol_0008.pgc" + long h_row_fee ; + +#line 30 "app/online/ac_ol_0008.pgc" + int h_install_month ; +/* exec sql end declare section */ +#line 31 "app/online/ac_ol_0008.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0008] 매입 트랜잭션 오케스트레이션 진입"); + + char key[16], merch[16], card[20], bizdate[16]; + char base_key[16]; + long amount = 0, months_l = 0; + long monthly, remainder, row_amt, totalfee = 0; + int months, i, errcnt = 0; + size_t klen; + + tx_log(TX_LOG_INFO, "[AC_OL_0008] 할부매입 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0008] 필수 필드 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "MONTHS", &months_l); if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { tx_log(TX_LOG_WARN, "[AC_OL_0008] 입력 검증 실패 amount=%ld date=%s", @@ -118,59 +163,91 @@ TX_SERVICE(AC_OL_0008, ctx) tx_return(ctx, TX_EINVAL, ctx->out); return; } - - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + if (months_l < 2) { + tx_log(TX_LOG_WARN, "[AC_OL_0008] 할부개월 오류 months=%ld", months_l); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + months = (int)months_l; - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + monthly = amount / months; + remainder = amount - (monthly * months); - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0008] 원장반영 실패 rc=%d", rc); + /* 원거래키 앞부분 12자 + "-NN" (16바이트 한도 준수) */ + klen = strlen(key); + if (klen > 12) + klen = 12; + memcpy(base_key, key, klen); + base_key[klen] = '\0'; + + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_card_no, card, sizeof(h_card_no) - 1); + h_card_no[sizeof(h_card_no) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, AC_ST_RECV, sizeof(h_status)); + strncpy(h_acq_type, "INST", sizeof(h_acq_type) - 1); + h_acq_type[sizeof(h_acq_type) - 1] = '\0'; + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[AC_OL_0008] tx_begin 경고"); + + for (i = 1; i <= months; i++) { + row_amt = (i == 1) ? (monthly + remainder) : monthly; + + snprintf(h_row_key, sizeof(h_row_key), "%s-%02d", base_key, i); + h_row_amount = row_amt; + h_row_fee = row_amt * 25 / 10000; /* 할부 수수료 0.25% */ + h_install_month = i; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into ac_ledger ( key , merch_id , card_no , amount , biz_date , status , fee , acq_type , install_month ) values ( $1 , $2 , $3 , $4 , $5 , $6 , $7 , $8 , $9 )", + ECPGt_char,(h_row_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_row_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_row_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_acq_type),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_install_month),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 103 "app/online/ac_ol_0008.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("INSERT ac_ledger(할부회차)"); + errcnt++; + break; + } + + totalfee += h_row_fee; + } + + if (errcnt > 0) { tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AC_OL_0008] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (ac_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AC_OL_0008] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_setlong(ctx->out, "MONTHCNT", months); + tx_buf_setlong(ctx->out, "FIRSTAMT", monthly + remainder); + tx_buf_setlong(ctx->out, "MONTHLYAMT", monthly); + tx_buf_setlong(ctx->out, "TOTALFEE", totalfee); + tx_log(TX_LOG_INFO, + "[AC_OL_0008] 할부매입 완료 key=%s 총액=%ld 개월=%d 1회차=%ld 월분=%ld", + key, amount, months, monthly + remainder, monthly); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0008.pgc b/app/online/ac_ol_0008.pgc index 8fbc128..a4dc157 100644 --- a/app/online/ac_ol_0008.pgc +++ b/app/online/ac_ol_0008.pgc @@ -1,14 +1,16 @@ /* @tier hard @module ac @transform acquire-online */ /* - * ac_ol_0008.pgc - 매입 온라인 서비스 (AC_OL_0008) [tier=hard] + * ac_ol_0008.pgc - 할부매입 서비스 (AC_OL_0008) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 총 매입금액을 개월수(MONTHS, install_month>=2)로 분할하여 월별 + * ac_ledger 행(키: KEY-01..KEY-NN, acq_type='INST')을 순차 INSERT 한다. + * 나머지 금액은 1회차에 몰아준다. 전 과정은 하나의 트랜잭션으로 처리. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,23 +18,37 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0008, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_card_no[20]; + char h_biz_date[9]; + char h_status[2]; + char h_acq_type[4]; + char h_row_key[16]; + long h_row_amount; + long h_row_fee; + int h_install_month; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0008] 매입 트랜잭션 오케스트레이션 진입"); + char key[16], merch[16], card[20], bizdate[16]; + char base_key[16]; + long amount = 0, months_l = 0; + long monthly, remainder, row_amt, totalfee = 0; + int months, i, errcnt = 0; + size_t klen; + + tx_log(TX_LOG_INFO, "[AC_OL_0008] 할부매입 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0008] 필수 필드 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "MONTHS", &months_l); if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { tx_log(TX_LOG_WARN, "[AC_OL_0008] 입력 검증 실패 amount=%ld date=%s", @@ -40,59 +56,77 @@ TX_SERVICE(AC_OL_0008, ctx) tx_return(ctx, TX_EINVAL, ctx->out); return; } - - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + if (months_l < 2) { + tx_log(TX_LOG_WARN, "[AC_OL_0008] 할부개월 오류 months=%ld", months_l); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + months = (int)months_l; - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + monthly = amount / months; + remainder = amount - (monthly * months); - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0008] 원장반영 실패 rc=%d", rc); + /* 원거래키 앞부분 12자 + "-NN" (16바이트 한도 준수) */ + klen = strlen(key); + if (klen > 12) + klen = 12; + memcpy(base_key, key, klen); + base_key[klen] = '\0'; + + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_card_no, card, sizeof(h_card_no) - 1); + h_card_no[sizeof(h_card_no) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, AC_ST_RECV, sizeof(h_status)); + strncpy(h_acq_type, "INST", sizeof(h_acq_type) - 1); + h_acq_type[sizeof(h_acq_type) - 1] = '\0'; + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[AC_OL_0008] tx_begin 경고"); + + for (i = 1; i <= months; i++) { + row_amt = (i == 1) ? (monthly + remainder) : monthly; + + snprintf(h_row_key, sizeof(h_row_key), "%s-%02d", base_key, i); + h_row_amount = row_amt; + h_row_fee = row_amt * 25 / 10000; /* 할부 수수료 0.25% */ + h_install_month = i; + + EXEC SQL + INSERT INTO ac_ledger + (key, merch_id, card_no, amount, biz_date, status, fee, + acq_type, install_month) + VALUES + (:h_row_key, :h_merch_id, :h_card_no, :h_row_amount, :h_biz_date, + :h_status, :h_row_fee, :h_acq_type, :h_install_month); + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("INSERT ac_ledger(할부회차)"); + errcnt++; + break; + } + + totalfee += h_row_fee; + } + + if (errcnt > 0) { tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AC_OL_0008] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (ac_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AC_OL_0008] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_setlong(ctx->out, "MONTHCNT", months); + tx_buf_setlong(ctx->out, "FIRSTAMT", monthly + remainder); + tx_buf_setlong(ctx->out, "MONTHLYAMT", monthly); + tx_buf_setlong(ctx->out, "TOTALFEE", totalfee); + tx_log(TX_LOG_INFO, + "[AC_OL_0008] 할부매입 완료 key=%s 총액=%ld 개월=%d 1회차=%ld 월분=%ld", + key, amount, months, monthly + remainder, monthly); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0009.c b/app/online/ac_ol_0009.c index 79213cc..af9a8ac 100644 --- a/app/online/ac_ol_0009.c +++ b/app/online/ac_ol_0009.c @@ -10,13 +10,15 @@ /* * ac_ol_0009.pgc - 매입 온라인 서비스 (AC_OL_0009) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 해외매입 환산: 외화금액(소수 2자리 포함 정수, 즉 외화금액*100)을 + * fx_rate 고시환율로 원화 환산한다. KEY 가 함께 오면 ac_ledger 원장에 + * 환산결과(원화금액/외화금액/통화코드)를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +91,130 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0009.pgc" +#line 17 "app/online/ac_ol_0009.pgc" TX_SERVICE(AC_OL_0009, ctx) { - ac_rec_t rec; + /* exec sql begin declare section */ + + + + + + + +#line 22 "app/online/ac_ol_0009.pgc" + char h_cur [ 4 ] ; + +#line 23 "app/online/ac_ol_0009.pgc" + char h_bizdate [ 9 ] ; + +#line 24 "app/online/ac_ol_0009.pgc" + long h_fxamt ; + +#line 25 "app/online/ac_ol_0009.pgc" + long h_rate ; + +#line 26 "app/online/ac_ol_0009.pgc" + char h_key [ 16 ] ; + +#line 27 "app/online/ac_ol_0009.pgc" + long h_krw ; +/* exec sql end declare section */ +#line 28 "app/online/ac_ol_0009.pgc" + + + char curcode[4]; + char bizdate[9]; char key[16]; - int rc; + long fxamt = 0; + long krw; + int has_key; - tx_log(TX_LOG_INFO, "[AC_OL_0009] 매입 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[AC_OL_0009] 해외매입 환산 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0009] KEY 누락"); + if (tx_buf_get(ctx->in, "CURCODE", curcode, sizeof(curcode)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0009] 필수 필드 누락(CURCODE/BIZDATE)"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "FXAMT", &fxamt); + + if (curcode[0] == '\0' || !date_is_valid(bizdate) || !amount_is_valid(fxamt)) { + tx_log(TX_LOG_WARN, "[AC_OL_0009] 입력 검증 실패 cur=%s date=%s fxamt=%ld", + curcode, bizdate, fxamt); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0009] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_cur, curcode, sizeof(h_cur) - 1); + h_cur[sizeof(h_cur) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select base_rate from fx_rate where cur_code = $1 and rate_date = $2 ", + ECPGt_char,(h_cur),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_rate),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 62 "app/online/ac_ol_0009.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0009] 고시환율 없음 cur=%s date=%s", curcode, bizdate); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0009 fx_rate select"); + tx_return(ctx, TX_EDB, ctx->out); return; } + h_fxamt = fxamt; + krw = (h_fxamt * h_rate) / 10000; /* base_rate 는 10000 배 스케일 고시값 */ + + /* KEY 가 함께 오면 원장에 환산결과 반영 (조회성 호출이면 생략 가능) */ + has_key = (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) == TX_OK && key[0] != '\0'); + if (has_key) { + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + h_krw = krw; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set amount = $1 , fx_amount = $2 , cur_code = $3 , upd_ts = now ( ) where key = $4 ", + ECPGt_long,&(h_krw),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fxamt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_cur),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 87 "app/online/ac_ol_0009.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0009 ac_ledger update"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) + tx_log(TX_LOG_WARN, "[AC_OL_0009] 반영대상 원장 없음 key=%s", key); + else + tx_log(TX_LOG_INFO, "[AC_OL_0009] 원장반영 완료 key=%s krw=%ld", key, krw); + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0009] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "CURCODE", curcode); + tx_buf_setlong(ctx->out, "FXAMT", fxamt); + tx_buf_setlong(ctx->out, "RATE", h_rate); + tx_buf_setlong(ctx->out, "KRWAMT", krw); + tx_log(TX_LOG_INFO, "[AC_OL_0009] 환산완료 cur=%s fxamt=%ld rate=%ld krw=%ld", + curcode, fxamt, h_rate, krw); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0009.pgc b/app/online/ac_ol_0009.pgc index f6b65ef..3b3195c 100644 --- a/app/online/ac_ol_0009.pgc +++ b/app/online/ac_ol_0009.pgc @@ -2,13 +2,15 @@ /* * ac_ol_0009.pgc - 매입 온라인 서비스 (AC_OL_0009) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 해외매입 환산: 외화금액(소수 2자리 포함 정수, 즉 외화금액*100)을 + * fx_rate 고시환율로 원화 환산한다. KEY 가 함께 오면 ac_ledger 원장에 + * 환산결과(원화금액/외화금액/통화코드)를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +18,92 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0009, ctx) { - ac_rec_t rec; + EXEC SQL BEGIN DECLARE SECTION; + char h_cur[4]; + char h_bizdate[9]; + long h_fxamt; + long h_rate; + char h_key[16]; + long h_krw; + EXEC SQL END DECLARE SECTION; + + char curcode[4]; + char bizdate[9]; char key[16]; - int rc; + long fxamt = 0; + long krw; + int has_key; - tx_log(TX_LOG_INFO, "[AC_OL_0009] 매입 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[AC_OL_0009] 해외매입 환산 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0009] KEY 누락"); + if (tx_buf_get(ctx->in, "CURCODE", curcode, sizeof(curcode)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0009] 필수 필드 누락(CURCODE/BIZDATE)"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "FXAMT", &fxamt); + + if (curcode[0] == '\0' || !date_is_valid(bizdate) || !amount_is_valid(fxamt)) { + tx_log(TX_LOG_WARN, "[AC_OL_0009] 입력 검증 실패 cur=%s date=%s fxamt=%ld", + curcode, bizdate, fxamt); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0009] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_cur, curcode, sizeof(h_cur) - 1); + h_cur[sizeof(h_cur) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + + EXEC SQL SELECT base_rate + INTO :h_rate + FROM fx_rate + WHERE cur_code = :h_cur AND rate_date = :h_bizdate; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0009] 고시환율 없음 cur=%s date=%s", curcode, bizdate); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0009 fx_rate select"); + tx_return(ctx, TX_EDB, ctx->out); return; } + h_fxamt = fxamt; + krw = (h_fxamt * h_rate) / 10000; /* base_rate 는 10000 배 스케일 고시값 */ + + /* KEY 가 함께 오면 원장에 환산결과 반영 (조회성 호출이면 생략 가능) */ + has_key = (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) == TX_OK && key[0] != '\0'); + if (has_key) { + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + h_krw = krw; + + EXEC SQL UPDATE ac_ledger + SET amount = :h_krw, fx_amount = :h_fxamt, cur_code = :h_cur, upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0009 ac_ledger update"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) + tx_log(TX_LOG_WARN, "[AC_OL_0009] 반영대상 원장 없음 key=%s", key); + else + tx_log(TX_LOG_INFO, "[AC_OL_0009] 원장반영 완료 key=%s krw=%ld", key, krw); + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0009] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "CURCODE", curcode); + tx_buf_setlong(ctx->out, "FXAMT", fxamt); + tx_buf_setlong(ctx->out, "RATE", h_rate); + tx_buf_setlong(ctx->out, "KRWAMT", krw); + tx_log(TX_LOG_INFO, "[AC_OL_0009] 환산완료 cur=%s fxamt=%ld rate=%ld krw=%ld", + curcode, fxamt, h_rate, krw); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0010.c b/app/online/ac_ol_0010.c index 3316013..2dda87e 100644 --- a/app/online/ac_ol_0010.c +++ b/app/online/ac_ol_0010.c @@ -10,13 +10,14 @@ /* * ac_ol_0010.pgc - 매입 온라인 서비스 (AC_OL_0010) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 한도체크: 가맹점의 당일 누적매입액(미확정 제외)에 신규 매입금액을 + * 더해 merchant.limit_amt 를 넘는지 검사한다. 초과 시 TX_FAIL 로 거절한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +90,120 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0010.pgc" +#line 16 "app/online/ac_ol_0010.pgc" TX_SERVICE(AC_OL_0010, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 21 "app/online/ac_ol_0010.pgc" + char h_merch [ 16 ] ; + +#line 22 "app/online/ac_ol_0010.pgc" + char h_today [ 9 ] ; + +#line 23 "app/online/ac_ol_0010.pgc" + long h_limit ; + +#line 24 "app/online/ac_ol_0010.pgc" + long h_used ; +/* exec sql end declare section */ +#line 25 "app/online/ac_ol_0010.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0010] 매입 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0010] KEY 누락"); + char merch[16]; + char today[9]; + long amount = 0; + long remain; + int over; + + tx_log(TX_LOG_INFO, "[AC_OL_0010] 매입 한도체크 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0010] MERCHID 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + if (merch[0] == '\0' || !amount_is_valid(amount)) { + tx_log(TX_LOG_WARN, "[AC_OL_0010] 입력 검증 실패 merch=%s amount=%ld", merch, amount); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch, merch, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select limit_amt from merchant where merch_id = $1 ", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_limit),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 54 "app/online/ac_ol_0010.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0010] 가맹점 없음 merch=%s", merch); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0010 merchant select"); + tx_return(ctx, TX_EDB, ctx->out); return; } + date_today(today); + strncpy(h_today, today, sizeof(h_today) - 1); + h_today[sizeof(h_today) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from ac_ledger where merch_id = $1 and biz_date = $2 and status <> 'U'", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_today),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_used),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 78 "app/online/ac_ol_0010.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0010 ac_ledger sum select"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + over = (h_used + amount > h_limit) ? 1 : 0; + remain = h_limit - h_used - amount; + if (remain < 0) + remain = 0; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + if (over) { + tx_log(TX_LOG_WARN, "[AC_OL_0010] 한도초과 merch=%s used=%ld amount=%ld limit=%ld", + merch, h_used, amount, h_limit); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "OVER", 1); + tx_buf_setlong(ctx->out, "LIMIT", h_limit); + tx_buf_setlong(ctx->out, "USED", h_used); + tx_buf_setlong(ctx->out, "REMAIN", 0); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_setlong(ctx->out, "OVER", 0); + tx_buf_setlong(ctx->out, "LIMIT", h_limit); + tx_buf_setlong(ctx->out, "USED", h_used); + tx_buf_setlong(ctx->out, "REMAIN", remain); + tx_log(TX_LOG_INFO, "[AC_OL_0010] 한도체크 통과 merch=%s remain=%ld", merch, remain); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0010.pgc b/app/online/ac_ol_0010.pgc index 570359a..c8f99ff 100644 --- a/app/online/ac_ol_0010.pgc +++ b/app/online/ac_ol_0010.pgc @@ -2,13 +2,14 @@ /* * ac_ol_0010.pgc - 매입 온라인 서비스 (AC_OL_0010) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 한도체크: 가맹점의 당일 누적매입액(미확정 제외)에 신규 매입금액을 + * 더해 merchant.limit_amt 를 넘는지 검사한다. 초과 시 TX_FAIL 로 거절한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +17,95 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0010, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16]; + char h_today[9]; + long h_limit; + long h_used; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0010] 매입 단건조회 서비스 진입"); + char merch[16]; + char today[9]; + long amount = 0; + long remain; + int over; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0010] KEY 누락"); + tx_log(TX_LOG_INFO, "[AC_OL_0010] 매입 한도체크 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0010] MERCHID 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + if (merch[0] == '\0' || !amount_is_valid(amount)) { + tx_log(TX_LOG_WARN, "[AC_OL_0010] 입력 검증 실패 merch=%s amount=%ld", merch, amount); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch, merch, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + + EXEC SQL SELECT limit_amt + INTO :h_limit + FROM merchant + WHERE merch_id = :h_merch; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0010] 가맹점 없음 merch=%s", merch); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0010 merchant select"); + tx_return(ctx, TX_EDB, ctx->out); return; } + date_today(today); + strncpy(h_today, today, sizeof(h_today) - 1); + h_today[sizeof(h_today) - 1] = '\0'; + + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_used + FROM ac_ledger + WHERE merch_id = :h_merch + AND biz_date = :h_today + AND status <> 'U'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0010 ac_ledger sum select"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + over = (h_used + amount > h_limit) ? 1 : 0; + remain = h_limit - h_used - amount; + if (remain < 0) + remain = 0; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + if (over) { + tx_log(TX_LOG_WARN, "[AC_OL_0010] 한도초과 merch=%s used=%ld amount=%ld limit=%ld", + merch, h_used, amount, h_limit); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "OVER", 1); + tx_buf_setlong(ctx->out, "LIMIT", h_limit); + tx_buf_setlong(ctx->out, "USED", h_used); + tx_buf_setlong(ctx->out, "REMAIN", 0); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_setlong(ctx->out, "OVER", 0); + tx_buf_setlong(ctx->out, "LIMIT", h_limit); + tx_buf_setlong(ctx->out, "USED", h_used); + tx_buf_setlong(ctx->out, "REMAIN", remain); + tx_log(TX_LOG_INFO, "[AC_OL_0010] 한도체크 통과 merch=%s remain=%ld", merch, remain); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0011.c b/app/online/ac_ol_0011.c index 1d3e3a1..db85fe9 100644 --- a/app/online/ac_ol_0011.c +++ b/app/online/ac_ol_0011.c @@ -10,13 +10,15 @@ /* * ac_ol_0011.pgc - 매입 온라인 서비스 (AC_OL_0011) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 중복검사: 동일 가맹점/카드/금액/영업일 조합이 이미 유효 상태로 + * 존재하는지 검사한다. 중복이면 기존 원장키를 조회해 함께 반환하고 + * TX_FAIL 로 경보한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,28 +91,59 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0011.pgc" +#line 17 "app/online/ac_ol_0011.pgc" TX_SERVICE(AC_OL_0011, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 22 "app/online/ac_ol_0011.pgc" + char h_merch [ 16 ] ; + +#line 23 "app/online/ac_ol_0011.pgc" + char h_card [ 20 ] ; + +#line 24 "app/online/ac_ol_0011.pgc" + long h_amount ; + +#line 25 "app/online/ac_ol_0011.pgc" + char h_bizdate [ 9 ] ; + +#line 26 "app/online/ac_ol_0011.pgc" + int h_cnt ; + +#line 27 "app/online/ac_ol_0011.pgc" + char h_exist_key [ 16 ] ; +/* exec sql end declare section */ +#line 28 "app/online/ac_ol_0011.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0011] 매입 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + char merch[16]; + char card[20]; + char bizdate[9]; + long amount = 0; + long dup; + char exist_key[16]; + int found_exist; + + tx_log(TX_LOG_INFO, "[AC_OL_0011] 매입 중복검사 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0011] 필수 필드 누락"); + tx_log(TX_LOG_ERROR, "[AC_OL_0011] 필수 필드 누락(MERCHID/CARDNO/BIZDATE)"); tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); - /* 업무검증: 금액 범위 + 영업일 형식 */ if (!amount_is_valid(amount)) { tx_log(TX_LOG_WARN, "[AC_OL_0011] 금액 범위 오류 amount=%ld", amount); tx_return(ctx, TX_EINVAL, ctx->out); @@ -122,27 +155,78 @@ TX_SERVICE(AC_OL_0011, ctx) return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_merch, merch, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + strncpy(h_card, card, sizeof(h_card) - 1); + h_card[sizeof(h_card) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + h_amount = amount; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0011] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from ac_ledger where merch_id = $1 and card_no = $2 and amount = $3 and biz_date = $4 and status <> 'U'", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 75 "app/online/ac_ol_0011.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0011 dup count select"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + dup = (long)h_cnt; + + tx_buf_reset(ctx->out); + if (dup > 0) { + /* 중복건 중 하나의 기존 원장키를 참고용으로 조회한다 */ + found_exist = 0; + exist_key[0] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select key from ac_ledger where merch_id = $1 and card_no = $2 and amount = $3 and biz_date = $4 and status <> 'U' limit 1", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_exist_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 98 "app/online/ac_ol_0011.pgc" + + + if (sqlca.sqlcode == TX_SQL_OK) { + found_exist = 1; + strncpy(exist_key, h_exist_key, sizeof(exist_key) - 1); + exist_key[sizeof(exist_key) - 1] = '\0'; + } else if (sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("AC_OL_0011 exist key select"); + } + + tx_log(TX_LOG_WARN, "[AC_OL_0011] 중복매입 감지 merch=%s card=%s amount=%ld date=%s cnt=%ld", + merch, card, amount, bizdate, dup); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "DUP", 1); + tx_buf_setlong(ctx->out, "DUPCNT", dup); + if (found_exist) + tx_buf_sets(ctx->out, "EXISTKEY", exist_key); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); - - tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0011] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "DUP", 0); + tx_buf_setlong(ctx->out, "DUPCNT", 0); + tx_log(TX_LOG_INFO, "[AC_OL_0011] 중복없음 merch=%s card=%s amount=%ld date=%s", + merch, card, amount, bizdate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0011.pgc b/app/online/ac_ol_0011.pgc index eb7a024..934eb91 100644 --- a/app/online/ac_ol_0011.pgc +++ b/app/online/ac_ol_0011.pgc @@ -2,13 +2,15 @@ /* * ac_ol_0011.pgc - 매입 온라인 서비스 (AC_OL_0011) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 중복검사: 동일 가맹점/카드/금액/영업일 조합이 이미 유효 상태로 + * 존재하는지 검사한다. 중복이면 기존 원장키를 조회해 함께 반환하고 + * TX_FAIL 로 경보한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,23 +18,34 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0011, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16]; + char h_card[20]; + long h_amount; + char h_bizdate[9]; + int h_cnt; + char h_exist_key[16]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0011] 매입 등록 서비스 진입"); + char merch[16]; + char card[20]; + char bizdate[9]; + long amount = 0; + long dup; + char exist_key[16]; + int found_exist; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_log(TX_LOG_INFO, "[AC_OL_0011] 매입 중복검사 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0011] 필수 필드 누락"); + tx_log(TX_LOG_ERROR, "[AC_OL_0011] 필수 필드 누락(MERCHID/CARDNO/BIZDATE)"); tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); - /* 업무검증: 금액 범위 + 영업일 형식 */ if (!amount_is_valid(amount)) { tx_log(TX_LOG_WARN, "[AC_OL_0011] 금액 범위 오류 amount=%ld", amount); tx_return(ctx, TX_EINVAL, ctx->out); @@ -44,27 +57,69 @@ TX_SERVICE(AC_OL_0011, ctx) return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_merch, merch, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + strncpy(h_card, card, sizeof(h_card) - 1); + h_card[sizeof(h_card) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + h_amount = amount; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0011] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL SELECT count(*) + INTO :h_cnt + FROM ac_ledger + WHERE merch_id = :h_merch + AND card_no = :h_card + AND amount = :h_amount + AND biz_date = :h_bizdate + AND status <> 'U'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0011 dup count select"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + dup = (long)h_cnt; + + tx_buf_reset(ctx->out); + if (dup > 0) { + /* 중복건 중 하나의 기존 원장키를 참고용으로 조회한다 */ + found_exist = 0; + exist_key[0] = '\0'; + + EXEC SQL SELECT key + INTO :h_exist_key + FROM ac_ledger + WHERE merch_id = :h_merch + AND card_no = :h_card + AND amount = :h_amount + AND biz_date = :h_bizdate + AND status <> 'U' + LIMIT 1; + + if (sqlca.sqlcode == TX_SQL_OK) { + found_exist = 1; + strncpy(exist_key, h_exist_key, sizeof(exist_key) - 1); + exist_key[sizeof(exist_key) - 1] = '\0'; + } else if (sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("AC_OL_0011 exist key select"); + } + + tx_log(TX_LOG_WARN, "[AC_OL_0011] 중복매입 감지 merch=%s card=%s amount=%ld date=%s cnt=%ld", + merch, card, amount, bizdate, dup); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "DUP", 1); + tx_buf_setlong(ctx->out, "DUPCNT", dup); + if (found_exist) + tx_buf_sets(ctx->out, "EXISTKEY", exist_key); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); - - tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0011] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "DUP", 0); + tx_buf_setlong(ctx->out, "DUPCNT", 0); + tx_log(TX_LOG_INFO, "[AC_OL_0011] 중복없음 merch=%s card=%s amount=%ld date=%s", + merch, card, amount, bizdate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0012.c b/app/online/ac_ol_0012.c index bc605b7..5223c2b 100644 --- a/app/online/ac_ol_0012.c +++ b/app/online/ac_ol_0012.c @@ -10,13 +10,15 @@ /* * ac_ol_0012.pgc - 매입 온라인 서비스 (AC_OL_0012) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 수수료 조정: KEY 원장의 수수료를 재계산한다. NEWRATE(bp)가 오면 + * 해당 요율을, 없으면 merchant.fee_rate 를 사용해 신규 수수료를 산출하고 + * ac_ledger.fee 를 갱신한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +91,130 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0012.pgc" +#line 17 "app/online/ac_ol_0012.pgc" TX_SERVICE(AC_OL_0012, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 22 "app/online/ac_ol_0012.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/ac_ol_0012.pgc" + long h_amount ; + +#line 24 "app/online/ac_ol_0012.pgc" + char h_merch [ 16 ] ; + +#line 25 "app/online/ac_ol_0012.pgc" + long h_oldfee ; + +#line 26 "app/online/ac_ol_0012.pgc" + long h_rate ; + +#line 27 "app/online/ac_ol_0012.pgc" + long h_newfee ; +/* exec sql end declare section */ +#line 28 "app/online/ac_ol_0012.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0012] 매입 단건조회 서비스 진입"); + + char key[16]; + long newrate = 0; + int has_newrate; + + tx_log(TX_LOG_INFO, "[AC_OL_0012] 매입 수수료 조정 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0012] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + has_newrate = (tx_buf_getlong(ctx->in, "NEWRATE", &newrate) == TX_OK && newrate > 0); - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , merch_id , fee from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_oldfee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 49 "app/online/ac_ol_0012.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0012] 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0012 ac_ledger select"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (has_newrate) { + h_rate = newrate; + } else { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select fee_rate from merchant where merch_id = $1 ", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_rate),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 68 "app/online/ac_ol_0012.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0012] 가맹점 요율 없음 merch=%s", h_merch); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0012 merchant select"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + } + + h_newfee = (h_amount * h_rate) / 10000; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set fee = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_long,&(h_newfee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 86 "app/online/ac_ol_0012.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0012 ac_ledger update"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0012] 수수료 갱신대상 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "RATE", h_rate); + tx_buf_setlong(ctx->out, "OLDFEE", h_oldfee); + tx_buf_setlong(ctx->out, "NEWFEE", h_newfee); + tx_log(TX_LOG_INFO, "[AC_OL_0012] 수수료조정 완료 key=%s old=%ld new=%ld rate=%ld", + key, h_oldfee, h_newfee, h_rate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0012.pgc b/app/online/ac_ol_0012.pgc index 818cd4e..23596c5 100644 --- a/app/online/ac_ol_0012.pgc +++ b/app/online/ac_ol_0012.pgc @@ -2,13 +2,15 @@ /* * ac_ol_0012.pgc - 매입 온라인 서비스 (AC_OL_0012) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 수수료 조정: KEY 원장의 수수료를 재계산한다. NEWRATE(bp)가 오면 + * 해당 요율을, 없으면 merchant.fee_rate 를 사용해 신규 수수료를 산출하고 + * ac_ledger.fee 를 갱신한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +18,91 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0012, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_amount; + char h_merch[16]; + long h_oldfee; + long h_rate; + long h_newfee; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0012] 매입 단건조회 서비스 진입"); + char key[16]; + long newrate = 0; + int has_newrate; + + tx_log(TX_LOG_INFO, "[AC_OL_0012] 매입 수수료 조정 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0012] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + has_newrate = (tx_buf_getlong(ctx->in, "NEWRATE", &newrate) == TX_OK && newrate > 0); - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + EXEC SQL SELECT amount, merch_id, fee + INTO :h_amount, :h_merch, :h_oldfee + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0012] 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0012 ac_ledger select"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (has_newrate) { + h_rate = newrate; + } else { + EXEC SQL SELECT fee_rate + INTO :h_rate + FROM merchant + WHERE merch_id = :h_merch; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0012] 가맹점 요율 없음 merch=%s", h_merch); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0012 merchant select"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + } + + h_newfee = (h_amount * h_rate) / 10000; + + EXEC SQL UPDATE ac_ledger + SET fee = :h_newfee, upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0012 ac_ledger update"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0012] 수수료 갱신대상 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "RATE", h_rate); + tx_buf_setlong(ctx->out, "OLDFEE", h_oldfee); + tx_buf_setlong(ctx->out, "NEWFEE", h_newfee); + tx_log(TX_LOG_INFO, "[AC_OL_0012] 수수료조정 완료 key=%s old=%ld new=%ld rate=%ld", + key, h_oldfee, h_newfee, h_rate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0013.c b/app/online/ac_ol_0013.c index a70b857..468684e 100644 --- a/app/online/ac_ol_0013.c +++ b/app/online/ac_ol_0013.c @@ -10,13 +10,15 @@ /* * ac_ol_0013.pgc - 매입 온라인 서비스 (AC_OL_0013) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 선매입(정산일 이전 조기매입): 접수 상태(R) 원장만 조기매입 대상으로 + * 인정한다. 거래일 다음 영업일을 조기 정산목표일로 계산해 상태를 + * 처리완료(M)로 전환하고 정산목표일을 원장에 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,60 +91,130 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0013.pgc" +#line 17 "app/online/ac_ol_0013.pgc" TX_SERVICE(AC_OL_0013, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 22 "app/online/ac_ol_0013.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/ac_ol_0013.pgc" + char h_bizdate [ 9 ] ; + +#line 24 "app/online/ac_ol_0013.pgc" + char h_status [ 2 ] ; + +#line 25 "app/online/ac_ol_0013.pgc" + char h_new_status [ 2 ] ; + +#line 26 "app/online/ac_ol_0013.pgc" + char h_settle [ 9 ] ; +/* exec sql end declare section */ +#line 27 "app/online/ac_ol_0013.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0013] 매입 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0013] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char key[16]; + char bizdate[9]; + char status[2]; + char settle[9]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AC_OL_0013] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0013] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[AC_OL_0013] 선매입(조기매입) 서비스 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0013] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0013] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select biz_date , status from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 48 "app/online/ac_ol_0013.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0013] 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0013 ac_ledger select"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); + strncpy(bizdate, h_bizdate, sizeof(bizdate) - 1); + bizdate[sizeof(bizdate) - 1] = '\0'; + strncpy(status, h_status, sizeof(status) - 1); + status[sizeof(status) - 1] = '\0'; + + if (strcmp(status, AC_ST_RECV) != 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0013] 조기매입 불가 상태 key=%s status=%s", key, status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "ADVANCED", 0); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 조기 정산목표일 = 거래일의 다음 영업일 */ + if (date_next_business(bizdate, settle) != 0) { + strncpy(settle, bizdate, sizeof(settle) - 1); + settle[sizeof(settle) - 1] = '\0'; + } + + strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status) - 1); + h_new_status[sizeof(h_new_status) - 1] = '\0'; + strncpy(h_settle, settle, sizeof(h_settle) - 1); + h_settle[sizeof(h_settle) - 1] = '\0'; + + /* 낙관적 갱신: 조회 시점과 동일한 상태(R)일 때만 전환한다 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set status = $1 , settle_date = $2 , upd_ts = now ( ) where key = $3 and status = $4 ", + ECPGt_char,(h_new_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_settle),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 89 "app/online/ac_ol_0013.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0013 ac_ledger update"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0013] 동시성 충돌로 전환 실패 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "ADVANCED", 0); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0013] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "ADVANCED", 1); + tx_buf_sets(ctx->out, "SETTLEDATE", settle); + tx_log(TX_LOG_INFO, "[AC_OL_0013] 조기매입 완료 key=%s settle=%s", key, settle); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0013.pgc b/app/online/ac_ol_0013.pgc index 0bad136..1f03f81 100644 --- a/app/online/ac_ol_0013.pgc +++ b/app/online/ac_ol_0013.pgc @@ -2,13 +2,15 @@ /* * ac_ol_0013.pgc - 매입 온라인 서비스 (AC_OL_0013) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 선매입(정산일 이전 조기매입): 접수 상태(R) 원장만 조기매입 대상으로 + * 인정한다. 거래일 다음 영업일을 조기 정산목표일로 계산해 상태를 + * 처리완료(M)로 전환하고 정산목표일을 원장에 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,55 +18,95 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0013, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_bizdate[9]; + char h_status[2]; + char h_new_status[2]; + char h_settle[9]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0013] 매입 등록 서비스 진입"); + char key[16]; + char bizdate[9]; + char status[2]; + char settle[9]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0013] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[AC_OL_0013] 선매입(조기매입) 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AC_OL_0013] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0013] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0013] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0013] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL SELECT biz_date, status + INTO :h_bizdate, :h_status + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0013] 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0013 ac_ledger select"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); + strncpy(bizdate, h_bizdate, sizeof(bizdate) - 1); + bizdate[sizeof(bizdate) - 1] = '\0'; + strncpy(status, h_status, sizeof(status) - 1); + status[sizeof(status) - 1] = '\0'; + + if (strcmp(status, AC_ST_RECV) != 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0013] 조기매입 불가 상태 key=%s status=%s", key, status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "ADVANCED", 0); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 조기 정산목표일 = 거래일의 다음 영업일 */ + if (date_next_business(bizdate, settle) != 0) { + strncpy(settle, bizdate, sizeof(settle) - 1); + settle[sizeof(settle) - 1] = '\0'; + } + + strncpy(h_new_status, AC_ST_DONE, sizeof(h_new_status) - 1); + h_new_status[sizeof(h_new_status) - 1] = '\0'; + strncpy(h_settle, settle, sizeof(h_settle) - 1); + h_settle[sizeof(h_settle) - 1] = '\0'; + + /* 낙관적 갱신: 조회 시점과 동일한 상태(R)일 때만 전환한다 */ + EXEC SQL UPDATE ac_ledger + SET status = :h_new_status, settle_date = :h_settle, upd_ts = now() + WHERE key = :h_key AND status = :h_status; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0013 ac_ledger update"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0013] 동시성 충돌로 전환 실패 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "ADVANCED", 0); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0013] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "ADVANCED", 1); + tx_buf_sets(ctx->out, "SETTLEDATE", settle); + tx_log(TX_LOG_INFO, "[AC_OL_0013] 조기매입 완료 key=%s settle=%s", key, settle); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0014.c b/app/online/ac_ol_0014.c index 027cae8..5ee8425 100644 --- a/app/online/ac_ol_0014.c +++ b/app/online/ac_ol_0014.c @@ -10,13 +10,15 @@ /* * ac_ol_0014.pgc - 매입 온라인 서비스 (AC_OL_0014) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점별 매입집계: 지정 가맹점의 지정 영업일 처리완료(M) 매입 건에 + * 대해 건수/총매입금액/총수수료를 집계하고 순정산금액(총매입-총수수료)을 + * 함께 산출한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +91,99 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0014.pgc" +#line 17 "app/online/ac_ol_0014.pgc" TX_SERVICE(AC_OL_0014, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 22 "app/online/ac_ol_0014.pgc" + char h_merch [ 16 ] ; + +#line 23 "app/online/ac_ol_0014.pgc" + char h_bizdate [ 9 ] ; + +#line 24 "app/online/ac_ol_0014.pgc" + char h_status [ 2 ] ; + +#line 25 "app/online/ac_ol_0014.pgc" + int h_cnt ; + +#line 26 "app/online/ac_ol_0014.pgc" + long h_totamt ; + +#line 27 "app/online/ac_ol_0014.pgc" + long h_totfee ; +/* exec sql end declare section */ +#line 28 "app/online/ac_ol_0014.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0014] 매입 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0014] KEY 누락"); + char merch[16]; + char bizdate[9]; + long net; + + tx_log(TX_LOG_INFO, "[AC_OL_0014] 가맹점별 매입집계 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0014] 필수 필드 누락(MERCHID/BIZDATE)"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (merch[0] == '\0' || !date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[AC_OL_0014] 입력 검증 실패 merch=%s date=%s", merch, bizdate); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_merch, merch, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + strncpy(h_status, AC_ST_DONE, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from ac_ledger where merch_id = $1 and biz_date = $2 and status = $3 ", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_totamt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_totfee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 61 "app/online/ac_ol_0014.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0014 ac_ledger 집계 select"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + net = h_totamt - h_totfee; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "TOTAMT", h_totamt); + tx_buf_setlong(ctx->out, "TOTFEE", h_totfee); + tx_buf_setlong(ctx->out, "NET", net); + tx_log(TX_LOG_INFO, "[AC_OL_0014] 집계완료 merch=%s date=%s cnt=%d totamt=%ld totfee=%ld net=%ld", + merch, bizdate, h_cnt, h_totamt, h_totfee, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0014.pgc b/app/online/ac_ol_0014.pgc index 6283b4c..32ef742 100644 --- a/app/online/ac_ol_0014.pgc +++ b/app/online/ac_ol_0014.pgc @@ -2,13 +2,15 @@ /* * ac_ol_0014.pgc - 매입 온라인 서비스 (AC_OL_0014) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점별 매입집계: 지정 가맹점의 지정 영업일 처리완료(M) 매입 건에 + * 대해 건수/총매입금액/총수수료를 집계하고 순정산금액(총매입-총수수료)을 + * 함께 산출한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +18,65 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0014, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16]; + char h_bizdate[9]; + char h_status[2]; + int h_cnt; + long h_totamt; + long h_totfee; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0014] 매입 단건조회 서비스 진입"); + char merch[16]; + char bizdate[9]; + long net; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0014] KEY 누락"); + tx_log(TX_LOG_INFO, "[AC_OL_0014] 가맹점별 매입집계 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0014] 필수 필드 누락(MERCHID/BIZDATE)"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (merch[0] == '\0' || !date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[AC_OL_0014] 입력 검증 실패 merch=%s date=%s", merch, bizdate); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_merch, merch, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + strncpy(h_status, AC_ST_DONE, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + EXEC SQL SELECT count(*), coalesce(sum(amount), 0), coalesce(sum(fee), 0) + INTO :h_cnt, :h_totamt, :h_totfee + FROM ac_ledger + WHERE merch_id = :h_merch + AND biz_date = :h_bizdate + AND status = :h_status; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0014 ac_ledger 집계 select"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + net = h_totamt - h_totfee; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "TOTAMT", h_totamt); + tx_buf_setlong(ctx->out, "TOTFEE", h_totfee); + tx_buf_setlong(ctx->out, "NET", net); + tx_log(TX_LOG_INFO, "[AC_OL_0014] 집계완료 merch=%s date=%s cnt=%d totamt=%ld totfee=%ld net=%ld", + merch, bizdate, h_cnt, h_totamt, h_totfee, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0015.c b/app/online/ac_ol_0015.c index 71da75d..4039314 100644 --- a/app/online/ac_ol_0015.c +++ b/app/online/ac_ol_0015.c @@ -10,13 +10,15 @@ /* * ac_ol_0015.pgc - 매입 온라인 서비스 (AC_OL_0015) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 카드사별 매입분류: KEY 원장의 카드번호 BIN(앞 2자리)으로 카드사 코드를 + * 판별한다('94'→KB, '95'→SH, '96'→HN, 그 외 ETC). 판별한 코드를 + * ac_ledger.corp_cd 에 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,60 +91,106 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0015.pgc" +#line 17 "app/online/ac_ol_0015.pgc" TX_SERVICE(AC_OL_0015, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + +#line 22 "app/online/ac_ol_0015.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/ac_ol_0015.pgc" + char h_card [ 20 ] ; + +#line 24 "app/online/ac_ol_0015.pgc" + char h_corp [ 8 ] ; +/* exec sql end declare section */ +#line 25 "app/online/ac_ol_0015.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0015] 매입 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0015] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char key[16]; + char card[20]; + char bin[3]; + char corp[8]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AC_OL_0015] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0015] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[AC_OL_0015] 카드사별 매입분류 서비스 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0015] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0015] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select card_no from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_card),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 46 "app/online/ac_ol_0015.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0015] 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0015 ac_ledger select"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); + strncpy(card, h_card, sizeof(card) - 1); + card[sizeof(card) - 1] = '\0'; + + bin[0] = card[0]; + bin[1] = card[1]; + bin[2] = '\0'; + + if (strcmp(bin, "94") == 0) { + strcpy(corp, "KB"); + } else if (strcmp(bin, "95") == 0) { + strcpy(corp, "SH"); + } else if (strcmp(bin, "96") == 0) { + strcpy(corp, "HN"); + } else { + strcpy(corp, "ETC"); + } + + strncpy(h_corp, corp, sizeof(h_corp) - 1); + h_corp[sizeof(h_corp) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set corp_cd = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_char,(h_corp),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 81 "app/online/ac_ol_0015.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0015 ac_ledger update"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0015] 갱신대상 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0015] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "CORP", corp); + tx_log(TX_LOG_INFO, "[AC_OL_0015] 카드사분류 완료 key=%s bin=%s corp=%s", key, bin, corp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0015.pgc b/app/online/ac_ol_0015.pgc index 66294e9..8884e7e 100644 --- a/app/online/ac_ol_0015.pgc +++ b/app/online/ac_ol_0015.pgc @@ -2,13 +2,15 @@ /* * ac_ol_0015.pgc - 매입 온라인 서비스 (AC_OL_0015) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 카드사별 매입분류: KEY 원장의 카드번호 BIN(앞 2자리)으로 카드사 코드를 + * 판별한다('94'→KB, '95'→SH, '96'→HN, 그 외 ETC). 판별한 코드를 + * ac_ledger.corp_cd 에 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,55 +18,83 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0015, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_card[20]; + char h_corp[8]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0015] 매입 등록 서비스 진입"); + char key[16]; + char card[20]; + char bin[3]; + char corp[8]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0015] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[AC_OL_0015] 카드사별 매입분류 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AC_OL_0015] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0015] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0015] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0015] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL SELECT card_no + INTO :h_card + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0015] 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0015 ac_ledger select"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); + strncpy(card, h_card, sizeof(card) - 1); + card[sizeof(card) - 1] = '\0'; + + bin[0] = card[0]; + bin[1] = card[1]; + bin[2] = '\0'; + + if (strcmp(bin, "94") == 0) { + strcpy(corp, "KB"); + } else if (strcmp(bin, "95") == 0) { + strcpy(corp, "SH"); + } else if (strcmp(bin, "96") == 0) { + strcpy(corp, "HN"); + } else { + strcpy(corp, "ETC"); + } + + strncpy(h_corp, corp, sizeof(h_corp) - 1); + h_corp[sizeof(h_corp) - 1] = '\0'; + + EXEC SQL UPDATE ac_ledger + SET corp_cd = :h_corp, upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0015 ac_ledger update"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0015] 갱신대상 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0015] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "CORP", corp); + tx_log(TX_LOG_INFO, "[AC_OL_0015] 카드사분류 완료 key=%s bin=%s corp=%s", key, bin, corp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0016.c b/app/online/ac_ol_0016.c index 9c46737..bfcfce1 100644 --- a/app/online/ac_ol_0016.c +++ b/app/online/ac_ol_0016.c @@ -10,13 +10,15 @@ /* * ac_ol_0016.pgc - 매입 온라인 서비스 (AC_OL_0016) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 원천징수: KEY 원장의 매입금액에 대해 가맹점별 원천징수율 + * (merchant.withhold_rate, bp)을 적용해 원천징수액과 지급순액(세후 + * 실지급액)을 계산해 반환한다. 원장은 변경하지 않는다(조회성 산출). */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,16 +91,37 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0016.pgc" +#line 17 "app/online/ac_ol_0016.pgc" TX_SERVICE(AC_OL_0016, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 22 "app/online/ac_ol_0016.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/ac_ol_0016.pgc" + long h_amount ; + +#line 24 "app/online/ac_ol_0016.pgc" + char h_merch [ 16 ] ; + +#line 25 "app/online/ac_ol_0016.pgc" + long h_wrate ; +/* exec sql end declare section */ +#line 26 "app/online/ac_ol_0016.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0016] 매입 단건조회 서비스 진입"); + + char key[16]; + long withhold; + long net; + + tx_log(TX_LOG_INFO, "[AC_OL_0016] 매입 원천징수 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0016] KEY 누락"); @@ -106,20 +129,62 @@ TX_SERVICE(AC_OL_0016, ctx) return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0016] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , merch_id from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 46 "app/online/ac_ol_0016.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0016] 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0016 ac_ledger select"); + tx_return(ctx, TX_EDB, ctx->out); return; } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select withhold_rate from merchant where merch_id = $1 ", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_wrate),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 62 "app/online/ac_ol_0016.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0016] 가맹점 없음 merch=%s", h_merch); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0016 merchant select"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + withhold = (h_amount * h_wrate) / 10000; + net = h_amount - withhold; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0016] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "WRATE", h_wrate); + tx_buf_setlong(ctx->out, "WITHHOLD", withhold); + tx_buf_setlong(ctx->out, "NETPAY", net); + tx_log(TX_LOG_INFO, "[AC_OL_0016] 원천징수 산출완료 key=%s amount=%ld withhold=%ld net=%ld", + key, h_amount, withhold, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0016.pgc b/app/online/ac_ol_0016.pgc index 65d1ec8..0c363c5 100644 --- a/app/online/ac_ol_0016.pgc +++ b/app/online/ac_ol_0016.pgc @@ -2,13 +2,15 @@ /* * ac_ol_0016.pgc - 매입 온라인 서비스 (AC_OL_0016) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 원천징수: KEY 원장의 매입금액에 대해 가맹점별 원천징수율 + * (merchant.withhold_rate, bp)을 적용해 원천징수액과 지급순액(세후 + * 실지급액)을 계산해 반환한다. 원장은 변경하지 않는다(조회성 산출). */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,11 +18,18 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0016, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_amount; + char h_merch[16]; + long h_wrate; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0016] 매입 단건조회 서비스 진입"); + char key[16]; + long withhold; + long net; + + tx_log(TX_LOG_INFO, "[AC_OL_0016] 매입 원천징수 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0016] KEY 누락"); @@ -28,20 +37,54 @@ TX_SERVICE(AC_OL_0016, ctx) return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0016] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + EXEC SQL SELECT amount, merch_id + INTO :h_amount, :h_merch + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0016] 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0016 ac_ledger select"); + tx_return(ctx, TX_EDB, ctx->out); return; } + EXEC SQL SELECT withhold_rate + INTO :h_wrate + FROM merchant + WHERE merch_id = :h_merch; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0016] 가맹점 없음 merch=%s", h_merch); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("AC_OL_0016 merchant select"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + withhold = (h_amount * h_wrate) / 10000; + net = h_amount - withhold; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0016] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "WRATE", h_wrate); + tx_buf_setlong(ctx->out, "WITHHOLD", withhold); + tx_buf_setlong(ctx->out, "NETPAY", net); + tx_log(TX_LOG_INFO, "[AC_OL_0016] 원천징수 산출완료 key=%s amount=%ld withhold=%ld net=%ld", + key, h_amount, withhold, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0017.c b/app/online/ac_ol_0017.c index 6b61f3f..223038d 100644 --- a/app/online/ac_ol_0017.c +++ b/app/online/ac_ol_0017.c @@ -10,13 +10,14 @@ /* * ac_ol_0017.pgc - 매입 온라인 서비스 (AC_OL_0017) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 세금계산서 연계: 지정 가맹점의 특정 영업일 처리완료(M) 매입 합계를 + * 집계하여 세금계산서를 발행하고, 대상 매입 원장에 세금계산서번호를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,88 +90,205 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0017.pgc" +#line 16 "app/online/ac_ol_0017.pgc" TX_SERVICE(AC_OL_0017, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + + + + +#line 21 "app/online/ac_ol_0017.pgc" + char h_merch_id [ 16 ] ; + +#line 22 "app/online/ac_ol_0017.pgc" + char h_biz_date [ 9 ] ; + +#line 23 "app/online/ac_ol_0017.pgc" + char h_key [ 16 ] ; + +#line 24 "app/online/ac_ol_0017.pgc" + char h_tax_no [ 17 ] ; + +#line 25 "app/online/ac_ol_0017.pgc" + long h_supply ; + +#line 26 "app/online/ac_ol_0017.pgc" + long h_vat ; + +#line 27 "app/online/ac_ol_0017.pgc" + long h_supply_amt ; +/* exec sql end declare section */ +#line 28 "app/online/ac_ol_0017.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0017] 매입 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + char merch[16], bizdate[9], won[32]; + long tagged = 0; + + tx_log(TX_LOG_INFO, "[AC_OL_0017] 매입 세금계산서 연계 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0017] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0017] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + tx_log(TX_LOG_ERROR, "[AC_OL_0017] 필수 필드(MERCHID/BIZDATE) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[AC_OL_0017] 영업일 형식 오류 date=%s", bizdate); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0017] 트랜잭션 시작 실패"); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 처리완료(M) 매입 합계 집계 (부가세 포함 총액) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from ac_ledger where merch_id = $1 and biz_date = $2 and status = 'M'", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_supply),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 65 "app/online/ac_ol_0017.pgc" - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0017] 원장반영 실패 rc=%d", rc); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT sum ac_ledger(tax)"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + if (h_supply <= 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0017] 집계대상 없음 merch=%s date=%s", + merch, bizdate); + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); + return; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AC_OL_0017] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); + /* 부가세 10% 계산, 공급가액 = 매입합계 - 부가세 */ + h_vat = h_supply / 10; + h_supply_amt = h_supply - h_vat; + + /* 세금계산서번호 채번: TX + 영업일 + 가맹점번호(6자리) */ + snprintf(h_tax_no, sizeof(h_tax_no), "TX%s%.6s", bizdate, merch); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into tax_invoice ( tax_no , merch_id , biz_date , supply_amt , vat_amt , status ) values ( $1 , $2 , $3 , $4 , $5 , 'I' )", + ECPGt_char,(h_tax_no),(long)17,(long)1,(17)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_supply_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_vat),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 92 "app/online/ac_ol_0017.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT tax_invoice"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 대상 매입 원장 커서 순회, 발행된 세금계산서번호 반영 */ + ECPGset_var( 0, ( h_biz_date ), __LINE__);\ + ECPGset_var( 1, ( h_merch_id ), __LINE__);\ + /* declare cur_ac_ol_0017 cursor for select key from ac_ledger where merch_id = $1 and biz_date = $2 and status = 'M' */ +#line 107 "app/online/ac_ol_0017.pgc" + +#line 107 "app/online/ac_ol_0017.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_ol_0017 cursor for select key from ac_ledger where merch_id = $1 and biz_date = $2 and status = 'M'", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 109 "app/online/ac_ol_0017.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_ac_ol_0017"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_ol_0017", ECPGt_EOIT, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 118 "app/online/ac_ol_0017.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_ac_ol_0017"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_ol_0017", ECPGt_EOIT, ECPGt_EORT);} +#line 123 "app/online/ac_ol_0017.pgc" + + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set tax_no = $1 where key = $2 ", + ECPGt_char,(h_tax_no),(long)17,(long)1,(17)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 131 "app/online/ac_ol_0017.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger tax_no"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_ol_0017", ECPGt_EOIT, ECPGt_EORT);} +#line 135 "app/online/ac_ol_0017.pgc" + + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tagged += sqlca.sqlerrd[2]; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_ol_0017", ECPGt_EOIT, ECPGt_EORT);} +#line 143 "app/online/ac_ol_0017.pgc" - /* 교차 검증: 당일 합계 재계산 */ - if (ac_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; tx_commit(); + amount_format_won(h_supply_amt, won, sizeof(won)); + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AC_OL_0017] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "TAXNO", h_tax_no); + tx_buf_setlong(ctx->out, "SUPPLY", h_supply_amt); + tx_buf_setlong(ctx->out, "VAT", h_vat); + tx_buf_setlong(ctx->out, "TAGGED", tagged); + + tx_log(TX_LOG_INFO, + "[AC_OL_0017] 완료 taxno=%s 공급가액=%s(%ld) 부가세=%ld 반영=%ld건", + h_tax_no, won, h_supply_amt, h_vat, tagged); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0017.pgc b/app/online/ac_ol_0017.pgc index 34c230e..f95a720 100644 --- a/app/online/ac_ol_0017.pgc +++ b/app/online/ac_ol_0017.pgc @@ -2,13 +2,14 @@ /* * ac_ol_0017.pgc - 매입 온라인 서비스 (AC_OL_0017) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 세금계산서 연계: 지정 가맹점의 특정 영업일 처리완료(M) 매입 합계를 + * 집계하여 세금계산서를 발행하고, 대상 매입 원장에 세금계산서번호를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,83 +17,144 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0017, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_biz_date[9]; + char h_key[16]; + char h_tax_no[17]; + long h_supply; + long h_vat; + long h_supply_amt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0017] 매입 트랜잭션 오케스트레이션 진입"); + char merch[16], bizdate[9], won[32]; + long tagged = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_log(TX_LOG_INFO, "[AC_OL_0017] 매입 세금계산서 연계 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0017] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0017] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + tx_log(TX_LOG_ERROR, "[AC_OL_0017] 필수 필드(MERCHID/BIZDATE) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[AC_OL_0017] 영업일 형식 오류 date=%s", bizdate); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0017] 트랜잭션 시작 실패"); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 처리완료(M) 매입 합계 집계 (부가세 포함 총액) */ + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_supply + FROM ac_ledger + WHERE merch_id = :h_merch_id + AND biz_date = :h_biz_date + AND status = 'M'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0017] 원장반영 실패 rc=%d", rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT sum ac_ledger(tax)"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + if (h_supply <= 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0017] 집계대상 없음 merch=%s date=%s", + merch, bizdate); + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); + return; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AC_OL_0017] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); + /* 부가세 10% 계산, 공급가액 = 매입합계 - 부가세 */ + h_vat = h_supply / 10; + h_supply_amt = h_supply - h_vat; - /* 교차 검증: 당일 합계 재계산 */ - if (ac_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; + /* 세금계산서번호 채번: TX + 영업일 + 가맹점번호(6자리) */ + snprintf(h_tax_no, sizeof(h_tax_no), "TX%s%.6s", bizdate, merch); + + EXEC SQL INSERT INTO tax_invoice + (tax_no, merch_id, biz_date, supply_amt, vat_amt, status) + VALUES + (:h_tax_no, :h_merch_id, :h_biz_date, :h_supply_amt, :h_vat, 'I'); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT tax_invoice"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 대상 매입 원장 커서 순회, 발행된 세금계산서번호 반영 */ + EXEC SQL DECLARE cur_ac_ol_0017 CURSOR FOR + SELECT key + FROM ac_ledger + WHERE merch_id = :h_merch_id + AND biz_date = :h_biz_date + AND status = 'M'; + + EXEC SQL OPEN cur_ac_ol_0017; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_ac_ol_0017"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + for (;;) { + EXEC SQL FETCH cur_ac_ol_0017 INTO :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_ac_ol_0017"); + EXEC SQL CLOSE cur_ac_ol_0017; + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL UPDATE ac_ledger + SET tax_no = :h_tax_no + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger tax_no"); + EXEC SQL CLOSE cur_ac_ol_0017; + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tagged += sqlca.sqlerrd[2]; + } + + EXEC SQL CLOSE cur_ac_ol_0017; tx_commit(); + amount_format_won(h_supply_amt, won, sizeof(won)); + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AC_OL_0017] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "TAXNO", h_tax_no); + tx_buf_setlong(ctx->out, "SUPPLY", h_supply_amt); + tx_buf_setlong(ctx->out, "VAT", h_vat); + tx_buf_setlong(ctx->out, "TAGGED", tagged); + + tx_log(TX_LOG_INFO, + "[AC_OL_0017] 완료 taxno=%s 공급가액=%s(%ld) 부가세=%ld 반영=%ld건", + h_tax_no, won, h_supply_amt, h_vat, tagged); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0018.c b/app/online/ac_ol_0018.c index 436327f..dd301c9 100644 --- a/app/online/ac_ol_0018.c +++ b/app/online/ac_ol_0018.c @@ -10,13 +10,14 @@ /* * ac_ol_0018.pgc - 매입 온라인 서비스 (AC_OL_0018) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입-승인 대사연계: 매입 원장 금액과 카드사 승인 금액을 대사하여 + * 일치 여부에 따라 매입 상태를 완료/불일치로 전이한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +90,138 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0018.pgc" +#line 16 "app/online/ac_ol_0018.pgc" TX_SERVICE(AC_OL_0018, ctx) { - ac_rec_t rec; - char key[16]; + /* exec sql begin declare section */ + + + + + +#line 21 "app/online/ac_ol_0018.pgc" + char h_key [ 16 ] ; + +#line 22 "app/online/ac_ol_0018.pgc" + char h_approval_no [ 16 ] ; + +#line 23 "app/online/ac_ol_0018.pgc" + long h_ledger_amt ; + +#line 24 "app/online/ac_ol_0018.pgc" + long h_appr_amt ; +/* exec sql end declare section */ +#line 25 "app/online/ac_ol_0018.pgc" + + + char key[16], apprno[16]; + char won_ledger[32], won_appr[32]; + long diff; + int match; + const char *newstatus; int rc; - tx_log(TX_LOG_INFO, "[AC_OL_0018] 매입 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[AC_OL_0018] 매입-승인 대사연계 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0018] KEY 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "APPROVALNO", apprno, sizeof(apprno)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0018] 필수 필드(KEY/APPROVALNO) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (key[0] == '\0' || apprno[0] == '\0') { + tx_log(TX_LOG_ERROR, "[AC_OL_0018] KEY/APPROVALNO 빈 값"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_approval_no, apprno, sizeof(h_approval_no) - 1); + h_approval_no[sizeof(h_approval_no) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_ledger_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 56 "app/online/ac_ol_0018.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0018] 매입 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger amount"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount from approval where approval_no = $1 ", + ECPGt_char,(h_approval_no),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_appr_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 72 "app/online/ac_ol_0018.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0018] 승인내역 없음 approvalno=%s", apprno); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT approval amount"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + diff = h_ledger_amt - h_appr_amt; + match = (diff == 0) ? 1 : 0; + newstatus = match ? AC_ST_DONE : AC_ST_FAIL; + + amount_format_won(h_ledger_amt, won_ledger, sizeof(won_ledger)); + amount_format_won(h_appr_amt, won_appr, sizeof(won_appr)); + + if (match) { + tx_log(TX_LOG_INFO, + "[AC_OL_0018] 대사일치 key=%s 원장=%s 승인=%s", + key, won_ledger, won_appr); + } else { + tx_log(TX_LOG_WARN, + "[AC_OL_0018] 대사불일치 key=%s 원장=%s 승인=%s diff=%ld", + key, won_ledger, won_appr, diff); + } + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0018] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + rc = ac_rec_update_status(key, newstatus); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0018] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_ERROR, "[AC_OL_0018] 상태전이 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); + tx_abort(); tx_return(ctx, rc, ctx->out); return; } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0018] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "MATCH", (long)match); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "STATUS", newstatus); + + tx_log(TX_LOG_INFO, "[AC_OL_0018] 완료 key=%s match=%d diff=%ld status=%s", + key, match, diff, newstatus); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0018.pgc b/app/online/ac_ol_0018.pgc index e88174d..0d75890 100644 --- a/app/online/ac_ol_0018.pgc +++ b/app/online/ac_ol_0018.pgc @@ -2,13 +2,14 @@ /* * ac_ol_0018.pgc - 매입 온라인 서비스 (AC_OL_0018) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입-승인 대사연계: 매입 원장 금액과 카드사 승인 금액을 대사하여 + * 일치 여부에 따라 매입 상태를 완료/불일치로 전이한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +17,113 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0018, ctx) { - ac_rec_t rec; - char key[16]; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_approval_no[16]; + long h_ledger_amt; + long h_appr_amt; + EXEC SQL END DECLARE SECTION; + + char key[16], apprno[16]; + char won_ledger[32], won_appr[32]; + long diff; + int match; + const char *newstatus; int rc; - tx_log(TX_LOG_INFO, "[AC_OL_0018] 매입 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[AC_OL_0018] 매입-승인 대사연계 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0018] KEY 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "APPROVALNO", apprno, sizeof(apprno)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0018] 필수 필드(KEY/APPROVALNO) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (key[0] == '\0' || apprno[0] == '\0') { + tx_log(TX_LOG_ERROR, "[AC_OL_0018] KEY/APPROVALNO 빈 값"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_approval_no, apprno, sizeof(h_approval_no) - 1); + h_approval_no[sizeof(h_approval_no) - 1] = '\0'; + + EXEC SQL SELECT amount + INTO :h_ledger_amt + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0018] 매입 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger amount"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL SELECT amount + INTO :h_appr_amt + FROM approval + WHERE approval_no = :h_approval_no; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0018] 승인내역 없음 approvalno=%s", apprno); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT approval amount"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + diff = h_ledger_amt - h_appr_amt; + match = (diff == 0) ? 1 : 0; + newstatus = match ? AC_ST_DONE : AC_ST_FAIL; + + amount_format_won(h_ledger_amt, won_ledger, sizeof(won_ledger)); + amount_format_won(h_appr_amt, won_appr, sizeof(won_appr)); + + if (match) { + tx_log(TX_LOG_INFO, + "[AC_OL_0018] 대사일치 key=%s 원장=%s 승인=%s", + key, won_ledger, won_appr); + } else { + tx_log(TX_LOG_WARN, + "[AC_OL_0018] 대사불일치 key=%s 원장=%s 승인=%s diff=%ld", + key, won_ledger, won_appr, diff); + } + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0018] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + rc = ac_rec_update_status(key, newstatus); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0018] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_ERROR, "[AC_OL_0018] 상태전이 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); + tx_abort(); tx_return(ctx, rc, ctx->out); return; } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0018] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "MATCH", (long)match); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "STATUS", newstatus); + + tx_log(TX_LOG_INFO, "[AC_OL_0018] 완료 key=%s match=%d diff=%ld status=%s", + key, match, diff, newstatus); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0019.c b/app/online/ac_ol_0019.c index fd537be..8b8ddac 100644 --- a/app/online/ac_ol_0019.c +++ b/app/online/ac_ol_0019.c @@ -10,13 +10,13 @@ /* * ac_ol_0019.pgc - 매입 온라인 서비스 (AC_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입일자 변경: 매입 원장의 영업일을 지정일 또는 다음 영업일로 변경한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -94,11 +94,27 @@ struct sqlca_t *ECPGget_sqlca(void); TX_SERVICE(AC_OL_0019, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 20 "app/online/ac_ol_0019.pgc" + char h_key [ 16 ] ; + +#line 21 "app/online/ac_ol_0019.pgc" + char h_old_date [ 9 ] ; + +#line 22 "app/online/ac_ol_0019.pgc" + char h_new_date [ 9 ] ; +/* exec sql end declare section */ +#line 23 "app/online/ac_ol_0019.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0019] 매입 단건조회 서비스 진입"); + + char key[16], newdate_in[9], computed[9]; + int has_newdate; + + tx_log(TX_LOG_INFO, "[AC_OL_0019] 매입일자 변경 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0019] KEY 누락"); @@ -106,20 +122,96 @@ TX_SERVICE(AC_OL_0019, ctx) return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + has_newdate = (tx_buf_get(ctx->in, "NEWDATE", newdate_in, sizeof(newdate_in)) == TX_OK); + + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select biz_date from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_old_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 44 "app/online/ac_ol_0019.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0019] 매입 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger biz_date"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (has_newdate && date_is_valid(newdate_in)) { + strncpy(computed, newdate_in, sizeof(computed) - 1); + computed[sizeof(computed) - 1] = '\0'; + } else { + if (has_newdate) + tx_log(TX_LOG_WARN, + "[AC_OL_0019] NEWDATE 형식 오류(%s) - 다음영업일로 대체", + newdate_in); + if (date_next_business(h_old_date, computed) != 0) { + tx_log(TX_LOG_ERROR, "[AC_OL_0019] 다음영업일 산출 실패 date=%s", + h_old_date); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + } + + strncpy(h_new_date, computed, sizeof(h_new_date) - 1); + h_new_date[sizeof(h_new_date) - 1] = '\0'; + + if (strcmp(h_old_date, h_new_date) == 0) { + tx_log(TX_LOG_INFO, "[AC_OL_0019] 변경 불필요 date=%s", h_old_date); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "OLDDATE", h_old_date); + tx_buf_sets(ctx->out, "NEWDATE", h_new_date); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0019] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set biz_date = $1 where key = $2 ", + ECPGt_char,(h_new_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 95 "app/online/ac_ol_0019.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger biz_date"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0019] 변경대상 없음 key=%s", key); + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "OLDDATE", h_old_date); + tx_buf_sets(ctx->out, "NEWDATE", h_new_date); + + tx_log(TX_LOG_INFO, "[AC_OL_0019] 완료 key=%s %s -> %s", + key, h_old_date, h_new_date); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0019.pgc b/app/online/ac_ol_0019.pgc index f84caa3..d22c0ef 100644 --- a/app/online/ac_ol_0019.pgc +++ b/app/online/ac_ol_0019.pgc @@ -2,13 +2,13 @@ /* * ac_ol_0019.pgc - 매입 온라인 서비스 (AC_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입일자 변경: 매입 원장의 영업일을 지정일 또는 다음 영업일로 변경한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,11 +16,16 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0019, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_old_date[9]; + char h_new_date[9]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0019] 매입 단건조회 서비스 진입"); + char key[16], newdate_in[9], computed[9]; + int has_newdate; + + tx_log(TX_LOG_INFO, "[AC_OL_0019] 매입일자 변경 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0019] KEY 누락"); @@ -28,20 +33,89 @@ TX_SERVICE(AC_OL_0019, ctx) return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + has_newdate = (tx_buf_get(ctx->in, "NEWDATE", newdate_in, sizeof(newdate_in)) == TX_OK); + + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + EXEC SQL SELECT biz_date + INTO :h_old_date + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0019] 매입 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger biz_date"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (has_newdate && date_is_valid(newdate_in)) { + strncpy(computed, newdate_in, sizeof(computed) - 1); + computed[sizeof(computed) - 1] = '\0'; + } else { + if (has_newdate) + tx_log(TX_LOG_WARN, + "[AC_OL_0019] NEWDATE 형식 오류(%s) - 다음영업일로 대체", + newdate_in); + if (date_next_business(h_old_date, computed) != 0) { + tx_log(TX_LOG_ERROR, "[AC_OL_0019] 다음영업일 산출 실패 date=%s", + h_old_date); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + } + + strncpy(h_new_date, computed, sizeof(h_new_date) - 1); + h_new_date[sizeof(h_new_date) - 1] = '\0'; + + if (strcmp(h_old_date, h_new_date) == 0) { + tx_log(TX_LOG_INFO, "[AC_OL_0019] 변경 불필요 date=%s", h_old_date); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "OLDDATE", h_old_date); + tx_buf_sets(ctx->out, "NEWDATE", h_new_date); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0019] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE ac_ledger + SET biz_date = :h_new_date + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger biz_date"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0019] 변경대상 없음 key=%s", key); + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "OLDDATE", h_old_date); + tx_buf_sets(ctx->out, "NEWDATE", h_new_date); + + tx_log(TX_LOG_INFO, "[AC_OL_0019] 완료 key=%s %s -> %s", + key, h_old_date, h_new_date); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0020.c b/app/online/ac_ol_0020.c index b9073f4..ba8ae03 100644 --- a/app/online/ac_ol_0020.c +++ b/app/online/ac_ol_0020.c @@ -10,13 +10,14 @@ /* * ac_ol_0020.pgc - 매입 온라인 서비스 (AC_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입금액 보정: 지정 보정액(DELTA)만큼 매입금액을 가감하고, 가맹점 + * 수수료율로 수수료를 재계산하여 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,60 +90,159 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0020.pgc" +#line 16 "app/online/ac_ol_0020.pgc" TX_SERVICE(AC_OL_0020, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + + + +#line 21 "app/online/ac_ol_0020.pgc" + char h_key [ 16 ] ; + +#line 22 "app/online/ac_ol_0020.pgc" + char h_merch_id [ 16 ] ; + +#line 23 "app/online/ac_ol_0020.pgc" + long h_amount ; + +#line 24 "app/online/ac_ol_0020.pgc" + long h_fee ; + +#line 25 "app/online/ac_ol_0020.pgc" + long h_new_amount ; + +#line 26 "app/online/ac_ol_0020.pgc" + long h_new_fee ; + +#line 27 "app/online/ac_ol_0020.pgc" + double h_fee_rate ; +/* exec sql end declare section */ +#line 28 "app/online/ac_ol_0020.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0020] 매입 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0020] 필수 필드 누락"); + char key[16]; + long delta = 0; + + tx_log(TX_LOG_INFO, "[AC_OL_0020] 매입금액 보정 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0020] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AC_OL_0020] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0020] 영업일 오류 date=%s", bizdate); + if (tx_buf_getlong(ctx->in, "DELTA", &delta) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0020] DELTA 누락 key=%s", key); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0020] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , fee , merch_id from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 52 "app/online/ac_ol_0020.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0020] 매입 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger amount/fee"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); + h_new_amount = h_amount + delta; + if (!amount_is_valid(h_new_amount)) { + tx_log(TX_LOG_WARN, + "[AC_OL_0020] 보정후 금액 유효성 오류 key=%s delta=%ld amount=%ld", + key, delta, h_new_amount); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select fee_rate from merchant where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_double,&(h_fee_rate),(long)1,(long)1,sizeof(double), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 77 "app/online/ac_ol_0020.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0020] 가맹점 정보 없음 merch=%s", h_merch_id); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT merchant fee_rate"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + h_new_fee = (long)(h_new_amount * h_fee_rate); + if (h_new_fee < 0) + h_new_fee = 0; + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0020] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set amount = $1 , fee = $2 where key = $3 ", + ECPGt_long,&(h_new_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_new_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 102 "app/online/ac_ol_0020.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger amount/fee"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0020] 보정대상 없음 key=%s", key); + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0020] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "OLDAMOUNT", h_amount); + tx_buf_setlong(ctx->out, "NEWAMOUNT", h_new_amount); + tx_buf_setlong(ctx->out, "OLDFEE", h_fee); + tx_buf_setlong(ctx->out, "NEWFEE", h_new_fee); + + tx_log(TX_LOG_INFO, + "[AC_OL_0020] 완료 key=%s amount %ld->%ld fee %ld->%ld", + key, h_amount, h_new_amount, h_fee, h_new_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0020.pgc b/app/online/ac_ol_0020.pgc index 1284748..8be4231 100644 --- a/app/online/ac_ol_0020.pgc +++ b/app/online/ac_ol_0020.pgc @@ -2,13 +2,14 @@ /* * ac_ol_0020.pgc - 매입 온라인 서비스 (AC_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입금액 보정: 지정 보정액(DELTA)만큼 매입금액을 가감하고, 가맹점 + * 수수료율로 수수료를 재계산하여 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,55 +17,115 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0020, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_merch_id[16]; + long h_amount; + long h_fee; + long h_new_amount; + long h_new_fee; + double h_fee_rate; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0020] 매입 등록 서비스 진입"); + char key[16]; + long delta = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0020] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[AC_OL_0020] 매입금액 보정 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0020] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AC_OL_0020] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0020] 영업일 오류 date=%s", bizdate); + if (tx_buf_getlong(ctx->in, "DELTA", &delta) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0020] DELTA 누락 key=%s", key); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0020] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL SELECT amount, fee, merch_id + INTO :h_amount, :h_fee, :h_merch_id + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0020] 매입 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger amount/fee"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); + h_new_amount = h_amount + delta; + if (!amount_is_valid(h_new_amount)) { + tx_log(TX_LOG_WARN, + "[AC_OL_0020] 보정후 금액 유효성 오류 key=%s delta=%ld amount=%ld", + key, delta, h_new_amount); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + EXEC SQL SELECT fee_rate + INTO :h_fee_rate + FROM merchant + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0020] 가맹점 정보 없음 merch=%s", h_merch_id); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT merchant fee_rate"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + h_new_fee = (long)(h_new_amount * h_fee_rate); + if (h_new_fee < 0) + h_new_fee = 0; + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0020] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE ac_ledger + SET amount = :h_new_amount, fee = :h_new_fee + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger amount/fee"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0020] 보정대상 없음 key=%s", key); + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0020] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "OLDAMOUNT", h_amount); + tx_buf_setlong(ctx->out, "NEWAMOUNT", h_new_amount); + tx_buf_setlong(ctx->out, "OLDFEE", h_fee); + tx_buf_setlong(ctx->out, "NEWFEE", h_new_fee); + + tx_log(TX_LOG_INFO, + "[AC_OL_0020] 완료 key=%s amount %ld->%ld fee %ld->%ld", + key, h_amount, h_new_amount, h_fee, h_new_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0021.c b/app/online/ac_ol_0021.c index 140ed31..21c126c 100644 --- a/app/online/ac_ol_0021.c +++ b/app/online/ac_ol_0021.c @@ -10,13 +10,14 @@ /* * ac_ol_0021.pgc - 매입 온라인 서비스 (AC_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 승인번호 매핑: 카드사 승인내역 존재를 확인한 뒤 매입 원장에 + * 승인번호를 매핑한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +90,135 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0021.pgc" +#line 16 "app/online/ac_ol_0021.pgc" TX_SERVICE(AC_OL_0021, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 21 "app/online/ac_ol_0021.pgc" + char h_key [ 16 ] ; + +#line 22 "app/online/ac_ol_0021.pgc" + char h_approval_no [ 16 ] ; + +#line 23 "app/online/ac_ol_0021.pgc" + int h_cnt ; + +#line 24 "app/online/ac_ol_0021.pgc" + int h_ledger_cnt ; +/* exec sql end declare section */ +#line 25 "app/online/ac_ol_0021.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0021] 매입 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0021] KEY 누락"); + char key[16], apprno[16]; + + tx_log(TX_LOG_INFO, "[AC_OL_0021] 매입 승인번호 매핑 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "APPROVALNO", apprno, sizeof(apprno)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0021] 필수 필드(KEY/APPROVALNO) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (key[0] == '\0' || apprno[0] == '\0') { + tx_log(TX_LOG_ERROR, "[AC_OL_0021] KEY/APPROVALNO 빈 값"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_approval_no, apprno, sizeof(h_approval_no) - 1); + h_approval_no[sizeof(h_approval_no) - 1] = '\0'; + + /* 매입 원장 존재 확인 (선행 검증) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_ledger_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 52 "app/online/ac_ol_0021.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT count ac_ledger"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_ledger_cnt <= 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0021] 매입 원장 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "MAPPED", 0L); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + /* 카드사 승인내역 존재 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from approval where approval_no = $1 ", + ECPGt_char,(h_approval_no),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 72 "app/online/ac_ol_0021.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT count approval"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (h_cnt <= 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0021] 승인내역 없음 approvalno=%s", apprno); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "MAPPED", 0L); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0021] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set approval_no = $1 where key = $2 ", + ECPGt_char,(h_approval_no),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 97 "app/online/ac_ol_0021.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger approval_no"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0021] 매핑대상 원장 없음 key=%s", key); + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "APPROVALNO", apprno); + tx_buf_setlong(ctx->out, "MAPPED", 1L); + + tx_log(TX_LOG_INFO, "[AC_OL_0021] 완료 key=%s approvalno=%s", key, apprno); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0021.pgc b/app/online/ac_ol_0021.pgc index e8993b9..365bb3b 100644 --- a/app/online/ac_ol_0021.pgc +++ b/app/online/ac_ol_0021.pgc @@ -2,13 +2,14 @@ /* * ac_ol_0021.pgc - 매입 온라인 서비스 (AC_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 승인번호 매핑: 카드사 승인내역 존재를 확인한 뒤 매입 원장에 + * 승인번호를 매핑한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +17,106 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0021, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_approval_no[16]; + int h_cnt; + int h_ledger_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0021] 매입 단건조회 서비스 진입"); + char key[16], apprno[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0021] KEY 누락"); + tx_log(TX_LOG_INFO, "[AC_OL_0021] 매입 승인번호 매핑 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "APPROVALNO", apprno, sizeof(apprno)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0021] 필수 필드(KEY/APPROVALNO) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (key[0] == '\0' || apprno[0] == '\0') { + tx_log(TX_LOG_ERROR, "[AC_OL_0021] KEY/APPROVALNO 빈 값"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_approval_no, apprno, sizeof(h_approval_no) - 1); + h_approval_no[sizeof(h_approval_no) - 1] = '\0'; + + /* 매입 원장 존재 확인 (선행 검증) */ + EXEC SQL SELECT count(*) + INTO :h_ledger_cnt + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT count ac_ledger"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_ledger_cnt <= 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0021] 매입 원장 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "MAPPED", 0L); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + /* 카드사 승인내역 존재 확인 */ + EXEC SQL SELECT count(*) + INTO :h_cnt + FROM approval + WHERE approval_no = :h_approval_no; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT count approval"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (h_cnt <= 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0021] 승인내역 없음 approvalno=%s", apprno); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "MAPPED", 0L); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0021] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE ac_ledger + SET approval_no = :h_approval_no + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger approval_no"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0021] 매핑대상 원장 없음 key=%s", key); + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "APPROVALNO", apprno); + tx_buf_setlong(ctx->out, "MAPPED", 1L); + + tx_log(TX_LOG_INFO, "[AC_OL_0021] 완료 key=%s approvalno=%s", key, apprno); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0022.c b/app/online/ac_ol_0022.c index 44c6778..3574c8c 100644 --- a/app/online/ac_ol_0022.c +++ b/app/online/ac_ol_0022.c @@ -10,13 +10,14 @@ /* * ac_ol_0022.pgc - 매입 온라인 서비스 (AC_OL_0022) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 정산연계: 처리완료 매입 건의 수수료를 차감한 정산금액을 산출하여 + * 정산 테이블에 반영하고 매입 원장을 정산완료 상태로 전이한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,60 +90,159 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0022.pgc" +#line 16 "app/online/ac_ol_0022.pgc" TX_SERVICE(AC_OL_0022, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + + + + +#line 21 "app/online/ac_ol_0022.pgc" + char h_key [ 16 ] ; + +#line 22 "app/online/ac_ol_0022.pgc" + char h_merch_id [ 16 ] ; + +#line 23 "app/online/ac_ol_0022.pgc" + char h_biz_date [ 9 ] ; + +#line 24 "app/online/ac_ol_0022.pgc" + char h_status [ 2 ] ; + +#line 25 "app/online/ac_ol_0022.pgc" + long h_amount ; + +#line 26 "app/online/ac_ol_0022.pgc" + long h_fee ; + +#line 27 "app/online/ac_ol_0022.pgc" + long h_settle_amt ; + +#line 28 "app/online/ac_ol_0022.pgc" + char h_settle_no [ 17 ] ; +/* exec sql end declare section */ +#line 29 "app/online/ac_ol_0022.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0022] 매입 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0022] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char key[16], won[32]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AC_OL_0022] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0022] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[AC_OL_0022] 매입 정산연계 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0022] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0022] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , fee , merch_id , biz_date , status from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 47 "app/online/ac_ol_0022.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0022] 매입 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger for settle"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); + if (strncmp(h_status, AC_ST_DONE, 1) != 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0022] 정산연계 불가 상태 key=%s status=%s", + key, h_status); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + h_settle_amt = h_amount - h_fee; + if (h_settle_amt < 0) + h_settle_amt = 0; + + /* 정산번호 채번: ST + 영업일 + 매입키(뒤 6자리) */ + snprintf(h_settle_no, sizeof(h_settle_no), "ST%s%.6s", h_biz_date, key); + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0022] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into settle ( settle_no , merch_id , biz_date , settle_amt , status ) values ( $1 , $2 , $3 , $4 , 'S' )", + ECPGt_char,(h_settle_no),(long)17,(long)1,(17)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_settle_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 83 "app/online/ac_ol_0022.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT settle"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set status = 'S' , settle_no = $1 where key = $2 ", + ECPGt_char,(h_settle_no),(long)17,(long)1,(17)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 94 "app/online/ac_ol_0022.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger settle status"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0022] 정산반영 대상 없음 key=%s", key); + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + tx_commit(); + + amount_format_won(h_settle_amt, won, sizeof(won)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0022] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "SETTLENO", h_settle_no); + tx_buf_setlong(ctx->out, "SETTLEAMT", h_settle_amt); + + tx_log(TX_LOG_INFO, "[AC_OL_0022] 완료 key=%s settleno=%s 정산액=%s(%ld)", + key, h_settle_no, won, h_settle_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0022.pgc b/app/online/ac_ol_0022.pgc index e84dfff..c10c51f 100644 --- a/app/online/ac_ol_0022.pgc +++ b/app/online/ac_ol_0022.pgc @@ -2,13 +2,14 @@ /* * ac_ol_0022.pgc - 매입 온라인 서비스 (AC_OL_0022) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 정산연계: 처리완료 매입 건의 수수료를 차감한 정산금액을 산출하여 + * 정산 테이블에 반영하고 매입 원장을 정산완료 상태로 전이한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,55 +17,106 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0022, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_merch_id[16]; + char h_biz_date[9]; + char h_status[2]; + long h_amount; + long h_fee; + long h_settle_amt; + char h_settle_no[17]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0022] 매입 등록 서비스 진입"); + char key[16], won[32]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0022] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[AC_OL_0022] 매입 정산연계 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AC_OL_0022] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0022] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0022] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0022] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL SELECT amount, fee, merch_id, biz_date, status + INTO :h_amount, :h_fee, :h_merch_id, :h_biz_date, :h_status + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0022] 매입 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger for settle"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); + if (strncmp(h_status, AC_ST_DONE, 1) != 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0022] 정산연계 불가 상태 key=%s status=%s", + key, h_status); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + h_settle_amt = h_amount - h_fee; + if (h_settle_amt < 0) + h_settle_amt = 0; + + /* 정산번호 채번: ST + 영업일 + 매입키(뒤 6자리) */ + snprintf(h_settle_no, sizeof(h_settle_no), "ST%s%.6s", h_biz_date, key); + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0022] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL INSERT INTO settle + (settle_no, merch_id, biz_date, settle_amt, status) + VALUES + (:h_settle_no, :h_merch_id, :h_biz_date, :h_settle_amt, 'S'); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT settle"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL UPDATE ac_ledger + SET status = 'S', settle_no = :h_settle_no + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger settle status"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0022] 정산반영 대상 없음 key=%s", key); + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + tx_commit(); + + amount_format_won(h_settle_amt, won, sizeof(won)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0022] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "SETTLENO", h_settle_no); + tx_buf_setlong(ctx->out, "SETTLEAMT", h_settle_amt); + + tx_log(TX_LOG_INFO, "[AC_OL_0022] 완료 key=%s settleno=%s 정산액=%s(%ld)", + key, h_settle_no, won, h_settle_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0023.c b/app/online/ac_ol_0023.c index fbb2322..4504591 100644 --- a/app/online/ac_ol_0023.c +++ b/app/online/ac_ol_0023.c @@ -10,13 +10,14 @@ /* * ac_ol_0023.pgc - 매입 온라인 서비스 (AC_OL_0023) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 이체연계: 정산완료 매입 건의 순매입액(수수료 차감 합계)을 집계하여 + * 이체지시 레코드를 생성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +90,140 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0023.pgc" +#line 16 "app/online/ac_ol_0023.pgc" TX_SERVICE(AC_OL_0023, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 21 "app/online/ac_ol_0023.pgc" + char h_merch_id [ 16 ] ; + +#line 22 "app/online/ac_ol_0023.pgc" + char h_biz_date [ 9 ] ; + +#line 23 "app/online/ac_ol_0023.pgc" + long h_net ; + +#line 24 "app/online/ac_ol_0023.pgc" + char h_transfer_id [ 17 ] ; + +#line 25 "app/online/ac_ol_0023.pgc" + int h_dup_cnt ; +/* exec sql end declare section */ +#line 26 "app/online/ac_ol_0023.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0023] 매입 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0023] KEY 누락"); + char merch[16], bizdate[9]; + + tx_log(TX_LOG_INFO, "[AC_OL_0023] 매입 이체연계 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0023] 필수 필드(MERCHID/BIZDATE) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[AC_OL_0023] 영업일 형식 오류 date=%s", bizdate); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount - fee ) , 0 ) from ac_ledger where merch_id = $1 and biz_date = $2 and status = 'S'", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_net),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 55 "app/online/ac_ol_0023.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT sum net ac_ledger"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (h_net <= 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0023] 이체대상 없음 merch=%s date=%s", + merch, bizdate); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + /* 중복 이체지시 방지: 동일 가맹점/영업일 이체(T) 이력 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from settle where merch_id = $1 and biz_date = $2 and status = 'T'", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_dup_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 76 "app/online/ac_ol_0023.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT count settle dup"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup_cnt > 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0023] 이체지시 중복 merch=%s date=%s", + merch, bizdate); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 이체지시번호 채번: TR + 영업일 + 가맹점번호(6자리) */ + snprintf(h_transfer_id, sizeof(h_transfer_id), "TR%s%.6s", bizdate, merch); + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0023] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into settle ( settle_no , merch_id , biz_date , settle_amt , status ) values ( $1 , $2 , $3 , $4 , 'T' )", + ECPGt_char,(h_transfer_id),(long)17,(long)1,(17)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_net),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 102 "app/online/ac_ol_0023.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT settle transfer"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "TRANSFERID", h_transfer_id); + tx_buf_setlong(ctx->out, "NETAMOUNT", h_net); + + tx_log(TX_LOG_INFO, "[AC_OL_0023] 완료 transferid=%s 순매입액=%ld", + h_transfer_id, h_net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0023.pgc b/app/online/ac_ol_0023.pgc index 82b0a38..fef2a24 100644 --- a/app/online/ac_ol_0023.pgc +++ b/app/online/ac_ol_0023.pgc @@ -2,13 +2,14 @@ /* * ac_ol_0023.pgc - 매입 온라인 서비스 (AC_OL_0023) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 이체연계: 정산완료 매입 건의 순매입액(수수료 차감 합계)을 집계하여 + * 이체지시 레코드를 생성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +17,105 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0023, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_biz_date[9]; + long h_net; + char h_transfer_id[17]; + int h_dup_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0023] 매입 단건조회 서비스 진입"); + char merch[16], bizdate[9]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0023] KEY 누락"); + tx_log(TX_LOG_INFO, "[AC_OL_0023] 매입 이체연계 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0023] 필수 필드(MERCHID/BIZDATE) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[AC_OL_0023] 영업일 형식 오류 date=%s", bizdate); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + EXEC SQL SELECT coalesce(sum(amount - fee), 0) + INTO :h_net + FROM ac_ledger + WHERE merch_id = :h_merch_id + AND biz_date = :h_biz_date + AND status = 'S'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT sum net ac_ledger"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (h_net <= 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0023] 이체대상 없음 merch=%s date=%s", + merch, bizdate); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + /* 중복 이체지시 방지: 동일 가맹점/영업일 이체(T) 이력 확인 */ + EXEC SQL SELECT count(*) + INTO :h_dup_cnt + FROM settle + WHERE merch_id = :h_merch_id + AND biz_date = :h_biz_date + AND status = 'T'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT count settle dup"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup_cnt > 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0023] 이체지시 중복 merch=%s date=%s", + merch, bizdate); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 이체지시번호 채번: TR + 영업일 + 가맹점번호(6자리) */ + snprintf(h_transfer_id, sizeof(h_transfer_id), "TR%s%.6s", bizdate, merch); + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0023] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL INSERT INTO settle + (settle_no, merch_id, biz_date, settle_amt, status) + VALUES + (:h_transfer_id, :h_merch_id, :h_biz_date, :h_net, 'T'); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT settle transfer"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "TRANSFERID", h_transfer_id); + tx_buf_setlong(ctx->out, "NETAMOUNT", h_net); + + tx_log(TX_LOG_INFO, "[AC_OL_0023] 완료 transferid=%s 순매입액=%ld", + h_transfer_id, h_net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0024.c b/app/online/ac_ol_0024.c index c1fef44..9a85a98 100644 --- a/app/online/ac_ol_0024.c +++ b/app/online/ac_ol_0024.c @@ -10,13 +10,14 @@ /* * ac_ol_0024.pgc - 매입 온라인 서비스 (AC_OL_0024) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 재분류: 매입금액 구간에 따라 매입유형(acq_type)을 대형/중형/소형 + * 가맹점 기준으로 재분류하여 매입 원장에 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,60 +90,132 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0024.pgc" +#line 16 "app/online/ac_ol_0024.pgc" +#define AC_TYPE_LARGE "LG" +#define AC_TYPE_MEDIUM "MD" +#define AC_TYPE_SMALL "SM" + TX_SERVICE(AC_OL_0024, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + +#line 25 "app/online/ac_ol_0024.pgc" + char h_key [ 16 ] ; + +#line 26 "app/online/ac_ol_0024.pgc" + long h_amount ; + +#line 27 "app/online/ac_ol_0024.pgc" + char h_old_type [ 4 ] ; + +#line 28 "app/online/ac_ol_0024.pgc" + char h_acq_type [ 4 ] ; +/* exec sql end declare section */ +#line 29 "app/online/ac_ol_0024.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0024] 매입 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0024] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char key[16]; + const char *class_name; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AC_OL_0024] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0024] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[AC_OL_0024] 매입 재분류 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0024] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0024] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , coalesce ( acq_type , '' ) from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_old_type),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 48 "app/online/ac_ol_0024.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0024] 매입 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger amount(reclass)"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); + /* 업무규칙: 금액 구간별 매입유형 재분류 (대형/중형/소형) */ + if (h_amount >= 1000000L) { + class_name = AC_TYPE_LARGE; + } else if (h_amount >= 100000L) { + class_name = AC_TYPE_MEDIUM; + } else { + class_name = AC_TYPE_SMALL; + } + strncpy(h_acq_type, class_name, sizeof(h_acq_type) - 1); + h_acq_type[sizeof(h_acq_type) - 1] = '\0'; + + if (strcmp(h_old_type, h_acq_type) == 0) { + tx_log(TX_LOG_INFO, "[AC_OL_0024] 재분류 변경 없음 key=%s type=%s", + key, h_acq_type); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_sets(ctx->out, "OLDTYPE", h_old_type); + tx_buf_sets(ctx->out, "ACQTYPE", h_acq_type); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0024] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set acq_type = $1 where key = $2 ", + ECPGt_char,(h_acq_type),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 93 "app/online/ac_ol_0024.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger acq_type"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0024] 재분류대상 없음 key=%s", key); + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0024] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_sets(ctx->out, "OLDTYPE", h_old_type); + tx_buf_sets(ctx->out, "ACQTYPE", h_acq_type); + + tx_log(TX_LOG_INFO, "[AC_OL_0024] 완료 key=%s amount=%ld %s -> %s", + key, h_amount, h_old_type, h_acq_type); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0024.pgc b/app/online/ac_ol_0024.pgc index f009a71..81290f2 100644 --- a/app/online/ac_ol_0024.pgc +++ b/app/online/ac_ol_0024.pgc @@ -2,69 +2,119 @@ /* * ac_ol_0024.pgc - 매입 온라인 서비스 (AC_OL_0024) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 재분류: 매입금액 구간에 따라 매입유형(acq_type)을 대형/중형/소형 + * 가맹점 기준으로 재분류하여 매입 원장에 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" EXEC SQL INCLUDE sqlca; +#define AC_TYPE_LARGE "LG" +#define AC_TYPE_MEDIUM "MD" +#define AC_TYPE_SMALL "SM" + TX_SERVICE(AC_OL_0024, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_amount; + char h_old_type[4]; + char h_acq_type[4]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0024] 매입 등록 서비스 진입"); + char key[16]; + const char *class_name; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0024] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[AC_OL_0024] 매입 재분류 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AC_OL_0024] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0024] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0024] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0024] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL SELECT amount, coalesce(acq_type, '') + INTO :h_amount, :h_old_type + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0024] 매입 원장 없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger amount(reclass)"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); + /* 업무규칙: 금액 구간별 매입유형 재분류 (대형/중형/소형) */ + if (h_amount >= 1000000L) { + class_name = AC_TYPE_LARGE; + } else if (h_amount >= 100000L) { + class_name = AC_TYPE_MEDIUM; + } else { + class_name = AC_TYPE_SMALL; + } + strncpy(h_acq_type, class_name, sizeof(h_acq_type) - 1); + h_acq_type[sizeof(h_acq_type) - 1] = '\0'; + + if (strcmp(h_old_type, h_acq_type) == 0) { + tx_log(TX_LOG_INFO, "[AC_OL_0024] 재분류 변경 없음 key=%s type=%s", + key, h_acq_type); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_sets(ctx->out, "OLDTYPE", h_old_type); + tx_buf_sets(ctx->out, "ACQTYPE", h_acq_type); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0024] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE ac_ledger + SET acq_type = :h_acq_type + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger acq_type"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0024] 재분류대상 없음 key=%s", key); + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0024] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_sets(ctx->out, "OLDTYPE", h_old_type); + tx_buf_sets(ctx->out, "ACQTYPE", h_acq_type); + + tx_log(TX_LOG_INFO, "[AC_OL_0024] 완료 key=%s amount=%ld %s -> %s", + key, h_amount, h_old_type, h_acq_type); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0025.c b/app/online/ac_ol_0025.c index 56b6278..0fe2841 100644 --- a/app/online/ac_ol_0025.c +++ b/app/online/ac_ol_0025.c @@ -10,13 +10,19 @@ /* * ac_ol_0025.pgc - 매입 온라인 서비스 (AC_OL_0025) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 미매입 추출: 지정 영업일(BIZDATE) 이전(포함)까지 누적된 미매입 접수 + * (ac_ledger.status='R') 건을 집계한다. 정산 배치 실행 전, 처리 대상 + * 잔여 건수/합계금액과 최장 대기 건(최소 key)을 파악하기 위한 조회 + * 전용 서비스이다. + * + * 조회기준일이 시스템 영업일보다 미래이면 업무상 비정상 조회로 보고 + * 경고 로그만 남기고 조회는 계속 진행한다(운영 모니터링 목적). */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +95,131 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0025.pgc" +#line 21 "app/online/ac_ol_0025.pgc" TX_SERVICE(AC_OL_0025, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* ---- ECPG 호스트 변수 선언부 ---- */ + /* exec sql begin declare section */ + + + + + + +#line 27 "app/online/ac_ol_0025.pgc" + char h_biz_date [ 9 ] ; + +#line 28 "app/online/ac_ol_0025.pgc" + long h_cnt ; + +#line 29 "app/online/ac_ol_0025.pgc" + long h_totamt ; + +#line 30 "app/online/ac_ol_0025.pgc" + char h_oldest [ 16 ] ; + +#line 31 "app/online/ac_ol_0025.pgc" + short h_oldest_ind ; +/* exec sql end declare section */ +#line 32 "app/online/ac_ol_0025.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0025] 매입 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0025] KEY 누락"); + char bizdate[9]; + char totbuf[32]; + + tx_log(TX_LOG_INFO, "[AC_OL_0025] 미매입 추출 서비스 진입"); + + /* ---- 1. 입력 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + bizdate[0] == '\0') { + tx_log(TX_LOG_ERROR, "[AC_OL_0025] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[AC_OL_0025] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0025] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + /* ---- 2. 조회기준일 업무 정합성 점검 (참고용 경고, 조회는 계속 진행) ---- */ + { + char today[9]; + + date_today(today); + if (strcmp(bizdate, today) > 0) { + tx_log(TX_LOG_WARN, + "[AC_OL_0025] 조회기준일이 시스템 영업일보다 미래임 date=%s today=%s", + bizdate, today); + } } + /* ---- 3. 미매입 접수건 집계 (건수 + 합계금액) ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( SUM ( amount ) , 0 ) from ac_ledger where biz_date <= $1 and status = 'R'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_totamt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 77 "app/online/ac_ol_0025.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT ac_ledger 미매입 집계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlcode == TX_SQL_NOTFOUND || h_cnt < 0) { + h_cnt = 0; + h_totamt = 0; + } + + /* ---- 4. 최장 대기 건(최소 key) 조회 ---- */ + memset(h_oldest, 0, sizeof(h_oldest)); + h_oldest_ind = -1; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select MIN ( key ) from ac_ledger where biz_date <= $1 and status = 'R'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_oldest),(long)16,(long)1,(16)*sizeof(char), + ECPGt_short,&(h_oldest_ind),(long)1,(long)1,sizeof(short), ECPGt_EORT);} +#line 100 "app/online/ac_ol_0025.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT ac_ledger 최장대기건"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_oldest_ind < 0 || h_cnt == 0) + h_oldest[0] = '\0'; + + amount_format_won(h_totamt, totbuf, sizeof(totbuf)); + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0025] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_setlong(ctx->out, "TOTAMT", h_totamt); + tx_buf_sets(ctx->out, "OLDEST", h_oldest); + + tx_log(TX_LOG_INFO, + "[AC_OL_0025] 미매입 추출완료 date=%s 건수=%ld 합계=%s 최장대기=%s", + bizdate, h_cnt, totbuf, h_oldest[0] ? h_oldest : "-"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0025.pgc b/app/online/ac_ol_0025.pgc index 57d1d07..d3e34a2 100644 --- a/app/online/ac_ol_0025.pgc +++ b/app/online/ac_ol_0025.pgc @@ -2,13 +2,19 @@ /* * ac_ol_0025.pgc - 매입 온라인 서비스 (AC_OL_0025) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 미매입 추출: 지정 영업일(BIZDATE) 이전(포함)까지 누적된 미매입 접수 + * (ac_ledger.status='R') 건을 집계한다. 정산 배치 실행 전, 처리 대상 + * 잔여 건수/합계금액과 최장 대기 건(최소 key)을 파악하기 위한 조회 + * 전용 서비스이다. + * + * 조회기준일이 시스템 영업일보다 미래이면 업무상 비정상 조회로 보고 + * 경고 로그만 남기고 조회는 계속 진행한다(운영 모니터링 목적). */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +22,105 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0025, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* ---- ECPG 호스트 변수 선언부 ---- */ + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + long h_cnt; + long h_totamt; + char h_oldest[16]; + short h_oldest_ind; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0025] 매입 단건조회 서비스 진입"); + char bizdate[9]; + char totbuf[32]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0025] KEY 누락"); + tx_log(TX_LOG_INFO, "[AC_OL_0025] 미매입 추출 서비스 진입"); + + /* ---- 1. 입력 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + bizdate[0] == '\0') { + tx_log(TX_LOG_ERROR, "[AC_OL_0025] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[AC_OL_0025] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0025] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + /* ---- 2. 조회기준일 업무 정합성 점검 (참고용 경고, 조회는 계속 진행) ---- */ + { + char today[9]; + + date_today(today); + if (strcmp(bizdate, today) > 0) { + tx_log(TX_LOG_WARN, + "[AC_OL_0025] 조회기준일이 시스템 영업일보다 미래임 date=%s today=%s", + bizdate, today); + } } + /* ---- 3. 미매입 접수건 집계 (건수 + 합계금액) ---- */ + EXEC SQL + SELECT COUNT(*), COALESCE(SUM(amount), 0) + INTO :h_cnt, :h_totamt + FROM ac_ledger + WHERE biz_date <= :h_biz_date + AND status = 'R'; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT ac_ledger 미매입 집계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlcode == TX_SQL_NOTFOUND || h_cnt < 0) { + h_cnt = 0; + h_totamt = 0; + } + + /* ---- 4. 최장 대기 건(최소 key) 조회 ---- */ + memset(h_oldest, 0, sizeof(h_oldest)); + h_oldest_ind = -1; + + EXEC SQL + SELECT MIN(key) + INTO :h_oldest:h_oldest_ind + FROM ac_ledger + WHERE biz_date <= :h_biz_date + AND status = 'R'; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT ac_ledger 최장대기건"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_oldest_ind < 0 || h_cnt == 0) + h_oldest[0] = '\0'; + + amount_format_won(h_totamt, totbuf, sizeof(totbuf)); + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0025] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_setlong(ctx->out, "TOTAMT", h_totamt); + tx_buf_sets(ctx->out, "OLDEST", h_oldest); + + tx_log(TX_LOG_INFO, + "[AC_OL_0025] 미매입 추출완료 date=%s 건수=%ld 합계=%s 최장대기=%s", + bizdate, h_cnt, totbuf, h_oldest[0] ? h_oldest : "-"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0026.c b/app/online/ac_ol_0026.c index 088b5a6..23d6f11 100644 --- a/app/online/ac_ol_0026.c +++ b/app/online/ac_ol_0026.c @@ -10,13 +10,17 @@ /* * ac_ol_0026.pgc - 매입 온라인 서비스 (AC_OL_0026) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 미매입 재처리: 지정 영업일의 실패건(ac_ledger.status='U')을 커서로 + * 순회하며 금액 유효성을 재검증한다. 유효하면 완료(status='M')로 + * 전이(재처리 성공)하고, 여전히 유효하지 않으면 실패(status='U') 상태를 + * 유지(재처리 보류)한다. 전 건을 하나의 트랜잭션으로 묶어 처리하며, + * DB 오류 발생 시 전체 롤백한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,88 +93,170 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0026.pgc" +#line 19 "app/online/ac_ol_0026.pgc" TX_SERVICE(AC_OL_0026, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* ---- ECPG 호스트 변수 선언부 ---- */ + /* exec sql begin declare section */ + + + + + + +#line 25 "app/online/ac_ol_0026.pgc" + char h_biz_date [ 9 ] ; + +#line 26 "app/online/ac_ol_0026.pgc" + char h_status_fail [ 2 ] ; + +#line 27 "app/online/ac_ol_0026.pgc" + char h_status_done [ 2 ] ; + +#line 28 "app/online/ac_ol_0026.pgc" + char h_key [ 16 ] ; + +#line 29 "app/online/ac_ol_0026.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 30 "app/online/ac_ol_0026.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0026] 매입 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0026] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char bizdate[9]; + long reprocessed = 0; + long failed = 0; + long dberr = 0; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0026] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + tx_log(TX_LOG_INFO, "[AC_OL_0026] 미매입 재처리 서비스 진입"); + + /* ---- 1. 입력 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_ERROR, "[AC_OL_0026] BIZDATE 누락/오류"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + strncpy(h_status_fail, AC_ST_FAIL, sizeof(h_status_fail) - 1); + h_status_fail[sizeof(h_status_fail) - 1] = '\0'; + strncpy(h_status_done, AC_ST_DONE, sizeof(h_status_done) - 1); + h_status_done[sizeof(h_status_done) - 1] = '\0'; + + /* ---- 2. 트랜잭션 시작 ---- */ if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0026] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* ---- 3. 실패건 커서 오픈 ---- */ + ECPGset_var( 0, ( h_status_fail ), __LINE__);\ + ECPGset_var( 1, ( h_biz_date ), __LINE__);\ + /* declare cur_ac_ol_0026 cursor for select key , amount from ac_ledger where biz_date = $1 and status = $2 order by key */ +#line 71 "app/online/ac_ol_0026.pgc" - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0026] 원장반영 실패 rc=%d", rc); +#line 71 "app/online/ac_ol_0026.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_ol_0026 cursor for select key , amount from ac_ledger where biz_date = $1 and status = $2 order by key", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status_fail),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 73 "app/online/ac_ol_0026.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_ac_ol_0026 open"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + /* ---- 4. 순회 재검증 ---- */ + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_ol_0026", ECPGt_EOIT, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 85 "app/online/ac_ol_0026.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_ac_ol_0026 fetch"); + dberr++; + break; + } + + if (amount_is_valid(h_amount)) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set status = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_char,(h_status_done),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 99 "app/online/ac_ol_0026.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_ac_ol_0026 update done"); + dberr++; + break; + } + reprocessed++; + tx_log(TX_LOG_DEBUG, "[AC_OL_0026] 재처리성공 key=%s amount=%ld", + h_key, h_amount); + } else { + /* 여전히 유효하지 않은 금액 -> 실패(U) 상태 유지 */ + failed++; + tx_log(TX_LOG_DEBUG, "[AC_OL_0026] 재처리보류 key=%s amount=%ld", + h_key, h_amount); + } } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AC_OL_0026] 후속연계 경고 rc=%d", rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_ol_0026", ECPGt_EOIT, ECPGt_EORT);} +#line 117 "app/online/ac_ol_0026.pgc" + + + /* ---- 5. 커밋/롤백 ---- */ + if (dberr > 0) { + tx_log(TX_LOG_ERROR, "[AC_OL_0026] DB 오류로 전체 롤백 dberr=%ld", dberr); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - /* 교차 검증: 당일 합계 재계산 */ - if (ac_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0026] 커밋 실패"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + /* ---- 6. 응답 구성 ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AC_OL_0026] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "REPROCESSED", reprocessed); + tx_buf_setlong(ctx->out, "FAILED", failed); + + tx_log(TX_LOG_INFO, + "[AC_OL_0026] 재처리완료 date=%s 성공=%ld 보류=%ld", + bizdate, reprocessed, failed); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0026.pgc b/app/online/ac_ol_0026.pgc index 2bbfef9..333c9a4 100644 --- a/app/online/ac_ol_0026.pgc +++ b/app/online/ac_ol_0026.pgc @@ -2,13 +2,17 @@ /* * ac_ol_0026.pgc - 매입 온라인 서비스 (AC_OL_0026) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 미매입 재처리: 지정 영업일의 실패건(ac_ledger.status='U')을 커서로 + * 순회하며 금액 유효성을 재검증한다. 유효하면 완료(status='M')로 + * 전이(재처리 성공)하고, 여전히 유효하지 않으면 실패(status='U') 상태를 + * 유지(재처리 보류)한다. 전 건을 하나의 트랜잭션으로 묶어 처리하며, + * DB 오류 발생 시 전체 롤백한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,83 +20,130 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0026, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* ---- ECPG 호스트 변수 선언부 ---- */ + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_status_fail[2]; + char h_status_done[2]; + char h_key[16]; + long h_amount; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0026] 매입 트랜잭션 오케스트레이션 진입"); + char bizdate[9]; + long reprocessed = 0; + long failed = 0; + long dberr = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0026] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[AC_OL_0026] 미매입 재처리 서비스 진입"); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0026] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + /* ---- 1. 입력 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_ERROR, "[AC_OL_0026] BIZDATE 누락/오류"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + strncpy(h_status_fail, AC_ST_FAIL, sizeof(h_status_fail) - 1); + h_status_fail[sizeof(h_status_fail) - 1] = '\0'; + strncpy(h_status_done, AC_ST_DONE, sizeof(h_status_done) - 1); + h_status_done[sizeof(h_status_done) - 1] = '\0'; + + /* ---- 2. 트랜잭션 시작 ---- */ if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0026] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* ---- 3. 실패건 커서 오픈 ---- */ + EXEC SQL DECLARE cur_ac_ol_0026 CURSOR FOR + SELECT key, amount + FROM ac_ledger + WHERE biz_date = :h_biz_date + AND status = :h_status_fail + ORDER BY key; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0026] 원장반영 실패 rc=%d", rc); + EXEC SQL OPEN cur_ac_ol_0026; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_ac_ol_0026 open"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + /* ---- 4. 순회 재검증 ---- */ + for (;;) { + EXEC SQL FETCH cur_ac_ol_0026 INTO :h_key, :h_amount; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_ac_ol_0026 fetch"); + dberr++; + break; + } + + if (amount_is_valid(h_amount)) { + EXEC SQL + UPDATE ac_ledger + SET status = :h_status_done, upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_ac_ol_0026 update done"); + dberr++; + break; + } + reprocessed++; + tx_log(TX_LOG_DEBUG, "[AC_OL_0026] 재처리성공 key=%s amount=%ld", + h_key, h_amount); + } else { + /* 여전히 유효하지 않은 금액 -> 실패(U) 상태 유지 */ + failed++; + tx_log(TX_LOG_DEBUG, "[AC_OL_0026] 재처리보류 key=%s amount=%ld", + h_key, h_amount); + } } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AC_OL_0026] 후속연계 경고 rc=%d", rc); + EXEC SQL CLOSE cur_ac_ol_0026; + + /* ---- 5. 커밋/롤백 ---- */ + if (dberr > 0) { + tx_log(TX_LOG_ERROR, "[AC_OL_0026] DB 오류로 전체 롤백 dberr=%ld", dberr); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - /* 교차 검증: 당일 합계 재계산 */ - if (ac_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0026] 커밋 실패"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + /* ---- 6. 응답 구성 ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AC_OL_0026] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "REPROCESSED", reprocessed); + tx_buf_setlong(ctx->out, "FAILED", failed); + + tx_log(TX_LOG_INFO, + "[AC_OL_0026] 재처리완료 date=%s 성공=%ld 보류=%ld", + bizdate, reprocessed, failed); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0027.c b/app/online/ac_ol_0027.c index 82539d9..53ad606 100644 --- a/app/online/ac_ol_0027.c +++ b/app/online/ac_ol_0027.c @@ -10,13 +10,20 @@ /* * ac_ol_0027.pgc - 매입 온라인 서비스 (AC_OL_0027) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 통계: 지정 영업일의 완료(status='M') + 정산완료(status='S') 건을 + * 집계하여 건수/매입금액합계/수수료합계/건당 평균금액을 산출한다. + * 일별 매입 현황 대시보드/모니터링 화면에서 조회하는 통계 전용 + * 서비스이다. + * + * 평균금액은 매입금액합계를 건수로 나눈 값이며, 대상 건이 0건이면 + * 0으로 가드하여 0-division 을 방지한다. 실효 수수료율(bp)도 함께 + * 산출하여 가맹점 수수료율 이상 징후를 조기에 확인할 수 있게 한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +96,134 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0027.pgc" +#line 22 "app/online/ac_ol_0027.pgc" TX_SERVICE(AC_OL_0027, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* ---- ECPG 호스트 변수 선언부 ---- */ + /* exec sql begin declare section */ + + + + + + + +#line 28 "app/online/ac_ol_0027.pgc" + char h_biz_date [ 9 ] ; + +#line 29 "app/online/ac_ol_0027.pgc" + char h_status_done [ 2 ] ; + +#line 30 "app/online/ac_ol_0027.pgc" + char h_status_settled [ 2 ] ; + +#line 31 "app/online/ac_ol_0027.pgc" + long h_cnt ; + +#line 32 "app/online/ac_ol_0027.pgc" + long h_tot ; + +#line 33 "app/online/ac_ol_0027.pgc" + long h_totfee ; +/* exec sql end declare section */ +#line 34 "app/online/ac_ol_0027.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0027] 매입 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0027] KEY 누락"); + char bizdate[9]; + char totbuf[32]; + long avg = 0; + long avgfee = 0; + long feeratebp = 0; + + tx_log(TX_LOG_INFO, "[AC_OL_0027] 매입 통계 서비스 진입"); + + /* ---- 1. 입력 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_ERROR, "[AC_OL_0027] BIZDATE 누락/오류"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0027] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + strncpy(h_status_done, AC_ST_DONE, sizeof(h_status_done) - 1); + h_status_done[sizeof(h_status_done) - 1] = '\0'; + strncpy(h_status_settled, AC_ST_SETTLED, sizeof(h_status_settled) - 1); + h_status_settled[sizeof(h_status_settled) - 1] = '\0'; + + /* ---- 2. 완료 + 정산완료 건 집계 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( SUM ( amount ) , 0 ) , coalesce ( SUM ( fee ) , 0 ) from ac_ledger where biz_date = $1 and status in ( $2 , $3 )", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status_done),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status_settled),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_tot),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_totfee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 67 "app/online/ac_ol_0027.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT ac_ledger 매입통계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (sqlca.sqlcode == TX_SQL_NOTFOUND || h_cnt < 0) { + h_cnt = 0; + h_tot = 0; + h_totfee = 0; + } + tx_log(TX_LOG_DEBUG, + "[AC_OL_0027] 원시집계 date=%s 건수=%ld 합계=%ld 수수료=%ld", + bizdate, h_cnt, h_tot, h_totfee); + /* ---- 3. 건당 평균금액/평균수수료 산출 (0건 가드) ---- */ + if (h_cnt > 0) { + avg = h_tot / h_cnt; + avgfee = h_totfee / h_cnt; + } else { + avg = 0; + avgfee = 0; + } + + /* + * ---- 4. 실효 수수료율(bp, 1bp=0.01%) 산출 (매입금액합계 0 가드) ---- + * feeratebp = 수수료합계 * 10000 / 매입금액합계 + * (예: feeratebp=150 -> 실효 수수료율 1.50%) + */ + if (h_tot > 0) + feeratebp = (h_totfee * 10000) / h_tot; + else + feeratebp = 0; + + amount_format_won(h_tot, totbuf, sizeof(totbuf)); + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0027] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_setlong(ctx->out, "TOTAMT", h_tot); + tx_buf_setlong(ctx->out, "TOTFEE", h_totfee); + tx_buf_setlong(ctx->out, "AVGAMT", avg); + tx_buf_setlong(ctx->out, "AVGFEE", avgfee); + tx_buf_setlong(ctx->out, "FEERATEBP", feeratebp); + + tx_log(TX_LOG_INFO, + "[AC_OL_0027] 매입통계완료 date=%s 건수=%ld 합계=%s 수수료=%ld 평균금액=%ld " + "평균수수료=%ld 수수료율(bp)=%ld", + bizdate, h_cnt, totbuf, h_totfee, avg, avgfee, feeratebp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0027.pgc b/app/online/ac_ol_0027.pgc index e96164c..7ff3fd9 100644 --- a/app/online/ac_ol_0027.pgc +++ b/app/online/ac_ol_0027.pgc @@ -2,13 +2,20 @@ /* * ac_ol_0027.pgc - 매입 온라인 서비스 (AC_OL_0027) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 통계: 지정 영업일의 완료(status='M') + 정산완료(status='S') 건을 + * 집계하여 건수/매입금액합계/수수료합계/건당 평균금액을 산출한다. + * 일별 매입 현황 대시보드/모니터링 화면에서 조회하는 통계 전용 + * 서비스이다. + * + * 평균금액은 매입금액합계를 건수로 나눈 값이며, 대상 건이 0건이면 + * 0으로 가드하여 0-division 을 방지한다. 실효 수수료율(bp)도 함께 + * 산출하여 가맹점 수수료율 이상 징후를 조기에 확인할 수 있게 한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +23,100 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0027, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* ---- ECPG 호스트 변수 선언부 ---- */ + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_status_done[2]; + char h_status_settled[2]; + long h_cnt; + long h_tot; + long h_totfee; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0027] 매입 단건조회 서비스 진입"); + char bizdate[9]; + char totbuf[32]; + long avg = 0; + long avgfee = 0; + long feeratebp = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0027] KEY 누락"); + tx_log(TX_LOG_INFO, "[AC_OL_0027] 매입 통계 서비스 진입"); + + /* ---- 1. 입력 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_ERROR, "[AC_OL_0027] BIZDATE 누락/오류"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0027] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + strncpy(h_status_done, AC_ST_DONE, sizeof(h_status_done) - 1); + h_status_done[sizeof(h_status_done) - 1] = '\0'; + strncpy(h_status_settled, AC_ST_SETTLED, sizeof(h_status_settled) - 1); + h_status_settled[sizeof(h_status_settled) - 1] = '\0'; + + /* ---- 2. 완료 + 정산완료 건 집계 ---- */ + EXEC SQL + SELECT COUNT(*), COALESCE(SUM(amount), 0), COALESCE(SUM(fee), 0) + INTO :h_cnt, :h_tot, :h_totfee + FROM ac_ledger + WHERE biz_date = :h_biz_date + AND status IN (:h_status_done, :h_status_settled); + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT ac_ledger 매입통계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (sqlca.sqlcode == TX_SQL_NOTFOUND || h_cnt < 0) { + h_cnt = 0; + h_tot = 0; + h_totfee = 0; + } + tx_log(TX_LOG_DEBUG, + "[AC_OL_0027] 원시집계 date=%s 건수=%ld 합계=%ld 수수료=%ld", + bizdate, h_cnt, h_tot, h_totfee); + /* ---- 3. 건당 평균금액/평균수수료 산출 (0건 가드) ---- */ + if (h_cnt > 0) { + avg = h_tot / h_cnt; + avgfee = h_totfee / h_cnt; + } else { + avg = 0; + avgfee = 0; + } + + /* + * ---- 4. 실효 수수료율(bp, 1bp=0.01%) 산출 (매입금액합계 0 가드) ---- + * feeratebp = 수수료합계 * 10000 / 매입금액합계 + * (예: feeratebp=150 -> 실효 수수료율 1.50%) + */ + if (h_tot > 0) + feeratebp = (h_totfee * 10000) / h_tot; + else + feeratebp = 0; + + amount_format_won(h_tot, totbuf, sizeof(totbuf)); + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0027] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_setlong(ctx->out, "TOTAMT", h_tot); + tx_buf_setlong(ctx->out, "TOTFEE", h_totfee); + tx_buf_setlong(ctx->out, "AVGAMT", avg); + tx_buf_setlong(ctx->out, "AVGFEE", avgfee); + tx_buf_setlong(ctx->out, "FEERATEBP", feeratebp); + + tx_log(TX_LOG_INFO, + "[AC_OL_0027] 매입통계완료 date=%s 건수=%ld 합계=%s 수수료=%ld 평균금액=%ld " + "평균수수료=%ld 수수료율(bp)=%ld", + bizdate, h_cnt, totbuf, h_totfee, avg, avgfee, feeratebp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0028.c b/app/online/ac_ol_0028.c index d418019..1d1c7b5 100644 --- a/app/online/ac_ol_0028.c +++ b/app/online/ac_ol_0028.c @@ -10,13 +10,17 @@ /* * ac_ol_0028.pgc - 매입 온라인 서비스 (AC_OL_0028) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 세금계산서 발행집계: 지정 영업일의 완료(status='M') 매입 원장을 + * 가맹점별로 그룹핑하여 매입 합계금액을 공급가액으로, 그 10%를 부가세로 + * 산정한 세금계산서(tax_invoice)를 가맹점 단위로 1건씩 발행/적재한다. + * 전 가맹점을 하나의 트랜잭션으로 묶어 처리하며, DB 오류 발생 시 전체 + * 롤백한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,88 +93,183 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0028.pgc" +#line 19 "app/online/ac_ol_0028.pgc" TX_SERVICE(AC_OL_0028, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* ---- ECPG 호스트 변수 선언부 ---- */ + /* exec sql begin declare section */ + + + + + + + +#line 25 "app/online/ac_ol_0028.pgc" + char h_biz_date [ 9 ] ; + +#line 26 "app/online/ac_ol_0028.pgc" + char h_status_done [ 2 ] ; + +#line 27 "app/online/ac_ol_0028.pgc" + char h_merch_id [ 16 ] ; + +#line 28 "app/online/ac_ol_0028.pgc" + long h_supply ; + +#line 29 "app/online/ac_ol_0028.pgc" + char h_tax_no [ 32 ] ; + +#line 30 "app/online/ac_ol_0028.pgc" + long h_vat ; +/* exec sql end declare section */ +#line 31 "app/online/ac_ol_0028.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0028] 매입 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0028] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char bizdate[9]; + long merch_cnt = 0; + long tot_supply = 0; + long tot_vat = 0; + long dberr = 0; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0028] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + tx_log(TX_LOG_INFO, "[AC_OL_0028] 매입 세금계산서 발행집계 서비스 진입"); + + /* ---- 1. 입력 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_ERROR, "[AC_OL_0028] BIZDATE 누락/오류"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + strncpy(h_status_done, AC_ST_DONE, sizeof(h_status_done) - 1); + h_status_done[sizeof(h_status_done) - 1] = '\0'; + + /* ---- 2. 트랜잭션 시작 ---- */ if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0028] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* ---- 3. 가맹점별 매입합계 그룹핑 커서 ---- */ + ECPGset_var( 0, ( h_status_done ), __LINE__);\ + ECPGset_var( 1, ( h_biz_date ), __LINE__);\ + /* declare cur_ac_ol_0028 cursor for select merch_id , SUM ( amount ) from ac_ledger where biz_date = $1 and status = $2 group by merch_id order by merch_id */ +#line 72 "app/online/ac_ol_0028.pgc" - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0028] 원장반영 실패 rc=%d", rc); +#line 72 "app/online/ac_ol_0028.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_ac_ol_0028 cursor for select merch_id , SUM ( amount ) from ac_ledger where biz_date = $1 and status = $2 group by merch_id order by merch_id", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status_done),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 74 "app/online/ac_ol_0028.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_ac_ol_0028 open"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + /* ---- 4. 가맹점 순회: 세금계산서 발행 ---- */ + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_ac_ol_0028", ECPGt_EOIT, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_supply),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 86 "app/online/ac_ol_0028.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_ac_ol_0028 fetch"); + dberr++; + break; + } + + /* 업무규칙: 매입 합계금액을 공급가액으로 보고 부가세 10% 산정 */ + h_vat = h_supply / 10; + + memset(h_tax_no, 0, sizeof(h_tax_no)); + snprintf(h_tax_no, sizeof(h_tax_no), "%s%s", h_biz_date, h_merch_id); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into tax_invoice ( tax_no , merch_id , biz_date , supply_amt , vat_amt ) values ( $1 , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_tax_no),(long)32,(long)1,(32)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_supply),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_vat),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 104 "app/online/ac_ol_0028.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_ac_ol_0028 insert tax_invoice"); + dberr++; + break; + } + + merch_cnt++; + tot_supply += h_supply; + tot_vat += h_vat; + tx_log(TX_LOG_DEBUG, + "[AC_OL_0028] 발행 merch=%s 공급가액=%ld 부가세=%ld", + h_merch_id, h_supply, h_vat); } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AC_OL_0028] 후속연계 경고 rc=%d", rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_ac_ol_0028", ECPGt_EOIT, ECPGt_EORT);} +#line 120 "app/online/ac_ol_0028.pgc" + + + /* ---- 5. 커밋/롤백 ---- */ + if (dberr > 0) { + tx_log(TX_LOG_ERROR, "[AC_OL_0028] DB 오류로 전체 롤백 dberr=%ld", dberr); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - /* 교차 검증: 당일 합계 재계산 */ - if (ac_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0028] 커밋 실패"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + /* ---- 6. 응답 구성 ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AC_OL_0028] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "MERCHCNT", merch_cnt); + tx_buf_setlong(ctx->out, "TOTSUPPLY", tot_supply); + tx_buf_setlong(ctx->out, "TOTVAT", tot_vat); + + tx_log(TX_LOG_INFO, + "[AC_OL_0028] 발행집계완료 date=%s 가맹점수=%ld 공급가액합계=%ld 부가세합계=%ld", + bizdate, merch_cnt, tot_supply, tot_vat); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0028.pgc b/app/online/ac_ol_0028.pgc index 3b56314..93eed26 100644 --- a/app/online/ac_ol_0028.pgc +++ b/app/online/ac_ol_0028.pgc @@ -2,13 +2,17 @@ /* * ac_ol_0028.pgc - 매입 온라인 서비스 (AC_OL_0028) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 세금계산서 발행집계: 지정 영업일의 완료(status='M') 매입 원장을 + * 가맹점별로 그룹핑하여 매입 합계금액을 공급가액으로, 그 10%를 부가세로 + * 산정한 세금계산서(tax_invoice)를 가맹점 단위로 1건씩 발행/적재한다. + * 전 가맹점을 하나의 트랜잭션으로 묶어 처리하며, DB 오류 발생 시 전체 + * 롤백한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,83 +20,134 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0028, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* ---- ECPG 호스트 변수 선언부 ---- */ + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_status_done[2]; + char h_merch_id[16]; + long h_supply; + char h_tax_no[32]; + long h_vat; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0028] 매입 트랜잭션 오케스트레이션 진입"); + char bizdate[9]; + long merch_cnt = 0; + long tot_supply = 0; + long tot_vat = 0; + long dberr = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0028] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[AC_OL_0028] 매입 세금계산서 발행집계 서비스 진입"); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0028] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + /* ---- 1. 입력 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_ERROR, "[AC_OL_0028] BIZDATE 누락/오류"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + strncpy(h_status_done, AC_ST_DONE, sizeof(h_status_done) - 1); + h_status_done[sizeof(h_status_done) - 1] = '\0'; + + /* ---- 2. 트랜잭션 시작 ---- */ if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0028] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* ---- 3. 가맹점별 매입합계 그룹핑 커서 ---- */ + EXEC SQL DECLARE cur_ac_ol_0028 CURSOR FOR + SELECT merch_id, SUM(amount) + FROM ac_ledger + WHERE biz_date = :h_biz_date + AND status = :h_status_done + GROUP BY merch_id + ORDER BY merch_id; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0028] 원장반영 실패 rc=%d", rc); + EXEC SQL OPEN cur_ac_ol_0028; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_ac_ol_0028 open"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + /* ---- 4. 가맹점 순회: 세금계산서 발행 ---- */ + for (;;) { + EXEC SQL FETCH cur_ac_ol_0028 INTO :h_merch_id, :h_supply; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_ac_ol_0028 fetch"); + dberr++; + break; + } + + /* 업무규칙: 매입 합계금액을 공급가액으로 보고 부가세 10% 산정 */ + h_vat = h_supply / 10; + + memset(h_tax_no, 0, sizeof(h_tax_no)); + snprintf(h_tax_no, sizeof(h_tax_no), "%s%s", h_biz_date, h_merch_id); + + EXEC SQL + INSERT INTO tax_invoice (tax_no, merch_id, biz_date, supply_amt, vat_amt) + VALUES (:h_tax_no, :h_merch_id, :h_biz_date, :h_supply, :h_vat); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_ac_ol_0028 insert tax_invoice"); + dberr++; + break; + } + + merch_cnt++; + tot_supply += h_supply; + tot_vat += h_vat; + tx_log(TX_LOG_DEBUG, + "[AC_OL_0028] 발행 merch=%s 공급가액=%ld 부가세=%ld", + h_merch_id, h_supply, h_vat); } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AC_OL_0028] 후속연계 경고 rc=%d", rc); + EXEC SQL CLOSE cur_ac_ol_0028; + + /* ---- 5. 커밋/롤백 ---- */ + if (dberr > 0) { + tx_log(TX_LOG_ERROR, "[AC_OL_0028] DB 오류로 전체 롤백 dberr=%ld", dberr); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - /* 교차 검증: 당일 합계 재계산 */ - if (ac_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0028] 커밋 실패"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + /* ---- 6. 응답 구성 ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AC_OL_0028] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "MERCHCNT", merch_cnt); + tx_buf_setlong(ctx->out, "TOTSUPPLY", tot_supply); + tx_buf_setlong(ctx->out, "TOTVAT", tot_vat); + + tx_log(TX_LOG_INFO, + "[AC_OL_0028] 발행집계완료 date=%s 가맹점수=%ld 공급가액합계=%ld 부가세합계=%ld", + bizdate, merch_cnt, tot_supply, tot_vat); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0029.c b/app/online/ac_ol_0029.c index 8501840..47b94ea 100644 --- a/app/online/ac_ol_0029.c +++ b/app/online/ac_ol_0029.c @@ -10,13 +10,18 @@ /* * ac_ol_0029.pgc - 매입 온라인 서비스 (AC_OL_0029) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 청구조회(EDI): 청구번호(CLAIMNO)로 매입 청구내역(ac_claim)을 + * 단건 조회한다. EDI 연계 파일로 실제 적재된 청구건의 가맹점/영업일/ + * 금액/파일순번/상태를 확인하기 위한 조회 전용 서비스이다. + * + * 청구번호는 EDI 파일 헤더에서 채번되어 전달되므로, 조회 전 앞뒤 + * 공백을 제거하여 정규화한 뒤 원장 키로 사용한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +94,138 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0029.pgc" +#line 20 "app/online/ac_ol_0029.pgc" TX_SERVICE(AC_OL_0029, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* ---- ECPG 호스트 변수 선언부 ---- */ + /* exec sql begin declare section */ + + + + + + + +#line 26 "app/online/ac_ol_0029.pgc" + char h_claim_no [ 16 ] ; + +#line 27 "app/online/ac_ol_0029.pgc" + char h_merch_id [ 16 ] ; + +#line 28 "app/online/ac_ol_0029.pgc" + char h_biz_date [ 9 ] ; + +#line 29 "app/online/ac_ol_0029.pgc" + long h_amount ; + +#line 30 "app/online/ac_ol_0029.pgc" + long h_file_seq ; + +#line 31 "app/online/ac_ol_0029.pgc" + char h_status [ 2 ] ; +/* exec sql end declare section */ +#line 32 "app/online/ac_ol_0029.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0029] 매입 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0029] KEY 누락"); + char claimno[16]; + int len; + + tx_log(TX_LOG_INFO, "[AC_OL_0029] 매입 청구조회(EDI) 서비스 진입"); + + /* ---- 1. 입력 검증 ---- */ + if (tx_buf_get(ctx->in, "CLAIMNO", claimno, sizeof(claimno)) != TX_OK || + claimno[0] == '\0') { + tx_log(TX_LOG_ERROR, "[AC_OL_0029] CLAIMNO 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0029] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* ---- 2. 청구번호 정규화 (EDI 고정길이 전문의 트레일링 공백 제거) ---- */ + len = (int)strlen(claimno); + while (len > 0 && claimno[len - 1] == ' ') + claimno[--len] = '\0'; + + if (len == 0) { + tx_log(TX_LOG_ERROR, "[AC_OL_0029] CLAIMNO 공백 전문"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + memset(h_claim_no, 0, sizeof(h_claim_no)); + strncpy(h_claim_no, claimno, sizeof(h_claim_no) - 1); + + /* ---- 3. 청구내역 단건 조회 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select merch_id , biz_date , amount , file_seq , status from ac_claim where claim_no = $1 ", + ECPGt_char,(h_claim_no),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_file_seq),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 70 "app/online/ac_ol_0029.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0029] 청구건 없음 claim_no=%s", claimno); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_claim"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* ---- 4. EDI 연계 정합성 참고 확인 (파일순번 미채번 시 경고만 남김) ---- */ + if (h_file_seq <= 0) { + tx_log(TX_LOG_WARN, + "[AC_OL_0029] 파일순번 미채번 claim_no=%s file_seq=%ld", + claimno, h_file_seq); + } + + /* ---- 5. 상태코드 설명 참고 로그 (응답필드는 원본 코드값 그대로 반환) ---- */ + { + const char *statusdesc = "알수없음"; + + if (strcmp(h_status, "W") == 0) + statusdesc = "대기"; + else if (strcmp(h_status, "S") == 0) + statusdesc = "전송완료"; + else if (strcmp(h_status, "E") == 0) + statusdesc = "오류"; + + tx_log(TX_LOG_DEBUG, "[AC_OL_0029] 상태설명 claim_no=%s status=%s(%s)", + claimno, h_status, statusdesc); + } + + /* ---- 6. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0029] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "CLAIMNO", claimno); + tx_buf_sets(ctx->out, "MERCHID", h_merch_id); + tx_buf_sets(ctx->out, "BIZDATE", h_biz_date); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "FILESEQ", h_file_seq); + tx_buf_sets(ctx->out, "STATUS", h_status); + + tx_log(TX_LOG_INFO, + "[AC_OL_0029] 청구조회완료 claim_no=%s merch=%s amount=%ld status=%s", + claimno, h_merch_id, h_amount, h_status); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0029.pgc b/app/online/ac_ol_0029.pgc index 94d40e5..27b6ca2 100644 --- a/app/online/ac_ol_0029.pgc +++ b/app/online/ac_ol_0029.pgc @@ -2,13 +2,18 @@ /* * ac_ol_0029.pgc - 매입 온라인 서비스 (AC_OL_0029) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 청구조회(EDI): 청구번호(CLAIMNO)로 매입 청구내역(ac_claim)을 + * 단건 조회한다. EDI 연계 파일로 실제 적재된 청구건의 가맹점/영업일/ + * 금액/파일순번/상태를 확인하기 위한 조회 전용 서비스이다. + * + * 청구번호는 EDI 파일 헤더에서 채번되어 전달되므로, 조회 전 앞뒤 + * 공백을 제거하여 정규화한 뒤 원장 키로 사용한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +21,103 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0029, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* ---- ECPG 호스트 변수 선언부 ---- */ + EXEC SQL BEGIN DECLARE SECTION; + char h_claim_no[16]; + char h_merch_id[16]; + char h_biz_date[9]; + long h_amount; + long h_file_seq; + char h_status[2]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0029] 매입 단건조회 서비스 진입"); + char claimno[16]; + int len; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0029] KEY 누락"); + tx_log(TX_LOG_INFO, "[AC_OL_0029] 매입 청구조회(EDI) 서비스 진입"); + + /* ---- 1. 입력 검증 ---- */ + if (tx_buf_get(ctx->in, "CLAIMNO", claimno, sizeof(claimno)) != TX_OK || + claimno[0] == '\0') { + tx_log(TX_LOG_ERROR, "[AC_OL_0029] CLAIMNO 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0029] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* ---- 2. 청구번호 정규화 (EDI 고정길이 전문의 트레일링 공백 제거) ---- */ + len = (int)strlen(claimno); + while (len > 0 && claimno[len - 1] == ' ') + claimno[--len] = '\0'; + + if (len == 0) { + tx_log(TX_LOG_ERROR, "[AC_OL_0029] CLAIMNO 공백 전문"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + memset(h_claim_no, 0, sizeof(h_claim_no)); + strncpy(h_claim_no, claimno, sizeof(h_claim_no) - 1); + + /* ---- 3. 청구내역 단건 조회 ---- */ + EXEC SQL + SELECT merch_id, biz_date, amount, file_seq, status + INTO :h_merch_id, :h_biz_date, :h_amount, :h_file_seq, :h_status + FROM ac_claim + WHERE claim_no = :h_claim_no; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0029] 청구건 없음 claim_no=%s", claimno); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_claim"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* ---- 4. EDI 연계 정합성 참고 확인 (파일순번 미채번 시 경고만 남김) ---- */ + if (h_file_seq <= 0) { + tx_log(TX_LOG_WARN, + "[AC_OL_0029] 파일순번 미채번 claim_no=%s file_seq=%ld", + claimno, h_file_seq); + } + + /* ---- 5. 상태코드 설명 참고 로그 (응답필드는 원본 코드값 그대로 반환) ---- */ + { + const char *statusdesc = "알수없음"; + + if (strcmp(h_status, "W") == 0) + statusdesc = "대기"; + else if (strcmp(h_status, "S") == 0) + statusdesc = "전송완료"; + else if (strcmp(h_status, "E") == 0) + statusdesc = "오류"; + + tx_log(TX_LOG_DEBUG, "[AC_OL_0029] 상태설명 claim_no=%s status=%s(%s)", + claimno, h_status, statusdesc); + } + + /* ---- 6. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0029] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "CLAIMNO", claimno); + tx_buf_sets(ctx->out, "MERCHID", h_merch_id); + tx_buf_sets(ctx->out, "BIZDATE", h_biz_date); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "FILESEQ", h_file_seq); + tx_buf_sets(ctx->out, "STATUS", h_status); + + tx_log(TX_LOG_INFO, + "[AC_OL_0029] 청구조회완료 claim_no=%s merch=%s amount=%ld status=%s", + claimno, h_merch_id, h_amount, h_status); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0030.c b/app/online/ac_ol_0030.c index 3b19a4f..9984420 100644 --- a/app/online/ac_ol_0030.c +++ b/app/online/ac_ol_0030.c @@ -10,13 +10,15 @@ /* * ac_ol_0030.pgc - 매입 온라인 서비스 (AC_OL_0030) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 중복해제: 동일 가맹점/카드번호/금액/영업일 조합으로 중복 접수된 + * ac_ledger 건 중 최초 건(최소 key)만 유효로 남기고 나머지는 중복 + * 무효(status='U') 처리한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +91,168 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0030.pgc" +#line 17 "app/online/ac_ol_0030.pgc" TX_SERVICE(AC_OL_0030, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* ---- ECPG 호스트 변수 선언부 ---- */ + /* exec sql begin declare section */ + + + + + + + +#line 23 "app/online/ac_ol_0030.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/ac_ol_0030.pgc" + char h_card_no [ 20 ] ; + +#line 25 "app/online/ac_ol_0030.pgc" + long h_amount ; + +#line 26 "app/online/ac_ol_0030.pgc" + char h_biz_date [ 9 ] ; + +#line 27 "app/online/ac_ol_0030.pgc" + char h_status_fail [ 2 ] ; + +#line 28 "app/online/ac_ol_0030.pgc" + long h_dupcnt ; +/* exec sql end declare section */ +#line 29 "app/online/ac_ol_0030.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0030] 매입 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0030] KEY 누락"); + char merch[16], cardno[20], bizdate[9]; + long amount = 0; + long voided = 0; + + tx_log(TX_LOG_INFO, "[AC_OL_0030] 매입 중복해제 서비스 진입"); + + /* ---- 1. 입력 검증 ---- */ + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "CARDNO", cardno, sizeof(cardno)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0030] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[AC_OL_0030] 입력 검증 실패 amount=%ld date=%s", + amount, bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_merch_id, 0, sizeof(h_merch_id)); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + memset(h_card_no, 0, sizeof(h_card_no)); + strncpy(h_card_no, cardno, sizeof(h_card_no) - 1); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_amount = amount; + strncpy(h_status_fail, AC_ST_FAIL, sizeof(h_status_fail) - 1); + h_status_fail[sizeof(h_status_fail) - 1] = '\0'; + + /* ---- 2. 중복 집합 크기 사전 확인 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from ac_ledger where merch_id = $1 and card_no = $2 and amount = $3 and biz_date = $4 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_dupcnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 76 "app/online/ac_ol_0030.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT ac_ledger 중복집합 확인"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dupcnt <= 1) { + tx_log(TX_LOG_INFO, "[AC_OL_0030] 중복건 없음 merch=%s card=%s", merch, cardno); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "VOIDED", 0); + tx_return(ctx, TX_OK, ctx->out); return; } + /* ---- 3. 트랜잭션: 최초 건(최소 key) 제외 나머지 무효화 ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0030] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set status = $1 , upd_ts = now ( ) where merch_id = $2 and card_no = $3 and amount = $4 and biz_date = $5 and key <> ( select MIN ( key ) from ac_ledger where merch_id = $6 and card_no = $7 and amount = $8 and biz_date = $9 )", + ECPGt_char,(h_status_fail),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 118 "app/online/ac_ol_0030.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger 중복무효"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + voided = sqlca.sqlerrd[2]; + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0030] 커밋 실패"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 4. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "VOIDED", voided); + + tx_log(TX_LOG_INFO, + "[AC_OL_0030] 중복해제완료 merch=%s card=%s 무효건수=%ld", + merch, cardno, voided); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0030.pgc b/app/online/ac_ol_0030.pgc index baafa90..9396655 100644 --- a/app/online/ac_ol_0030.pgc +++ b/app/online/ac_ol_0030.pgc @@ -2,13 +2,15 @@ /* * ac_ol_0030.pgc - 매입 온라인 서비스 (AC_OL_0030) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 중복해제: 동일 가맹점/카드번호/금액/영업일 조합으로 중복 접수된 + * ac_ledger 건 중 최초 건(최소 key)만 유효로 남기고 나머지는 중복 + * 무효(status='U') 처리한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +18,132 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0030, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* ---- ECPG 호스트 변수 선언부 ---- */ + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_card_no[20]; + long h_amount; + char h_biz_date[9]; + char h_status_fail[2]; + long h_dupcnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0030] 매입 단건조회 서비스 진입"); + char merch[16], cardno[20], bizdate[9]; + long amount = 0; + long voided = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0030] KEY 누락"); + tx_log(TX_LOG_INFO, "[AC_OL_0030] 매입 중복해제 서비스 진입"); + + /* ---- 1. 입력 검증 ---- */ + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "CARDNO", cardno, sizeof(cardno)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0030] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[AC_OL_0030] 입력 검증 실패 amount=%ld date=%s", + amount, bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_merch_id, 0, sizeof(h_merch_id)); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + memset(h_card_no, 0, sizeof(h_card_no)); + strncpy(h_card_no, cardno, sizeof(h_card_no) - 1); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_amount = amount; + strncpy(h_status_fail, AC_ST_FAIL, sizeof(h_status_fail) - 1); + h_status_fail[sizeof(h_status_fail) - 1] = '\0'; + + /* ---- 2. 중복 집합 크기 사전 확인 ---- */ + EXEC SQL + SELECT COUNT(*) + INTO :h_dupcnt + FROM ac_ledger + WHERE merch_id = :h_merch_id + AND card_no = :h_card_no + AND amount = :h_amount + AND biz_date = :h_biz_date; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT ac_ledger 중복집합 확인"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dupcnt <= 1) { + tx_log(TX_LOG_INFO, "[AC_OL_0030] 중복건 없음 merch=%s card=%s", merch, cardno); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "VOIDED", 0); + tx_return(ctx, TX_OK, ctx->out); return; } + /* ---- 3. 트랜잭션: 최초 건(최소 key) 제외 나머지 무효화 ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0030] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL + UPDATE ac_ledger + SET status = :h_status_fail, upd_ts = now() + WHERE merch_id = :h_merch_id + AND card_no = :h_card_no + AND amount = :h_amount + AND biz_date = :h_biz_date + AND key <> ( + SELECT MIN(key) + FROM ac_ledger + WHERE merch_id = :h_merch_id + AND card_no = :h_card_no + AND amount = :h_amount + AND biz_date = :h_biz_date + ); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger 중복무효"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + voided = sqlca.sqlerrd[2]; + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0030] 커밋 실패"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 4. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "VOIDED", voided); + + tx_log(TX_LOG_INFO, + "[AC_OL_0030] 중복해제완료 merch=%s card=%s 무효건수=%ld", + merch, cardno, voided); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0031.c b/app/online/ac_ol_0031.c index b2b1300..badd8d9 100644 --- a/app/online/ac_ol_0031.c +++ b/app/online/ac_ol_0031.c @@ -10,13 +10,14 @@ /* * ac_ol_0031.pgc - 매입 온라인 서비스 (AC_OL_0031) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 보류처리: 매입 원장 단건에 대해 보류 여부(hold_yn)를 설정한다. + * 이미 정산완료(status='S') 된 건은 보류 설정/해제를 허용하지 않는다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,60 +90,136 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0031.pgc" +#line 16 "app/online/ac_ol_0031.pgc" TX_SERVICE(AC_OL_0031, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* ---- ECPG 호스트 변수 선언부 ---- */ + /* exec sql begin declare section */ + + + + + +#line 22 "app/online/ac_ol_0031.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/ac_ol_0031.pgc" + char h_status [ 2 ] ; + +#line 24 "app/online/ac_ol_0031.pgc" + char h_status_settled [ 2 ] ; + +#line 25 "app/online/ac_ol_0031.pgc" + char h_hold [ 2 ] ; +/* exec sql end declare section */ +#line 26 "app/online/ac_ol_0031.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0031] 매입 등록 서비스 진입"); + char key[16], hold[2]; + + tx_log(TX_LOG_INFO, "[AC_OL_0031] 매입 보류처리 서비스 진입"); + + /* ---- 1. 입력 검증 ---- */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_buf_get(ctx->in, "HOLD", hold, sizeof(hold)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0031] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AC_OL_0031] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0031] 영업일 오류 date=%s", bizdate); + if (hold[0] != 'Y' && hold[0] != 'N') { + tx_log(TX_LOG_WARN, "[AC_OL_0031] HOLD 값 오류 hold=%s", hold); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + memset(h_key, 0, sizeof(h_key)); + strncpy(h_key, key, sizeof(h_key) - 1); + strncpy(h_status_settled, AC_ST_SETTLED, sizeof(h_status_settled) - 1); + h_status_settled[sizeof(h_status_settled) - 1] = '\0'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0031] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* ---- 2. 현재 상태 조회 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select status from ac_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 59 "app/online/ac_ol_0031.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0031] 대상건 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger status"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); + /* ---- 3. 업무규칙: 정산완료 건은 보류처리 불가 ---- */ + if (strncmp(h_status, h_status_settled, 1) == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0031] 정산완료 보류불가 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + memset(h_hold, 0, sizeof(h_hold)); + strncpy(h_hold, hold, sizeof(h_hold) - 1); + + /* ---- 4. 보류상태 갱신 ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0031] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set hold_yn = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_char,(h_hold),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 100 "app/online/ac_ol_0031.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger hold_yn"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0031] 커밋 실패"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0031] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "HOLD", hold); + + tx_log(TX_LOG_INFO, "[AC_OL_0031] 보류처리완료 key=%s hold=%s", key, hold); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0031.pgc b/app/online/ac_ol_0031.pgc index 94c4114..b8bfda3 100644 --- a/app/online/ac_ol_0031.pgc +++ b/app/online/ac_ol_0031.pgc @@ -2,13 +2,14 @@ /* * ac_ol_0031.pgc - 매입 온라인 서비스 (AC_OL_0031) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 보류처리: 매입 원장 단건에 대해 보류 여부(hold_yn)를 설정한다. + * 이미 정산완료(status='S') 된 건은 보류 설정/해제를 허용하지 않는다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,55 +17,112 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0031, ctx) { - ac_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* ---- ECPG 호스트 변수 선언부 ---- */ + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_status[2]; + char h_status_settled[2]; + char h_hold[2]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0031] 매입 등록 서비스 진입"); + char key[16], hold[2]; + tx_log(TX_LOG_INFO, "[AC_OL_0031] 매입 보류처리 서비스 진입"); + + /* ---- 1. 입력 검증 ---- */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_buf_get(ctx->in, "HOLD", hold, sizeof(hold)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AC_OL_0031] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AC_OL_0031] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AC_OL_0031] 영업일 오류 date=%s", bizdate); + if (hold[0] != 'Y' && hold[0] != 'N') { + tx_log(TX_LOG_WARN, "[AC_OL_0031] HOLD 값 오류 hold=%s", hold); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + memset(h_key, 0, sizeof(h_key)); + strncpy(h_key, key, sizeof(h_key) - 1); + strncpy(h_status_settled, AC_ST_SETTLED, sizeof(h_status_settled) - 1); + h_status_settled[sizeof(h_status_settled) - 1] = '\0'; - rc = ac_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0031] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* ---- 2. 현재 상태 조회 ---- */ + EXEC SQL + SELECT status + INTO :h_status + FROM ac_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0031] 대상건 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT ac_ledger status"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = ac_rec_count(bizdate, AC_ST_RECV); + /* ---- 3. 업무규칙: 정산완료 건은 보류처리 불가 ---- */ + if (strncmp(h_status, h_status_settled, 1) == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0031] 정산완료 보류불가 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + memset(h_hold, 0, sizeof(h_hold)); + strncpy(h_hold, hold, sizeof(h_hold) - 1); + + /* ---- 4. 보류상태 갱신 ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0031] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL + UPDATE ac_ledger + SET hold_yn = :h_hold, upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger hold_yn"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0031] 커밋 실패"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AC_OL_0031] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "HOLD", hold); + + tx_log(TX_LOG_INFO, "[AC_OL_0031] 보류처리완료 key=%s hold=%s", key, hold); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0032.c b/app/online/ac_ol_0032.c index 24f97dc..84f1964 100644 --- a/app/online/ac_ol_0032.c +++ b/app/online/ac_ol_0032.c @@ -10,13 +10,15 @@ /* * ac_ol_0032.pgc - 매입 온라인 서비스 (AC_OL_0032) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 재승인 매핑: 기존 매입 원장 건에 새로운 승인번호(NEWAPPROVALNO)를 + * 매핑한다. 신규 승인번호가 정상승인(approval.appr_status='A') 상태일 + * 때만 ac_ledger.approval_no 를 갱신하고 완료(status='M') 로 전이한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -89,37 +91,143 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/ac_ol_0032.pgc" +#line 17 "app/online/ac_ol_0032.pgc" TX_SERVICE(AC_OL_0032, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* ---- ECPG 호스트 변수 선언부 ---- */ + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/ac_ol_0032.pgc" + char h_key [ 16 ] ; + +#line 24 "app/online/ac_ol_0032.pgc" + char h_new_appr [ 16 ] ; + +#line 25 "app/online/ac_ol_0032.pgc" + char h_appr_status_ok [ 2 ] ; + +#line 26 "app/online/ac_ol_0032.pgc" + long h_appr_amount ; + +#line 27 "app/online/ac_ol_0032.pgc" + char h_status_done [ 2 ] ; +/* exec sql end declare section */ +#line 28 "app/online/ac_ol_0032.pgc" - tx_log(TX_LOG_INFO, "[AC_OL_0032] 매입 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0032] KEY 누락"); + char key[16], newappr[16]; + long remapped = 0; + + tx_log(TX_LOG_INFO, "[AC_OL_0032] 매입 재승인 매핑 서비스 진입"); + + /* ---- 1. 입력 검증 ---- */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "NEWAPPROVALNO", newappr, sizeof(newappr)) != TX_OK || + newappr[0] == '\0') { + tx_log(TX_LOG_ERROR, "[AC_OL_0032] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_key, 0, sizeof(h_key)); + strncpy(h_key, key, sizeof(h_key) - 1); + memset(h_new_appr, 0, sizeof(h_new_appr)); + strncpy(h_new_appr, newappr, sizeof(h_new_appr) - 1); + strncpy(h_appr_status_ok, "A", sizeof(h_appr_status_ok)); + strncpy(h_status_done, AC_ST_DONE, sizeof(h_status_done) - 1); + h_status_done[sizeof(h_status_done) - 1] = '\0'; + + /* ---- 2. 신규 승인번호 유효성 확인 (정상승인 건만 매핑 허용) ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount from approval where approval_no = $1 and appr_status = $2 ", + ECPGt_char,(h_new_appr),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_appr_status_ok),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_appr_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 60 "app/online/ac_ol_0032.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0032] 유효승인 아님 new_appr=%s", newappr); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT approval"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* ---- 3. 매입 원장에 재승인번호 매핑 ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0032] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update ac_ledger set approval_no = $1 , status = $2 , upd_ts = now ( ) where key = $3 ", + ECPGt_char,(h_new_appr),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status_done),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 89 "app/online/ac_ol_0032.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger approval_no"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0032] 대상건 없음 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + remapped = 1; + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0032] 커밋 실패"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 4. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "NEWAPPROVALNO", newappr); + tx_buf_setlong(ctx->out, "REMAPPED", remapped); + tx_buf_setlong(ctx->out, "AMOUNT", h_appr_amount); + + tx_log(TX_LOG_INFO, + "[AC_OL_0032] 재승인매핑완료 key=%s new_appr=%s amount=%ld", + key, newappr, h_appr_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/ac_ol_0032.pgc b/app/online/ac_ol_0032.pgc index e68d27f..7394f03 100644 --- a/app/online/ac_ol_0032.pgc +++ b/app/online/ac_ol_0032.pgc @@ -2,13 +2,15 @@ /* * ac_ol_0032.pgc - 매입 온라인 서비스 (AC_OL_0032) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 ac 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 재승인 매핑: 기존 매입 원장 건에 새로운 승인번호(NEWAPPROVALNO)를 + * 매핑한다. 신규 승인번호가 정상승인(approval.appr_status='A') 상태일 + * 때만 ac_ledger.approval_no 를 갱신하고 완료(status='M') 로 전이한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "ac_core.h" @@ -16,32 +18,113 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AC_OL_0032, ctx) { - ac_rec_t rec; - char key[16]; - int rc; + /* ---- ECPG 호스트 변수 선언부 ---- */ + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_new_appr[16]; + char h_appr_status_ok[2]; + long h_appr_amount; + char h_status_done[2]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AC_OL_0032] 매입 단건조회 서비스 진입"); + char key[16], newappr[16]; + long remapped = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AC_OL_0032] KEY 누락"); + tx_log(TX_LOG_INFO, "[AC_OL_0032] 매입 재승인 매핑 서비스 진입"); + + /* ---- 1. 입력 검증 ---- */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "NEWAPPROVALNO", newappr, sizeof(newappr)) != TX_OK || + newappr[0] == '\0') { + tx_log(TX_LOG_ERROR, "[AC_OL_0032] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = ac_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AC_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_key, 0, sizeof(h_key)); + strncpy(h_key, key, sizeof(h_key) - 1); + memset(h_new_appr, 0, sizeof(h_new_appr)); + strncpy(h_new_appr, newappr, sizeof(h_new_appr) - 1); + strncpy(h_appr_status_ok, "A", sizeof(h_appr_status_ok)); + strncpy(h_status_done, AC_ST_DONE, sizeof(h_status_done) - 1); + h_status_done[sizeof(h_status_done) - 1] = '\0'; + + /* ---- 2. 신규 승인번호 유효성 확인 (정상승인 건만 매핑 허용) ---- */ + EXEC SQL + SELECT amount + INTO :h_appr_amount + FROM approval + WHERE approval_no = :h_new_appr + AND appr_status = :h_appr_status_ok; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[AC_OL_0032] 유효승인 아님 new_appr=%s", newappr); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT approval"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* ---- 3. 매입 원장에 재승인번호 매핑 ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0032] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL + UPDATE ac_ledger + SET approval_no = :h_new_appr, status = :h_status_done, upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE ac_ledger approval_no"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[AC_OL_0032] 대상건 없음 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + remapped = 1; + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[AC_OL_0032] 커밋 실패"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 4. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AC_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "NEWAPPROVALNO", newappr); + tx_buf_setlong(ctx->out, "REMAPPED", remapped); + tx_buf_setlong(ctx->out, "AMOUNT", h_appr_amount); + + tx_log(TX_LOG_INFO, + "[AC_OL_0032] 재승인매핑완료 key=%s new_appr=%s amount=%ld", + key, newappr, h_appr_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0002.c b/app/online/au_ol_0002.c index ec7aa7d..966a2b5 100644 --- a/app/online/au_ol_0002.c +++ b/app/online/au_ol_0002.c @@ -8,15 +8,16 @@ #line 1 "app/online/au_ol_0002.pgc" /* @tier medium @module au @transform authorization-online */ /* - * au_ol_0002.pgc - 승인한도 온라인 서비스 (AU_OL_0002) [tier=medium] + * au_ol_0002.pgc - 승인 접수등록(중복검사+INSERT) (AU_OL_0002) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,60 +90,98 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0002.pgc" +#line 16 "app/online/au_ol_0002.pgc" TX_SERVICE(AU_OL_0002, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + +#line 21 "app/online/au_ol_0002.pgc" + char h_key [ 16 ] , h_merch [ 16 ] , h_card [ 20 ] , h_bdate [ 9 ] , h_status [ 2 ] ; + +#line 22 "app/online/au_ol_0002.pgc" + long h_amount , h_fee ; + +#line 23 "app/online/au_ol_0002.pgc" + int h_dup ; +/* exec sql end declare section */ +#line 24 "app/online/au_ol_0002.pgc" - tx_log(TX_LOG_INFO, "[AU_OL_0002] 승인한도 등록 서비스 진입"); + char key[16], merch[16], card[20], bdate[16]; + long amount = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0002] 승인 접수등록 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0002] 필수 필드 누락"); + tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) + strcpy(card, "0000"); tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0002] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0002] 영업일 오류 date=%s", bizdate); + if (!amount_is_valid(amount) || !date_is_valid(bdate)) { + tx_log(TX_LOG_WARN, "[AU_OL_0002] 금액/영업일 오류"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, 15); h_key[15] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_dup),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 46 "app/online/au_ol_0002.pgc" - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0002] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (sqlca.sqlcode == TX_SQL_OK && h_dup > 0) { + tx_log(TX_LOG_WARN, "[AU_OL_0002] 중복 승인키 key=%s", key); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = au_rec_count(bizdate, AU_ST_RECV); + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + strncpy(h_card, card, 19); h_card[19] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + strncpy(h_status, AU_ST_RECV, 1); h_status[1] = '\0'; + h_amount = amount; + h_fee = amount / 100; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0002] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into au_ledger ( key , merch_id , card_no , amount , biz_date , status , fee ) values ( $1 , $2 , $3 , $4 , $5 , $6 , $7 )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 62 "app/online/au_ol_0002.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0002 INSERT"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0002] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_log(TX_LOG_INFO, "[AU_OL_0002] 접수완료 key=%s fee=%ld", key, h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0002.pgc b/app/online/au_ol_0002.pgc index b33b8af..820a14d 100644 --- a/app/online/au_ol_0002.pgc +++ b/app/online/au_ol_0002.pgc @@ -1,14 +1,15 @@ /* @tier medium @module au @transform authorization-online */ /* - * au_ol_0002.pgc - 승인한도 온라인 서비스 (AU_OL_0002) [tier=medium] + * au_ol_0002.pgc - 승인 접수등록(중복검사+INSERT) (AU_OL_0002) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,55 +17,61 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0002, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_merch[16], h_card[20], h_bdate[9], h_status[2]; + long h_amount, h_fee; + int h_dup; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AU_OL_0002] 승인한도 등록 서비스 진입"); + char key[16], merch[16], card[20], bdate[16]; + long amount = 0; + tx_log(TX_LOG_INFO, "[AU_OL_0002] 승인 접수등록 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0002] 필수 필드 누락"); + tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) + strcpy(card, "0000"); tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0002] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0002] 영업일 오류 date=%s", bizdate); + if (!amount_is_valid(amount) || !date_is_valid(bdate)) { + tx_log(TX_LOG_WARN, "[AU_OL_0002] 금액/영업일 오류"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0002] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, 15); h_key[15] = '\0'; + EXEC SQL SELECT count(*) INTO :h_dup FROM au_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_OK && h_dup > 0) { + tx_log(TX_LOG_WARN, "[AU_OL_0002] 중복 승인키 key=%s", key); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = au_rec_count(bizdate, AU_ST_RECV); + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + strncpy(h_card, card, 19); h_card[19] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + strncpy(h_status, AU_ST_RECV, 1); h_status[1] = '\0'; + h_amount = amount; + h_fee = amount / 100; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0002] tx_begin 경고"); + EXEC SQL INSERT INTO au_ledger (key, merch_id, card_no, amount, biz_date, status, fee) + VALUES (:h_key, :h_merch, :h_card, :h_amount, :h_bdate, :h_status, :h_fee); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0002 INSERT"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0002] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_log(TX_LOG_INFO, "[AU_OL_0002] 접수완료 key=%s fee=%ld", key, h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0003.c b/app/online/au_ol_0003.c index d84f719..260d872 100644 --- a/app/online/au_ol_0003.c +++ b/app/online/au_ol_0003.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0003.pgc" -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0003.pgc - 승인한도 온라인 서비스 (AU_OL_0003) [tier=easy] + * au_ol_0003.pgc - 가맹점 한도 조회(잔여한도 산출) (AU_OL_0003) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,67 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0003.pgc" +#line 16 "app/online/au_ol_0003.pgc" TX_SERVICE(AU_OL_0003, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0003] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0003] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/au_ol_0003.pgc" + char h_merch [ 16 ] , h_bdate [ 9 ] ; + +#line 28 "app/online/au_ol_0003.pgc" + long h_used ; +/* exec sql end declare section */ +#line 29 "app/online/au_ol_0003.pgc" - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + + char merch[16], bdate[16]; + long limit_amt = 50000000L, remain; + + tx_log(TX_LOG_INFO, "[AU_OL_0003] 가맹점 한도조회 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from au_ledger where merch_id = $1 and biz_date = $2 and status in ( 'R' , 'M' , 'S' )", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_used),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 48 "app/online/au_ol_0003.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0003 한도조회"); + tx_return(ctx, TX_EDB, ctx->out); return; } + remain = limit_amt - h_used; + if (remain < 0) remain = 0; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "LIMIT", limit_amt); + tx_buf_setlong(ctx->out, "USED", h_used); + tx_buf_setlong(ctx->out, "REMAIN", remain); + tx_log(TX_LOG_INFO, "[AU_OL_0003] 한도 used=%ld remain=%ld", h_used, remain); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0003.pgc b/app/online/au_ol_0003.pgc index 932f6a7..e9a0ea6 100644 --- a/app/online/au_ol_0003.pgc +++ b/app/online/au_ol_0003.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0003.pgc - 승인한도 온라인 서비스 (AU_OL_0003) [tier=easy] + * au_ol_0003.pgc - 가맹점 한도 조회(잔여한도 산출) (AU_OL_0003) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,49 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0003, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0003] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0003] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16], h_bdate[9]; + long h_used; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + char merch[16], bdate[16]; + long limit_amt = 50000000L, remain; + + tx_log(TX_LOG_INFO, "[AU_OL_0003] 가맹점 한도조회 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + + EXEC SQL SELECT coalesce(sum(amount), 0) INTO :h_used + FROM au_ledger + WHERE merch_id = :h_merch AND biz_date = :h_bdate + AND status IN ('R', 'M', 'S'); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0003 한도조회"); + tx_return(ctx, TX_EDB, ctx->out); return; } + remain = limit_amt - h_used; + if (remain < 0) remain = 0; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "LIMIT", limit_amt); + tx_buf_setlong(ctx->out, "USED", h_used); + tx_buf_setlong(ctx->out, "REMAIN", remain); + tx_log(TX_LOG_INFO, "[AU_OL_0003] 한도 used=%ld remain=%ld", h_used, remain); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0004.c b/app/online/au_ol_0004.c index 64d8bb7..15f52a4 100644 --- a/app/online/au_ol_0004.c +++ b/app/online/au_ol_0004.c @@ -8,15 +8,16 @@ #line 1 "app/online/au_ol_0004.pgc" /* @tier medium @module au @transform authorization-online */ /* - * au_ol_0004.pgc - 승인한도 온라인 서비스 (AU_OL_0004) [tier=medium] + * au_ol_0004.pgc - 승인 한도차감(UPDATE 잔액 감액) (AU_OL_0004) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,60 +90,89 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0004.pgc" +#line 16 "app/online/au_ol_0004.pgc" TX_SERVICE(AU_OL_0004, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0004] 승인한도 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0004] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0004] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/au_ol_0004.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; + +#line 28 "app/online/au_ol_0004.pgc" + long h_amount , h_bal ; +/* exec sql end declare section */ +#line 29 "app/online/au_ol_0004.pgc" + + + long amount = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0004] 승인 한도차감 진입"); tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0004] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0004] 영업일 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_amount = amount; - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0004] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , status from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_bal),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 44 "app/online/au_ol_0004.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (h_bal < h_amount) { + tx_log(TX_LOG_WARN, "[AU_OL_0004] 한도부족 bal=%ld req=%ld", h_bal, h_amount); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = au_rec_count(bizdate, AU_ST_RECV); + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0004] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set amount = amount - $1 , upd_ts = now ( ) where key = $2 and status = 'R'", + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 60 "app/online/au_ol_0004.pgc" + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] == 0) { + TX_DBIO_LOG_ERR("au_ol_0004 차감"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_bal),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 67 "app/online/au_ol_0004.pgc" + + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0004] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "REMAIN", h_bal); + tx_log(TX_LOG_INFO, "[AU_OL_0004] 차감완료 잔액=%ld", h_bal); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0004.pgc b/app/online/au_ol_0004.pgc index 0ef040f..f2cbb2e 100644 --- a/app/online/au_ol_0004.pgc +++ b/app/online/au_ol_0004.pgc @@ -1,14 +1,15 @@ /* @tier medium @module au @transform authorization-online */ /* - * au_ol_0004.pgc - 승인한도 온라인 서비스 (AU_OL_0004) [tier=medium] + * au_ol_0004.pgc - 승인 한도차감(UPDATE 잔액 감액) (AU_OL_0004) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,55 +17,59 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0004, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0004] 승인한도 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0004] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0004] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2]; + long h_amount, h_bal; + EXEC SQL END DECLARE SECTION; + + long amount = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0004] 승인 한도차감 진입"); tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0004] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0004] 영업일 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_amount = amount; - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0004] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL SELECT amount, status INTO :h_bal, :h_status + FROM au_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (h_bal < h_amount) { + tx_log(TX_LOG_WARN, "[AU_OL_0004] 한도부족 bal=%ld req=%ld", h_bal, h_amount); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = au_rec_count(bizdate, AU_ST_RECV); + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0004] tx_begin 경고"); + EXEC SQL UPDATE au_ledger + SET amount = amount - :h_amount, upd_ts = now() + WHERE key = :h_key AND status = 'R'; + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] == 0) { + TX_DBIO_LOG_ERR("au_ol_0004 차감"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + EXEC SQL SELECT amount INTO :h_bal FROM au_ledger WHERE key = :h_key; + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0004] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "REMAIN", h_bal); + tx_log(TX_LOG_INFO, "[AU_OL_0004] 차감완료 잔액=%ld", h_bal); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0005.c b/app/online/au_ol_0005.c index 562c015..183ae55 100644 --- a/app/online/au_ol_0005.c +++ b/app/online/au_ol_0005.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0005.pgc" -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0005.pgc - 승인한도 온라인 서비스 (AU_OL_0005) [tier=easy] + * au_ol_0005.pgc - 승인 한도복원(취소분 가산) (AU_OL_0005) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,73 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0005.pgc" +#line 16 "app/online/au_ol_0005.pgc" TX_SERVICE(AU_OL_0005, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0005] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0005] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/au_ol_0005.pgc" + char h_key [ 16 ] ; + +#line 28 "app/online/au_ol_0005.pgc" + long h_amount , h_bal ; +/* exec sql end declare section */ +#line 29 "app/online/au_ol_0005.pgc" - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0005] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + + long amount = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0005] 승인 한도복원 진입"); + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (amount <= 0) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_amount = amount; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0005] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set amount = amount + $1 , status = 'R' , upd_ts = now ( ) where key = $2 and status in ( 'U' , 'M' )", + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 45 "app/online/au_ol_0005.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0005 복원"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_abort(); + tx_log(TX_LOG_WARN, "[AU_OL_0005] 복원 대상없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_bal),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 58 "app/online/au_ol_0005.pgc" + + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0005] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "REMAIN", h_bal); + tx_log(TX_LOG_INFO, "[AU_OL_0005] 복원완료 key=%s 잔액=%ld", key, h_bal); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0005.pgc b/app/online/au_ol_0005.pgc index 7935312..6598cf3 100644 --- a/app/online/au_ol_0005.pgc +++ b/app/online/au_ol_0005.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0005.pgc - 승인한도 온라인 서비스 (AU_OL_0005) [tier=easy] + * au_ol_0005.pgc - 승인 한도복원(취소분 가산) (AU_OL_0005) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,50 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0005, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0005] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0005] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_amount, h_bal; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0005] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + long amount = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0005] 승인 한도복원 진입"); + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (amount <= 0) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_amount = amount; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0005] tx_begin 경고"); + EXEC SQL UPDATE au_ledger + SET amount = amount + :h_amount, status = 'R', upd_ts = now() + WHERE key = :h_key AND status IN ('U', 'M'); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0005 복원"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_abort(); + tx_log(TX_LOG_WARN, "[AU_OL_0005] 복원 대상없음 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + EXEC SQL SELECT amount INTO :h_bal FROM au_ledger WHERE key = :h_key; + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0005] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "REMAIN", h_bal); + tx_log(TX_LOG_INFO, "[AU_OL_0005] 복원완료 key=%s 잔액=%ld", key, h_bal); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0006.c b/app/online/au_ol_0006.c index 4513c5a..c783baf 100644 --- a/app/online/au_ol_0006.c +++ b/app/online/au_ol_0006.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0006.pgc" -/* @tier medium @module au @transform authorization-online */ +/* @tier easy @module au @transform authorization-online */ /* - * au_ol_0006.pgc - 승인한도 온라인 서비스 (AU_OL_0006) [tier=medium] + * au_ol_0006.pgc - 승인 취소(전체취소 상태전이) (AU_OL_0006) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,60 +90,70 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0006.pgc" +#line 16 "app/online/au_ol_0006.pgc" TX_SERVICE(AU_OL_0006, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0006] 승인한도 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0006] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0006] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + + +#line 27 "app/online/au_ol_0006.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; + +#line 28 "app/online/au_ol_0006.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 29 "app/online/au_ol_0006.pgc" - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0006] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); + + tx_log(TX_LOG_INFO, "[AU_OL_0006] 승인 전체취소 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , status from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 35 "app/online/au_ol_0006.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0006] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + if (h_status[0] == 'S') { + tx_log(TX_LOG_WARN, "[AU_OL_0006] 정산완료건 취소불가 key=%s", key); + tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0006] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set status = 'U' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 49 "app/online/au_ol_0006.pgc" - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0006] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0006 취소"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = au_rec_count(bizdate, AU_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0006] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "CANCELAMT", h_amount); + tx_log(TX_LOG_INFO, "[AU_OL_0006] 취소완료 key=%s 금액=%ld", key, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0006.pgc b/app/online/au_ol_0006.pgc index 03dc5b2..7763cd7 100644 --- a/app/online/au_ol_0006.pgc +++ b/app/online/au_ol_0006.pgc @@ -1,14 +1,15 @@ -/* @tier medium @module au @transform authorization-online */ +/* @tier easy @module au @transform authorization-online */ /* - * au_ol_0006.pgc - 승인한도 온라인 서비스 (AU_OL_0006) [tier=medium] + * au_ol_0006.pgc - 승인 취소(전체취소 상태전이) (AU_OL_0006) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,55 +17,48 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0006, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0006] 승인한도 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0006] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0006] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0006] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); + tx_log(TX_LOG_INFO, "[AU_OL_0006] 승인 전체취소 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT amount, status INTO :h_amount, :h_status + FROM au_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0006] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + if (h_status[0] == 'S') { + tx_log(TX_LOG_WARN, "[AU_OL_0006] 정산완료건 취소불가 key=%s", key); + tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0006] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0006] tx_begin 경고"); + EXEC SQL UPDATE au_ledger + SET status = 'U', upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0006 취소"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = au_rec_count(bizdate, AU_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0006] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "CANCELAMT", h_amount); + tx_log(TX_LOG_INFO, "[AU_OL_0006] 취소완료 key=%s 금액=%ld", key, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0007.c b/app/online/au_ol_0007.c index 280e2d7..ceb69fd 100644 --- a/app/online/au_ol_0007.c +++ b/app/online/au_ol_0007.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0007.pgc" -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0007.pgc - 승인한도 온라인 서비스 (AU_OL_0007) [tier=easy] + * au_ol_0007.pgc - 승인 부분취소(부분금액 차감) (AU_OL_0007) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,86 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0007.pgc" +#line 16 "app/online/au_ol_0007.pgc" TX_SERVICE(AU_OL_0007, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0007] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0007] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/au_ol_0007.pgc" + char h_key [ 16 ] ; + +#line 28 "app/online/au_ol_0007.pgc" + long h_part , h_orig , h_new ; +/* exec sql end declare section */ +#line 29 "app/online/au_ol_0007.pgc" - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0007] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + + long part = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0007] 승인 부분취소 진입"); + tx_buf_getlong(ctx->in, "CANCELAMT", &part); + if (part <= 0) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_part = part; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_orig),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 42 "app/online/au_ol_0007.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (h_part > h_orig) { + tx_log(TX_LOG_WARN, "[AU_OL_0007] 취소금액 초과 orig=%ld req=%ld", h_orig, h_part); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0007] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set amount = amount - $1 , fee = ( amount - $2 ) / 100 , upd_ts = now ( ) where key = $3 ", + ECPGt_long,&(h_part),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_part),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 56 "app/online/au_ol_0007.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0007 부분취소"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_new),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 63 "app/online/au_ol_0007.pgc" + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0007] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_setlong(ctx->out, "REMAINAMT", h_new); + tx_log(TX_LOG_INFO, "[AU_OL_0007] 부분취소 완료 잔액=%ld", h_new); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0007.pgc b/app/online/au_ol_0007.pgc index 22384ed..4d235bf 100644 --- a/app/online/au_ol_0007.pgc +++ b/app/online/au_ol_0007.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0007.pgc - 승인한도 온라인 서비스 (AU_OL_0007) [tier=easy] + * au_ol_0007.pgc - 승인 부분취소(부분금액 차감) (AU_OL_0007) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,55 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0007, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0007] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0007] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_part, h_orig, h_new; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0007] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + long part = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0007] 승인 부분취소 진입"); + tx_buf_getlong(ctx->in, "CANCELAMT", &part); + if (part <= 0) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_part = part; + + EXEC SQL SELECT amount INTO :h_orig FROM au_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (h_part > h_orig) { + tx_log(TX_LOG_WARN, "[AU_OL_0007] 취소금액 초과 orig=%ld req=%ld", h_orig, h_part); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0007] tx_begin 경고"); + EXEC SQL UPDATE au_ledger + SET amount = amount - :h_part, fee = (amount - :h_part) / 100, upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0007 부분취소"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + EXEC SQL SELECT amount INTO :h_new FROM au_ledger WHERE key = :h_key; + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0007] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_setlong(ctx->out, "REMAINAMT", h_new); + tx_log(TX_LOG_INFO, "[AU_OL_0007] 부분취소 완료 잔액=%ld", h_new); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0008.c b/app/online/au_ol_0008.c index 559812c..7741788 100644 --- a/app/online/au_ol_0008.c +++ b/app/online/au_ol_0008.c @@ -8,15 +8,16 @@ #line 1 "app/online/au_ol_0008.pgc" /* @tier hard @module au @transform authorization-online */ /* - * au_ol_0008.pgc - 승인한도 온라인 서비스 (AU_OL_0008) [tier=hard] + * au_ol_0008.pgc - 승인요청 매입사 중계(tx_call) (AU_OL_0008) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,88 +90,53 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0008.pgc" +#line 16 "app/online/au_ol_0008.pgc" TX_SERVICE(AU_OL_0008, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0008] KEY 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + TXBUF *req; + char merch[16], card[20], resp[8]; + long amount = 0; int rc; - TXBUF *sub_in; - TXBUF *sub_out; - tx_log(TX_LOG_INFO, "[AU_OL_0008] 승인한도 트랜잭션 오케스트레이션 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0008] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[AU_OL_0008] 승인요청 중계 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0008] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + req = tx_buf_alloc(); + if (!req) { + tx_return(ctx, TX_ENOMEM, ctx->out); return; } + tx_buf_sets(req, "KEY", key); + tx_buf_sets(req, "MERCHID", merch); + tx_buf_sets(req, "CARDNO", card); + tx_buf_setlong(req, "AMOUNT", amount); + tx_buf_sets(req, "MTI", MSG_TYPE_RECV); - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = au_rec_insert(&rec); + rc = tx_call("AU_ISSUER", req, ctx->out); + tx_buf_free(req); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0008] 원장반영 실패 rc=%d", rc); - tx_abort(); + tx_log(TX_LOG_ERROR, "[AU_OL_0008] 매입사 중계 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, rc, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AU_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AU_OL_0008] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (au_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AU_OL_0008] 완료 key=%s 당일합계=%ld", key, daysum); + if (tx_buf_get(ctx->out, "RESPCODE", resp, sizeof(resp)) != TX_OK) + strcpy(resp, RESP_OK); + tx_log(TX_LOG_INFO, "[AU_OL_0008] 중계 응답 key=%s resp=%s", key, resp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0008.pgc b/app/online/au_ol_0008.pgc index 6ff2762..71789af 100644 --- a/app/online/au_ol_0008.pgc +++ b/app/online/au_ol_0008.pgc @@ -1,14 +1,15 @@ /* @tier hard @module au @transform authorization-online */ /* - * au_ol_0008.pgc - 승인한도 온라인 서비스 (AU_OL_0008) [tier=hard] + * au_ol_0008.pgc - 승인요청 매입사 중계(tx_call) (AU_OL_0008) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,83 +17,48 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0008, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0008] KEY 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + TXBUF *req; + char merch[16], card[20], resp[8]; + long amount = 0; int rc; - TXBUF *sub_in; - TXBUF *sub_out; - tx_log(TX_LOG_INFO, "[AU_OL_0008] 승인한도 트랜잭션 오케스트레이션 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0008] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[AU_OL_0008] 승인요청 중계 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0008] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + req = tx_buf_alloc(); + if (!req) { + tx_return(ctx, TX_ENOMEM, ctx->out); return; } + tx_buf_sets(req, "KEY", key); + tx_buf_sets(req, "MERCHID", merch); + tx_buf_sets(req, "CARDNO", card); + tx_buf_setlong(req, "AMOUNT", amount); + tx_buf_sets(req, "MTI", MSG_TYPE_RECV); - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = au_rec_insert(&rec); + rc = tx_call("AU_ISSUER", req, ctx->out); + tx_buf_free(req); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0008] 원장반영 실패 rc=%d", rc); - tx_abort(); + tx_log(TX_LOG_ERROR, "[AU_OL_0008] 매입사 중계 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, rc, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AU_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AU_OL_0008] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (au_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AU_OL_0008] 완료 key=%s 당일합계=%ld", key, daysum); + if (tx_buf_get(ctx->out, "RESPCODE", resp, sizeof(resp)) != TX_OK) + strcpy(resp, RESP_OK); + tx_log(TX_LOG_INFO, "[AU_OL_0008] 중계 응답 key=%s resp=%s", key, resp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0009.c b/app/online/au_ol_0009.c index f54810e..6726141 100644 --- a/app/online/au_ol_0009.c +++ b/app/online/au_ol_0009.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0009.pgc" -/* @tier easy @module au @transform authorization-online */ +/* @tier hard @module au @transform authorization-online */ /* - * au_ol_0009.pgc - 승인한도 온라인 서비스 (AU_OL_0009) [tier=easy] + * au_ol_0009.pgc - 대행승인 fallback(주센터 실패시 백업) (AU_OL_0009) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,50 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0009.pgc" +#line 16 "app/online/au_ol_0009.pgc" TX_SERVICE(AU_OL_0009, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0009] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0009] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + TXBUF *req; + long amount = 0; + int rc; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); + tx_log(TX_LOG_INFO, "[AU_OL_0009] 대행승인 fallback 진입"); + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + req = tx_buf_alloc(); + if (!req) { + tx_return(ctx, TX_ENOMEM, ctx->out); + return; + } + tx_buf_sets(req, "KEY", key); + tx_buf_setlong(req, "AMOUNT", amount); + + rc = tx_call("AU_ISSUER", req, ctx->out); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0009] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[AU_OL_0009] 주센터 실패 rc=%d, 대행센터 전환", rc); + tx_buf_reset(ctx->out); + rc = tx_call("AU_STANDIN", req, ctx->out); + if (rc == TX_OK) + tx_buf_sets(ctx->out, "STANDIN", "Y"); + } else { + tx_buf_sets(ctx->out, "STANDIN", "N"); + } + tx_buf_free(req); + + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0009] 대행승인까지 실패 rc=%d", rc); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, rc, ctx->out); return; } - - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0009] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_log(TX_LOG_INFO, "[AU_OL_0009] fallback 처리완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0009.pgc b/app/online/au_ol_0009.pgc index 3def24b..f1300e5 100644 --- a/app/online/au_ol_0009.pgc +++ b/app/online/au_ol_0009.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module au @transform authorization-online */ +/* @tier hard @module au @transform authorization-online */ /* - * au_ol_0009.pgc - 승인한도 온라인 서비스 (AU_OL_0009) [tier=easy] + * au_ol_0009.pgc - 대행승인 fallback(주센터 실패시 백업) (AU_OL_0009) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,45 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0009, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0009] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0009] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + TXBUF *req; + long amount = 0; + int rc; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); + tx_log(TX_LOG_INFO, "[AU_OL_0009] 대행승인 fallback 진입"); + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + req = tx_buf_alloc(); + if (!req) { + tx_return(ctx, TX_ENOMEM, ctx->out); + return; + } + tx_buf_sets(req, "KEY", key); + tx_buf_setlong(req, "AMOUNT", amount); + + rc = tx_call("AU_ISSUER", req, ctx->out); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0009] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[AU_OL_0009] 주센터 실패 rc=%d, 대행센터 전환", rc); + tx_buf_reset(ctx->out); + rc = tx_call("AU_STANDIN", req, ctx->out); + if (rc == TX_OK) + tx_buf_sets(ctx->out, "STANDIN", "Y"); + } else { + tx_buf_sets(ctx->out, "STANDIN", "N"); + } + tx_buf_free(req); + + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0009] 대행승인까지 실패 rc=%d", rc); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, rc, ctx->out); return; } - - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0009] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_log(TX_LOG_INFO, "[AU_OL_0009] fallback 처리완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0010.c b/app/online/au_ol_0010.c index b9b4959..7bd48b1 100644 --- a/app/online/au_ol_0010.c +++ b/app/online/au_ol_0010.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0010.pgc" -/* @tier easy @module au @transform authorization-online */ +/* @tier hard @module au @transform authorization-online */ /* - * au_ol_0010.pgc - 승인한도 온라인 서비스 (AU_OL_0010) [tier=easy] + * au_ol_0010.pgc - 카드유효성 연계 후 승인접수 (AU_OL_0010) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,88 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0010.pgc" +#line 16 "app/online/au_ol_0010.pgc" TX_SERVICE(AU_OL_0010, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0010] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0010] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/au_ol_0010.pgc" + char h_key [ 16 ] , h_card [ 20 ] , h_bdate [ 9 ] , h_status [ 2 ] ; + +#line 28 "app/online/au_ol_0010.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 29 "app/online/au_ol_0010.pgc" - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + + TXBUF *req; + char card[20], vresp[8]; + long amount = 0; + int rc; + + tx_log(TX_LOG_INFO, "[AU_OL_0010] 카드유효성 연계승인 진입"); + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + req = tx_buf_alloc(); + if (!req) { tx_return(ctx, TX_ENOMEM, ctx->out); return; } + tx_buf_sets(req, "CARDNO", card); + rc = tx_call("VL_CARD", req, ctx->out); + tx_buf_free(req); + if (rc != TX_OK || + (tx_buf_get(ctx->out, "RESPCODE", vresp, sizeof(vresp)) == TX_OK && + strcmp(vresp, RESP_OK) != 0)) { + tx_log(TX_LOG_WARN, "[AU_OL_0010] 카드 유효성 실패 card=%s", card); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } + strncpy(h_key, key, 15); h_key[15] = '\0'; + strncpy(h_card, card, 19); h_card[19] = '\0'; + date_today(h_bdate); + strncpy(h_status, AU_ST_RECV, 1); h_status[1] = '\0'; + h_amount = amount; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0010] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into au_ledger ( key , merch_id , card_no , amount , biz_date , status , fee ) values ( $1 , 'ONLINE' , $2 , $3 , $4 , $5 , 0 )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 66 "app/online/au_ol_0010.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0010 접수"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_log(TX_LOG_INFO, "[AU_OL_0010] 카드검증+접수 완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0010.pgc b/app/online/au_ol_0010.pgc index 453e9c4..5bc22ac 100644 --- a/app/online/au_ol_0010.pgc +++ b/app/online/au_ol_0010.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module au @transform authorization-online */ +/* @tier hard @module au @transform authorization-online */ /* - * au_ol_0010.pgc - 승인한도 온라인 서비스 (AU_OL_0010) [tier=easy] + * au_ol_0010.pgc - 카드유효성 연계 후 승인접수 (AU_OL_0010) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,64 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0010, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0010] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0010] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_card[20], h_bdate[9], h_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + TXBUF *req; + char card[20], vresp[8]; + long amount = 0; + int rc; + + tx_log(TX_LOG_INFO, "[AU_OL_0010] 카드유효성 연계승인 진입"); + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + req = tx_buf_alloc(); + if (!req) { tx_return(ctx, TX_ENOMEM, ctx->out); return; } + tx_buf_sets(req, "CARDNO", card); + rc = tx_call("VL_CARD", req, ctx->out); + tx_buf_free(req); + if (rc != TX_OK || + (tx_buf_get(ctx->out, "RESPCODE", vresp, sizeof(vresp)) == TX_OK && + strcmp(vresp, RESP_OK) != 0)) { + tx_log(TX_LOG_WARN, "[AU_OL_0010] 카드 유효성 실패 card=%s", card); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } + strncpy(h_key, key, 15); h_key[15] = '\0'; + strncpy(h_card, card, 19); h_card[19] = '\0'; + date_today(h_bdate); + strncpy(h_status, AU_ST_RECV, 1); h_status[1] = '\0'; + h_amount = amount; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0010] tx_begin 경고"); + EXEC SQL INSERT INTO au_ledger (key, merch_id, card_no, amount, biz_date, status, fee) + VALUES (:h_key, 'ONLINE', :h_card, :h_amount, :h_bdate, :h_status, 0); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0010 접수"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_log(TX_LOG_INFO, "[AU_OL_0010] 카드검증+접수 완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0011.c b/app/online/au_ol_0011.c index c94e200..f7d40ad 100644 --- a/app/online/au_ol_0011.c +++ b/app/online/au_ol_0011.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0011.pgc" -/* @tier medium @module au @transform authorization-online */ +/* @tier hard @module au @transform authorization-online */ /* - * au_ol_0011.pgc - 승인한도 온라인 서비스 (AU_OL_0011) [tier=medium] + * au_ol_0011.pgc - 이상거래 탐지(카드 단시간 다건) (AU_OL_0011) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,60 +90,75 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0011.pgc" +#line 16 "app/online/au_ol_0011.pgc" TX_SERVICE(AU_OL_0011, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0011] 승인한도 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0011] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0011] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + + + +#line 27 "app/online/au_ol_0011.pgc" + char h_card [ 20 ] , h_bdate [ 9 ] ; + +#line 28 "app/online/au_ol_0011.pgc" + int h_cnt ; + +#line 29 "app/online/au_ol_0011.pgc" + long h_sum ; +/* exec sql end declare section */ +#line 30 "app/online/au_ol_0011.pgc" - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0011] 금액 범위 오류 amount=%ld", amount); + + char card[20], bdate[16]; + int fraud = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0011] 이상거래 탐지 진입"); + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0011] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + + strncpy(h_card, card, 19); h_card[19] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from au_ledger where card_no = $1 and biz_date = $2 ", + ECPGt_char,(h_card),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 49 "app/online/au_ol_0011.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0011 탐지"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0011] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = au_rec_count(bizdate, AU_ST_RECV); + if (h_cnt >= 10 || h_sum >= 30000000L) + fraud = 1; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0011] 등록완료 key=%s 당일건수=%ld", key, cnt); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "CARDNO", card); + tx_buf_setlong(ctx->out, "TXNCNT", h_cnt); + tx_buf_setlong(ctx->out, "TXNSUM", h_sum); + tx_buf_sets(ctx->out, "FRAUD", fraud ? "Y" : "N"); + tx_buf_sets(ctx->out, "RESPCODE", fraud ? RESP_INVALID : RESP_OK); + tx_log(TX_LOG_INFO, "[AU_OL_0011] 탐지결과 card=%s cnt=%d sum=%ld fraud=%d", + card, h_cnt, h_sum, fraud); + tx_return(ctx, fraud ? TX_FAIL : TX_OK, ctx->out); } diff --git a/app/online/au_ol_0011.pgc b/app/online/au_ol_0011.pgc index 36a3a95..effaea8 100644 --- a/app/online/au_ol_0011.pgc +++ b/app/online/au_ol_0011.pgc @@ -1,14 +1,15 @@ -/* @tier medium @module au @transform authorization-online */ +/* @tier hard @module au @transform authorization-online */ /* - * au_ol_0011.pgc - 승인한도 온라인 서비스 (AU_OL_0011) [tier=medium] + * au_ol_0011.pgc - 이상거래 탐지(카드 단시간 다건) (AU_OL_0011) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,55 +17,52 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0011, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0011] 승인한도 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0011] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0011] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_card[20], h_bdate[9]; + int h_cnt; + long h_sum; + EXEC SQL END DECLARE SECTION; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0011] 금액 범위 오류 amount=%ld", amount); + char card[20], bdate[16]; + int fraud = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0011] 이상거래 탐지 진입"); + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0011] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + + strncpy(h_card, card, 19); h_card[19] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_cnt, :h_sum + FROM au_ledger + WHERE card_no = :h_card AND biz_date = :h_bdate; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0011 탐지"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0011] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = au_rec_count(bizdate, AU_ST_RECV); + if (h_cnt >= 10 || h_sum >= 30000000L) + fraud = 1; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0011] 등록완료 key=%s 당일건수=%ld", key, cnt); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "CARDNO", card); + tx_buf_setlong(ctx->out, "TXNCNT", h_cnt); + tx_buf_setlong(ctx->out, "TXNSUM", h_sum); + tx_buf_sets(ctx->out, "FRAUD", fraud ? "Y" : "N"); + tx_buf_sets(ctx->out, "RESPCODE", fraud ? RESP_INVALID : RESP_OK); + tx_log(TX_LOG_INFO, "[AU_OL_0011] 탐지결과 card=%s cnt=%d sum=%ld fraud=%d", + card, h_cnt, h_sum, fraud); + tx_return(ctx, fraud ? TX_FAIL : TX_OK, ctx->out); } diff --git a/app/online/au_ol_0012.c b/app/online/au_ol_0012.c index 9b60028..686fe5e 100644 --- a/app/online/au_ol_0012.c +++ b/app/online/au_ol_0012.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0012.pgc" -/* @tier easy @module au @transform authorization-online */ +/* @tier hard @module au @transform authorization-online */ /* - * au_ol_0012.pgc - 승인한도 온라인 서비스 (AU_OL_0012) [tier=easy] + * au_ol_0012.pgc - 승인 재시도(실패건 매입사 재요청) (AU_OL_0012) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,77 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0012.pgc" +#line 16 "app/online/au_ol_0012.pgc" TX_SERVICE(AU_OL_0012, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0012] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0012] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/au_ol_0012.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; + +#line 28 "app/online/au_ol_0012.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 29 "app/online/au_ol_0012.pgc" - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + + TXBUF *req; + int rc, attempt; + + tx_log(TX_LOG_INFO, "[AU_OL_0012] 승인 재시도 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , status from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 38 "app/online/au_ol_0012.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (h_status[0] != 'U') { + tx_log(TX_LOG_WARN, "[AU_OL_0012] 재시도 대상아님 status=%s", h_status); + tx_return(ctx, TX_FAIL, ctx->out); return; } - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + req = tx_buf_alloc(); + if (!req) { tx_return(ctx, TX_ENOMEM, ctx->out); return; } + tx_buf_sets(req, "KEY", key); + tx_buf_setlong(req, "AMOUNT", h_amount); + + rc = TX_FAIL; + for (attempt = 1; attempt <= 3; attempt++) { + tx_buf_reset(ctx->out); + rc = tx_call("AU_ISSUER", req, ctx->out); + tx_log(TX_LOG_INFO, "[AU_OL_0012] 재시도 %d회 rc=%d", attempt, rc); + if (rc == TX_OK) break; + } + tx_buf_free(req); + + if (rc == TX_OK) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set status = 'M' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 65 "app/online/au_ol_0012.pgc" + + tx_commit(); + } + tx_buf_setlong(ctx->out, "ATTEMPTS", attempt); + tx_return(ctx, rc, ctx->out); } diff --git a/app/online/au_ol_0012.pgc b/app/online/au_ol_0012.pgc index 3c93a7a..61de437 100644 --- a/app/online/au_ol_0012.pgc +++ b/app/online/au_ol_0012.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module au @transform authorization-online */ +/* @tier hard @module au @transform authorization-online */ /* - * au_ol_0012.pgc - 승인한도 온라인 서비스 (AU_OL_0012) [tier=easy] + * au_ol_0012.pgc - 승인 재시도(실패건 매입사 재요청) (AU_OL_0012) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,54 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0012, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0012] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0012] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + TXBUF *req; + int rc, attempt; + + tx_log(TX_LOG_INFO, "[AU_OL_0012] 승인 재시도 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT amount, status INTO :h_amount, :h_status + FROM au_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (h_status[0] != 'U') { + tx_log(TX_LOG_WARN, "[AU_OL_0012] 재시도 대상아님 status=%s", h_status); + tx_return(ctx, TX_FAIL, ctx->out); return; } - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + req = tx_buf_alloc(); + if (!req) { tx_return(ctx, TX_ENOMEM, ctx->out); return; } + tx_buf_sets(req, "KEY", key); + tx_buf_setlong(req, "AMOUNT", h_amount); + + rc = TX_FAIL; + for (attempt = 1; attempt <= 3; attempt++) { + tx_buf_reset(ctx->out); + rc = tx_call("AU_ISSUER", req, ctx->out); + tx_log(TX_LOG_INFO, "[AU_OL_0012] 재시도 %d회 rc=%d", attempt, rc); + if (rc == TX_OK) break; + } + tx_buf_free(req); + + if (rc == TX_OK) { + EXEC SQL UPDATE au_ledger SET status = 'M', upd_ts = now() + WHERE key = :h_key; + tx_commit(); + } + tx_buf_setlong(ctx->out, "ATTEMPTS", attempt); + tx_return(ctx, rc, ctx->out); } diff --git a/app/online/au_ol_0013.c b/app/online/au_ol_0013.c index 8e782da..8c8ec77 100644 --- a/app/online/au_ol_0013.c +++ b/app/online/au_ol_0013.c @@ -8,15 +8,16 @@ #line 1 "app/online/au_ol_0013.pgc" /* @tier medium @module au @transform authorization-online */ /* - * au_ol_0013.pgc - 승인한도 온라인 서비스 (AU_OL_0013) [tier=medium] + * au_ol_0013.pgc - 무이자할부 승인(할부개월 검증+등록) (AU_OL_0013) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,60 +90,88 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0013.pgc" +#line 16 "app/online/au_ol_0013.pgc" TX_SERVICE(AU_OL_0013, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0013] KEY 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + /* exec sql begin declare section */ + + + + +#line 27 "app/online/au_ol_0013.pgc" + char h_key [ 16 ] , h_merch [ 16 ] , h_bdate [ 9 ] ; + +#line 28 "app/online/au_ol_0013.pgc" + long h_amount , h_fee ; + +#line 29 "app/online/au_ol_0013.pgc" + int h_inst ; +/* exec sql end declare section */ +#line 30 "app/online/au_ol_0013.pgc" - tx_log(TX_LOG_INFO, "[AU_OL_0013] 승인한도 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0013] 필수 필드 누락"); + char merch[16]; + long amount = 0; + int inst = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0013] 무이자할부 승인 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); + { long v = 0; tx_buf_getlong(ctx->in, "INSTMONTH", &v); inst = (int)v; } - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0013] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0013] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + if (inst < 2 || inst > 12 || amount < 50000L) { + tx_log(TX_LOG_WARN, "[AU_OL_0013] 무이자 조건미달 inst=%d amt=%ld", inst, amount); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, 15); h_key[15] = '\0'; + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + date_today(h_bdate); + h_amount = amount; + h_inst = inst; + h_fee = 0; /* 무이자: 수수료 0 */ - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0013] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0013] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into au_ledger ( key , merch_id , card_no , amount , biz_date , status , fee ) values ( $1 , $2 , 'INSTALLMENT' , $3 , $4 , 'R' , $5 )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 61 "app/online/au_ol_0013.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0013 할부등록"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = au_rec_count(bizdate, AU_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0013] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "INSTMONTH", h_inst); + tx_buf_setlong(ctx->out, "MONTHLYAMT", h_amount / h_inst); + tx_log(TX_LOG_INFO, "[AU_OL_0013] 무이자할부 승인 %d개월 월납=%ld", + h_inst, h_amount / h_inst); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0013.pgc b/app/online/au_ol_0013.pgc index 4122d17..99f55a5 100644 --- a/app/online/au_ol_0013.pgc +++ b/app/online/au_ol_0013.pgc @@ -1,14 +1,15 @@ /* @tier medium @module au @transform authorization-online */ /* - * au_ol_0013.pgc - 승인한도 온라인 서비스 (AU_OL_0013) [tier=medium] + * au_ol_0013.pgc - 무이자할부 승인(할부개월 검증+등록) (AU_OL_0013) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,55 +17,61 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0013, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0013] KEY 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_merch[16], h_bdate[9]; + long h_amount, h_fee; + int h_inst; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AU_OL_0013] 승인한도 등록 서비스 진입"); + char merch[16]; + long amount = 0; + int inst = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0013] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[AU_OL_0013] 무이자할부 승인 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); + { long v = 0; tx_buf_getlong(ctx->in, "INSTMONTH", &v); inst = (int)v; } - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0013] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0013] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + if (inst < 2 || inst > 12 || amount < 50000L) { + tx_log(TX_LOG_WARN, "[AU_OL_0013] 무이자 조건미달 inst=%d amt=%ld", inst, amount); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, 15); h_key[15] = '\0'; + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + date_today(h_bdate); + h_amount = amount; + h_inst = inst; + h_fee = 0; /* 무이자: 수수료 0 */ - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0013] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0013] tx_begin 경고"); + EXEC SQL INSERT INTO au_ledger (key, merch_id, card_no, amount, biz_date, status, fee) + VALUES (:h_key, :h_merch, 'INSTALLMENT', :h_amount, :h_bdate, 'R', :h_fee); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0013 할부등록"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = au_rec_count(bizdate, AU_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0013] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "INSTMONTH", h_inst); + tx_buf_setlong(ctx->out, "MONTHLYAMT", h_amount / h_inst); + tx_log(TX_LOG_INFO, "[AU_OL_0013] 무이자할부 승인 %d개월 월납=%ld", + h_inst, h_amount / h_inst); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0014.c b/app/online/au_ol_0014.c index 7a938a5..880693f 100644 --- a/app/online/au_ol_0014.c +++ b/app/online/au_ol_0014.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0014.pgc" -/* @tier easy @module au @transform authorization-online */ +/* @tier hard @module au @transform authorization-online */ /* - * au_ol_0014.pgc - 승인한도 온라인 서비스 (AU_OL_0014) [tier=easy] + * au_ol_0014.pgc - 승인 한도초과 처리(누적합 판정) (AU_OL_0014) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,74 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0014.pgc" +#line 16 "app/online/au_ol_0014.pgc" TX_SERVICE(AU_OL_0014, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0014] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0014] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/au_ol_0014.pgc" + char h_merch [ 16 ] , h_bdate [ 9 ] ; + +#line 28 "app/online/au_ol_0014.pgc" + long h_used , h_req ; +/* exec sql end declare section */ +#line 29 "app/online/au_ol_0014.pgc" - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + + char merch[16], bdate[16]; + long req = 0, limit_amt = 100000000L; + + tx_log(TX_LOG_INFO, "[AU_OL_0014] 승인 한도초과 처리 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + tx_buf_getlong(ctx->in, "AMOUNT", &req); + + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + h_req = req; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from au_ledger where merch_id = $1 and biz_date = $2 and status = 'M'", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_used),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 49 "app/online/au_ol_0014.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0014 누적조회"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + if (h_used + h_req > limit_amt) { + tx_log(TX_LOG_WARN, "[AU_OL_0014] 한도초과 used=%ld req=%ld limit=%ld", + h_used, h_req, limit_amt); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "OVERLIMIT", "Y"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "OVERLIMIT", "N"); + tx_buf_setlong(ctx->out, "AVAIL", limit_amt - h_used - h_req); + tx_log(TX_LOG_INFO, "[AU_OL_0014] 한도내 승인가능 잔여=%ld", + limit_amt - h_used - h_req); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0014.pgc b/app/online/au_ol_0014.pgc index 4b09598..ca1f3dd 100644 --- a/app/online/au_ol_0014.pgc +++ b/app/online/au_ol_0014.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module au @transform authorization-online */ +/* @tier hard @module au @transform authorization-online */ /* - * au_ol_0014.pgc - 승인한도 온라인 서비스 (AU_OL_0014) [tier=easy] + * au_ol_0014.pgc - 승인 한도초과 처리(누적합 판정) (AU_OL_0014) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,55 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0014, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0014] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0014] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16], h_bdate[9]; + long h_used, h_req; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + char merch[16], bdate[16]; + long req = 0, limit_amt = 100000000L; + + tx_log(TX_LOG_INFO, "[AU_OL_0014] 승인 한도초과 처리 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + tx_buf_getlong(ctx->in, "AMOUNT", &req); + + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + h_req = req; + + EXEC SQL SELECT coalesce(sum(amount), 0) INTO :h_used + FROM au_ledger + WHERE merch_id = :h_merch AND biz_date = :h_bdate AND status = 'M'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0014 누적조회"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + if (h_used + h_req > limit_amt) { + tx_log(TX_LOG_WARN, "[AU_OL_0014] 한도초과 used=%ld req=%ld limit=%ld", + h_used, h_req, limit_amt); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "OVERLIMIT", "Y"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "OVERLIMIT", "N"); + tx_buf_setlong(ctx->out, "AVAIL", limit_amt - h_used - h_req); + tx_log(TX_LOG_INFO, "[AU_OL_0014] 한도내 승인가능 잔여=%ld", + limit_amt - h_used - h_req); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0015.c b/app/online/au_ol_0015.c index 306ca79..e905b44 100644 --- a/app/online/au_ol_0015.c +++ b/app/online/au_ol_0015.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0015.pgc" -/* @tier medium @module au @transform authorization-online */ +/* @tier easy @module au @transform authorization-online */ /* - * au_ol_0015.pgc - 승인한도 온라인 서비스 (AU_OL_0015) [tier=medium] + * au_ol_0015.pgc - 승인 상태조회(상세: 상태/수수료) (AU_OL_0015) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,60 +90,72 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0015.pgc" +#line 16 "app/online/au_ol_0015.pgc" TX_SERVICE(AU_OL_0015, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0015] 승인한도 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0015] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0015] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + + +#line 27 "app/online/au_ol_0015.pgc" + char h_key [ 16 ] , h_status [ 2 ] , h_bdate [ 9 ] ; + +#line 28 "app/online/au_ol_0015.pgc" + long h_amount , h_fee ; +/* exec sql end declare section */ +#line 29 "app/online/au_ol_0015.pgc" - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0015] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); + + const char *label; + + tx_log(TX_LOG_INFO, "[AU_OL_0015] 승인 상태조회 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , fee , status , biz_date from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 38 "app/online/au_ol_0015.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0015] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0015 상태조회"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0015] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + switch (h_status[0]) { + case 'R': label = "접수"; break; + case 'M': label = "승인완료"; break; + case 'U': label = "거절/취소"; break; + case 'S': label = "정산완료"; break; + default: label = "미상"; break; } - cnt = au_rec_count(bizdate, AU_ST_RECV); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0015] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "STATUSLBL", label); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_log(TX_LOG_INFO, "[AU_OL_0015] 상태 key=%s status=%s(%s)", key, h_status, label); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0015.pgc b/app/online/au_ol_0015.pgc index 6c731df..eff6797 100644 --- a/app/online/au_ol_0015.pgc +++ b/app/online/au_ol_0015.pgc @@ -1,14 +1,15 @@ -/* @tier medium @module au @transform authorization-online */ +/* @tier easy @module au @transform authorization-online */ /* - * au_ol_0015.pgc - 승인한도 온라인 서비스 (AU_OL_0015) [tier=medium] + * au_ol_0015.pgc - 승인 상태조회(상세: 상태/수수료) (AU_OL_0015) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,55 +17,49 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0015, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0015] 승인한도 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0015] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0015] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2], h_bdate[9]; + long h_amount, h_fee; + EXEC SQL END DECLARE SECTION; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0015] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); + const char *label; + + tx_log(TX_LOG_INFO, "[AU_OL_0015] 승인 상태조회 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT amount, fee, status, biz_date + INTO :h_amount, :h_fee, :h_status, :h_bdate + FROM au_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0015] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0015 상태조회"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0015] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + switch (h_status[0]) { + case 'R': label = "접수"; break; + case 'M': label = "승인완료"; break; + case 'U': label = "거절/취소"; break; + case 'S': label = "정산완료"; break; + default: label = "미상"; break; } - cnt = au_rec_count(bizdate, AU_ST_RECV); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0015] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "STATUSLBL", label); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_log(TX_LOG_INFO, "[AU_OL_0015] 상태 key=%s status=%s(%s)", key, h_status, label); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0016.c b/app/online/au_ol_0016.c index 5aba921..9df2e19 100644 --- a/app/online/au_ol_0016.c +++ b/app/online/au_ol_0016.c @@ -8,15 +8,16 @@ #line 1 "app/online/au_ol_0016.pgc" /* @tier easy @module au @transform authorization-online */ /* - * au_ol_0016.pgc - 승인한도 온라인 서비스 (AU_OL_0016) [tier=easy] + * au_ol_0016.pgc - 승인 로그적재(감사원장 INSERT) (AU_OL_0016) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,76 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0016.pgc" +#line 16 "app/online/au_ol_0016.pgc" TX_SERVICE(AU_OL_0016, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0016] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0016] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/au_ol_0016.pgc" + char h_key [ 16 ] , h_merch [ 16 ] , h_bdate [ 9 ] , h_status [ 2 ] ; + +#line 28 "app/online/au_ol_0016.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 29 "app/online/au_ol_0016.pgc" - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0016] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + + char merch[16], evt[8]; + long amount = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0016] 승인 로그적재 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) + strcpy(merch, "SYS"); + if (tx_buf_get(ctx->in, "EVENT", evt, sizeof(evt)) != TX_OK) + strcpy(evt, "APPROVE"); + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + strncpy(h_key, key, 15); h_key[15] = '\0'; + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + date_today(h_bdate); + strncpy(h_status, AU_ST_RECV, 1); h_status[1] = '\0'; + h_amount = amount; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0016] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into au_ledger ( key , merch_id , card_no , amount , biz_date , status , fee ) values ( $1 , $2 , $3 , $4 , $5 , $6 , 0 ) on conflict ( key ) do update set amount = $7 , upd_ts = now ( )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 50 "app/online/au_ol_0016.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0016 로그적재"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0016] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "LOGKEY", key); + tx_buf_sets(ctx->out, "EVENT", evt); + tx_log(TX_LOG_INFO, "[AU_OL_0016] 로그적재 완료 key=%s event=%s", key, evt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0016.pgc b/app/online/au_ol_0016.pgc index 735b59a..dda0e61 100644 --- a/app/online/au_ol_0016.pgc +++ b/app/online/au_ol_0016.pgc @@ -1,14 +1,15 @@ /* @tier easy @module au @transform authorization-online */ /* - * au_ol_0016.pgc - 승인한도 온라인 서비스 (AU_OL_0016) [tier=easy] + * au_ol_0016.pgc - 승인 로그적재(감사원장 INSERT) (AU_OL_0016) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,49 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0016, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0016] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0016] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_merch[16], h_bdate[9], h_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0016] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + char merch[16], evt[8]; + long amount = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0016] 승인 로그적재 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) + strcpy(merch, "SYS"); + if (tx_buf_get(ctx->in, "EVENT", evt, sizeof(evt)) != TX_OK) + strcpy(evt, "APPROVE"); + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + strncpy(h_key, key, 15); h_key[15] = '\0'; + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + date_today(h_bdate); + strncpy(h_status, AU_ST_RECV, 1); h_status[1] = '\0'; + h_amount = amount; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0016] tx_begin 경고"); + EXEC SQL INSERT INTO au_ledger (key, merch_id, card_no, amount, biz_date, status, fee) + VALUES (:h_key, :h_merch, :h_bdate, :h_amount, :h_bdate, :h_status, 0) + ON CONFLICT (key) DO UPDATE SET amount = :h_amount, upd_ts = now(); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0016 로그적재"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0016] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "LOGKEY", key); + tx_buf_sets(ctx->out, "EVENT", evt); + tx_log(TX_LOG_INFO, "[AU_OL_0016] 로그적재 완료 key=%s event=%s", key, evt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0017.c b/app/online/au_ol_0017.c index 5898990..b15372f 100644 --- a/app/online/au_ol_0017.c +++ b/app/online/au_ol_0017.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0017.pgc" -/* @tier hard @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0017.pgc - 승인한도 온라인 서비스 (AU_OL_0017) [tier=hard] + * au_ol_0017.pgc - 승인 금액집계(가맹점 당일 합계) (AU_OL_0017) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,88 +90,67 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0017.pgc" +#line 16 "app/online/au_ol_0017.pgc" TX_SERVICE(AU_OL_0017, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + +#line 21 "app/online/au_ol_0017.pgc" + char h_merch [ 16 ] , h_bdate [ 9 ] ; + +#line 22 "app/online/au_ol_0017.pgc" + long h_sum , h_fee ; + +#line 23 "app/online/au_ol_0017.pgc" + int h_cnt ; +/* exec sql end declare section */ +#line 24 "app/online/au_ol_0017.pgc" - tx_log(TX_LOG_INFO, "[AU_OL_0017] 승인한도 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0017] 필수 필드 누락"); + char merch[16], bdate[16], won[32]; + + tx_log(TX_LOG_INFO, "[AU_OL_0017] 승인 금액집계 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0017] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from au_ledger where merch_id = $1 and biz_date = $2 and status in ( 'M' , 'S' )", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 43 "app/online/au_ol_0017.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0017 집계"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0017] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AU_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AU_OL_0017] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (au_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - + amount_format_won(h_sum, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AU_OL_0017] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_setlong(ctx->out, "SUMAMT", h_sum); + tx_buf_setlong(ctx->out, "SUMFEE", h_fee); + tx_buf_sets(ctx->out, "SUMWON", won); + tx_log(TX_LOG_INFO, "[AU_OL_0017] 집계 merch=%s cnt=%d sum=%ld", merch, h_cnt, h_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0017.pgc b/app/online/au_ol_0017.pgc index de9f935..40d871e 100644 --- a/app/online/au_ol_0017.pgc +++ b/app/online/au_ol_0017.pgc @@ -1,14 +1,15 @@ -/* @tier hard @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0017.pgc - 승인한도 온라인 서비스 (AU_OL_0017) [tier=hard] + * au_ol_0017.pgc - 승인 금액집계(가맹점 당일 합계) (AU_OL_0017) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,83 +17,43 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0017, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16], h_bdate[9]; + long h_sum, h_fee; + int h_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AU_OL_0017] 승인한도 트랜잭션 오케스트레이션 진입"); + char merch[16], bdate[16], won[32]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0017] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[AU_OL_0017] 승인 금액집계 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0017] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + + EXEC SQL SELECT count(*), coalesce(sum(amount), 0), coalesce(sum(fee), 0) + INTO :h_cnt, :h_sum, :h_fee + FROM au_ledger + WHERE merch_id = :h_merch AND biz_date = :h_bdate + AND status IN ('M', 'S'); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0017 집계"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0017] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AU_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AU_OL_0017] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (au_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - + amount_format_won(h_sum, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AU_OL_0017] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_setlong(ctx->out, "SUMAMT", h_sum); + tx_buf_setlong(ctx->out, "SUMFEE", h_fee); + tx_buf_sets(ctx->out, "SUMWON", won); + tx_log(TX_LOG_INFO, "[AU_OL_0017] 집계 merch=%s cnt=%d sum=%ld", merch, h_cnt, h_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0018.c b/app/online/au_ol_0018.c index 5b867f1..97e844c 100644 --- a/app/online/au_ol_0018.c +++ b/app/online/au_ol_0018.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0018.pgc" -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0018.pgc - 승인한도 온라인 서비스 (AU_OL_0018) [tier=easy] + * au_ol_0018.pgc - 승인 통계조회(상태별 건수 집계) (AU_OL_0018) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,60 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0018.pgc" +#line 16 "app/online/au_ol_0018.pgc" TX_SERVICE(AU_OL_0018, ctx) { - au_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + +#line 21 "app/online/au_ol_0018.pgc" + char h_bdate [ 9 ] , h_st [ 2 ] ; + +#line 22 "app/online/au_ol_0018.pgc" + int h_cnt ; +/* exec sql end declare section */ +#line 23 "app/online/au_ol_0018.pgc" - tx_log(TX_LOG_INFO, "[AU_OL_0018] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0018] KEY 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } + char bdate[16]; + const char *codes = "RMUS"; + int i, total = 0; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0018] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + tx_log(TX_LOG_INFO, "[AU_OL_0018] 승인 통계조회 진입"); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0018] 조회완료 key=%s amount=%ld", key, rec.amount); + for (i = 0; codes[i]; i++) { + char field[8] = "CNT_"; + h_st[0] = codes[i]; + h_st[1] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from au_ledger where biz_date = $1 and status = $2 ", + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_st),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 41 "app/online/au_ol_0018.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0018 통계"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + field[4] = codes[i]; + field[5] = '\0'; + tx_buf_setlong(ctx->out, field, h_cnt); + total += h_cnt; + } + + tx_buf_sets(ctx->out, "BIZDATE", bdate); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_log(TX_LOG_INFO, "[AU_OL_0018] 통계 date=%s total=%d", bdate, total); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0018.pgc b/app/online/au_ol_0018.pgc index 311cfbe..58ad708 100644 --- a/app/online/au_ol_0018.pgc +++ b/app/online/au_ol_0018.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0018.pgc - 승인한도 온라인 서비스 (AU_OL_0018) [tier=easy] + * au_ol_0018.pgc - 승인 통계조회(상태별 건수 집계) (AU_OL_0018) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,41 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0018, ctx) { - au_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_bdate[9], h_st[2]; + int h_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AU_OL_0018] 승인한도 단건조회 서비스 진입"); + char bdate[16]; + const char *codes = "RMUS"; + int i, total = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0018] KEY 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0018] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + tx_log(TX_LOG_INFO, "[AU_OL_0018] 승인 통계조회 진입"); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0018] 조회완료 key=%s amount=%ld", key, rec.amount); + for (i = 0; codes[i]; i++) { + char field[8] = "CNT_"; + h_st[0] = codes[i]; + h_st[1] = '\0'; + EXEC SQL SELECT count(*) INTO :h_cnt + FROM au_ledger + WHERE biz_date = :h_bdate AND status = :h_st; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0018 통계"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + field[4] = codes[i]; + field[5] = '\0'; + tx_buf_setlong(ctx->out, field, h_cnt); + total += h_cnt; + } + + tx_buf_sets(ctx->out, "BIZDATE", bdate); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_log(TX_LOG_INFO, "[AU_OL_0018] 통계 date=%s total=%d", bdate, total); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0019.c b/app/online/au_ol_0019.c index 5404cff..ac64047 100644 --- a/app/online/au_ol_0019.c +++ b/app/online/au_ol_0019.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0019.pgc" -/* @tier hard @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0019.pgc - 승인한도 온라인 서비스 (AU_OL_0019) [tier=hard] + * au_ol_0019.pgc - 승인 수수료 재계산(요율 적용 UPDATE) (AU_OL_0019) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,88 +90,75 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0019.pgc" +#line 16 "app/online/au_ol_0019.pgc" TX_SERVICE(AU_OL_0019, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; - - tx_log(TX_LOG_INFO, "[AU_OL_0019] 승인한도 트랜잭션 오케스트레이션 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0019] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0019] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + + + +#line 27 "app/online/au_ol_0019.pgc" + char h_key [ 16 ] ; + +#line 28 "app/online/au_ol_0019.pgc" + long h_amount , h_fee ; + +#line 29 "app/online/au_ol_0019.pgc" + int h_rate ; +/* exec sql end declare section */ +#line 30 "app/online/au_ol_0019.pgc" - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0019] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + + long rate = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0019] 승인 수수료 재계산 진입"); + tx_buf_getlong(ctx->in, "RATEBP", &rate); /* basis point */ + if (rate <= 0 || rate > 1000) rate = 100; /* 기본 1.00% */ + h_rate = (int)rate; + + strncpy(h_key, key, 15); h_key[15] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 40 "app/online/au_ol_0019.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); return; } + h_fee = h_amount * h_rate / 10000; - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0019] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set fee = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 49 "app/online/au_ol_0019.pgc" - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0019] 원장반영 실패 rc=%d", rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0019 수수료"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_return(ctx, TX_EDB, ctx->out); return; } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AU_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AU_OL_0019] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (au_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AU_OL_0019] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "RATEBP", h_rate); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_log(TX_LOG_INFO, "[AU_OL_0019] 수수료 재계산 fee=%ld(rate=%dbp)", h_fee, h_rate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0019.pgc b/app/online/au_ol_0019.pgc index b03d3f6..d911556 100644 --- a/app/online/au_ol_0019.pgc +++ b/app/online/au_ol_0019.pgc @@ -1,14 +1,15 @@ -/* @tier hard @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0019.pgc - 승인한도 온라인 서비스 (AU_OL_0019) [tier=hard] + * au_ol_0019.pgc - 승인 수수료 재계산(요율 적용 UPDATE) (AU_OL_0019) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,83 +17,48 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0019, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; - - tx_log(TX_LOG_INFO, "[AU_OL_0019] 승인한도 트랜잭션 오케스트레이션 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0019] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0019] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_amount, h_fee; + int h_rate; + EXEC SQL END DECLARE SECTION; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0019] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + long rate = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0019] 승인 수수료 재계산 진입"); + tx_buf_getlong(ctx->in, "RATEBP", &rate); /* basis point */ + if (rate <= 0 || rate > 1000) rate = 100; /* 기본 1.00% */ + h_rate = (int)rate; + + strncpy(h_key, key, 15); h_key[15] = '\0'; + EXEC SQL SELECT amount INTO :h_amount FROM au_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); return; } + h_fee = h_amount * h_rate / 10000; - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0019] 원장반영 실패 rc=%d", rc); + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0019] tx_begin 경고"); + EXEC SQL UPDATE au_ledger SET fee = :h_fee, upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0019 수수료"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_return(ctx, TX_EDB, ctx->out); return; } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AU_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AU_OL_0019] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (au_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AU_OL_0019] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "RATEBP", h_rate); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_log(TX_LOG_INFO, "[AU_OL_0019] 수수료 재계산 fee=%ld(rate=%dbp)", h_fee, h_rate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0020.c b/app/online/au_ol_0020.c index e24f401..46a1bdd 100644 --- a/app/online/au_ol_0020.c +++ b/app/online/au_ol_0020.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0020.pgc" -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0020.pgc - 승인한도 온라인 서비스 (AU_OL_0020) [tier=easy] + * au_ol_0020.pgc - 승인번호 채번(당일 순번 생성) (AU_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,48 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0020.pgc" +#line 16 "app/online/au_ol_0020.pgc" TX_SERVICE(AU_OL_0020, ctx) { - au_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + +#line 21 "app/online/au_ol_0020.pgc" + char h_bdate [ 9 ] ; + +#line 22 "app/online/au_ol_0020.pgc" + int h_seq ; +/* exec sql end declare section */ +#line 23 "app/online/au_ol_0020.pgc" - tx_log(TX_LOG_INFO, "[AU_OL_0020] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0020] KEY 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0020] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + char bdate[16], approval[16]; + + tx_log(TX_LOG_INFO, "[AU_OL_0020] 승인번호 채번 진입"); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) + 1 from au_ledger where biz_date = $1 ", + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_seq),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 33 "app/online/au_ol_0020.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0020 채번"); + tx_return(ctx, TX_EDB, ctx->out); return; } + snprintf(approval, sizeof(approval), "%.8s%06d", bdate, h_seq); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0020] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "APPROVALNO", approval); + tx_buf_setlong(ctx->out, "SEQ", h_seq); + tx_log(TX_LOG_INFO, "[AU_OL_0020] 승인번호 채번 %s (seq=%d)", approval, h_seq); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0020.pgc b/app/online/au_ol_0020.pgc index 6c93689..05c0f70 100644 --- a/app/online/au_ol_0020.pgc +++ b/app/online/au_ol_0020.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0020.pgc - 승인한도 온라인 서비스 (AU_OL_0020) [tier=easy] + * au_ol_0020.pgc - 승인번호 채번(당일 순번 생성) (AU_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,30 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0020, ctx) { - au_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_bdate[9]; + int h_seq; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AU_OL_0020] 승인한도 단건조회 서비스 진입"); + char bdate[16], approval[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0020] KEY 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0020] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + tx_log(TX_LOG_INFO, "[AU_OL_0020] 승인번호 채번 진입"); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + + EXEC SQL SELECT count(*) + 1 INTO :h_seq + FROM au_ledger WHERE biz_date = :h_bdate; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0020 채번"); + tx_return(ctx, TX_EDB, ctx->out); return; } + snprintf(approval, sizeof(approval), "%.8s%06d", bdate, h_seq); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0020] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "APPROVALNO", approval); + tx_buf_setlong(ctx->out, "SEQ", h_seq); + tx_log(TX_LOG_INFO, "[AU_OL_0020] 승인번호 채번 %s (seq=%d)", approval, h_seq); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0021.c b/app/online/au_ol_0021.c index b28ddc9..51c8bb0 100644 --- a/app/online/au_ol_0021.c +++ b/app/online/au_ol_0021.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0021.pgc" -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0021.pgc - 승인한도 온라인 서비스 (AU_OL_0021) [tier=easy] + * au_ol_0021.pgc - 가맹점별 한도잔여 조회(승인율 포함) (AU_OL_0021) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,61 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0021.pgc" +#line 16 "app/online/au_ol_0021.pgc" TX_SERVICE(AU_OL_0021, ctx) { - au_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 21 "app/online/au_ol_0021.pgc" + char h_merch [ 16 ] ; + +#line 22 "app/online/au_ol_0021.pgc" + long h_used ; + +#line 23 "app/online/au_ol_0021.pgc" + int h_total , h_appr ; +/* exec sql end declare section */ +#line 24 "app/online/au_ol_0021.pgc" - tx_log(TX_LOG_INFO, "[AU_OL_0021] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0021] KEY 누락"); + char merch[16]; + long limit_amt = 200000000L; + int ratio = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0021] 가맹점 한도잔여 조회 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) , count ( * ) filter ( where status in ( 'M' , 'S' ) ) from au_ledger where merch_id = $1 ", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_used),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_total),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_appr),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 41 "app/online/au_ol_0021.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0021 잔여조회"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (h_total > 0) ratio = h_appr * 100 / h_total; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "REMAIN", (limit_amt > h_used) ? limit_amt - h_used : 0); + tx_buf_setlong(ctx->out, "APPRRATE", ratio); + tx_log(TX_LOG_INFO, "[AU_OL_0021] merch=%s used=%ld 승인율=%d%%", merch, h_used, ratio); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0021.pgc b/app/online/au_ol_0021.pgc index f2abd38..611eea2 100644 --- a/app/online/au_ol_0021.pgc +++ b/app/online/au_ol_0021.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0021.pgc - 승인한도 온라인 서비스 (AU_OL_0021) [tier=easy] + * au_ol_0021.pgc - 가맹점별 한도잔여 조회(승인율 포함) (AU_OL_0021) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,39 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0021, ctx) { - au_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16]; + long h_used; + int h_total, h_appr; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AU_OL_0021] 승인한도 단건조회 서비스 진입"); + char merch[16]; + long limit_amt = 200000000L; + int ratio = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0021] KEY 누락"); + tx_log(TX_LOG_INFO, "[AU_OL_0021] 가맹점 한도잔여 조회 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL SELECT coalesce(sum(amount), 0), + count(*), + count(*) FILTER (WHERE status IN ('M','S')) + INTO :h_used, :h_total, :h_appr + FROM au_ledger WHERE merch_id = :h_merch; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0021 잔여조회"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (h_total > 0) ratio = h_appr * 100 / h_total; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "REMAIN", (limit_amt > h_used) ? limit_amt - h_used : 0); + tx_buf_setlong(ctx->out, "APPRRATE", ratio); + tx_log(TX_LOG_INFO, "[AU_OL_0021] merch=%s used=%ld 승인율=%d%%", merch, h_used, ratio); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0022.c b/app/online/au_ol_0022.c index 6b5fb7a..a9cea15 100644 --- a/app/online/au_ol_0022.c +++ b/app/online/au_ol_0022.c @@ -8,15 +8,16 @@ #line 1 "app/online/au_ol_0022.pgc" /* @tier medium @module au @transform authorization-online */ /* - * au_ol_0022.pgc - 승인한도 온라인 서비스 (AU_OL_0022) [tier=medium] + * au_ol_0022.pgc - 승인 중복검사(동일카드/금액 단시간) (AU_OL_0022) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,60 +90,74 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0022.pgc" +#line 16 "app/online/au_ol_0022.pgc" TX_SERVICE(AU_OL_0022, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0022] 승인한도 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0022] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0022] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + + +#line 27 "app/online/au_ol_0022.pgc" + char h_card [ 20 ] , h_bdate [ 9 ] , h_key [ 16 ] ; + +#line 28 "app/online/au_ol_0022.pgc" + long h_amount ; + +#line 29 "app/online/au_ol_0022.pgc" + int h_cnt ; +/* exec sql end declare section */ +#line 30 "app/online/au_ol_0022.pgc" + + + char card[20], bdate[16]; + long amount = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0022] 승인 중복검사 진입"); + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); tx_buf_getlong(ctx->in, "AMOUNT", &amount); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0022] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); + strncpy(h_card, card, 19); h_card[19] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_amount = amount; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from au_ledger where card_no = $1 and biz_date = $2 and amount = $3 and key <> $4 ", + ECPGt_char,(h_card),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 52 "app/online/au_ol_0022.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0022 중복검사"); + tx_return(ctx, TX_EDB, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0022] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0022] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = au_rec_count(bizdate, AU_ST_RECV); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0022] 등록완료 key=%s 당일건수=%ld", key, cnt); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "DUP", (h_cnt > 0) ? "Y" : "N"); + tx_buf_setlong(ctx->out, "DUPCNT", h_cnt); + tx_buf_sets(ctx->out, "RESPCODE", (h_cnt > 0) ? RESP_INVALID : RESP_OK); + tx_log(TX_LOG_INFO, "[AU_OL_0022] 중복검사 card=%s cnt=%d", card, h_cnt); + tx_return(ctx, (h_cnt > 0) ? TX_FAIL : TX_OK, ctx->out); } diff --git a/app/online/au_ol_0022.pgc b/app/online/au_ol_0022.pgc index e7b6aa3..044cf12 100644 --- a/app/online/au_ol_0022.pgc +++ b/app/online/au_ol_0022.pgc @@ -1,14 +1,15 @@ /* @tier medium @module au @transform authorization-online */ /* - * au_ol_0022.pgc - 승인한도 온라인 서비스 (AU_OL_0022) [tier=medium] + * au_ol_0022.pgc - 승인 중복검사(동일카드/금액 단시간) (AU_OL_0022) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,55 +17,49 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0022, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0022] 승인한도 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0022] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0022] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_card[20], h_bdate[9], h_key[16]; + long h_amount; + int h_cnt; + EXEC SQL END DECLARE SECTION; + + char card[20], bdate[16]; + long amount = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0022] 승인 중복검사 진입"); + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); tx_buf_getlong(ctx->in, "AMOUNT", &amount); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0022] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); + strncpy(h_card, card, 19); h_card[19] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_amount = amount; + + EXEC SQL SELECT count(*) INTO :h_cnt + FROM au_ledger + WHERE card_no = :h_card AND biz_date = :h_bdate + AND amount = :h_amount AND key <> :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0022 중복검사"); + tx_return(ctx, TX_EDB, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0022] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0022] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = au_rec_count(bizdate, AU_ST_RECV); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0022] 등록완료 key=%s 당일건수=%ld", key, cnt); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "DUP", (h_cnt > 0) ? "Y" : "N"); + tx_buf_setlong(ctx->out, "DUPCNT", h_cnt); + tx_buf_sets(ctx->out, "RESPCODE", (h_cnt > 0) ? RESP_INVALID : RESP_OK); + tx_log(TX_LOG_INFO, "[AU_OL_0022] 중복검사 card=%s cnt=%d", card, h_cnt); + tx_return(ctx, (h_cnt > 0) ? TX_FAIL : TX_OK, ctx->out); } diff --git a/app/online/au_ol_0023.c b/app/online/au_ol_0023.c index b755c04..5876593 100644 --- a/app/online/au_ol_0023.c +++ b/app/online/au_ol_0023.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0023.pgc" -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0023.pgc - 승인한도 온라인 서비스 (AU_OL_0023) [tier=easy] + * au_ol_0023.pgc - 승인 금액정정(정정사유 검증 UPDATE) (AU_OL_0023) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,81 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0023.pgc" +#line 16 "app/online/au_ol_0023.pgc" TX_SERVICE(AU_OL_0023, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0023] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0023] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/au_ol_0023.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; + +#line 28 "app/online/au_ol_0023.pgc" + long h_new , h_old ; +/* exec sql end declare section */ +#line 29 "app/online/au_ol_0023.pgc" - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + + long newamt = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0023] 승인 금액정정 진입"); + tx_buf_getlong(ctx->in, "NEWAMT", &newamt); + if (!amount_is_valid(newamt)) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_new = newamt; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , status from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_old),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 43 "app/online/au_ol_0023.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (h_status[0] != 'R') { + tx_log(TX_LOG_WARN, "[AU_OL_0023] 접수상태만 정정가능 status=%s", h_status); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0023] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set amount = $1 , fee = $2 / 100 , upd_ts = now ( ) where key = $3 ", + ECPGt_long,&(h_new),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_new),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 57 "app/online/au_ol_0023.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0023 정정"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "OLDAMT", h_old); + tx_buf_setlong(ctx->out, "NEWAMT", h_new); + tx_log(TX_LOG_INFO, "[AU_OL_0023] 금액정정 %ld -> %ld", h_old, h_new); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0023.pgc b/app/online/au_ol_0023.pgc index 860e444..368e9cb 100644 --- a/app/online/au_ol_0023.pgc +++ b/app/online/au_ol_0023.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0023.pgc - 승인한도 온라인 서비스 (AU_OL_0023) [tier=easy] + * au_ol_0023.pgc - 승인 금액정정(정정사유 검증 UPDATE) (AU_OL_0023) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,55 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0023, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0023] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0023] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2]; + long h_new, h_old; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + long newamt = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0023] 승인 금액정정 진입"); + tx_buf_getlong(ctx->in, "NEWAMT", &newamt); + if (!amount_is_valid(newamt)) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_new = newamt; + + EXEC SQL SELECT amount, status INTO :h_old, :h_status + FROM au_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (h_status[0] != 'R') { + tx_log(TX_LOG_WARN, "[AU_OL_0023] 접수상태만 정정가능 status=%s", h_status); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0023] tx_begin 경고"); + EXEC SQL UPDATE au_ledger + SET amount = :h_new, fee = :h_new / 100, upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0023 정정"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "OLDAMT", h_old); + tx_buf_setlong(ctx->out, "NEWAMT", h_new); + tx_log(TX_LOG_INFO, "[AU_OL_0023] 금액정정 %ld -> %ld", h_old, h_new); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0024.c b/app/online/au_ol_0024.c index 6fa87f4..90a991f 100644 --- a/app/online/au_ol_0024.c +++ b/app/online/au_ol_0024.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0024.pgc" -/* @tier medium @module au @transform authorization-online */ +/* @tier easy @module au @transform authorization-online */ /* - * au_ol_0024.pgc - 승인한도 온라인 서비스 (AU_OL_0024) [tier=medium] + * au_ol_0024.pgc - 승인 보류(홀드 상태전이) (AU_OL_0024) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,60 +90,56 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0024.pgc" +#line 16 "app/online/au_ol_0024.pgc" TX_SERVICE(AU_OL_0024, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0024] 승인한도 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0024] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0024] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + +#line 27 "app/online/au_ol_0024.pgc" + char h_key [ 16 ] ; +/* exec sql end declare section */ +#line 28 "app/online/au_ol_0024.pgc" - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0024] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); + + char reason[64]; + + tx_log(TX_LOG_INFO, "[AU_OL_0024] 승인 보류 진입"); + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) + strcpy(reason, "심사대기"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0024] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set status = 'U' , upd_ts = now ( ) where key = $1 and status = 'R'", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 40 "app/online/au_ol_0024.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0024 보류"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0024] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + if (sqlca.sqlerrd[2] == 0) { + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0024] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = au_rec_count(bizdate, AU_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0024] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "HOLD", "Y"); + tx_buf_sets(ctx->out, "REASON", reason); + tx_log(TX_LOG_INFO, "[AU_OL_0024] 보류설정 key=%s reason=%s", key, reason); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0024.pgc b/app/online/au_ol_0024.pgc index 600e8db..260cbc4 100644 --- a/app/online/au_ol_0024.pgc +++ b/app/online/au_ol_0024.pgc @@ -1,14 +1,15 @@ -/* @tier medium @module au @transform authorization-online */ +/* @tier easy @module au @transform authorization-online */ /* - * au_ol_0024.pgc - 승인한도 온라인 서비스 (AU_OL_0024) [tier=medium] + * au_ol_0024.pgc - 승인 보류(홀드 상태전이) (AU_OL_0024) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,55 +17,44 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0024, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0024] 승인한도 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0024] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0024] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + EXEC SQL END DECLARE SECTION; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0024] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); + char reason[64]; + + tx_log(TX_LOG_INFO, "[AU_OL_0024] 승인 보류 진입"); + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) + strcpy(reason, "심사대기"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0024] tx_begin 경고"); + EXEC SQL UPDATE au_ledger + SET status = 'U', upd_ts = now() + WHERE key = :h_key AND status = 'R'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0024 보류"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0024] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + if (sqlca.sqlerrd[2] == 0) { + tx_abort(); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0024] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = au_rec_count(bizdate, AU_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0024] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "HOLD", "Y"); + tx_buf_sets(ctx->out, "REASON", reason); + tx_log(TX_LOG_INFO, "[AU_OL_0024] 보류설정 key=%s reason=%s", key, reason); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0025.c b/app/online/au_ol_0025.c index 400f3c6..ce48b68 100644 --- a/app/online/au_ol_0025.c +++ b/app/online/au_ol_0025.c @@ -8,15 +8,16 @@ #line 1 "app/online/au_ol_0025.pgc" /* @tier easy @module au @transform authorization-online */ /* - * au_ol_0025.pgc - 승인한도 온라인 서비스 (AU_OL_0025) [tier=easy] + * au_ol_0025.pgc - 승인 보류해제(재접수 전이) (AU_OL_0025) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,63 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0025.pgc" +#line 16 "app/online/au_ol_0025.pgc" TX_SERVICE(AU_OL_0025, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0025] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0025] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + +#line 27 "app/online/au_ol_0025.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; +/* exec sql end declare section */ +#line 28 "app/online/au_ol_0025.pgc" - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0025] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + + tx_log(TX_LOG_INFO, "[AU_OL_0025] 승인 보류해제 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select status from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 33 "app/online/au_ol_0025.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (h_status[0] != 'U') { + tx_log(TX_LOG_WARN, "[AU_OL_0025] 보류상태 아님 status=%s", h_status); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0025] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set status = 'R' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 47 "app/online/au_ol_0025.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0025 보류해제"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0025] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "HOLD", "N"); + tx_log(TX_LOG_INFO, "[AU_OL_0025] 보류해제 완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0025.pgc b/app/online/au_ol_0025.pgc index f99b387..da9ad9d 100644 --- a/app/online/au_ol_0025.pgc +++ b/app/online/au_ol_0025.pgc @@ -1,14 +1,15 @@ /* @tier easy @module au @transform authorization-online */ /* - * au_ol_0025.pgc - 승인한도 온라인 서비스 (AU_OL_0025) [tier=easy] + * au_ol_0025.pgc - 승인 보류해제(재접수 전이) (AU_OL_0025) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,45 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0025, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0025] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0025] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2]; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0025] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + tx_log(TX_LOG_INFO, "[AU_OL_0025] 승인 보류해제 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT status INTO :h_status FROM au_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (h_status[0] != 'U') { + tx_log(TX_LOG_WARN, "[AU_OL_0025] 보류상태 아님 status=%s", h_status); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0025] tx_begin 경고"); + EXEC SQL UPDATE au_ledger + SET status = 'R', upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0025 보류해제"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0025] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "HOLD", "N"); + tx_log(TX_LOG_INFO, "[AU_OL_0025] 보류해제 완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0026.c b/app/online/au_ol_0026.c index 102db42..c392e96 100644 --- a/app/online/au_ol_0026.c +++ b/app/online/au_ol_0026.c @@ -8,15 +8,16 @@ #line 1 "app/online/au_ol_0026.pgc" /* @tier medium @module au @transform authorization-online */ /* - * au_ol_0026.pgc - 승인한도 온라인 서비스 (AU_OL_0026) [tier=medium] + * au_ol_0026.pgc - 카드 블랙리스트 조회(거래이력 기반) (AU_OL_0026) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,60 +90,58 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0026.pgc" +#line 16 "app/online/au_ol_0026.pgc" TX_SERVICE(AU_OL_0026, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + +#line 21 "app/online/au_ol_0026.pgc" + char h_card [ 20 ] ; + +#line 22 "app/online/au_ol_0026.pgc" + int h_fail , h_total ; +/* exec sql end declare section */ +#line 23 "app/online/au_ol_0026.pgc" - tx_log(TX_LOG_INFO, "[AU_OL_0026] 승인한도 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0026] 필수 필드 누락"); + char card[20]; + int blacklisted = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0026] 카드 블랙리스트 조회 진입"); + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + strncpy(h_card, card, 19); h_card[19] = '\0'; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0026] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0026] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) filter ( where status = 'U' ) , count ( * ) from au_ledger where card_no = $1 ", + ECPGt_char,(h_card),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_fail),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_total),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 37 "app/online/au_ol_0026.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0026 블랙리스트"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0026] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = au_rec_count(bizdate, AU_ST_RECV); + if (h_total >= 5 && h_fail * 2 >= h_total) + blacklisted = 1; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0026] 등록완료 key=%s 당일건수=%ld", key, cnt); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "CARDNO", card); + tx_buf_sets(ctx->out, "BLACKLIST", blacklisted ? "Y" : "N"); + tx_buf_setlong(ctx->out, "FAILCNT", h_fail); + tx_buf_sets(ctx->out, "RESPCODE", blacklisted ? RESP_INVALID : RESP_OK); + tx_log(TX_LOG_INFO, "[AU_OL_0026] 블랙리스트 card=%s fail=%d/%d bl=%d", + card, h_fail, h_total, blacklisted); + tx_return(ctx, blacklisted ? TX_FAIL : TX_OK, ctx->out); } diff --git a/app/online/au_ol_0026.pgc b/app/online/au_ol_0026.pgc index de7faf7..753d74f 100644 --- a/app/online/au_ol_0026.pgc +++ b/app/online/au_ol_0026.pgc @@ -1,14 +1,15 @@ /* @tier medium @module au @transform authorization-online */ /* - * au_ol_0026.pgc - 승인한도 온라인 서비스 (AU_OL_0026) [tier=medium] + * au_ol_0026.pgc - 카드 블랙리스트 조회(거래이력 기반) (AU_OL_0026) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,55 +17,39 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0026, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[20]; + int h_fail, h_total; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AU_OL_0026] 승인한도 등록 서비스 진입"); + char card[20]; + int blacklisted = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0026] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[AU_OL_0026] 카드 블랙리스트 조회 진입"); + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + strncpy(h_card, card, 19); h_card[19] = '\0'; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0026] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0026] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + EXEC SQL SELECT count(*) FILTER (WHERE status = 'U'), count(*) + INTO :h_fail, :h_total + FROM au_ledger WHERE card_no = :h_card; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0026 블랙리스트"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0026] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = au_rec_count(bizdate, AU_ST_RECV); + if (h_total >= 5 && h_fail * 2 >= h_total) + blacklisted = 1; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0026] 등록완료 key=%s 당일건수=%ld", key, cnt); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "CARDNO", card); + tx_buf_sets(ctx->out, "BLACKLIST", blacklisted ? "Y" : "N"); + tx_buf_setlong(ctx->out, "FAILCNT", h_fail); + tx_buf_sets(ctx->out, "RESPCODE", blacklisted ? RESP_INVALID : RESP_OK); + tx_log(TX_LOG_INFO, "[AU_OL_0026] 블랙리스트 card=%s fail=%d/%d bl=%d", + card, h_fail, h_total, blacklisted); + tx_return(ctx, blacklisted ? TX_FAIL : TX_OK, ctx->out); } diff --git a/app/online/au_ol_0027.c b/app/online/au_ol_0027.c index b889566..cd41e3c 100644 --- a/app/online/au_ol_0027.c +++ b/app/online/au_ol_0027.c @@ -8,15 +8,16 @@ #line 1 "app/online/au_ol_0027.pgc" /* @tier easy @module au @transform authorization-online */ /* - * au_ol_0027.pgc - 승인한도 온라인 서비스 (AU_OL_0027) [tier=easy] + * au_ol_0027.pgc - 승인 응답코드 매핑(상태→ISO 응답) (AU_OL_0027) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,57 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0027.pgc" +#line 16 "app/online/au_ol_0027.pgc" TX_SERVICE(AU_OL_0027, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0027] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0027] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + +#line 27 "app/online/au_ol_0027.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; +/* exec sql end declare section */ +#line 28 "app/online/au_ol_0027.pgc" - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0027] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + + const char *resp; + + tx_log(TX_LOG_INFO, "[AU_OL_0027] 승인 응답코드 매핑 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select status from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 35 "app/online/au_ol_0027.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + switch (h_status[0]) { + case 'M': + case 'S': resp = RESP_OK; break; + case 'U': resp = RESP_INVALID; break; + case 'R': resp = "0002"; break; /* 처리중 */ + default: resp = RESP_SYSERR; break; + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0027] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "RESPCODE", resp); + tx_log(TX_LOG_INFO, "[AU_OL_0027] 매핑 status=%s -> resp=%s", h_status, resp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0027.pgc b/app/online/au_ol_0027.pgc index e71e761..180bccd 100644 --- a/app/online/au_ol_0027.pgc +++ b/app/online/au_ol_0027.pgc @@ -1,14 +1,15 @@ /* @tier easy @module au @transform authorization-online */ /* - * au_ol_0027.pgc - 승인한도 온라인 서비스 (AU_OL_0027) [tier=easy] + * au_ol_0027.pgc - 승인 응답코드 매핑(상태→ISO 응답) (AU_OL_0027) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,41 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0027, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0027] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0027] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2]; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0027] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + const char *resp; + + tx_log(TX_LOG_INFO, "[AU_OL_0027] 승인 응답코드 매핑 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT status INTO :h_status FROM au_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + switch (h_status[0]) { + case 'M': + case 'S': resp = RESP_OK; break; + case 'U': resp = RESP_INVALID; break; + case 'R': resp = "0002"; break; /* 처리중 */ + default: resp = RESP_SYSERR; break; + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0027] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "RESPCODE", resp); + tx_log(TX_LOG_INFO, "[AU_OL_0027] 매핑 status=%s -> resp=%s", h_status, resp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0028.c b/app/online/au_ol_0028.c index dea7248..fe656d6 100644 --- a/app/online/au_ol_0028.c +++ b/app/online/au_ol_0028.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0028.pgc" -/* @tier hard @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0028.pgc - 승인한도 온라인 서비스 (AU_OL_0028) [tier=hard] + * au_ol_0028.pgc - 일한도 잔여조회(당일 승인가능액) (AU_OL_0028) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,88 +90,59 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0028.pgc" +#line 16 "app/online/au_ol_0028.pgc" TX_SERVICE(AU_OL_0028, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + +#line 21 "app/online/au_ol_0028.pgc" + char h_card [ 20 ] , h_bdate [ 9 ] ; + +#line 22 "app/online/au_ol_0028.pgc" + long h_used ; +/* exec sql end declare section */ +#line 23 "app/online/au_ol_0028.pgc" - tx_log(TX_LOG_INFO, "[AU_OL_0028] 승인한도 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0028] 필수 필드 누락"); + char card[20], bdate[16]; + long daily_limit = 5000000L, remain; + + tx_log(TX_LOG_INFO, "[AU_OL_0028] 일한도 잔여조회 진입"); + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + date_today(bdate); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) == TX_OK) { /* override */ } - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0028] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + strncpy(h_card, card, 19); h_card[19] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from au_ledger where card_no = $1 and biz_date = $2 and status in ( 'R' , 'M' , 'S' )", + ECPGt_char,(h_card),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_used),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 42 "app/online/au_ol_0028.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0028 일한도"); + tx_return(ctx, TX_EDB, ctx->out); return; } - - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0028] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AU_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AU_OL_0028] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (au_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + remain = (daily_limit > h_used) ? daily_limit - h_used : 0; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AU_OL_0028] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "CARDNO", card); + tx_buf_setlong(ctx->out, "DAILYLIMIT", daily_limit); + tx_buf_setlong(ctx->out, "USED", h_used); + tx_buf_setlong(ctx->out, "REMAIN", remain); + tx_log(TX_LOG_INFO, "[AU_OL_0028] 일한도 card=%s remain=%ld", card, remain); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0028.pgc b/app/online/au_ol_0028.pgc index c8d2d32..f816d66 100644 --- a/app/online/au_ol_0028.pgc +++ b/app/online/au_ol_0028.pgc @@ -1,14 +1,15 @@ -/* @tier hard @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0028.pgc - 승인한도 온라인 서비스 (AU_OL_0028) [tier=hard] + * au_ol_0028.pgc - 일한도 잔여조회(당일 승인가능액) (AU_OL_0028) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,83 +17,41 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0028, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[20], h_bdate[9]; + long h_used; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AU_OL_0028] 승인한도 트랜잭션 오케스트레이션 진입"); + char card[20], bdate[16]; + long daily_limit = 5000000L, remain; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0028] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[AU_OL_0028] 일한도 잔여조회 진입"); + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + date_today(bdate); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) == TX_OK) { /* override */ } - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0028] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + strncpy(h_card, card, 19); h_card[19] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + + EXEC SQL SELECT coalesce(sum(amount), 0) INTO :h_used + FROM au_ledger + WHERE card_no = :h_card AND biz_date = :h_bdate + AND status IN ('R', 'M', 'S'); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0028 일한도"); + tx_return(ctx, TX_EDB, ctx->out); return; } - - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0028] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("AU_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[AU_OL_0028] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (au_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + remain = (daily_limit > h_used) ? daily_limit - h_used : 0; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[AU_OL_0028] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "CARDNO", card); + tx_buf_setlong(ctx->out, "DAILYLIMIT", daily_limit); + tx_buf_setlong(ctx->out, "USED", h_used); + tx_buf_setlong(ctx->out, "REMAIN", remain); + tx_log(TX_LOG_INFO, "[AU_OL_0028] 일한도 card=%s remain=%ld", card, remain); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0029.c b/app/online/au_ol_0029.c index ad0551f..903517d 100644 --- a/app/online/au_ol_0029.c +++ b/app/online/au_ol_0029.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0029.pgc" -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0029.pgc - 승인한도 온라인 서비스 (AU_OL_0029) [tier=easy] + * au_ol_0029.pgc - 승인율 조회(기간 성공/전체 비율) (AU_OL_0029) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,59 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0029.pgc" +#line 16 "app/online/au_ol_0029.pgc" TX_SERVICE(AU_OL_0029, ctx) { - au_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + +#line 21 "app/online/au_ol_0029.pgc" + char h_from [ 9 ] , h_to [ 9 ] ; + +#line 22 "app/online/au_ol_0029.pgc" + int h_ok , h_all ; +/* exec sql end declare section */ +#line 23 "app/online/au_ol_0029.pgc" - tx_log(TX_LOG_INFO, "[AU_OL_0029] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0029] KEY 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0029] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + char from[16], to[16]; + int rate = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0029] 승인율 조회 진입"); + if (tx_buf_get(ctx->in, "FROMDATE", from, sizeof(from)) != TX_OK) + date_today(from); + if (tx_buf_get(ctx->in, "TODATE", to, sizeof(to)) != TX_OK) + date_today(to); + + strncpy(h_from, from, 8); h_from[8] = '\0'; + strncpy(h_to, to, 8); h_to[8] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) filter ( where status in ( 'M' , 'S' ) ) , count ( * ) from au_ledger where biz_date between $1 and $2 ", + ECPGt_char,(h_from),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_to),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_ok),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_all),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 40 "app/online/au_ol_0029.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0029 승인율"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (h_all > 0) rate = h_ok * 10000 / h_all; /* 0.01% 단위 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0029] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "OKCNT", h_ok); + tx_buf_setlong(ctx->out, "ALLCNT", h_all); + tx_buf_setlong(ctx->out, "RATEBP", rate); + tx_log(TX_LOG_INFO, "[AU_OL_0029] 승인율 %d/%d = %d.%02d%%", + h_ok, h_all, rate / 100, rate % 100); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0029.pgc b/app/online/au_ol_0029.pgc index d7642db..b36c549 100644 --- a/app/online/au_ol_0029.pgc +++ b/app/online/au_ol_0029.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0029.pgc - 승인한도 온라인 서비스 (AU_OL_0029) [tier=easy] + * au_ol_0029.pgc - 승인율 조회(기간 성공/전체 비율) (AU_OL_0029) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,39 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0029, ctx) { - au_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_from[9], h_to[9]; + int h_ok, h_all; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AU_OL_0029] 승인한도 단건조회 서비스 진입"); + char from[16], to[16]; + int rate = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0029] KEY 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0029] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + tx_log(TX_LOG_INFO, "[AU_OL_0029] 승인율 조회 진입"); + if (tx_buf_get(ctx->in, "FROMDATE", from, sizeof(from)) != TX_OK) + date_today(from); + if (tx_buf_get(ctx->in, "TODATE", to, sizeof(to)) != TX_OK) + date_today(to); + + strncpy(h_from, from, 8); h_from[8] = '\0'; + strncpy(h_to, to, 8); h_to[8] = '\0'; + + EXEC SQL SELECT count(*) FILTER (WHERE status IN ('M','S')), count(*) + INTO :h_ok, :h_all + FROM au_ledger + WHERE biz_date BETWEEN :h_from AND :h_to; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0029 승인율"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (h_all > 0) rate = h_ok * 10000 / h_all; /* 0.01% 단위 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0029] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "OKCNT", h_ok); + tx_buf_setlong(ctx->out, "ALLCNT", h_all); + tx_buf_setlong(ctx->out, "RATEBP", rate); + tx_log(TX_LOG_INFO, "[AU_OL_0029] 승인율 %d/%d = %d.%02d%%", + h_ok, h_all, rate / 100, rate % 100); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0030.c b/app/online/au_ol_0030.c index 558aa6c..b166af2 100644 --- a/app/online/au_ol_0030.c +++ b/app/online/au_ol_0030.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0030.pgc" -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0030.pgc - 승인한도 온라인 서비스 (AU_OL_0030) [tier=easy] + * au_ol_0030.pgc - 카드 최근승인 이력조회(커서 상위 N) (AU_OL_0030) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,79 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0030.pgc" +#line 16 "app/online/au_ol_0030.pgc" TX_SERVICE(AU_OL_0030, ctx) { - au_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 21 "app/online/au_ol_0030.pgc" + char h_card [ 20 ] ; + +#line 22 "app/online/au_ol_0030.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; + +#line 23 "app/online/au_ol_0030.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 24 "app/online/au_ol_0030.pgc" - tx_log(TX_LOG_INFO, "[AU_OL_0030] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0030] KEY 누락"); + char card[20], field[16]; + int n = 0; + + tx_log(TX_LOG_INFO, "[AU_OL_0030] 카드 최근승인 이력조회 진입"); + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_card, card, 19); h_card[19] = '\0'; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + ECPGset_var( 0, ( h_card ), __LINE__);\ + /* declare cur_recent cursor for select key , amount , status from au_ledger where card_no = $1 order by reg_ts desc limit 5 */ +#line 41 "app/online/au_ol_0030.pgc" + +#line 41 "app/online/au_ol_0030.pgc" + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_recent cursor for select key , amount , status from au_ledger where card_no = $1 order by reg_ts desc limit 5", + ECPGt_char,(h_card),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 42 "app/online/au_ol_0030.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0030 OPEN"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_recent", ECPGt_EOIT, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 51 "app/online/au_ol_0030.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + snprintf(field, sizeof(field), "KEY%d", n); + tx_buf_sets(ctx->out, field, h_key); + snprintf(field, sizeof(field), "AMT%d", n); + tx_buf_setlong(ctx->out, field, h_amount); + n++; + } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_recent", ECPGt_EOIT, ECPGt_EORT);} +#line 60 "app/online/au_ol_0030.pgc" + + + tx_buf_setlong(ctx->out, "COUNT", n); + tx_log(TX_LOG_INFO, "[AU_OL_0030] 최근이력 card=%s cnt=%d", card, n); + tx_return(ctx, (n > 0) ? TX_OK : TX_ENOENT, ctx->out); } diff --git a/app/online/au_ol_0030.pgc b/app/online/au_ol_0030.pgc index 085132f..bbf7cb2 100644 --- a/app/online/au_ol_0030.pgc +++ b/app/online/au_ol_0030.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0030.pgc - 승인한도 온라인 서비스 (AU_OL_0030) [tier=easy] + * au_ol_0030.pgc - 카드 최근승인 이력조회(커서 상위 N) (AU_OL_0030) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,49 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0030, ctx) { - au_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[20]; + char h_key[16], h_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[AU_OL_0030] 승인한도 단건조회 서비스 진입"); + char card[20], field[16]; + int n = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0030] KEY 누락"); + tx_log(TX_LOG_INFO, "[AU_OL_0030] 카드 최근승인 이력조회 진입"); + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_card, card, 19); h_card[19] = '\0'; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL DECLARE cur_recent CURSOR FOR + SELECT key, amount, status + FROM au_ledger + WHERE card_no = :h_card + ORDER BY reg_ts DESC + LIMIT 5; + EXEC SQL OPEN cur_recent; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0030 OPEN"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + for (;;) { + EXEC SQL FETCH cur_recent INTO :h_key, :h_amount, :h_status; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + snprintf(field, sizeof(field), "KEY%d", n); + tx_buf_sets(ctx->out, field, h_key); + snprintf(field, sizeof(field), "AMT%d", n); + tx_buf_setlong(ctx->out, field, h_amount); + n++; + } + EXEC SQL CLOSE cur_recent; + + tx_buf_setlong(ctx->out, "COUNT", n); + tx_log(TX_LOG_INFO, "[AU_OL_0030] 최근이력 card=%s cnt=%d", card, n); + tx_return(ctx, (n > 0) ? TX_OK : TX_ENOENT, ctx->out); } diff --git a/app/online/au_ol_0031.c b/app/online/au_ol_0031.c index 67ddd19..cbd13b6 100644 --- a/app/online/au_ol_0031.c +++ b/app/online/au_ol_0031.c @@ -8,15 +8,16 @@ #line 1 "app/online/au_ol_0031.pgc" /* @tier medium @module au @transform authorization-online */ /* - * au_ol_0031.pgc - 승인한도 온라인 서비스 (AU_OL_0031) [tier=medium] + * au_ol_0031.pgc - 승인 재처리 접수(거절건 재접수 전이) (AU_OL_0031) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,60 +90,70 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0031.pgc" +#line 16 "app/online/au_ol_0031.pgc" TX_SERVICE(AU_OL_0031, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0031] 승인한도 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0031] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0031] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + + +#line 27 "app/online/au_ol_0031.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; + +#line 28 "app/online/au_ol_0031.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 29 "app/online/au_ol_0031.pgc" - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0031] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); + + tx_log(TX_LOG_INFO, "[AU_OL_0031] 승인 재처리 접수 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , status from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 35 "app/online/au_ol_0031.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0031] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + if (h_status[0] != 'U') { + tx_log(TX_LOG_WARN, "[AU_OL_0031] 재처리 대상아님 status=%s", h_status); + tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0031] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update au_ledger set status = 'R' , fee = 0 , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 49 "app/online/au_ol_0031.pgc" - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0031] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0031 재처리"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = au_rec_count(bizdate, AU_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0031] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_log(TX_LOG_INFO, "[AU_OL_0031] 재처리 접수완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0031.pgc b/app/online/au_ol_0031.pgc index 3ba10c4..46180d9 100644 --- a/app/online/au_ol_0031.pgc +++ b/app/online/au_ol_0031.pgc @@ -1,14 +1,15 @@ /* @tier medium @module au @transform authorization-online */ /* - * au_ol_0031.pgc - 승인한도 온라인 서비스 (AU_OL_0031) [tier=medium] + * au_ol_0031.pgc - 승인 재처리 접수(거절건 재접수 전이) (AU_OL_0031) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,55 +17,48 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0031, ctx) { - au_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0031] 승인한도 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0031] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[AU_OL_0031] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[AU_OL_0031] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); + tx_log(TX_LOG_INFO, "[AU_OL_0031] 승인 재처리 접수 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT amount, status INTO :h_amount, :h_status + FROM au_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_return(ctx, TX_ENOENT, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[AU_OL_0031] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + if (h_status[0] != 'U') { + tx_log(TX_LOG_WARN, "[AU_OL_0031] 재처리 대상아님 status=%s", h_status); + tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, AU_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = au_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[AU_OL_0031] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[AU_OL_0031] tx_begin 경고"); + EXEC SQL UPDATE au_ledger + SET status = 'R', fee = 0, upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("au_ol_0031 재처리"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = au_rec_count(bizdate, AU_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[AU_OL_0031] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_log(TX_LOG_INFO, "[AU_OL_0031] 재처리 접수완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/au_ol_0032.c b/app/online/au_ol_0032.c index 07427ea..5517085 100644 --- a/app/online/au_ol_0032.c +++ b/app/online/au_ol_0032.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/au_ol_0032.pgc" -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0032.pgc - 승인한도 온라인 서비스 (AU_OL_0032) [tier=easy] + * au_ol_0032.pgc - 승인 대사(원장 vs 요청 금액 검증) (AU_OL_0032) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -89,37 +90,58 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/au_ol_0032.pgc" +#line 16 "app/online/au_ol_0032.pgc" TX_SERVICE(AU_OL_0032, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0032] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0032] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/au_ol_0032.pgc" + char h_key [ 16 ] ; + +#line 28 "app/online/au_ol_0032.pgc" + long h_ledger ; +/* exec sql end declare section */ +#line 29 "app/online/au_ol_0032.pgc" - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + + long claim = 0, diff; + + tx_log(TX_LOG_INFO, "[AU_OL_0032] 승인 대사 진입"); + tx_buf_getlong(ctx->in, "CLAIMAMT", &claim); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount from au_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_ledger),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 37 "app/online/au_ol_0032.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "MATCH", "N"); + tx_buf_sets(ctx->out, "RESULT", "원장없음"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + diff = h_ledger - claim; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_setlong(ctx->out, "LEDGERAMT", h_ledger); + tx_buf_setlong(ctx->out, "CLAIMAMT", claim); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "MATCH", (diff == 0) ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[AU_OL_0032] 대사 key=%s ledger=%ld claim=%ld diff=%ld", + key, h_ledger, claim, diff); + tx_return(ctx, (diff == 0) ? TX_OK : TX_FAIL, ctx->out); } diff --git a/app/online/au_ol_0032.pgc b/app/online/au_ol_0032.pgc index b485c6c..a53e39a 100644 --- a/app/online/au_ol_0032.pgc +++ b/app/online/au_ol_0032.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module au @transform authorization-online */ +/* @tier medium @module au @transform authorization-online */ /* - * au_ol_0032.pgc - 승인한도 온라인 서비스 (AU_OL_0032) [tier=easy] + * au_ol_0032.pgc - 승인 대사(원장 vs 요청 금액 검증) (AU_OL_0032) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 au 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "au_core.h" @@ -16,32 +17,39 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(AU_OL_0032, ctx) { - au_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[AU_OL_0032] 승인한도 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[AU_OL_0032] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_ledger; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = au_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[AU_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + long claim = 0, diff; + + tx_log(TX_LOG_INFO, "[AU_OL_0032] 승인 대사 진입"); + tx_buf_getlong(ctx->in, "CLAIMAMT", &claim); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT amount INTO :h_ledger FROM au_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "MATCH", "N"); + tx_buf_sets(ctx->out, "RESULT", "원장없음"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + diff = h_ledger - claim; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[AU_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_setlong(ctx->out, "LEDGERAMT", h_ledger); + tx_buf_setlong(ctx->out, "CLAIMAMT", claim); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "MATCH", (diff == 0) ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[AU_OL_0032] 대사 key=%s ledger=%ld claim=%ld diff=%ld", + key, h_ledger, claim, diff); + tx_return(ctx, (diff == 0) ? TX_OK : TX_FAIL, ctx->out); } diff --git a/app/online/cl_ol_0002.c b/app/online/cl_ol_0002.c index 0367483..1c38b08 100644 --- a/app/online/cl_ol_0002.c +++ b/app/online/cl_ol_0002.c @@ -10,13 +10,16 @@ /* * cl_ol_0002.pgc - 마감 온라인 서비스 (CL_OL_0002) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 일마감(日마감) 요청. 요청 영업일(BIZDATE)에 대해 아직 마감되지 않은 + * 접수(R) 건이 남아 있는지 먼저 확인하고, 남아있지 않으면 처리완료(M)/ + * 불일치(U) 건을 정산완료(S)로 일괄 마감한다. 마감건수와 마감금액 합계를 + * 응답한다. (직접 EXEC SQL 로 집계/일괄 UPDATE 수행) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +92,132 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0002.pgc" +#line 18 "app/online/cl_ol_0002.pgc" TX_SERVICE(CL_OL_0002, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 23 "app/online/cl_ol_0002.pgc" + char h_biz_date [ 9 ] ; + +#line 24 "app/online/cl_ol_0002.pgc" + int h_recv_cnt ; + +#line 25 "app/online/cl_ol_0002.pgc" + long h_settle_sum ; +/* exec sql end declare section */ +#line 26 "app/online/cl_ol_0002.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0002] 마감 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0002] KEY 누락"); + char bizdate[16]; + long closed_cnt = 0; + char won[40]; + + tx_log(TX_LOG_INFO, "[CL_OL_0002] 일마감 요청 서비스 진입"); + + /* 1. 요청 검증 : 영업일 필수 및 형식 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0002] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0002] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0002] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 2. 선행검사 : 미마감(R) 접수건 잔존여부 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date = $1 and status = 'R'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_recv_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 58 "app/online/cl_ol_0002.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0002 recv count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_recv_cnt > 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0002] 미마감 접수건 잔존 date=%s cnt=%d", + bizdate, h_recv_cnt); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "RECVCNT", (long)h_recv_cnt); + tx_buf_sets(ctx->out, "MESSAGE", "미마감 접수건 잔존으로 일마감 불가"); + tx_return(ctx, TX_FAIL, ctx->out); return; } + /* 3. 마감 트랜잭션 : M/U 건을 S 로 일괄 확정 */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0002] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cl_ledger set status = 'S' , upd_ts = now ( ) where biz_date = $1 and status in ( 'M' , 'U' )", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 90 "app/online/cl_ol_0002.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0002 settle update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + closed_cnt = (long)sqlca.sqlerrd[2]; + + /* 4. 마감금액 합계 재집계 (S 확정분) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settle_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 106 "app/online/cl_ol_0002.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0002 settle sum"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 5. 응답 구성 */ + amount_format_won(h_settle_sum, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0002] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CLOSECNT", closed_cnt); + tx_buf_setlong(ctx->out, "SETTLESUM", h_settle_sum); + tx_buf_sets(ctx->out, "SETTLEWON", won); + tx_log(TX_LOG_INFO, "[CL_OL_0002] 일마감 완료 date=%s 건수=%ld 합계=%ld", + bizdate, closed_cnt, h_settle_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0002.pgc b/app/online/cl_ol_0002.pgc index 6a8498a..c80a810 100644 --- a/app/online/cl_ol_0002.pgc +++ b/app/online/cl_ol_0002.pgc @@ -2,13 +2,16 @@ /* * cl_ol_0002.pgc - 마감 온라인 서비스 (CL_OL_0002) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 일마감(日마감) 요청. 요청 영업일(BIZDATE)에 대해 아직 마감되지 않은 + * 접수(R) 건이 남아 있는지 먼저 확인하고, 남아있지 않으면 처리완료(M)/ + * 불일치(U) 건을 정산완료(S)로 일괄 마감한다. 마감건수와 마감금액 합계를 + * 응답한다. (직접 EXEC SQL 로 집계/일괄 UPDATE 수행) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,32 +19,111 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0002, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + int h_recv_cnt; + long h_settle_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0002] 마감 단건조회 서비스 진입"); + char bizdate[16]; + long closed_cnt = 0; + char won[40]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0002] KEY 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0002] 일마감 요청 서비스 진입"); + + /* 1. 요청 검증 : 영업일 필수 및 형식 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0002] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0002] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0002] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 2. 선행검사 : 미마감(R) 접수건 잔존여부 확인 */ + EXEC SQL SELECT count(*) + INTO :h_recv_cnt + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'R'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0002 recv count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_recv_cnt > 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0002] 미마감 접수건 잔존 date=%s cnt=%d", + bizdate, h_recv_cnt); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "RECVCNT", (long)h_recv_cnt); + tx_buf_sets(ctx->out, "MESSAGE", "미마감 접수건 잔존으로 일마감 불가"); + tx_return(ctx, TX_FAIL, ctx->out); return; } + /* 3. 마감 트랜잭션 : M/U 건을 S 로 일괄 확정 */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0002] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE cl_ledger + SET status = 'S', upd_ts = now() + WHERE biz_date = :h_biz_date + AND status IN ('M', 'U'); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0002 settle update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + closed_cnt = (long)sqlca.sqlerrd[2]; + + /* 4. 마감금액 합계 재집계 (S 확정분) */ + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_settle_sum + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'S'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0002 settle sum"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 5. 응답 구성 */ + amount_format_won(h_settle_sum, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0002] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CLOSECNT", closed_cnt); + tx_buf_setlong(ctx->out, "SETTLESUM", h_settle_sum); + tx_buf_sets(ctx->out, "SETTLEWON", won); + tx_log(TX_LOG_INFO, "[CL_OL_0002] 일마감 완료 date=%s 건수=%ld 합계=%ld", + bizdate, closed_cnt, h_settle_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0003.c b/app/online/cl_ol_0003.c index 1567590..bde6729 100644 --- a/app/online/cl_ol_0003.c +++ b/app/online/cl_ol_0003.c @@ -10,13 +10,37 @@ /* * cl_ol_0003.pgc - 마감 온라인 서비스 (CL_OL_0003) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 대사(對査)/검증. 요청 영업일(BIZDATE)의 실제 원장 건수/금액 합계를 + * 집계하여, 요청자가 제출한 기대건수(EXPECTCNT)·기대금액(EXPECTAMT)과 + * 대사한다. 건수 또는 금액에 차이가 있으면 RESP_INVALID 로 차이내역을 + * 반환하고, 완전 일치하면 RESP_OK 를 반환한다. (read-only 집계 대사) + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * BIZDATE (필수) 영업일(YYYYMMDD) + * EXPECTCNT (선택) 기대 건수 (미제출 시 0 으로 대사) + * EXPECTAMT (선택) 기대 금액합계(원) + * 출력 필드: + * RESPCODE 0000=일치 / 9001=불일치·입력오류 / 9999=시스템오류 + * BIZDATE 대사 대상 영업일 + * ACTCNT 실제 원장 건수 + * ACTAMT 실제 금액합계(원) / ACTWON 한글 표기 + * EXPECTCNT 대사 기준 기대건수 + * EXPECTAMT 대사 기준 기대금액 + * DIFFCNT 건수 차이(실제-기대) + * DIFFAMT 금액 차이(실제-기대) + * MATCH 대사 일치여부(Y/N) + * + * ── 대사 규칙 ──────────────────────────────────────────────────────── + * · 상태 무관 전체 원장 건을 대상으로 건수/금액을 집계한다. + * · 건수·금액 모두 차이가 0 일 때만 일치(RESP_OK)로 판정한다. + * · 하나라도 차이가 있으면 불일치(RESP_INVALID)로 차이내역을 반환한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,60 +113,106 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0003.pgc" +#line 39 "app/online/cl_ol_0003.pgc" TX_SERVICE(CL_OL_0003, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + +#line 44 "app/online/cl_ol_0003.pgc" + char h_biz_date [ 9 ] ; + +#line 45 "app/online/cl_ol_0003.pgc" + int h_act_cnt ; + +#line 46 "app/online/cl_ol_0003.pgc" + long h_act_amt ; +/* exec sql end declare section */ +#line 47 "app/online/cl_ol_0003.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0003] 마감 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0003] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char bizdate[16]; + long expect_cnt = 0, expect_amt = 0; + long diff_cnt = 0, diff_amt = 0; + int matched; + char won[40]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0003] 금액 범위 오류 amount=%ld", amount); + tx_log(TX_LOG_INFO, "[CL_OL_0003] 마감 대사/검증 서비스 진입"); + + /* 1. 요청 검증 : 영업일 필수 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0003] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0003] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[CL_OL_0003] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + /* 기대건수/기대금액은 대사 기준값 (미제출 시 0 으로 대사) */ + tx_buf_getlong(ctx->in, "EXPECTCNT", &expect_cnt); + tx_buf_getlong(ctx->in, "EXPECTAMT", &expect_amt); - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0003] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 2. 실제 원장 집계 (건수 + 금액합계 동시 singleton SELECT) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date = $1 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_act_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_act_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 84 "app/online/cl_ol_0003.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0003 aggregate"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* 3. 대사 : 차이 계산 (실제 - 기대) */ + diff_cnt = (long)h_act_cnt - expect_cnt; + diff_amt = h_act_amt - expect_amt; + matched = (diff_cnt == 0 && diff_amt == 0); + /* 4. 응답 구성 : 실제/기대/차이 내역 */ + amount_format_won(h_act_amt, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0003] 등록완료 key=%s 당일건수=%ld", key, cnt); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "ACTCNT", (long)h_act_cnt); + tx_buf_setlong(ctx->out, "ACTAMT", h_act_amt); + tx_buf_sets(ctx->out, "ACTWON", won); + tx_buf_setlong(ctx->out, "EXPECTCNT", expect_cnt); + tx_buf_setlong(ctx->out, "EXPECTAMT", expect_amt); + tx_buf_setlong(ctx->out, "DIFFCNT", diff_cnt); + tx_buf_setlong(ctx->out, "DIFFAMT", diff_amt); + + if (matched) { + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MATCH", "Y"); + tx_log(TX_LOG_INFO, "[CL_OL_0003] 대사일치 date=%s cnt=%d amt=%ld", + bizdate, h_act_cnt, h_act_amt); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "MATCH", "N"); + tx_log(TX_LOG_WARN, "[CL_OL_0003] 대사불일치 date=%s diffcnt=%ld diffamt=%ld", + bizdate, diff_cnt, diff_amt); + tx_return(ctx, TX_FAIL, ctx->out); } diff --git a/app/online/cl_ol_0003.pgc b/app/online/cl_ol_0003.pgc index 292fc41..632fa74 100644 --- a/app/online/cl_ol_0003.pgc +++ b/app/online/cl_ol_0003.pgc @@ -2,13 +2,37 @@ /* * cl_ol_0003.pgc - 마감 온라인 서비스 (CL_OL_0003) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 대사(對査)/검증. 요청 영업일(BIZDATE)의 실제 원장 건수/금액 합계를 + * 집계하여, 요청자가 제출한 기대건수(EXPECTCNT)·기대금액(EXPECTAMT)과 + * 대사한다. 건수 또는 금액에 차이가 있으면 RESP_INVALID 로 차이내역을 + * 반환하고, 완전 일치하면 RESP_OK 를 반환한다. (read-only 집계 대사) + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * BIZDATE (필수) 영업일(YYYYMMDD) + * EXPECTCNT (선택) 기대 건수 (미제출 시 0 으로 대사) + * EXPECTAMT (선택) 기대 금액합계(원) + * 출력 필드: + * RESPCODE 0000=일치 / 9001=불일치·입력오류 / 9999=시스템오류 + * BIZDATE 대사 대상 영업일 + * ACTCNT 실제 원장 건수 + * ACTAMT 실제 금액합계(원) / ACTWON 한글 표기 + * EXPECTCNT 대사 기준 기대건수 + * EXPECTAMT 대사 기준 기대금액 + * DIFFCNT 건수 차이(실제-기대) + * DIFFAMT 금액 차이(실제-기대) + * MATCH 대사 일치여부(Y/N) + * + * ── 대사 규칙 ──────────────────────────────────────────────────────── + * · 상태 무관 전체 원장 건을 대상으로 건수/금액을 집계한다. + * · 건수·금액 모두 차이가 0 일 때만 일치(RESP_OK)로 판정한다. + * · 하나라도 차이가 있으면 불일치(RESP_INVALID)로 차이내역을 반환한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,55 +40,85 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0003, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + int h_act_cnt; + long h_act_amt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0003] 마감 등록 서비스 진입"); + char bizdate[16]; + long expect_cnt = 0, expect_amt = 0; + long diff_cnt = 0, diff_amt = 0; + int matched; + char won[40]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0003] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[CL_OL_0003] 마감 대사/검증 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0003] 금액 범위 오류 amount=%ld", amount); + /* 1. 요청 검증 : 영업일 필수 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0003] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0003] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[CL_OL_0003] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + /* 기대건수/기대금액은 대사 기준값 (미제출 시 0 으로 대사) */ + tx_buf_getlong(ctx->in, "EXPECTCNT", &expect_cnt); + tx_buf_getlong(ctx->in, "EXPECTAMT", &expect_amt); - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0003] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 2. 실제 원장 집계 (건수 + 금액합계 동시 singleton SELECT) */ + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_act_cnt, :h_act_amt + FROM cl_ledger + WHERE biz_date = :h_biz_date; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0003 aggregate"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* 3. 대사 : 차이 계산 (실제 - 기대) */ + diff_cnt = (long)h_act_cnt - expect_cnt; + diff_amt = h_act_amt - expect_amt; + matched = (diff_cnt == 0 && diff_amt == 0); + /* 4. 응답 구성 : 실제/기대/차이 내역 */ + amount_format_won(h_act_amt, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0003] 등록완료 key=%s 당일건수=%ld", key, cnt); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "ACTCNT", (long)h_act_cnt); + tx_buf_setlong(ctx->out, "ACTAMT", h_act_amt); + tx_buf_sets(ctx->out, "ACTWON", won); + tx_buf_setlong(ctx->out, "EXPECTCNT", expect_cnt); + tx_buf_setlong(ctx->out, "EXPECTAMT", expect_amt); + tx_buf_setlong(ctx->out, "DIFFCNT", diff_cnt); + tx_buf_setlong(ctx->out, "DIFFAMT", diff_amt); + + if (matched) { + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MATCH", "Y"); + tx_log(TX_LOG_INFO, "[CL_OL_0003] 대사일치 date=%s cnt=%d amt=%ld", + bizdate, h_act_cnt, h_act_amt); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "MATCH", "N"); + tx_log(TX_LOG_WARN, "[CL_OL_0003] 대사불일치 date=%s diffcnt=%ld diffamt=%ld", + bizdate, diff_cnt, diff_amt); + tx_return(ctx, TX_FAIL, ctx->out); } diff --git a/app/online/cl_ol_0004.c b/app/online/cl_ol_0004.c index 43f84a7..1f39d80 100644 --- a/app/online/cl_ol_0004.c +++ b/app/online/cl_ol_0004.c @@ -10,13 +10,38 @@ /* * cl_ol_0004.pgc - 마감 온라인 서비스 (CL_OL_0004) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감후 집계 조회. 요청 영업일(BIZDATE)의 정산완료(S) 건에 대해 + * 건수 / 거래금액 합계 / 수수료 합계 / 순액(합계-수수료)을 singleton + * SELECT 로 산출하여 응답한다. read-only 조회 서비스이다. + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * BIZDATE (필수) 영업일(YYYYMMDD) + * 출력 필드: + * RESPCODE 0000=정상 / 9001=입력오류 / 9999=시스템오류 + * BIZDATE 집계 대상 영업일 + * SETTLECNT 정산완료(S) 건수 + * AMTSUM 거래금액 합계(Σamount, 원) + * FEESUM 수수료 합계(Σfee, 원) + * NETAMT 순액(= AMTSUM - FEESUM, 원) + * NETWON 순액 한글 표기 + * + * ── 집계 규칙 ──────────────────────────────────────────────────────── + * · 마감완료(status='S') 건만 집계 대상으로 삼는다. + * · 순액(NETAMT)은 가맹점 지급 기준액으로, 거래금액에서 매입 + * 수수료(fee)를 차감하여 산출한다. + * · 집계 대상이 0건이면 모든 합계를 0 으로 응답하고 경고 로그를 남긴다. + * + * ── 처리 흐름 ──────────────────────────────────────────────────────── + * 1) BIZDATE 추출/형식검증 + * 2) status='S' 대상 count/Σamount/Σfee singleton SELECT + * 3) 순액 산출 및 응답 TXBUF 구성 */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +114,105 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0004.pgc" +#line 40 "app/online/cl_ol_0004.pgc" TX_SERVICE(CL_OL_0004, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 45 "app/online/cl_ol_0004.pgc" + char h_biz_date [ 9 ] ; + +#line 46 "app/online/cl_ol_0004.pgc" + int h_cnt ; + +#line 47 "app/online/cl_ol_0004.pgc" + long h_amt_sum ; + +#line 48 "app/online/cl_ol_0004.pgc" + long h_fee_sum ; +/* exec sql end declare section */ +#line 49 "app/online/cl_ol_0004.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0004] 마감 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0004] KEY 누락"); + char bizdate[16]; + long net_amt = 0; + long avg_amt = 0; + char won[40]; + char avgwon[40]; + + tx_log(TX_LOG_INFO, "[CL_OL_0004] 마감후 집계조회 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0004] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0004] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0004] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 2. 마감완료(S) 집계 : 건수 / 금액합계 / 수수료합계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from cl_ledger where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amt_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 85 "app/online/cl_ol_0004.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0004 settled aggregate"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3. 순액 = 거래금액합계 - 수수료합계, 건당 평균거래금액 산출 */ + net_amt = h_amt_sum - h_fee_sum; + avg_amt = (h_cnt > 0) ? (h_amt_sum / (long)h_cnt) : 0; + + /* 4. 응답 구성 */ + amount_format_won(net_amt, won, sizeof(won)); + amount_format_won(avg_amt, avgwon, sizeof(avgwon)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0004] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SETTLECNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "AMTSUM", h_amt_sum); + tx_buf_setlong(ctx->out, "FEESUM", h_fee_sum); + tx_buf_setlong(ctx->out, "NETAMT", net_amt); + tx_buf_sets(ctx->out, "NETWON", won); + tx_buf_setlong(ctx->out, "AVGAMT", avg_amt); + tx_buf_sets(ctx->out, "AVGWON", avgwon); + + /* 5. 처리 결과 로깅 (건당 평균거래금액 포함 상세) */ + tx_log(TX_LOG_DEBUG, + "[CL_OL_0004] 집계상세 date=%s Σamount=%ld Σfee=%ld avg=%ld", + bizdate, h_amt_sum, h_fee_sum, avg_amt); + if (h_cnt == 0) + tx_log(TX_LOG_WARN, "[CL_OL_0004] 마감완료 건 없음 date=%s", bizdate); + else + tx_log(TX_LOG_INFO, "[CL_OL_0004] 집계완료 date=%s 건수=%d 순액=%ld", + bizdate, h_cnt, net_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0004.pgc b/app/online/cl_ol_0004.pgc index 6e82273..fabab5e 100644 --- a/app/online/cl_ol_0004.pgc +++ b/app/online/cl_ol_0004.pgc @@ -2,13 +2,38 @@ /* * cl_ol_0004.pgc - 마감 온라인 서비스 (CL_OL_0004) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감후 집계 조회. 요청 영업일(BIZDATE)의 정산완료(S) 건에 대해 + * 건수 / 거래금액 합계 / 수수료 합계 / 순액(합계-수수료)을 singleton + * SELECT 로 산출하여 응답한다. read-only 조회 서비스이다. + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * BIZDATE (필수) 영업일(YYYYMMDD) + * 출력 필드: + * RESPCODE 0000=정상 / 9001=입력오류 / 9999=시스템오류 + * BIZDATE 집계 대상 영업일 + * SETTLECNT 정산완료(S) 건수 + * AMTSUM 거래금액 합계(Σamount, 원) + * FEESUM 수수료 합계(Σfee, 원) + * NETAMT 순액(= AMTSUM - FEESUM, 원) + * NETWON 순액 한글 표기 + * + * ── 집계 규칙 ──────────────────────────────────────────────────────── + * · 마감완료(status='S') 건만 집계 대상으로 삼는다. + * · 순액(NETAMT)은 가맹점 지급 기준액으로, 거래금액에서 매입 + * 수수료(fee)를 차감하여 산출한다. + * · 집계 대상이 0건이면 모든 합계를 0 으로 응답하고 경고 로그를 남긴다. + * + * ── 처리 흐름 ──────────────────────────────────────────────────────── + * 1) BIZDATE 추출/형식검증 + * 2) status='S' 대상 count/Σamount/Σfee singleton SELECT + * 3) 순액 산출 및 응답 TXBUF 구성 */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,32 +41,82 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0004, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + int h_cnt; + long h_amt_sum; + long h_fee_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0004] 마감 단건조회 서비스 진입"); + char bizdate[16]; + long net_amt = 0; + long avg_amt = 0; + char won[40]; + char avgwon[40]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0004] KEY 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0004] 마감후 집계조회 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0004] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0004] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0004] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 2. 마감완료(S) 집계 : 건수 / 금액합계 / 수수료합계 */ + EXEC SQL SELECT count(*), + coalesce(sum(amount), 0), + coalesce(sum(fee), 0) + INTO :h_cnt, :h_amt_sum, :h_fee_sum + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'S'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0004 settled aggregate"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3. 순액 = 거래금액합계 - 수수료합계, 건당 평균거래금액 산출 */ + net_amt = h_amt_sum - h_fee_sum; + avg_amt = (h_cnt > 0) ? (h_amt_sum / (long)h_cnt) : 0; + + /* 4. 응답 구성 */ + amount_format_won(net_amt, won, sizeof(won)); + amount_format_won(avg_amt, avgwon, sizeof(avgwon)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0004] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SETTLECNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "AMTSUM", h_amt_sum); + tx_buf_setlong(ctx->out, "FEESUM", h_fee_sum); + tx_buf_setlong(ctx->out, "NETAMT", net_amt); + tx_buf_sets(ctx->out, "NETWON", won); + tx_buf_setlong(ctx->out, "AVGAMT", avg_amt); + tx_buf_sets(ctx->out, "AVGWON", avgwon); + + /* 5. 처리 결과 로깅 (건당 평균거래금액 포함 상세) */ + tx_log(TX_LOG_DEBUG, + "[CL_OL_0004] 집계상세 date=%s Σamount=%ld Σfee=%ld avg=%ld", + bizdate, h_amt_sum, h_fee_sum, avg_amt); + if (h_cnt == 0) + tx_log(TX_LOG_WARN, "[CL_OL_0004] 마감완료 건 없음 date=%s", bizdate); + else + tx_log(TX_LOG_INFO, "[CL_OL_0004] 집계완료 date=%s 건수=%d 순액=%ld", + bizdate, h_cnt, net_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0005.c b/app/online/cl_ol_0005.c index e9a1552..f17fa0e 100644 --- a/app/online/cl_ol_0005.c +++ b/app/online/cl_ol_0005.c @@ -10,13 +10,36 @@ /* * cl_ol_0005.pgc - 마감 온라인 서비스 (CL_OL_0005) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 취소(재개방). 요청 영업일(BIZDATE)의 정산완료(S) 건을 처리완료(M) + * 상태로 되돌려 마감을 재개방한다. 취소된 건수와 재개방 대상 금액합계를 + * 응답한다. 취소 대상(S 건)이 없으면 경고로 응답한다. (tx_begin/tx_commit) + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * BIZDATE (필수) 재개방 대상 영업일(YYYYMMDD) + * 출력 필드: + * RESPCODE 0000=정상 / 9001=입력오류·대상없음 / 9999=시스템오류 + * BIZDATE 처리 영업일 + * CANCELCNT 취소(재개방)된 건수 (S→M) + * REOPENSUM 재개방 대상 금액합계(원) + * + * ── 상태전이 규칙 ──────────────────────────────────────────────────── + * · 정산완료(S) 건만 취소 대상이며, 처리완료(M) 로 되돌린다. + * · 되돌린 M 건은 재마감(CL_OL_0006) 또는 마감확정(CL_OL_0016)의 + * 대상이 되어 다시 S 로 전이될 수 있다. + * · 취소 대상(S)이 0건이면 트랜잭션을 시작하지 않고 경고로 응답한다. + * + * ── 처리 흐름 ──────────────────────────────────────────────────────── + * 1) BIZDATE 추출/형식검증 + * 2) 취소 대상(S) 사전 건수/금액 집계 (0건이면 조기 반환) + * 3) tx_begin → S→M 일괄 UPDATE → tx_commit + * 4) 취소건수/금액 응답 구성 */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,60 +112,113 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0005.pgc" +#line 38 "app/online/cl_ol_0005.pgc" TX_SERVICE(CL_OL_0005, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + +#line 43 "app/online/cl_ol_0005.pgc" + char h_biz_date [ 9 ] ; + +#line 44 "app/online/cl_ol_0005.pgc" + int h_settled_cnt ; + +#line 45 "app/online/cl_ol_0005.pgc" + long h_reopen_sum ; +/* exec sql end declare section */ +#line 46 "app/online/cl_ol_0005.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0005] 마감 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0005] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char bizdate[16]; + long cancel_cnt = 0; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0005] 금액 범위 오류 amount=%ld", amount); + tx_log(TX_LOG_INFO, "[CL_OL_0005] 마감 취소(재개방) 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0005] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0005] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[CL_OL_0005] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0005] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2. 취소 대상(S) 사전 집계 : 건수/금액 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_settled_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_reopen_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 77 "app/online/cl_ol_0005.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0005 settled precount"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_settled_cnt == 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0005] 취소 대상 마감건 없음 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CANCELCNT", 0); + tx_buf_sets(ctx->out, "MESSAGE", "취소 대상 마감건 없음"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* 3. 취소 트랜잭션 : S → M 재개방 UPDATE */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0005] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cl_ledger set status = 'M' , upd_ts = now ( ) where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 108 "app/online/cl_ol_0005.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0005 reopen update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + cancel_cnt = (long)sqlca.sqlerrd[2]; + + tx_commit(); + + /* 4. 응답 구성 */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0005] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CANCELCNT", cancel_cnt); + tx_buf_setlong(ctx->out, "REOPENSUM", h_reopen_sum); + tx_log(TX_LOG_INFO, "[CL_OL_0005] 마감취소 완료 date=%s 취소건수=%ld 금액=%ld", + bizdate, cancel_cnt, h_reopen_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0005.pgc b/app/online/cl_ol_0005.pgc index c21eec7..79aeb36 100644 --- a/app/online/cl_ol_0005.pgc +++ b/app/online/cl_ol_0005.pgc @@ -2,13 +2,36 @@ /* * cl_ol_0005.pgc - 마감 온라인 서비스 (CL_OL_0005) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 취소(재개방). 요청 영업일(BIZDATE)의 정산완료(S) 건을 처리완료(M) + * 상태로 되돌려 마감을 재개방한다. 취소된 건수와 재개방 대상 금액합계를 + * 응답한다. 취소 대상(S 건)이 없으면 경고로 응답한다. (tx_begin/tx_commit) + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * BIZDATE (필수) 재개방 대상 영업일(YYYYMMDD) + * 출력 필드: + * RESPCODE 0000=정상 / 9001=입력오류·대상없음 / 9999=시스템오류 + * BIZDATE 처리 영업일 + * CANCELCNT 취소(재개방)된 건수 (S→M) + * REOPENSUM 재개방 대상 금액합계(원) + * + * ── 상태전이 규칙 ──────────────────────────────────────────────────── + * · 정산완료(S) 건만 취소 대상이며, 처리완료(M) 로 되돌린다. + * · 되돌린 M 건은 재마감(CL_OL_0006) 또는 마감확정(CL_OL_0016)의 + * 대상이 되어 다시 S 로 전이될 수 있다. + * · 취소 대상(S)이 0건이면 트랜잭션을 시작하지 않고 경고로 응답한다. + * + * ── 처리 흐름 ──────────────────────────────────────────────────────── + * 1) BIZDATE 추출/형식검증 + * 2) 취소 대상(S) 사전 건수/금액 집계 (0건이면 조기 반환) + * 3) tx_begin → S→M 일괄 UPDATE → tx_commit + * 4) 취소건수/금액 응답 구성 */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,55 +39,92 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0005, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + int h_settled_cnt; + long h_reopen_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0005] 마감 등록 서비스 진입"); + char bizdate[16]; + long cancel_cnt = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0005] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[CL_OL_0005] 마감 취소(재개방) 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0005] 금액 범위 오류 amount=%ld", amount); + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0005] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0005] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[CL_OL_0005] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0005] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2. 취소 대상(S) 사전 집계 : 건수/금액 */ + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_settled_cnt, :h_reopen_sum + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'S'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0005 settled precount"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_settled_cnt == 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0005] 취소 대상 마감건 없음 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CANCELCNT", 0); + tx_buf_sets(ctx->out, "MESSAGE", "취소 대상 마감건 없음"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* 3. 취소 트랜잭션 : S → M 재개방 UPDATE */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0005] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + EXEC SQL UPDATE cl_ledger + SET status = 'M', upd_ts = now() + WHERE biz_date = :h_biz_date + AND status = 'S'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0005 reopen update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + cancel_cnt = (long)sqlca.sqlerrd[2]; + + tx_commit(); + + /* 4. 응답 구성 */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0005] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CANCELCNT", cancel_cnt); + tx_buf_setlong(ctx->out, "REOPENSUM", h_reopen_sum); + tx_log(TX_LOG_INFO, "[CL_OL_0005] 마감취소 완료 date=%s 취소건수=%ld 금액=%ld", + bizdate, cancel_cnt, h_reopen_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0006.c b/app/online/cl_ol_0006.c index c0c8628..f0f90a5 100644 --- a/app/online/cl_ol_0006.c +++ b/app/online/cl_ol_0006.c @@ -10,13 +10,15 @@ /* * cl_ol_0006.pgc - 마감 온라인 서비스 (CL_OL_0006) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 재마감. 마감 취소(CL_OL_0005)로 재개방된 영업일에 대해 처리완료(M)/ + * 불일치(U) 건을 다시 정산완료(S)로 재마감한다. 재마감 건수와 금액합계를 + * 응답한다. (tx_begin/tx_commit 로 일괄 UPDATE) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +91,131 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0006.pgc" +#line 17 "app/online/cl_ol_0006.pgc" TX_SERVICE(CL_OL_0006, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 22 "app/online/cl_ol_0006.pgc" + char h_biz_date [ 9 ] ; + +#line 23 "app/online/cl_ol_0006.pgc" + int h_target_cnt ; + +#line 24 "app/online/cl_ol_0006.pgc" + long h_settle_sum ; +/* exec sql end declare section */ +#line 25 "app/online/cl_ol_0006.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0006] 마감 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0006] KEY 누락"); + char bizdate[16]; + long reclose_cnt = 0; + char won[40]; + + tx_log(TX_LOG_INFO, "[CL_OL_0006] 재마감 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0006] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0006] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0006] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 2. 재마감 대상(M/U) 사전 건수 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date = $1 and status in ( 'M' , 'U' )", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_target_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 57 "app/online/cl_ol_0006.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0006 target count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_target_cnt == 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0006] 재마감 대상 없음 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "RECLOSECNT", 0); + tx_buf_sets(ctx->out, "MESSAGE", "재마감 대상(M/U) 없음"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + /* 3. 재마감 트랜잭션 : M/U → S */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0006] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cl_ledger set status = 'S' , upd_ts = now ( ) where biz_date = $1 and status in ( 'M' , 'U' )", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 88 "app/online/cl_ol_0006.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0006 reclose update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + reclose_cnt = (long)sqlca.sqlerrd[2]; + + /* 4. 재마감후 S 금액합계 재집계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settle_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 104 "app/online/cl_ol_0006.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0006 settle sum"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 5. 응답 구성 */ + amount_format_won(h_settle_sum, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0006] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "RECLOSECNT", reclose_cnt); + tx_buf_setlong(ctx->out, "SETTLESUM", h_settle_sum); + tx_buf_sets(ctx->out, "SETTLEWON", won); + tx_log(TX_LOG_INFO, "[CL_OL_0006] 재마감 완료 date=%s 건수=%ld 합계=%ld", + bizdate, reclose_cnt, h_settle_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0006.pgc b/app/online/cl_ol_0006.pgc index 35c26b4..63055f2 100644 --- a/app/online/cl_ol_0006.pgc +++ b/app/online/cl_ol_0006.pgc @@ -2,13 +2,15 @@ /* * cl_ol_0006.pgc - 마감 온라인 서비스 (CL_OL_0006) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 재마감. 마감 취소(CL_OL_0005)로 재개방된 영업일에 대해 처리완료(M)/ + * 불일치(U) 건을 다시 정산완료(S)로 재마감한다. 재마감 건수와 금액합계를 + * 응답한다. (tx_begin/tx_commit 로 일괄 UPDATE) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,32 +18,110 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0006, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + int h_target_cnt; + long h_settle_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0006] 마감 단건조회 서비스 진입"); + char bizdate[16]; + long reclose_cnt = 0; + char won[40]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0006] KEY 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0006] 재마감 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0006] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0006] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0006] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 2. 재마감 대상(M/U) 사전 건수 확인 */ + EXEC SQL SELECT count(*) + INTO :h_target_cnt + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status IN ('M', 'U'); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0006 target count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_target_cnt == 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0006] 재마감 대상 없음 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "RECLOSECNT", 0); + tx_buf_sets(ctx->out, "MESSAGE", "재마감 대상(M/U) 없음"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + /* 3. 재마감 트랜잭션 : M/U → S */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0006] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE cl_ledger + SET status = 'S', upd_ts = now() + WHERE biz_date = :h_biz_date + AND status IN ('M', 'U'); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0006 reclose update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + reclose_cnt = (long)sqlca.sqlerrd[2]; + + /* 4. 재마감후 S 금액합계 재집계 */ + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_settle_sum + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'S'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0006 settle sum"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 5. 응답 구성 */ + amount_format_won(h_settle_sum, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0006] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "RECLOSECNT", reclose_cnt); + tx_buf_setlong(ctx->out, "SETTLESUM", h_settle_sum); + tx_buf_sets(ctx->out, "SETTLEWON", won); + tx_log(TX_LOG_INFO, "[CL_OL_0006] 재마감 완료 date=%s 건수=%ld 합계=%ld", + bizdate, reclose_cnt, h_settle_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0007.c b/app/online/cl_ol_0007.c index feb4f5d..a7bd646 100644 --- a/app/online/cl_ol_0007.c +++ b/app/online/cl_ol_0007.c @@ -10,13 +10,36 @@ /* * cl_ol_0007.pgc - 마감 온라인 서비스 (CL_OL_0007) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 잔액검증. 요청 영업일(BIZDATE)의 정산완료(S) 건에 대해 거래금액 + * 합계(Σamount), 수수료 합계(Σfee), 순액(Σamount-Σfee)을 singleton + * SELECT 로 산출하고, 요청 기대순액(EXPECTNET)과 비교하여 잔액 일치여부를 + * 판정한다. 불일치 시 RESP_INVALID. (read-only 검증) + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * BIZDATE (필수) 영업일(YYYYMMDD) + * EXPECTNET (선택) 기대 순액(원, 미제출 시 0 으로 비교) + * 출력 필드: + * RESPCODE 0000=잔액일치 / 9001=불일치·입력오류 / 9999=시스템오류 + * BIZDATE 검증 대상 영업일 + * SETTLECNT 정산완료(S) 건수 + * AMTSUM 거래금액 합계(Σamount) + * FEESUM 수수료 합계(Σfee) + * NETAMT 순액(= AMTSUM - FEESUM) / NETWON 한글 표기 + * EXPECTNET 기대 순액 + * DIFFNET 순액 차이(실제-기대) + * BALANCED 잔액 일치여부(Y/N) + * + * ── 잔액검증 규칙 ──────────────────────────────────────────────────── + * · 마감완료(S) 건의 거래금액과 수수료를 각각 합산한다. + * · 순액 = Σamount - Σfee 로, 가맹점 실지급 예정액을 의미한다. + * · 순액이 기대순액과 정확히 일치할 때만 잔액일치(RESP_OK)로 본다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,60 +112,108 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0007.pgc" +#line 38 "app/online/cl_ol_0007.pgc" TX_SERVICE(CL_OL_0007, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + +#line 43 "app/online/cl_ol_0007.pgc" + char h_biz_date [ 9 ] ; + +#line 44 "app/online/cl_ol_0007.pgc" + int h_cnt ; + +#line 45 "app/online/cl_ol_0007.pgc" + long h_amt_sum ; + +#line 46 "app/online/cl_ol_0007.pgc" + long h_fee_sum ; +/* exec sql end declare section */ +#line 47 "app/online/cl_ol_0007.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0007] 마감 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0007] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char bizdate[16]; + long expect_net = 0; + long net_amt = 0, diff_net = 0; + int balanced; + char won[40]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0007] 금액 범위 오류 amount=%ld", amount); + tx_log(TX_LOG_INFO, "[CL_OL_0007] 마감 잔액검증 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0007] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0007] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[CL_OL_0007] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 기대순액 (미제출 시 0) */ + tx_buf_getlong(ctx->in, "EXPECTNET", &expect_net); - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0007] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2. 잔액 집계 : 건수 / Σamount / Σfee (S 확정분) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from cl_ledger where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amt_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 85 "app/online/cl_ol_0007.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0007 balance aggregate"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* 3. 순액 산출 및 기대순액 대비 검증 */ + net_amt = h_amt_sum - h_fee_sum; + diff_net = net_amt - expect_net; + balanced = (diff_net == 0); + /* 4. 응답 구성 */ + amount_format_won(net_amt, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0007] 등록완료 key=%s 당일건수=%ld", key, cnt); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SETTLECNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "AMTSUM", h_amt_sum); + tx_buf_setlong(ctx->out, "FEESUM", h_fee_sum); + tx_buf_setlong(ctx->out, "NETAMT", net_amt); + tx_buf_sets(ctx->out, "NETWON", won); + tx_buf_setlong(ctx->out, "EXPECTNET", expect_net); + tx_buf_setlong(ctx->out, "DIFFNET", diff_net); + + if (balanced) { + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BALANCED", "Y"); + tx_log(TX_LOG_INFO, "[CL_OL_0007] 잔액일치 date=%s 순액=%ld", bizdate, net_amt); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "BALANCED", "N"); + tx_log(TX_LOG_WARN, "[CL_OL_0007] 잔액불일치 date=%s 순액차이=%ld", bizdate, diff_net); + tx_return(ctx, TX_FAIL, ctx->out); } diff --git a/app/online/cl_ol_0007.pgc b/app/online/cl_ol_0007.pgc index 80d21ed..f9e299c 100644 --- a/app/online/cl_ol_0007.pgc +++ b/app/online/cl_ol_0007.pgc @@ -2,13 +2,36 @@ /* * cl_ol_0007.pgc - 마감 온라인 서비스 (CL_OL_0007) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 잔액검증. 요청 영업일(BIZDATE)의 정산완료(S) 건에 대해 거래금액 + * 합계(Σamount), 수수료 합계(Σfee), 순액(Σamount-Σfee)을 singleton + * SELECT 로 산출하고, 요청 기대순액(EXPECTNET)과 비교하여 잔액 일치여부를 + * 판정한다. 불일치 시 RESP_INVALID. (read-only 검증) + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * BIZDATE (필수) 영업일(YYYYMMDD) + * EXPECTNET (선택) 기대 순액(원, 미제출 시 0 으로 비교) + * 출력 필드: + * RESPCODE 0000=잔액일치 / 9001=불일치·입력오류 / 9999=시스템오류 + * BIZDATE 검증 대상 영업일 + * SETTLECNT 정산완료(S) 건수 + * AMTSUM 거래금액 합계(Σamount) + * FEESUM 수수료 합계(Σfee) + * NETAMT 순액(= AMTSUM - FEESUM) / NETWON 한글 표기 + * EXPECTNET 기대 순액 + * DIFFNET 순액 차이(실제-기대) + * BALANCED 잔액 일치여부(Y/N) + * + * ── 잔액검증 규칙 ──────────────────────────────────────────────────── + * · 마감완료(S) 건의 거래금액과 수수료를 각각 합산한다. + * · 순액 = Σamount - Σfee 로, 가맹점 실지급 예정액을 의미한다. + * · 순액이 기대순액과 정확히 일치할 때만 잔액일치(RESP_OK)로 본다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,55 +39,85 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0007, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + int h_cnt; + long h_amt_sum; + long h_fee_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0007] 마감 등록 서비스 진입"); + char bizdate[16]; + long expect_net = 0; + long net_amt = 0, diff_net = 0; + int balanced; + char won[40]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0007] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[CL_OL_0007] 마감 잔액검증 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0007] 금액 범위 오류 amount=%ld", amount); + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0007] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0007] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[CL_OL_0007] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 기대순액 (미제출 시 0) */ + tx_buf_getlong(ctx->in, "EXPECTNET", &expect_net); - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0007] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2. 잔액 집계 : 건수 / Σamount / Σfee (S 확정분) */ + EXEC SQL SELECT count(*), + coalesce(sum(amount), 0), + coalesce(sum(fee), 0) + INTO :h_cnt, :h_amt_sum, :h_fee_sum + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'S'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0007 balance aggregate"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* 3. 순액 산출 및 기대순액 대비 검증 */ + net_amt = h_amt_sum - h_fee_sum; + diff_net = net_amt - expect_net; + balanced = (diff_net == 0); + /* 4. 응답 구성 */ + amount_format_won(net_amt, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0007] 등록완료 key=%s 당일건수=%ld", key, cnt); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SETTLECNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "AMTSUM", h_amt_sum); + tx_buf_setlong(ctx->out, "FEESUM", h_fee_sum); + tx_buf_setlong(ctx->out, "NETAMT", net_amt); + tx_buf_sets(ctx->out, "NETWON", won); + tx_buf_setlong(ctx->out, "EXPECTNET", expect_net); + tx_buf_setlong(ctx->out, "DIFFNET", diff_net); + + if (balanced) { + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BALANCED", "Y"); + tx_log(TX_LOG_INFO, "[CL_OL_0007] 잔액일치 date=%s 순액=%ld", bizdate, net_amt); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "BALANCED", "N"); + tx_log(TX_LOG_WARN, "[CL_OL_0007] 잔액불일치 date=%s 순액차이=%ld", bizdate, diff_net); + tx_return(ctx, TX_FAIL, ctx->out); } diff --git a/app/online/cl_ol_0008.c b/app/online/cl_ol_0008.c index fe611b1..fd29e2b 100644 --- a/app/online/cl_ol_0008.c +++ b/app/online/cl_ol_0008.c @@ -10,13 +10,16 @@ /* * cl_ol_0008.pgc - 마감 온라인 서비스 (CL_OL_0008) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 스냅샷 생성. 요청 영업일(BIZDATE)의 집계(건수/금액합계/수수료합계)를 + * 산출한 뒤, 그 결과를 cl_ledger 에 요약행(스냅샷)으로 INSERT 한다. + * 스냅샷 행 규약: key=BIZDATE+'SS', merch_id='*SNAP', amount=Σamount, + * fee=Σfee, status='S'. 동일 스냅샷이 이미 있으면 경고로 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +92,150 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0008.pgc" +#line 18 "app/online/cl_ol_0008.pgc" TX_SERVICE(CL_OL_0008, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 23 "app/online/cl_ol_0008.pgc" + char h_biz_date [ 9 ] ; + +#line 24 "app/online/cl_ol_0008.pgc" + char h_snap_key [ 16 ] ; + +#line 25 "app/online/cl_ol_0008.pgc" + int h_cnt ; + +#line 26 "app/online/cl_ol_0008.pgc" + long h_amt_sum ; + +#line 27 "app/online/cl_ol_0008.pgc" + long h_fee_sum ; + +#line 28 "app/online/cl_ol_0008.pgc" + int h_exist ; +/* exec sql end declare section */ +#line 29 "app/online/cl_ol_0008.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0008] 마감 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0008] KEY 누락"); + char bizdate[16]; + + tx_log(TX_LOG_INFO, "[CL_OL_0008] 마감 스냅샷 생성 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0008] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0008] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0008] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 스냅샷 채번키 = 영업일 + 'SS' */ + snprintf(h_snap_key, sizeof(h_snap_key), "%.8sSS", h_biz_date); + + /* 2. 스냅샷 중복 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where key = $1 ", + ECPGt_char,(h_snap_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_exist),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 60 "app/online/cl_ol_0008.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0008 snap exist"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_exist > 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0008] 스냅샷 기존재 key=%s", h_snap_key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "SNAPKEY", h_snap_key); + tx_buf_sets(ctx->out, "MESSAGE", "동일 영업일 스냅샷 기존재"); + tx_return(ctx, TX_FAIL, ctx->out); return; } + /* 3. 실거래(스냅샷 제외) 집계 : 건수 / Σamount / Σfee */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from cl_ledger where biz_date = $1 and merch_id <> '*SNAP'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amt_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 85 "app/online/cl_ol_0008.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0008 aggregate"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 4. 스냅샷 요약행 INSERT (트랜잭션) */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0008] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into cl_ledger ( key , merch_id , card_no , amount , biz_date , status , fee , upd_ts ) values ( $1 , '*SNAP' , '' , $2 , $3 , 'S' , $4 , now ( ) )", + ECPGt_char,(h_snap_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amt_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 106 "app/online/cl_ol_0008.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0008 snap insert"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 5. 응답 구성 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "SNAPKEY", h_snap_key); + tx_buf_setlong(ctx->out, "SNAPCNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "SNAPAMT", h_amt_sum); + tx_buf_setlong(ctx->out, "SNAPFEE", h_fee_sum); + tx_log(TX_LOG_INFO, "[CL_OL_0008] 스냅샷 생성 key=%s 건수=%d 금액=%ld", + h_snap_key, h_cnt, h_amt_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0008.pgc b/app/online/cl_ol_0008.pgc index 360ec1a..dd8678b 100644 --- a/app/online/cl_ol_0008.pgc +++ b/app/online/cl_ol_0008.pgc @@ -2,13 +2,16 @@ /* * cl_ol_0008.pgc - 마감 온라인 서비스 (CL_OL_0008) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 스냅샷 생성. 요청 영업일(BIZDATE)의 집계(건수/금액합계/수수료합계)를 + * 산출한 뒤, 그 결과를 cl_ledger 에 요약행(스냅샷)으로 INSERT 한다. + * 스냅샷 행 규약: key=BIZDATE+'SS', merch_id='*SNAP', amount=Σamount, + * fee=Σfee, status='S'. 동일 스냅샷이 이미 있으면 경고로 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,32 +19,111 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0008, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_snap_key[16]; + int h_cnt; + long h_amt_sum; + long h_fee_sum; + int h_exist; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0008] 마감 단건조회 서비스 진입"); + char bizdate[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0008] KEY 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0008] 마감 스냅샷 생성 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0008] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0008] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0008] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + /* 스냅샷 채번키 = 영업일 + 'SS' */ + snprintf(h_snap_key, sizeof(h_snap_key), "%.8sSS", h_biz_date); + + /* 2. 스냅샷 중복 확인 */ + EXEC SQL SELECT count(*) + INTO :h_exist + FROM cl_ledger + WHERE key = :h_snap_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0008 snap exist"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_exist > 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0008] 스냅샷 기존재 key=%s", h_snap_key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "SNAPKEY", h_snap_key); + tx_buf_sets(ctx->out, "MESSAGE", "동일 영업일 스냅샷 기존재"); + tx_return(ctx, TX_FAIL, ctx->out); return; } + /* 3. 실거래(스냅샷 제외) 집계 : 건수 / Σamount / Σfee */ + EXEC SQL SELECT count(*), + coalesce(sum(amount), 0), + coalesce(sum(fee), 0) + INTO :h_cnt, :h_amt_sum, :h_fee_sum + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND merch_id <> '*SNAP'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0008 aggregate"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 4. 스냅샷 요약행 INSERT (트랜잭션) */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0008] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL INSERT INTO cl_ledger + (key, merch_id, card_no, amount, biz_date, status, fee, upd_ts) + VALUES + (:h_snap_key, '*SNAP', '', :h_amt_sum, :h_biz_date, 'S', :h_fee_sum, now()); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0008 snap insert"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 5. 응답 구성 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "SNAPKEY", h_snap_key); + tx_buf_setlong(ctx->out, "SNAPCNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "SNAPAMT", h_amt_sum); + tx_buf_setlong(ctx->out, "SNAPFEE", h_fee_sum); + tx_log(TX_LOG_INFO, "[CL_OL_0008] 스냅샷 생성 key=%s 건수=%d 금액=%ld", + h_snap_key, h_cnt, h_amt_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0009.c b/app/online/cl_ol_0009.c index 21a26b1..5d8fa6f 100644 --- a/app/online/cl_ol_0009.c +++ b/app/online/cl_ol_0009.c @@ -10,13 +10,16 @@ /* * cl_ol_0009.pgc - 마감 온라인 서비스 (CL_OL_0009) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 순번 채번(採番). 요청 영업일(BIZDATE)에 대해 기존 마감완료(S) 건수를 + * 세어 다음 마감 순번(next = 기존건수 + 1)을 산출한다. 아울러 원장 전체 + * max(key) 기반 채번 참조값도 함께 조회하여, 영업일+순번 형식의 채번키를 + * 생성해 반환한다. 실제 원장을 변경하지 않는 read-only 채번 서비스이다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,88 +92,133 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0009.pgc" +#line 18 "app/online/cl_ol_0009.pgc" TX_SERVICE(CL_OL_0009, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/cl_ol_0009.pgc" + char h_biz_date [ 9 ] ; + +#line 24 "app/online/cl_ol_0009.pgc" + int h_settled_cnt ; + +#line 25 "app/online/cl_ol_0009.pgc" + int h_all_cnt ; + +#line 26 "app/online/cl_ol_0009.pgc" + char h_max_key [ 16 ] ; + +#line 27 "app/online/cl_ol_0009.pgc" + short h_maxkey_ind ; +/* exec sql end declare section */ +#line 28 "app/online/cl_ol_0009.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0009] 마감 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0009] 필수 필드 누락"); + char bizdate[16]; + long next_seq = 0; + char seq_key[24]; + char maxkey_out[16]; + + tx_log(TX_LOG_INFO, "[CL_OL_0009] 마감 순번 채번 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0009] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0009] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0009] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 2. 당일 기존 마감완료(S) 건수 채번 기준 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_settled_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 61 "app/online/cl_ol_0009.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0009 settled count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 3. 당일 전체 건수 (참조 지표) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date = $1 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_all_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 74 "app/online/cl_ol_0009.pgc" - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0009] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0009 all count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + /* 4. 당일 max(key) 참조 (없으면 널 → indicator 로 감지) */ + h_maxkey_ind = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select max ( key ) from cl_ledger where biz_date = $1 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_max_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_short,&(h_maxkey_ind),(long)1,(long)1,sizeof(short), ECPGt_EORT);} +#line 88 "app/online/cl_ol_0009.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("CL_OL_0009 max key"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_maxkey_ind < 0) { + strncpy(maxkey_out, "(none)", sizeof(maxkey_out) - 1); + maxkey_out[sizeof(maxkey_out) - 1] = '\0'; + } else { + h_max_key[sizeof(h_max_key) - 1] = '\0'; + strncpy(maxkey_out, h_max_key, sizeof(maxkey_out) - 1); + maxkey_out[sizeof(maxkey_out) - 1] = '\0'; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CL_OL_0009] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (cl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 5. 다음 순번 = 기존 마감건수 + 1, 채번키 = 영업일+순번(4자리) */ + next_seq = (long)h_settled_cnt + 1; + snprintf(seq_key, sizeof(seq_key), "%.8s%04ld", h_biz_date, next_seq); + /* 6. 응답 구성 (read-only) */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CL_OL_0009] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SETTLEDCNT", (long)h_settled_cnt); + tx_buf_setlong(ctx->out, "ALLCNT", (long)h_all_cnt); + tx_buf_setlong(ctx->out, "NEXTSEQ", next_seq); + tx_buf_sets(ctx->out, "SEQKEY", seq_key); + tx_buf_sets(ctx->out, "MAXKEY", maxkey_out); + tx_log(TX_LOG_INFO, "[CL_OL_0009] 채번완료 date=%s 다음순번=%ld 채번키=%s", + bizdate, next_seq, seq_key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0009.pgc b/app/online/cl_ol_0009.pgc index 0cb3511..45a25cd 100644 --- a/app/online/cl_ol_0009.pgc +++ b/app/online/cl_ol_0009.pgc @@ -2,13 +2,16 @@ /* * cl_ol_0009.pgc - 마감 온라인 서비스 (CL_OL_0009) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 순번 채번(採番). 요청 영업일(BIZDATE)에 대해 기존 마감완료(S) 건수를 + * 세어 다음 마감 순번(next = 기존건수 + 1)을 산출한다. 아울러 원장 전체 + * max(key) 기반 채번 참조값도 함께 조회하여, 영업일+순번 형식의 채번키를 + * 생성해 반환한다. 실제 원장을 변경하지 않는 read-only 채번 서비스이다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,83 +19,103 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0009, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + int h_settled_cnt; + int h_all_cnt; + char h_max_key[16]; + short h_maxkey_ind; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0009] 마감 트랜잭션 오케스트레이션 진입"); + char bizdate[16]; + long next_seq = 0; + char seq_key[24]; + char maxkey_out[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0009] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0009] 마감 순번 채번 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0009] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0009] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0009] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 2. 당일 기존 마감완료(S) 건수 채번 기준 */ + EXEC SQL SELECT count(*) + INTO :h_settled_cnt + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'S'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0009 settled count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0009] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + /* 3. 당일 전체 건수 (참조 지표) */ + EXEC SQL SELECT count(*) + INTO :h_all_cnt + FROM cl_ledger + WHERE biz_date = :h_biz_date; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0009 all count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + /* 4. 당일 max(key) 참조 (없으면 널 → indicator 로 감지) */ + h_maxkey_ind = 0; + EXEC SQL SELECT max(key) + INTO :h_max_key :h_maxkey_ind + FROM cl_ledger + WHERE biz_date = :h_biz_date; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("CL_OL_0009 max key"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_maxkey_ind < 0) { + strncpy(maxkey_out, "(none)", sizeof(maxkey_out) - 1); + maxkey_out[sizeof(maxkey_out) - 1] = '\0'; + } else { + h_max_key[sizeof(h_max_key) - 1] = '\0'; + strncpy(maxkey_out, h_max_key, sizeof(maxkey_out) - 1); + maxkey_out[sizeof(maxkey_out) - 1] = '\0'; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CL_OL_0009] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (cl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 5. 다음 순번 = 기존 마감건수 + 1, 채번키 = 영업일+순번(4자리) */ + next_seq = (long)h_settled_cnt + 1; + snprintf(seq_key, sizeof(seq_key), "%.8s%04ld", h_biz_date, next_seq); + /* 6. 응답 구성 (read-only) */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CL_OL_0009] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SETTLEDCNT", (long)h_settled_cnt); + tx_buf_setlong(ctx->out, "ALLCNT", (long)h_all_cnt); + tx_buf_setlong(ctx->out, "NEXTSEQ", next_seq); + tx_buf_sets(ctx->out, "SEQKEY", seq_key); + tx_buf_sets(ctx->out, "MAXKEY", maxkey_out); + tx_log(TX_LOG_INFO, "[CL_OL_0009] 채번완료 date=%s 다음순번=%ld 채번키=%s", + bizdate, next_seq, seq_key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0010.c b/app/online/cl_ol_0010.c index 4aca1eb..44c37a2 100644 --- a/app/online/cl_ol_0010.c +++ b/app/online/cl_ol_0010.c @@ -10,13 +10,33 @@ /* * cl_ol_0010.pgc - 마감 온라인 서비스 (CL_OL_0010) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 리포트 조회. 요청 영업일(BIZDATE)의 상태별 건수를 접수(R)/처리완료(M)/ + * 불일치(U)/정산완료(S) 각각 개별 SELECT 로 산출하여 리포트 형태로 응답한다. + * 마감 진척률(정산완료/전체)도 함께 계산한다. (read-only) + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * BIZDATE (필수) 영업일(YYYYMMDD) + * 출력 필드: + * RESPCODE 0000=정상 / 9001=입력오류 / 9999=시스템오류 + * BIZDATE 리포트 대상 영업일 + * CNTRECV 접수(R) 건수 + * CNTDONE 처리완료(M) 건수 + * CNTFAIL 불일치(U) 건수 + * CNTSETTLED 정산완료(S) 건수 + * CNTTOTAL 전체 건수(R+M+U+S) + * PROGRESS 마감 진척률(%) = 정산완료 / 전체 * 100 + * + * ── 리포트 규칙 ────────────────────────────────────────────────────── + * · 상태코드 R/M/U/S 각각에 대해 개별 count SELECT 를 순회 수행한다. + * · 진척률은 전체 건이 0 일 때 0% 로 처리(0 나눗셈 방지)한다. + * · 마감 담당자의 일일 마감현황 대시보드 소스로 사용된다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +109,107 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0010.pgc" +#line 35 "app/online/cl_ol_0010.pgc" TX_SERVICE(CL_OL_0010, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 40 "app/online/cl_ol_0010.pgc" + char h_biz_date [ 9 ] ; + +#line 41 "app/online/cl_ol_0010.pgc" + char h_status [ 2 ] ; + +#line 42 "app/online/cl_ol_0010.pgc" + int h_cnt ; +/* exec sql end declare section */ +#line 43 "app/online/cl_ol_0010.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0010] 마감 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0010] KEY 누락"); + char bizdate[16]; + long cnt_r = 0, cnt_m = 0, cnt_u = 0, cnt_s = 0, total = 0; + long remain = 0; + long progress = 0; + int i; + const char *codes = "RMUS"; + + tx_log(TX_LOG_INFO, "[CL_OL_0010] 마감 리포트 조회 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0010] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0010] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 2. 상태코드별 개별 count SELECT (R/M/U/S 순회) */ + for (i = 0; i < 4; i++) { + long v; + h_status[0] = codes[i]; + h_status[1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date = $1 and status = $2 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 83 "app/online/cl_ol_0010.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0010 status count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + v = (long)h_cnt; + switch (codes[i]) { + case 'R': cnt_r = v; break; + case 'M': cnt_m = v; break; + case 'U': cnt_u = v; break; + case 'S': cnt_s = v; break; + default: break; + } } + /* 3. 합계 / 미정산 잔여(R+M+U) / 진척률(%) 계산 */ + total = cnt_r + cnt_m + cnt_u + cnt_s; + remain = cnt_r + cnt_m + cnt_u; + progress = (total > 0) ? (cnt_s * 100 / total) : 0; + + /* 4. 리포트 응답 구성 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNTRECV", cnt_r); + tx_buf_setlong(ctx->out, "CNTDONE", cnt_m); + tx_buf_setlong(ctx->out, "CNTFAIL", cnt_u); + tx_buf_setlong(ctx->out, "CNTSETTLED", cnt_s); + tx_buf_setlong(ctx->out, "CNTTOTAL", total); + tx_buf_setlong(ctx->out, "CNTREMAIN", remain); + tx_buf_setlong(ctx->out, "PROGRESS", progress); + tx_buf_sets(ctx->out, "COMPLETED", (remain == 0 && total > 0) ? "Y" : "N"); + tx_log(TX_LOG_INFO, + "[CL_OL_0010] 리포트 date=%s R=%ld M=%ld U=%ld S=%ld 진척=%ld%%", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s, progress); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0010.pgc b/app/online/cl_ol_0010.pgc index 56b87fa..c719278 100644 --- a/app/online/cl_ol_0010.pgc +++ b/app/online/cl_ol_0010.pgc @@ -2,13 +2,33 @@ /* * cl_ol_0010.pgc - 마감 온라인 서비스 (CL_OL_0010) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 리포트 조회. 요청 영업일(BIZDATE)의 상태별 건수를 접수(R)/처리완료(M)/ + * 불일치(U)/정산완료(S) 각각 개별 SELECT 로 산출하여 리포트 형태로 응답한다. + * 마감 진척률(정산완료/전체)도 함께 계산한다. (read-only) + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * BIZDATE (필수) 영업일(YYYYMMDD) + * 출력 필드: + * RESPCODE 0000=정상 / 9001=입력오류 / 9999=시스템오류 + * BIZDATE 리포트 대상 영업일 + * CNTRECV 접수(R) 건수 + * CNTDONE 처리완료(M) 건수 + * CNTFAIL 불일치(U) 건수 + * CNTSETTLED 정산완료(S) 건수 + * CNTTOTAL 전체 건수(R+M+U+S) + * PROGRESS 마감 진척률(%) = 정산완료 / 전체 * 100 + * + * ── 리포트 규칙 ────────────────────────────────────────────────────── + * · 상태코드 R/M/U/S 각각에 대해 개별 count SELECT 를 순회 수행한다. + * · 진척률은 전체 건이 0 일 때 0% 로 처리(0 나눗셈 방지)한다. + * · 마감 담당자의 일일 마감현황 대시보드 소스로 사용된다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,32 +36,87 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0010, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_status[2]; + int h_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0010] 마감 단건조회 서비스 진입"); + char bizdate[16]; + long cnt_r = 0, cnt_m = 0, cnt_u = 0, cnt_s = 0, total = 0; + long remain = 0; + long progress = 0; + int i; + const char *codes = "RMUS"; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0010] KEY 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0010] 마감 리포트 조회 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0010] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0010] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 2. 상태코드별 개별 count SELECT (R/M/U/S 순회) */ + for (i = 0; i < 4; i++) { + long v; + h_status[0] = codes[i]; + h_status[1] = '\0'; + + EXEC SQL SELECT count(*) + INTO :h_cnt + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = :h_status; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0010 status count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + v = (long)h_cnt; + switch (codes[i]) { + case 'R': cnt_r = v; break; + case 'M': cnt_m = v; break; + case 'U': cnt_u = v; break; + case 'S': cnt_s = v; break; + default: break; + } } + /* 3. 합계 / 미정산 잔여(R+M+U) / 진척률(%) 계산 */ + total = cnt_r + cnt_m + cnt_u + cnt_s; + remain = cnt_r + cnt_m + cnt_u; + progress = (total > 0) ? (cnt_s * 100 / total) : 0; + + /* 4. 리포트 응답 구성 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNTRECV", cnt_r); + tx_buf_setlong(ctx->out, "CNTDONE", cnt_m); + tx_buf_setlong(ctx->out, "CNTFAIL", cnt_u); + tx_buf_setlong(ctx->out, "CNTSETTLED", cnt_s); + tx_buf_setlong(ctx->out, "CNTTOTAL", total); + tx_buf_setlong(ctx->out, "CNTREMAIN", remain); + tx_buf_setlong(ctx->out, "PROGRESS", progress); + tx_buf_sets(ctx->out, "COMPLETED", (remain == 0 && total > 0) ? "Y" : "N"); + tx_log(TX_LOG_INFO, + "[CL_OL_0010] 리포트 date=%s R=%ld M=%ld U=%ld S=%ld 진척=%ld%%", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s, progress); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0011.c b/app/online/cl_ol_0011.c index 0b18f9b..a679eb7 100644 --- a/app/online/cl_ol_0011.c +++ b/app/online/cl_ol_0011.c @@ -10,13 +10,46 @@ /* * cl_ol_0011.pgc - 마감 온라인 서비스 (CL_OL_0011) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 알림 발송(판정). 요청 영업일(BIZDATE)에 미마감(접수 R) 건이 남아 + * 있는지 확인하여, 남아 있으면 알림 대상(ALERT=Y)으로 미마감 건수·금액을 + * 함께 반환하고, 없으면 ALERT=N 으로 응답한다. 원장을 변경하지 않는 + * read-only 판정 서비스이다. (실제 발송채널 연계는 상위 오케스트레이션 담당) + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * BIZDATE (필수) 영업일(YYYYMMDD) + * 출력 필드: + * RESPCODE 0000=정상 / 9001=입력오류 / 9999=시스템오류 + * BIZDATE 점검 대상 영업일 + * RECVCNT 미마감(접수 R) 건수 + * RECVSUM 미마감 금액합계(원) + * RECVWON 미마감 금액 한글 표기 + * ALERT 알림 필요여부(Y=미마감 존재 / N=없음) + * MESSAGE 판정 사유 메시지 + * + * ── 알림 판정 규칙 ─────────────────────────────────────────────────── + * · 접수(R) 상태는 아직 마감처리 흐름에 진입하지 않은 원건이다. + * · R 건이 1건 이상이면 마감 담당자 알림 대상(ALERT=Y)으로 분류한다. + * · R 건이 없으면 마감 준비 완료로 보고 ALERT=N 을 반환한다. + * · 본 서비스는 판정만 수행하며 실제 SMS/메일 발송은 상위에서 처리. + * + * ── 처리 흐름 ──────────────────────────────────────────────────────── + * 1) BIZDATE 추출/형식검증 + * 2) status='R' 건수·금액합계 singleton SELECT + * 3) 건수 기준 ALERT Y/N 판정 및 응답 구성 + * + * ── 후속 연계(참고) ────────────────────────────────────────────────── + * · ALERT=Y 응답을 받은 상위 오케스트레이션은 마감 담당자 그룹으로 + * SMS/메일/사내 메신저 알림을 발송한다. + * · RECVCNT/RECVWON 은 알림 본문의 미마감 규모 안내에 사용된다. + * · 본 서비스는 판정 근거만 제공하고 실제 채널 I/O 는 수행하지 않아 + * DB 커넥션 외 외부 자원에 의존하지 않는 순수 조회 서비스이다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +122,92 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0011.pgc" +#line 48 "app/online/cl_ol_0011.pgc" TX_SERVICE(CL_OL_0011, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 53 "app/online/cl_ol_0011.pgc" + char h_biz_date [ 9 ] ; + +#line 54 "app/online/cl_ol_0011.pgc" + int h_recv_cnt ; + +#line 55 "app/online/cl_ol_0011.pgc" + long h_recv_sum ; +/* exec sql end declare section */ +#line 56 "app/online/cl_ol_0011.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0011] 마감 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0011] KEY 누락"); + char bizdate[16]; + char won[40]; + + tx_log(TX_LOG_INFO, "[CL_OL_0011] 마감 알림 판정 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0011] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0011] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0011] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 2. 미마감(R) 존재여부 및 금액 집계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date = $1 and status = 'R'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_recv_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_recv_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 87 "app/online/cl_ol_0011.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0011 recv aggregate"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3. 알림 판정 및 응답 구성 (판정 근거 상세 로깅) */ + tx_log(TX_LOG_DEBUG, "[CL_OL_0011] 판정근거 date=%s 미마감R건수=%d 금액=%ld", + bizdate, h_recv_cnt, h_recv_sum); + amount_format_won(h_recv_sum, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0011] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "RECVCNT", (long)h_recv_cnt); + tx_buf_setlong(ctx->out, "RECVSUM", h_recv_sum); + tx_buf_sets(ctx->out, "RECVWON", won); + + /* 미마감(R) 잔존 시 알림 대상, 아니면 마감 준비완료로 판정 */ + if (h_recv_cnt > 0) { + tx_buf_sets(ctx->out, "ALERT", "Y"); + tx_buf_setlong(ctx->out, "ALERTLEVEL", (h_recv_cnt >= 100) ? 2 : 1); + tx_buf_sets(ctx->out, "MESSAGE", "미마감 접수건 존재 - 마감 알림 대상"); + tx_log(TX_LOG_WARN, "[CL_OL_0011] 알림대상 date=%s 미마감=%d 금액=%ld", + bizdate, h_recv_cnt, h_recv_sum); + } else { + tx_buf_sets(ctx->out, "ALERT", "N"); + tx_buf_sets(ctx->out, "MESSAGE", "미마감 접수건 없음 - 알림 불필요"); + tx_log(TX_LOG_INFO, "[CL_OL_0011] 알림불요 date=%s", bizdate); + } tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0011.pgc b/app/online/cl_ol_0011.pgc index 28083f2..51d5d27 100644 --- a/app/online/cl_ol_0011.pgc +++ b/app/online/cl_ol_0011.pgc @@ -2,13 +2,46 @@ /* * cl_ol_0011.pgc - 마감 온라인 서비스 (CL_OL_0011) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 알림 발송(판정). 요청 영업일(BIZDATE)에 미마감(접수 R) 건이 남아 + * 있는지 확인하여, 남아 있으면 알림 대상(ALERT=Y)으로 미마감 건수·금액을 + * 함께 반환하고, 없으면 ALERT=N 으로 응답한다. 원장을 변경하지 않는 + * read-only 판정 서비스이다. (실제 발송채널 연계는 상위 오케스트레이션 담당) + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * BIZDATE (필수) 영업일(YYYYMMDD) + * 출력 필드: + * RESPCODE 0000=정상 / 9001=입력오류 / 9999=시스템오류 + * BIZDATE 점검 대상 영업일 + * RECVCNT 미마감(접수 R) 건수 + * RECVSUM 미마감 금액합계(원) + * RECVWON 미마감 금액 한글 표기 + * ALERT 알림 필요여부(Y=미마감 존재 / N=없음) + * MESSAGE 판정 사유 메시지 + * + * ── 알림 판정 규칙 ─────────────────────────────────────────────────── + * · 접수(R) 상태는 아직 마감처리 흐름에 진입하지 않은 원건이다. + * · R 건이 1건 이상이면 마감 담당자 알림 대상(ALERT=Y)으로 분류한다. + * · R 건이 없으면 마감 준비 완료로 보고 ALERT=N 을 반환한다. + * · 본 서비스는 판정만 수행하며 실제 SMS/메일 발송은 상위에서 처리. + * + * ── 처리 흐름 ──────────────────────────────────────────────────────── + * 1) BIZDATE 추출/형식검증 + * 2) status='R' 건수·금액합계 singleton SELECT + * 3) 건수 기준 ALERT Y/N 판정 및 응답 구성 + * + * ── 후속 연계(참고) ────────────────────────────────────────────────── + * · ALERT=Y 응답을 받은 상위 오케스트레이션은 마감 담당자 그룹으로 + * SMS/메일/사내 메신저 알림을 발송한다. + * · RECVCNT/RECVWON 은 알림 본문의 미마감 규모 안내에 사용된다. + * · 본 서비스는 판정 근거만 제공하고 실제 채널 I/O 는 수행하지 않아 + * DB 커넥션 외 외부 자원에 의존하지 않는 순수 조회 서비스이다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,32 +49,72 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0011, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + int h_recv_cnt; + long h_recv_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0011] 마감 단건조회 서비스 진입"); + char bizdate[16]; + char won[40]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0011] KEY 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0011] 마감 알림 판정 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0011] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0011] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0011] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 2. 미마감(R) 존재여부 및 금액 집계 */ + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_recv_cnt, :h_recv_sum + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'R'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0011 recv aggregate"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3. 알림 판정 및 응답 구성 (판정 근거 상세 로깅) */ + tx_log(TX_LOG_DEBUG, "[CL_OL_0011] 판정근거 date=%s 미마감R건수=%d 금액=%ld", + bizdate, h_recv_cnt, h_recv_sum); + amount_format_won(h_recv_sum, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0011] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "RECVCNT", (long)h_recv_cnt); + tx_buf_setlong(ctx->out, "RECVSUM", h_recv_sum); + tx_buf_sets(ctx->out, "RECVWON", won); + + /* 미마감(R) 잔존 시 알림 대상, 아니면 마감 준비완료로 판정 */ + if (h_recv_cnt > 0) { + tx_buf_sets(ctx->out, "ALERT", "Y"); + tx_buf_setlong(ctx->out, "ALERTLEVEL", (h_recv_cnt >= 100) ? 2 : 1); + tx_buf_sets(ctx->out, "MESSAGE", "미마감 접수건 존재 - 마감 알림 대상"); + tx_log(TX_LOG_WARN, "[CL_OL_0011] 알림대상 date=%s 미마감=%d 금액=%ld", + bizdate, h_recv_cnt, h_recv_sum); + } else { + tx_buf_sets(ctx->out, "ALERT", "N"); + tx_buf_sets(ctx->out, "MESSAGE", "미마감 접수건 없음 - 알림 불필요"); + tx_log(TX_LOG_INFO, "[CL_OL_0011] 알림불요 date=%s", bizdate); + } tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0012.c b/app/online/cl_ol_0012.c index de87b56..a5a33e8 100644 --- a/app/online/cl_ol_0012.c +++ b/app/online/cl_ol_0012.c @@ -10,13 +10,34 @@ /* * cl_ol_0012.pgc - 마감 온라인 서비스 (CL_OL_0012) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 미마감 점검. 요청 영업일(BIZDATE)에서 마감이 완료되지 않은 상태 + * (접수 R, 불일치 U) 건의 건수와 금액합계를 singleton SELECT 로 산출한다. + * 미마감 건이 존재하면 마감 진행 불가 경고(RESP_INVALID)로 응답하고, + * 없으면 마감 가능(RESP_OK)으로 응답한다. (read-only 점검) + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * BIZDATE (필수) 영업일(YYYYMMDD) + * 출력 필드: + * RESPCODE 0000=마감가능 / 9001=미마감존재·입력오류 / 9999=시스템오류 + * BIZDATE 점검 대상 영업일 + * OPENCNT 미마감(R+U) 총 건수 + * OPENSUM 미마감 금액합계(원) / OPENWON 한글 표기 + * RECVCNT 접수(R) 건수 + * FAILCNT 불일치(U) 건수 + * CLOSABLE 마감 가능여부(Y/N) + * + * ── 점검 규칙 ──────────────────────────────────────────────────────── + * · 접수(R)·불일치(U) 는 아직 마감확정 전 상태로 '미마감'으로 본다. + * · 미마감 건이 1건이라도 있으면 일마감을 진행할 수 없으므로 + * 경고 성격의 RESP_INVALID 로 응답한다. + * · 미마감 건이 전혀 없으면 마감 가능(RESP_OK)으로 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,60 +110,131 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0012.pgc" +#line 36 "app/online/cl_ol_0012.pgc" TX_SERVICE(CL_OL_0012, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 41 "app/online/cl_ol_0012.pgc" + char h_biz_date [ 9 ] ; + +#line 42 "app/online/cl_ol_0012.pgc" + int h_open_cnt ; + +#line 43 "app/online/cl_ol_0012.pgc" + long h_open_sum ; + +#line 44 "app/online/cl_ol_0012.pgc" + int h_recv_cnt ; + +#line 45 "app/online/cl_ol_0012.pgc" + int h_fail_cnt ; +/* exec sql end declare section */ +#line 46 "app/online/cl_ol_0012.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0012] 마감 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0012] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char bizdate[16]; + char won[40]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0012] 금액 범위 오류 amount=%ld", amount); + tx_log(TX_LOG_INFO, "[CL_OL_0012] 미마감 점검 서비스 진입"); + + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0012] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0012] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[CL_OL_0012] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0012] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2. 미마감(R,U) 건수/금액합계 통합 집계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date = $1 and status in ( 'R' , 'U' )", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_open_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_open_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 77 "app/online/cl_ol_0012.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0012 open aggregate"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* 3. 상태별 세부 건수 (R / U) 분해 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date = $1 and status = 'R'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_recv_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 91 "app/online/cl_ol_0012.pgc" + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0012 recv count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date = $1 and status = 'U'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_fail_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 103 "app/online/cl_ol_0012.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0012 fail count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 4. 판정 및 응답 구성 */ + amount_format_won(h_open_sum, won, sizeof(won)); tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "OPENCNT", (long)h_open_cnt); + tx_buf_setlong(ctx->out, "OPENSUM", h_open_sum); + tx_buf_sets(ctx->out, "OPENWON", won); + tx_buf_setlong(ctx->out, "RECVCNT", (long)h_recv_cnt); + tx_buf_setlong(ctx->out, "FAILCNT", (long)h_fail_cnt); + + if (h_open_cnt > 0) { + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "CLOSABLE", "N"); + tx_buf_sets(ctx->out, "MESSAGE", "미마감 건 존재 - 마감 진행 불가"); + tx_log(TX_LOG_WARN, "[CL_OL_0012] 미마감 존재 date=%s 건수=%d 금액=%ld", + bizdate, h_open_cnt, h_open_sum); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0012] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "CLOSABLE", "Y"); + tx_buf_sets(ctx->out, "MESSAGE", "미마감 건 없음 - 마감 가능"); + tx_log(TX_LOG_INFO, "[CL_OL_0012] 미마감 없음 date=%s 마감가능", bizdate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0012.pgc b/app/online/cl_ol_0012.pgc index 690fe2e..85f364b 100644 --- a/app/online/cl_ol_0012.pgc +++ b/app/online/cl_ol_0012.pgc @@ -2,13 +2,34 @@ /* * cl_ol_0012.pgc - 마감 온라인 서비스 (CL_OL_0012) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 미마감 점검. 요청 영업일(BIZDATE)에서 마감이 완료되지 않은 상태 + * (접수 R, 불일치 U) 건의 건수와 금액합계를 singleton SELECT 로 산출한다. + * 미마감 건이 존재하면 마감 진행 불가 경고(RESP_INVALID)로 응답하고, + * 없으면 마감 가능(RESP_OK)으로 응답한다. (read-only 점검) + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * BIZDATE (필수) 영업일(YYYYMMDD) + * 출력 필드: + * RESPCODE 0000=마감가능 / 9001=미마감존재·입력오류 / 9999=시스템오류 + * BIZDATE 점검 대상 영업일 + * OPENCNT 미마감(R+U) 총 건수 + * OPENSUM 미마감 금액합계(원) / OPENWON 한글 표기 + * RECVCNT 접수(R) 건수 + * FAILCNT 불일치(U) 건수 + * CLOSABLE 마감 가능여부(Y/N) + * + * ── 점검 규칙 ──────────────────────────────────────────────────────── + * · 접수(R)·불일치(U) 는 아직 마감확정 전 상태로 '미마감'으로 본다. + * · 미마감 건이 1건이라도 있으면 일마감을 진행할 수 없으므로 + * 경고 성격의 RESP_INVALID 로 응답한다. + * · 미마감 건이 전혀 없으면 마감 가능(RESP_OK)으로 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,55 +37,101 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0012, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + int h_open_cnt; + long h_open_sum; + int h_recv_cnt; + int h_fail_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0012] 마감 등록 서비스 진입"); + char bizdate[16]; + char won[40]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0012] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[CL_OL_0012] 미마감 점검 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0012] 금액 범위 오류 amount=%ld", amount); + /* 1. 요청 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0012] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0012] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[CL_OL_0012] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0012] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2. 미마감(R,U) 건수/금액합계 통합 집계 */ + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_open_cnt, :h_open_sum + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status IN ('R', 'U'); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0012 open aggregate"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* 3. 상태별 세부 건수 (R / U) 분해 조회 */ + EXEC SQL SELECT count(*) + INTO :h_recv_cnt + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'R'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0012 recv count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + EXEC SQL SELECT count(*) + INTO :h_fail_cnt + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'U'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0012 fail count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + /* 4. 판정 및 응답 구성 */ + amount_format_won(h_open_sum, won, sizeof(won)); tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "OPENCNT", (long)h_open_cnt); + tx_buf_setlong(ctx->out, "OPENSUM", h_open_sum); + tx_buf_sets(ctx->out, "OPENWON", won); + tx_buf_setlong(ctx->out, "RECVCNT", (long)h_recv_cnt); + tx_buf_setlong(ctx->out, "FAILCNT", (long)h_fail_cnt); + + if (h_open_cnt > 0) { + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "CLOSABLE", "N"); + tx_buf_sets(ctx->out, "MESSAGE", "미마감 건 존재 - 마감 진행 불가"); + tx_log(TX_LOG_WARN, "[CL_OL_0012] 미마감 존재 date=%s 건수=%d 금액=%ld", + bizdate, h_open_cnt, h_open_sum); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0012] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "CLOSABLE", "Y"); + tx_buf_sets(ctx->out, "MESSAGE", "미마감 건 없음 - 마감 가능"); + tx_log(TX_LOG_INFO, "[CL_OL_0012] 미마감 없음 date=%s 마감가능", bizdate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0013.c b/app/online/cl_ol_0013.c index 6a1153e..66c53d1 100644 --- a/app/online/cl_ol_0013.c +++ b/app/online/cl_ol_0013.c @@ -10,13 +10,16 @@ /* * cl_ol_0013.pgc - 마감 온라인 서비스 (CL_OL_0013) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 월마감 요청. 요청 년월(YYYYMM)에 해당하는 한 달치 원장의 처리완료(M)/ + * 불일치(U) 건을 정산완료(S)로 월(月)단위 일괄 마감한다. biz_date 가 해당 + * 년월로 시작하는지는 C 에서 'YYYYMM%' 패턴 문자열을 만들어 LIKE :h_pat + * 로 판별한다. 월 마감건수와 금액합계를 응답한다. (tx_begin/tx_commit) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +92,151 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0013.pgc" +#line 18 "app/online/cl_ol_0013.pgc" TX_SERVICE(CL_OL_0013, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 23 "app/online/cl_ol_0013.pgc" + char h_pat [ 16 ] ; + +#line 24 "app/online/cl_ol_0013.pgc" + int h_target_cnt ; + +#line 25 "app/online/cl_ol_0013.pgc" + long h_settle_sum ; +/* exec sql end declare section */ +#line 26 "app/online/cl_ol_0013.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0013] 마감 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0013] KEY 누락"); + char ym[16]; + char probe[9]; + int i; + long month_cnt = 0; + char won[40]; + + tx_log(TX_LOG_INFO, "[CL_OL_0013] 월마감 요청 서비스 진입"); + + /* 1. 요청 검증 : YYYYMM 6자리 숫자 */ + if (tx_buf_get(ctx->in, "YYYYMM", ym, sizeof(ym)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0013] YYYYMM 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (strlen(ym) != 6) { + tx_log(TX_LOG_WARN, "[CL_OL_0013] 년월 길이 오류 ym=%s", ym); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + for (i = 0; i < 6; i++) { + if (ym[i] < '0' || ym[i] > '9') { + tx_log(TX_LOG_WARN, "[CL_OL_0013] 년월 형식 오류 ym=%s", ym); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } + /* YYYYMM01 로 조립해 실재 년월 유효성 재확인 */ + snprintf(probe, sizeof(probe), "%.6s01", ym); + if (!date_is_valid(probe)) { + tx_log(TX_LOG_WARN, "[CL_OL_0013] 유효하지 않은 년월 ym=%s", ym); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0013] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2. LIKE 패턴 조립 : 'YYYYMM%' */ + snprintf(h_pat, sizeof(h_pat), "%.6s%%", ym); + + /* 3. 월마감 대상(M/U) 사전 건수 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date like $1 and status in ( 'M' , 'U' )", + ECPGt_char,(h_pat),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_target_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 78 "app/online/cl_ol_0013.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0013 target count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_target_cnt == 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0013] 월마감 대상 없음 ym=%s", ym); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "YYYYMM", ym); + tx_buf_setlong(ctx->out, "MONTHCNT", 0); + tx_buf_sets(ctx->out, "MESSAGE", "월마감 대상(M/U) 없음"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + /* 4. 월마감 트랜잭션 : M/U → S */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0013] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cl_ledger set status = 'S' , upd_ts = now ( ) where biz_date like $1 and status in ( 'M' , 'U' )", + ECPGt_char,(h_pat),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 109 "app/online/cl_ol_0013.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0013 month update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + month_cnt = (long)sqlca.sqlerrd[2]; + + /* 5. 월 마감금액 합계 재집계 (S) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date like $1 and status = 'S'", + ECPGt_char,(h_pat),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settle_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 125 "app/online/cl_ol_0013.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0013 month sum"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 6. 응답 구성 */ + amount_format_won(h_settle_sum, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0013] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "YYYYMM", ym); + tx_buf_setlong(ctx->out, "MONTHCNT", month_cnt); + tx_buf_setlong(ctx->out, "SETTLESUM", h_settle_sum); + tx_buf_sets(ctx->out, "SETTLEWON", won); + tx_log(TX_LOG_INFO, "[CL_OL_0013] 월마감 완료 ym=%s 건수=%ld 합계=%ld", + ym, month_cnt, h_settle_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0013.pgc b/app/online/cl_ol_0013.pgc index a8a4292..5764ae5 100644 --- a/app/online/cl_ol_0013.pgc +++ b/app/online/cl_ol_0013.pgc @@ -2,13 +2,16 @@ /* * cl_ol_0013.pgc - 마감 온라인 서비스 (CL_OL_0013) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 월마감 요청. 요청 년월(YYYYMM)에 해당하는 한 달치 원장의 처리완료(M)/ + * 불일치(U) 건을 정산완료(S)로 월(月)단위 일괄 마감한다. biz_date 가 해당 + * 년월로 시작하는지는 C 에서 'YYYYMM%' 패턴 문자열을 만들어 LIKE :h_pat + * 로 판별한다. 월 마감건수와 금액합계를 응답한다. (tx_begin/tx_commit) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,32 +19,130 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0013, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_pat[16]; + int h_target_cnt; + long h_settle_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0013] 마감 단건조회 서비스 진입"); + char ym[16]; + char probe[9]; + int i; + long month_cnt = 0; + char won[40]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0013] KEY 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0013] 월마감 요청 서비스 진입"); + + /* 1. 요청 검증 : YYYYMM 6자리 숫자 */ + if (tx_buf_get(ctx->in, "YYYYMM", ym, sizeof(ym)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0013] YYYYMM 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (strlen(ym) != 6) { + tx_log(TX_LOG_WARN, "[CL_OL_0013] 년월 길이 오류 ym=%s", ym); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + for (i = 0; i < 6; i++) { + if (ym[i] < '0' || ym[i] > '9') { + tx_log(TX_LOG_WARN, "[CL_OL_0013] 년월 형식 오류 ym=%s", ym); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } + /* YYYYMM01 로 조립해 실재 년월 유효성 재확인 */ + snprintf(probe, sizeof(probe), "%.6s01", ym); + if (!date_is_valid(probe)) { + tx_log(TX_LOG_WARN, "[CL_OL_0013] 유효하지 않은 년월 ym=%s", ym); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0013] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2. LIKE 패턴 조립 : 'YYYYMM%' */ + snprintf(h_pat, sizeof(h_pat), "%.6s%%", ym); + + /* 3. 월마감 대상(M/U) 사전 건수 확인 */ + EXEC SQL SELECT count(*) + INTO :h_target_cnt + FROM cl_ledger + WHERE biz_date LIKE :h_pat + AND status IN ('M', 'U'); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0013 target count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_target_cnt == 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0013] 월마감 대상 없음 ym=%s", ym); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "YYYYMM", ym); + tx_buf_setlong(ctx->out, "MONTHCNT", 0); + tx_buf_sets(ctx->out, "MESSAGE", "월마감 대상(M/U) 없음"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + /* 4. 월마감 트랜잭션 : M/U → S */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0013] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE cl_ledger + SET status = 'S', upd_ts = now() + WHERE biz_date LIKE :h_pat + AND status IN ('M', 'U'); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0013 month update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + month_cnt = (long)sqlca.sqlerrd[2]; + + /* 5. 월 마감금액 합계 재집계 (S) */ + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_settle_sum + FROM cl_ledger + WHERE biz_date LIKE :h_pat + AND status = 'S'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0013 month sum"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 6. 응답 구성 */ + amount_format_won(h_settle_sum, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0013] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "YYYYMM", ym); + tx_buf_setlong(ctx->out, "MONTHCNT", month_cnt); + tx_buf_setlong(ctx->out, "SETTLESUM", h_settle_sum); + tx_buf_sets(ctx->out, "SETTLEWON", won); + tx_log(TX_LOG_INFO, "[CL_OL_0013] 월마감 완료 ym=%s 건수=%ld 합계=%ld", + ym, month_cnt, h_settle_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0014.c b/app/online/cl_ol_0014.c index a20f9fd..c74f545 100644 --- a/app/online/cl_ol_0014.c +++ b/app/online/cl_ol_0014.c @@ -10,13 +10,16 @@ /* * cl_ol_0014.pgc - 마감 온라인 서비스 (CL_OL_0014) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 분기마감 요청. 요청 분기 시작영업일(FROMDATE=D1)과 종료영업일 + * (TODATE=D2) 범위에 대해 처리완료(M)/불일치(U) 건을 정산완료(S)로 + * 일괄 마감한다. 범위 판별은 biz_date BETWEEN :d1 AND :d2 로 수행한다. + * 분기 마감건수와 금액합계를 응답한다. (tx_begin/tx_commit) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,60 +92,155 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0014.pgc" +#line 18 "app/online/cl_ol_0014.pgc" TX_SERVICE(CL_OL_0014, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + +#line 23 "app/online/cl_ol_0014.pgc" + char h_d1 [ 9 ] ; + +#line 24 "app/online/cl_ol_0014.pgc" + char h_d2 [ 9 ] ; + +#line 25 "app/online/cl_ol_0014.pgc" + int h_target_cnt ; + +#line 26 "app/online/cl_ol_0014.pgc" + long h_settle_sum ; +/* exec sql end declare section */ +#line 27 "app/online/cl_ol_0014.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0014] 마감 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0014] 필수 필드 누락"); + char d1[16], d2[16]; + long quarter_cnt = 0; + char won[40]; + + tx_log(TX_LOG_INFO, "[CL_OL_0014] 분기마감 요청 서비스 진입"); + + /* 1. 요청 검증 : 시작/종료 영업일 필수 */ + if (tx_buf_get(ctx->in, "FROMDATE", d1, sizeof(d1)) != TX_OK || + tx_buf_get(ctx->in, "TODATE", d2, sizeof(d2)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0014] FROMDATE/TODATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0014] 금액 범위 오류 amount=%ld", amount); + if (!date_is_valid(d1) || !date_is_valid(d2)) { + tx_log(TX_LOG_WARN, "[CL_OL_0014] 영업일 형식 오류 d1=%s d2=%s", d1, d2); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0014] 영업일 오류 date=%s", bizdate); + /* 범위 정합성 : 시작 <= 종료 (YYYYMMDD 문자열 비교) */ + if (strncmp(d1, d2, 8) > 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0014] 범위 역전 d1=%s d2=%s", d1, d2); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "MESSAGE", "시작일이 종료일보다 큼"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_d1, d1, sizeof(h_d1) - 1); + h_d1[sizeof(h_d1) - 1] = '\0'; + strncpy(h_d2, d2, sizeof(h_d2) - 1); + h_d2[sizeof(h_d2) - 1] = '\0'; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0014] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2. 분기마감 대상(M/U) 사전 건수 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date between $1 and $2 and status in ( 'M' , 'U' )", + ECPGt_char,(h_d1),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_d2),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_target_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 71 "app/online/cl_ol_0014.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0014 target count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_target_cnt == 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0014] 분기마감 대상 없음 d1=%s d2=%s", d1, d2); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "FROMDATE", d1); + tx_buf_sets(ctx->out, "TODATE", d2); + tx_buf_setlong(ctx->out, "QUARTERCNT", 0); + tx_buf_sets(ctx->out, "MESSAGE", "분기마감 대상(M/U) 없음"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* 3. 분기마감 트랜잭션 : M/U → S */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0014] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cl_ledger set status = 'S' , upd_ts = now ( ) where biz_date between $1 and $2 and status in ( 'M' , 'U' )", + ECPGt_char,(h_d1),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_d2),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 103 "app/online/cl_ol_0014.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0014 quarter update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + quarter_cnt = (long)sqlca.sqlerrd[2]; + + /* 4. 분기 마감금액 합계 재집계 (S) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date between $1 and $2 and status = 'S'", + ECPGt_char,(h_d1),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_d2),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settle_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 119 "app/online/cl_ol_0014.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0014 quarter sum"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 5. 응답 구성 */ + amount_format_won(h_settle_sum, won, sizeof(won)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0014] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "FROMDATE", d1); + tx_buf_sets(ctx->out, "TODATE", d2); + tx_buf_setlong(ctx->out, "QUARTERCNT", quarter_cnt); + tx_buf_setlong(ctx->out, "SETTLESUM", h_settle_sum); + tx_buf_sets(ctx->out, "SETTLEWON", won); + tx_log(TX_LOG_INFO, "[CL_OL_0014] 분기마감 완료 %s~%s 건수=%ld 합계=%ld", + d1, d2, quarter_cnt, h_settle_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0014.pgc b/app/online/cl_ol_0014.pgc index f7d0b43..f9b7235 100644 --- a/app/online/cl_ol_0014.pgc +++ b/app/online/cl_ol_0014.pgc @@ -2,13 +2,16 @@ /* * cl_ol_0014.pgc - 마감 온라인 서비스 (CL_OL_0014) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 분기마감 요청. 요청 분기 시작영업일(FROMDATE=D1)과 종료영업일 + * (TODATE=D2) 범위에 대해 처리완료(M)/불일치(U) 건을 정산완료(S)로 + * 일괄 마감한다. 범위 판별은 biz_date BETWEEN :d1 AND :d2 로 수행한다. + * 분기 마감건수와 금액합계를 응답한다. (tx_begin/tx_commit) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,55 +19,125 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0014, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_d1[9]; + char h_d2[9]; + int h_target_cnt; + long h_settle_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0014] 마감 등록 서비스 진입"); + char d1[16], d2[16]; + long quarter_cnt = 0; + char won[40]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0014] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0014] 분기마감 요청 서비스 진입"); + + /* 1. 요청 검증 : 시작/종료 영업일 필수 */ + if (tx_buf_get(ctx->in, "FROMDATE", d1, sizeof(d1)) != TX_OK || + tx_buf_get(ctx->in, "TODATE", d2, sizeof(d2)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0014] FROMDATE/TODATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0014] 금액 범위 오류 amount=%ld", amount); + if (!date_is_valid(d1) || !date_is_valid(d2)) { + tx_log(TX_LOG_WARN, "[CL_OL_0014] 영업일 형식 오류 d1=%s d2=%s", d1, d2); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0014] 영업일 오류 date=%s", bizdate); + /* 범위 정합성 : 시작 <= 종료 (YYYYMMDD 문자열 비교) */ + if (strncmp(d1, d2, 8) > 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0014] 범위 역전 d1=%s d2=%s", d1, d2); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "MESSAGE", "시작일이 종료일보다 큼"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_d1, d1, sizeof(h_d1) - 1); + h_d1[sizeof(h_d1) - 1] = '\0'; + strncpy(h_d2, d2, sizeof(h_d2) - 1); + h_d2[sizeof(h_d2) - 1] = '\0'; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0014] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2. 분기마감 대상(M/U) 사전 건수 확인 */ + EXEC SQL SELECT count(*) + INTO :h_target_cnt + FROM cl_ledger + WHERE biz_date BETWEEN :h_d1 AND :h_d2 + AND status IN ('M', 'U'); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0014 target count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_target_cnt == 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0014] 분기마감 대상 없음 d1=%s d2=%s", d1, d2); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "FROMDATE", d1); + tx_buf_sets(ctx->out, "TODATE", d2); + tx_buf_setlong(ctx->out, "QUARTERCNT", 0); + tx_buf_sets(ctx->out, "MESSAGE", "분기마감 대상(M/U) 없음"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* 3. 분기마감 트랜잭션 : M/U → S */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0014] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + EXEC SQL UPDATE cl_ledger + SET status = 'S', upd_ts = now() + WHERE biz_date BETWEEN :h_d1 AND :h_d2 + AND status IN ('M', 'U'); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0014 quarter update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + quarter_cnt = (long)sqlca.sqlerrd[2]; + + /* 4. 분기 마감금액 합계 재집계 (S) */ + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_settle_sum + FROM cl_ledger + WHERE biz_date BETWEEN :h_d1 AND :h_d2 + AND status = 'S'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0014 quarter sum"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 5. 응답 구성 */ + amount_format_won(h_settle_sum, won, sizeof(won)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0014] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "FROMDATE", d1); + tx_buf_sets(ctx->out, "TODATE", d2); + tx_buf_setlong(ctx->out, "QUARTERCNT", quarter_cnt); + tx_buf_setlong(ctx->out, "SETTLESUM", h_settle_sum); + tx_buf_sets(ctx->out, "SETTLEWON", won); + tx_log(TX_LOG_INFO, "[CL_OL_0014] 분기마감 완료 %s~%s 건수=%ld 합계=%ld", + d1, d2, quarter_cnt, h_settle_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0015.c b/app/online/cl_ol_0015.c index b6d61f6..62e7bbd 100644 --- a/app/online/cl_ol_0015.c +++ b/app/online/cl_ol_0015.c @@ -10,8 +10,34 @@ /* * cl_ol_0015.pgc - 마감 온라인 서비스 (CL_OL_0015) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 상태조회. 요청 처리키(KEY) 단건에 대해 표준 DBIO cl_rec_select 로 + * 원장 레코드를 읽어, 상태코드/금액/영업일과 함께 마감여부(status='S')를 + * 판정하여 응답한다. (read-only 단건 판정) + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * KEY (필수) 처리키(승인/거래 번호) - 원장 PK + * 출력 필드: + * RESPCODE 0000=정상 / 9001=키누락·원장없음 / 9999=시스템오류 + * KEY 조회 대상 처리키 + * MERCHID 가맹점번호 + * BIZDATE 영업일(YYYYMMDD) + * AMOUNT 거래금액(원, 정수) + * AMOUNTWON 거래금액 한글 표기(예: 12,300원) + * STATUS 상태코드(R/M/U/S) + * STATUSLABEL 상태 한글 라벨(접수/처리완료/불일치/정산완료) + * CLOSED 마감여부(Y=정산완료 S / N=그 외) + * + * ── 상태코드 정의 ──────────────────────────────────────────────────── + * R(접수) → M(처리완료) → S(정산완료/마감확정) + * └ U(불일치) 는 재처리 후 M 으로 복귀 대상 + * 본 서비스는 상태를 변경하지 않으며, 마감확정 여부만 판정한다. + * + * ── 처리 흐름 ──────────────────────────────────────────────────────── + * 1) 요청 TXBUF 에서 KEY 추출 및 필수검증 + * 2) cl_rec_select 로 단건 원장 조회 (없으면 RESP_INVALID) + * 3) status 를 CL_ST_* 상수와 비교하여 라벨/마감여부 산출 + * 4) 응답 TXBUF 구성 후 tx_return */ #include #include @@ -89,37 +115,85 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0015.pgc" +#line 41 "app/online/cl_ol_0015.pgc" TX_SERVICE(CL_OL_0015, ctx) { cl_rec_t rec; char key[16]; + char won[40]; + const char *st_label; + int is_settled; + int is_snap; int rc; - tx_log(TX_LOG_INFO, "[CL_OL_0015] 마감 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CL_OL_0015] 마감 상태조회 서비스 진입"); + /* 1. 요청 검증 : 처리키 필수 */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[CL_OL_0015] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2. 단건 조회 (표준 DBIO) */ memset(&rec, 0, sizeof(rec)); rc = cl_rec_select(key, &rec); + if (rc == TX_ENOENT) { + tx_log(TX_LOG_WARN, "[CL_OL_0015] 원장 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "MESSAGE", "해당 처리키 원장 없음"); + tx_return(ctx, rc, ctx->out); + return; + } if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0015] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_ERROR, "[CL_OL_0015] 조회 오류 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, rc, ctx->out); return; } + /* 3. 마감여부 판정 : status='S' 이면 마감완료 */ + is_settled = (strcmp(rec.status, CL_ST_SETTLED) == 0); + if (strcmp(rec.status, CL_ST_RECV) == 0) st_label = "접수"; + else if (strcmp(rec.status, CL_ST_DONE) == 0) st_label = "처리완료"; + else if (strcmp(rec.status, CL_ST_FAIL) == 0) st_label = "불일치"; + else if (strcmp(rec.status, CL_ST_SETTLED) == 0) st_label = "정산완료"; + else st_label = "미정의"; + + /* 4. 스냅샷 요약행 여부 판정 (merch_id='*SNAP' 규약) */ + is_snap = (strcmp(rec.merch_id, "*SNAP") == 0); + + /* 5. 응답 구성 */ + amount_format_won(rec.amount, won, sizeof(won)); tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", rec.key); tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_sets(ctx->out, "BIZDATE", rec.biz_date); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_sets(ctx->out, "AMOUNTWON", won); + tx_buf_setlong(ctx->out, "FEE", rec.fee); tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0015] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "STATUSLABEL", st_label); + tx_buf_sets(ctx->out, "CLOSED", is_settled ? "Y" : "N"); + tx_buf_sets(ctx->out, "SNAPSHOT", is_snap ? "Y" : "N"); + + /* 6. 스냅샷 요약행은 집계행이므로 별도 안내 로깅 */ + if (is_snap) + tx_log(TX_LOG_INFO, + "[CL_OL_0015] 스냅샷 요약행 조회 key=%s biz=%s Σ=%ld", + rec.key, rec.biz_date, rec.amount); + + tx_log(TX_LOG_INFO, + "[CL_OL_0015] 상태조회 key=%s status=%s(%s) 마감여부=%s", + rec.key, rec.status, st_label, is_settled ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0015.pgc b/app/online/cl_ol_0015.pgc index e757fe0..7eda47f 100644 --- a/app/online/cl_ol_0015.pgc +++ b/app/online/cl_ol_0015.pgc @@ -2,8 +2,34 @@ /* * cl_ol_0015.pgc - 마감 온라인 서비스 (CL_OL_0015) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 상태조회. 요청 처리키(KEY) 단건에 대해 표준 DBIO cl_rec_select 로 + * 원장 레코드를 읽어, 상태코드/금액/영업일과 함께 마감여부(status='S')를 + * 판정하여 응답한다. (read-only 단건 판정) + * + * ── 서비스 계약(Contract) ──────────────────────────────────────────── + * 입력 필드: + * KEY (필수) 처리키(승인/거래 번호) - 원장 PK + * 출력 필드: + * RESPCODE 0000=정상 / 9001=키누락·원장없음 / 9999=시스템오류 + * KEY 조회 대상 처리키 + * MERCHID 가맹점번호 + * BIZDATE 영업일(YYYYMMDD) + * AMOUNT 거래금액(원, 정수) + * AMOUNTWON 거래금액 한글 표기(예: 12,300원) + * STATUS 상태코드(R/M/U/S) + * STATUSLABEL 상태 한글 라벨(접수/처리완료/불일치/정산완료) + * CLOSED 마감여부(Y=정산완료 S / N=그 외) + * + * ── 상태코드 정의 ──────────────────────────────────────────────────── + * R(접수) → M(처리완료) → S(정산완료/마감확정) + * └ U(불일치) 는 재처리 후 M 으로 복귀 대상 + * 본 서비스는 상태를 변경하지 않으며, 마감확정 여부만 판정한다. + * + * ── 처리 흐름 ──────────────────────────────────────────────────────── + * 1) 요청 TXBUF 에서 KEY 추출 및 필수검증 + * 2) cl_rec_select 로 단건 원장 조회 (없으면 RESP_INVALID) + * 3) status 를 CL_ST_* 상수와 비교하여 라벨/마감여부 산출 + * 4) 응답 TXBUF 구성 후 tx_return */ #include #include @@ -18,30 +44,78 @@ TX_SERVICE(CL_OL_0015, ctx) { cl_rec_t rec; char key[16]; + char won[40]; + const char *st_label; + int is_settled; + int is_snap; int rc; - tx_log(TX_LOG_INFO, "[CL_OL_0015] 마감 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CL_OL_0015] 마감 상태조회 서비스 진입"); + /* 1. 요청 검증 : 처리키 필수 */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[CL_OL_0015] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2. 단건 조회 (표준 DBIO) */ memset(&rec, 0, sizeof(rec)); rc = cl_rec_select(key, &rec); + if (rc == TX_ENOENT) { + tx_log(TX_LOG_WARN, "[CL_OL_0015] 원장 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "MESSAGE", "해당 처리키 원장 없음"); + tx_return(ctx, rc, ctx->out); + return; + } if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0015] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_ERROR, "[CL_OL_0015] 조회 오류 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, rc, ctx->out); return; } + /* 3. 마감여부 판정 : status='S' 이면 마감완료 */ + is_settled = (strcmp(rec.status, CL_ST_SETTLED) == 0); + if (strcmp(rec.status, CL_ST_RECV) == 0) st_label = "접수"; + else if (strcmp(rec.status, CL_ST_DONE) == 0) st_label = "처리완료"; + else if (strcmp(rec.status, CL_ST_FAIL) == 0) st_label = "불일치"; + else if (strcmp(rec.status, CL_ST_SETTLED) == 0) st_label = "정산완료"; + else st_label = "미정의"; + + /* 4. 스냅샷 요약행 여부 판정 (merch_id='*SNAP' 규약) */ + is_snap = (strcmp(rec.merch_id, "*SNAP") == 0); + + /* 5. 응답 구성 */ + amount_format_won(rec.amount, won, sizeof(won)); tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", rec.key); tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_sets(ctx->out, "BIZDATE", rec.biz_date); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_sets(ctx->out, "AMOUNTWON", won); + tx_buf_setlong(ctx->out, "FEE", rec.fee); tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0015] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "STATUSLABEL", st_label); + tx_buf_sets(ctx->out, "CLOSED", is_settled ? "Y" : "N"); + tx_buf_sets(ctx->out, "SNAPSHOT", is_snap ? "Y" : "N"); + + /* 6. 스냅샷 요약행은 집계행이므로 별도 안내 로깅 */ + if (is_snap) + tx_log(TX_LOG_INFO, + "[CL_OL_0015] 스냅샷 요약행 조회 key=%s biz=%s Σ=%ld", + rec.key, rec.biz_date, rec.amount); + + tx_log(TX_LOG_INFO, + "[CL_OL_0015] 상태조회 key=%s status=%s(%s) 마감여부=%s", + rec.key, rec.status, st_label, is_settled ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0016.c b/app/online/cl_ol_0016.c index 915f6d2..d0ce535 100644 --- a/app/online/cl_ol_0016.c +++ b/app/online/cl_ol_0016.c @@ -10,13 +10,16 @@ /* * cl_ol_0016.pgc - 마감 온라인 서비스 (CL_OL_0016) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 확정. 요청 처리키(KEY) 단건에 대해 현재 상태를 확인한 뒤 처리완료(M) + * 건을 정산완료(S)로 확정한다. 이미 정산완료(S)면 경고로 응답하고, 확정 + * 대상이 아닌 상태(R/U)이면 확정 불가로 응답한다. (singleton SELECT 후 + * 단건 UPDATE; tx_begin/tx_commit) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,60 +92,148 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0016.pgc" +#line 18 "app/online/cl_ol_0016.pgc" TX_SERVICE(CL_OL_0016, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + +#line 23 "app/online/cl_ol_0016.pgc" + char h_key [ 16 ] ; + +#line 24 "app/online/cl_ol_0016.pgc" + char h_status [ 2 ] ; + +#line 25 "app/online/cl_ol_0016.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 26 "app/online/cl_ol_0016.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0016] 마감 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0016] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char key[16]; + char won[40]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0016] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0016] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[CL_OL_0016] 마감 확정 서비스 진입"); + + /* 1. 요청 검증 : 처리키 필수 */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0016] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0016] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2. 현재 상태/금액 단건 조회 (PK singleton SELECT) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select status , amount from cl_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 49 "app/online/cl_ol_0016.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CL_OL_0016] 원장 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "MESSAGE", "해당 처리키 원장 없음"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0016 status select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + h_status[1] = '\0'; + amount_format_won(h_amount, won, sizeof(won)); + + /* 3. 이미 확정(S)인 경우 경고 응답 (멱등 처리) */ + if (strcmp(h_status, CL_ST_SETTLED) == 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0016] 이미 확정 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_sets(ctx->out, "CONFIRMED", "Y"); + tx_buf_sets(ctx->out, "MESSAGE", "이미 마감확정된 건"); + tx_return(ctx, TX_OK, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* 4. 확정 대상은 처리완료(M) 만 허용 */ + if (strcmp(h_status, CL_ST_DONE) != 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0016] 확정 불가 상태 key=%s status=%s", + key, h_status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "CONFIRMED", "N"); + tx_buf_sets(ctx->out, "MESSAGE", "처리완료(M) 건만 확정 가능"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + /* 5. 확정 트랜잭션 : M → S 단건 UPDATE */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0016] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cl_ledger set status = 'S' , upd_ts = now ( ) where key = $1 and status = 'M'", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 109 "app/online/cl_ol_0016.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0016 confirm update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + /* 조회~갱신 사이 상태 변동 (경합) → 롤백 후 경고 */ + tx_log(TX_LOG_WARN, "[CL_OL_0016] 확정 대상 소실(경합) key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "CONFIRMED", "N"); + tx_buf_sets(ctx->out, "MESSAGE", "확정 대상 상태 변동으로 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + tx_commit(); + + /* 6. 응답 구성 */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0016] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "STATUS", CL_ST_SETTLED); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_sets(ctx->out, "AMOUNTWON", won); + tx_buf_sets(ctx->out, "CONFIRMED", "Y"); + tx_log(TX_LOG_INFO, "[CL_OL_0016] 마감확정 완료 key=%s M→S 금액=%ld", + key, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0016.pgc b/app/online/cl_ol_0016.pgc index 616ccc7..5eb6919 100644 --- a/app/online/cl_ol_0016.pgc +++ b/app/online/cl_ol_0016.pgc @@ -2,13 +2,16 @@ /* * cl_ol_0016.pgc - 마감 온라인 서비스 (CL_OL_0016) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 확정. 요청 처리키(KEY) 단건에 대해 현재 상태를 확인한 뒤 처리완료(M) + * 건을 정산완료(S)로 확정한다. 이미 정산완료(S)면 경고로 응답하고, 확정 + * 대상이 아닌 상태(R/U)이면 확정 불가로 응답한다. (singleton SELECT 후 + * 단건 UPDATE; tx_begin/tx_commit) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,55 +19,126 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0016, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0016] 마감 등록 서비스 진입"); + char key[16]; + char won[40]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0016] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[CL_OL_0016] 마감 확정 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0016] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0016] 영업일 오류 date=%s", bizdate); + /* 1. 요청 검증 : 처리키 필수 */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0016] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0016] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2. 현재 상태/금액 단건 조회 (PK singleton SELECT) */ + EXEC SQL SELECT status, amount + INTO :h_status, :h_amount + FROM cl_ledger + WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CL_OL_0016] 원장 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "MESSAGE", "해당 처리키 원장 없음"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0016 status select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + h_status[1] = '\0'; + amount_format_won(h_amount, won, sizeof(won)); + + /* 3. 이미 확정(S)인 경우 경고 응답 (멱등 처리) */ + if (strcmp(h_status, CL_ST_SETTLED) == 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0016] 이미 확정 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_sets(ctx->out, "CONFIRMED", "Y"); + tx_buf_sets(ctx->out, "MESSAGE", "이미 마감확정된 건"); + tx_return(ctx, TX_OK, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* 4. 확정 대상은 처리완료(M) 만 허용 */ + if (strcmp(h_status, CL_ST_DONE) != 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0016] 확정 불가 상태 key=%s status=%s", + key, h_status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "CONFIRMED", "N"); + tx_buf_sets(ctx->out, "MESSAGE", "처리완료(M) 건만 확정 가능"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + /* 5. 확정 트랜잭션 : M → S 단건 UPDATE */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0016] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE cl_ledger + SET status = 'S', upd_ts = now() + WHERE key = :h_key + AND status = 'M'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CL_OL_0016 confirm update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + /* 조회~갱신 사이 상태 변동 (경합) → 롤백 후 경고 */ + tx_log(TX_LOG_WARN, "[CL_OL_0016] 확정 대상 소실(경합) key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "CONFIRMED", "N"); + tx_buf_sets(ctx->out, "MESSAGE", "확정 대상 상태 변동으로 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + tx_commit(); + + /* 6. 응답 구성 */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0016] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "STATUS", CL_ST_SETTLED); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_sets(ctx->out, "AMOUNTWON", won); + tx_buf_sets(ctx->out, "CONFIRMED", "Y"); + tx_log(TX_LOG_INFO, "[CL_OL_0016] 마감확정 완료 key=%s M→S 금액=%ld", + key, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0017.c b/app/online/cl_ol_0017.c index 6d8a9d8..093f8c9 100644 --- a/app/online/cl_ol_0017.c +++ b/app/online/cl_ol_0017.c @@ -10,13 +10,23 @@ /* * cl_ol_0017.pgc - 마감 온라인 서비스 (CL_OL_0017) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 일마감 검증 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)에 대해 아직 마감되지 않은 미마감(R) 건이 하나도 + * 없는지 확인하여 일마감 가능 여부를 판정한다. 미마감 건수가 0 이면 + * 해당 영업일은 일마감 유효(VALID=Y), 1건이라도 남아 있으면 미마감 + * 잔량이 존재하므로 일마감 불가(VALID=N)로 응답한다. + * + * 검증의 판단 근거를 함께 제공하기 위해 상태별 건수(R/M/U/S)를 모두 + * 수집하여 응답하고, 마감 확정 시 정산이 이루어질 예정영업일(다음 + * 영업일)을 계산하여 참고 필드로 반환한다. 요청 감사추적을 위해 + * 선택 입력 REQID/OPER 를 로그와 응답에 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +99,102 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0017.pgc" +#line 25 "app/online/cl_ol_0017.pgc" TX_SERVICE(CL_OL_0017, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + char bizdate[16]; + char reqid[32]; + char oper[32]; + char nextbiz[9]; + char summary[TX_VAL_LEN]; + long recv_cnt, done_cnt, fail_cnt, settled_cnt; + long total_cnt, processed_cnt, complete_rate; + int valid; - tx_log(TX_LOG_INFO, "[CL_OL_0017] 마감 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CL_OL_0017] 일마감 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0017] KEY 누락"); + /* 1) 필수 입력 수신 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0017] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0017] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 감사추적용 선택 입력 (없으면 기본값) */ + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + if (tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) + strncpy(oper, "BATCH", sizeof(oper) - 1); + + /* 2) 영업일 형식 검증 */ + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0017] 영업일 형식 오류 date=%s reqid=%s", + bizdate, reqid); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 3) 상태별 건수 수집 - 표준 DBIO 채널 이용 (read-only) */ + recv_cnt = cl_rec_count(bizdate, CL_ST_RECV); + done_cnt = cl_rec_count(bizdate, CL_ST_DONE); + fail_cnt = cl_rec_count(bizdate, CL_ST_FAIL); + settled_cnt = cl_rec_count(bizdate, CL_ST_SETTLED); + + if (recv_cnt < 0 || done_cnt < 0 || fail_cnt < 0 || settled_cnt < 0) { + tx_log(TX_LOG_ERROR, "[CL_OL_0017] 상태별 건수 조회 실패 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + total_cnt = recv_cnt + done_cnt + fail_cnt + settled_cnt; + + /* 4) 일마감 유효성 판정: 미마감 잔량이 0 이어야 유효 */ + valid = (recv_cnt == 0) ? 1 : 0; + + /* 5) 처리 진행률 = (마감 대상에서 미마감을 제외한 비율) */ + processed_cnt = done_cnt + fail_cnt + settled_cnt; + complete_rate = (total_cnt > 0) ? (processed_cnt * 100 / total_cnt) : 100; + + /* 6) 마감 확정 시 정산 예정영업일 = 다음 영업일 */ + if (date_next_business(bizdate, nextbiz) != 0) { + memcpy(nextbiz, bizdate, 8); + nextbiz[8] = '\0'; + } + + /* 7) 판정 요약 문구 구성 */ + snprintf(summary, sizeof(summary), + "영업일 %s 마감대상 %ld건 중 미마감 %ld건 (처리율 %ld%%)", + bizdate, total_cnt, recv_cnt, complete_rate); + + /* 8) 응답 구성 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0017] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "OPER", oper); + tx_buf_setlong(ctx->out, "RECVCNT", recv_cnt); + tx_buf_setlong(ctx->out, "DONECNT", done_cnt); + tx_buf_setlong(ctx->out, "FAILCNT", fail_cnt); + tx_buf_setlong(ctx->out, "SETTLEDCNT", settled_cnt); + tx_buf_setlong(ctx->out, "TOTALCNT", total_cnt); + tx_buf_setlong(ctx->out, "PROCESSEDCNT", processed_cnt); + tx_buf_setlong(ctx->out, "COMPLETERATE", complete_rate); + tx_buf_sets(ctx->out, "NEXTBIZ", nextbiz); + tx_buf_sets(ctx->out, "SUMMARY", summary); + tx_buf_sets(ctx->out, "VALID", valid ? "Y" : "N"); + tx_buf_sets(ctx->out, "MESSAGE", + valid ? "일마감 가능(미마감 없음)" : "미마감 잔량 존재로 일마감 불가"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0017] 완료 date=%s R=%ld M=%ld U=%ld S=%ld valid=%s reqid=%s", + bizdate, recv_cnt, done_cnt, fail_cnt, settled_cnt, + valid ? "Y" : "N", reqid); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0017.pgc b/app/online/cl_ol_0017.pgc index 7e3d69e..f439909 100644 --- a/app/online/cl_ol_0017.pgc +++ b/app/online/cl_ol_0017.pgc @@ -2,13 +2,23 @@ /* * cl_ol_0017.pgc - 마감 온라인 서비스 (CL_OL_0017) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 일마감 검증 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)에 대해 아직 마감되지 않은 미마감(R) 건이 하나도 + * 없는지 확인하여 일마감 가능 여부를 판정한다. 미마감 건수가 0 이면 + * 해당 영업일은 일마감 유효(VALID=Y), 1건이라도 남아 있으면 미마감 + * 잔량이 존재하므로 일마감 불가(VALID=N)로 응답한다. + * + * 검증의 판단 근거를 함께 제공하기 위해 상태별 건수(R/M/U/S)를 모두 + * 수집하여 응답하고, 마감 확정 시 정산이 이루어질 예정영업일(다음 + * 영업일)을 계산하여 참고 필드로 반환한다. 요청 감사추적을 위해 + * 선택 입력 REQID/OPER 를 로그와 응답에 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,32 +26,97 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0017, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + char bizdate[16]; + char reqid[32]; + char oper[32]; + char nextbiz[9]; + char summary[TX_VAL_LEN]; + long recv_cnt, done_cnt, fail_cnt, settled_cnt; + long total_cnt, processed_cnt, complete_rate; + int valid; - tx_log(TX_LOG_INFO, "[CL_OL_0017] 마감 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CL_OL_0017] 일마감 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0017] KEY 누락"); + /* 1) 필수 입력 수신 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0017] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0017] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 감사추적용 선택 입력 (없으면 기본값) */ + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + if (tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) + strncpy(oper, "BATCH", sizeof(oper) - 1); + + /* 2) 영업일 형식 검증 */ + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0017] 영업일 형식 오류 date=%s reqid=%s", + bizdate, reqid); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 3) 상태별 건수 수집 - 표준 DBIO 채널 이용 (read-only) */ + recv_cnt = cl_rec_count(bizdate, CL_ST_RECV); + done_cnt = cl_rec_count(bizdate, CL_ST_DONE); + fail_cnt = cl_rec_count(bizdate, CL_ST_FAIL); + settled_cnt = cl_rec_count(bizdate, CL_ST_SETTLED); + + if (recv_cnt < 0 || done_cnt < 0 || fail_cnt < 0 || settled_cnt < 0) { + tx_log(TX_LOG_ERROR, "[CL_OL_0017] 상태별 건수 조회 실패 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + total_cnt = recv_cnt + done_cnt + fail_cnt + settled_cnt; + + /* 4) 일마감 유효성 판정: 미마감 잔량이 0 이어야 유효 */ + valid = (recv_cnt == 0) ? 1 : 0; + + /* 5) 처리 진행률 = (마감 대상에서 미마감을 제외한 비율) */ + processed_cnt = done_cnt + fail_cnt + settled_cnt; + complete_rate = (total_cnt > 0) ? (processed_cnt * 100 / total_cnt) : 100; + + /* 6) 마감 확정 시 정산 예정영업일 = 다음 영업일 */ + if (date_next_business(bizdate, nextbiz) != 0) { + memcpy(nextbiz, bizdate, 8); + nextbiz[8] = '\0'; + } + + /* 7) 판정 요약 문구 구성 */ + snprintf(summary, sizeof(summary), + "영업일 %s 마감대상 %ld건 중 미마감 %ld건 (처리율 %ld%%)", + bizdate, total_cnt, recv_cnt, complete_rate); + + /* 8) 응답 구성 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0017] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "OPER", oper); + tx_buf_setlong(ctx->out, "RECVCNT", recv_cnt); + tx_buf_setlong(ctx->out, "DONECNT", done_cnt); + tx_buf_setlong(ctx->out, "FAILCNT", fail_cnt); + tx_buf_setlong(ctx->out, "SETTLEDCNT", settled_cnt); + tx_buf_setlong(ctx->out, "TOTALCNT", total_cnt); + tx_buf_setlong(ctx->out, "PROCESSEDCNT", processed_cnt); + tx_buf_setlong(ctx->out, "COMPLETERATE", complete_rate); + tx_buf_sets(ctx->out, "NEXTBIZ", nextbiz); + tx_buf_sets(ctx->out, "SUMMARY", summary); + tx_buf_sets(ctx->out, "VALID", valid ? "Y" : "N"); + tx_buf_sets(ctx->out, "MESSAGE", + valid ? "일마감 가능(미마감 없음)" : "미마감 잔량 존재로 일마감 불가"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0017] 완료 date=%s R=%ld M=%ld U=%ld S=%ld valid=%s reqid=%s", + bizdate, recv_cnt, done_cnt, fail_cnt, settled_cnt, + valid ? "Y" : "N", reqid); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0018.c b/app/online/cl_ol_0018.c index 1edb355..0cb6a48 100644 --- a/app/online/cl_ol_0018.c +++ b/app/online/cl_ol_0018.c @@ -10,13 +10,18 @@ /* * cl_ol_0018.pgc - 마감 온라인 서비스 (CL_OL_0018) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 집계 반영: 요청 영업일(BIZDATE)의 원장 전체를 Σamount/count 로 + * 집계한 뒤, 그 결과를 집계요약행으로 원장에 반영한다. 요약행은 + * merch_id='*AGG', key=BIZDATE+"AG", status='S'(정산완료) 로 식별하며 + * amount 에 합계금액, fee 에 집계건수를 적재한다. 이미 요약행이 있으면 + * 재집계값으로 UPDATE, 없으면 INSERT 하는 멱등 upsert 로 처리한다. + * 집계 조회 + 반영은 하나의 트랜잭션으로 커밋한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,88 +94,155 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0018.pgc" +#line 20 "app/online/cl_ol_0018.pgc" TX_SERVICE(CL_OL_0018, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + + +#line 25 "app/online/cl_ol_0018.pgc" + char h_biz_date [ 9 ] ; + +#line 26 "app/online/cl_ol_0018.pgc" + char h_agg_key [ 16 ] ; + +#line 27 "app/online/cl_ol_0018.pgc" + long h_sum ; + +#line 28 "app/online/cl_ol_0018.pgc" + int h_cnt ; + +#line 29 "app/online/cl_ol_0018.pgc" + int h_exist ; +/* exec sql end declare section */ +#line 30 "app/online/cl_ol_0018.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0018] 마감 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0018] 필수 필드 누락"); + char bizdate[16]; + + tx_log(TX_LOG_INFO, "[CL_OL_0018] 마감 집계 반영 서비스 진입"); + + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0018] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0018] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0018] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + /* 집계 요약행 키 = 영업일 + "AG" (실집계 대상에서 요약행은 제외) */ + snprintf(h_agg_key, sizeof(h_agg_key), "%.8sAG", h_biz_date); + + /* 2) 트랜잭션 시작 */ if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0018] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 3) 당일 실거래 집계 (요약행 '*AGG' 는 제외) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) from cl_ledger where biz_date = $1 and merch_id <> '*AGG'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 72 "app/online/cl_ol_0018.pgc" - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0018] 원장반영 실패 rc=%d", rc); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0018 집계"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + /* 4) 기존 집계요약행 존재 여부 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where key = $1 ", + ECPGt_char,(h_agg_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_exist),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 87 "app/online/cl_ol_0018.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0018 요약존재확인"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CL_OL_0018] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); + /* 5) 멱등 upsert: 있으면 갱신, 없으면 삽입 (금액=합계, 수수료=건수) */ + if (h_exist > 0) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cl_ledger set amount = $1 , fee = $2 , status = 'S' , upd_ts = now ( ) where key = $3 ", + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_agg_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 102 "app/online/cl_ol_0018.pgc" - /* 교차 검증: 당일 합계 재계산 */ - if (cl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; + } else { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into cl_ledger ( key , merch_id , card_no , amount , biz_date , status , fee ) values ( $1 , '*AGG' , '*AGGREGATE*' , $2 , $3 , 'S' , $4 )", + ECPGt_char,(h_agg_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 107 "app/online/cl_ol_0018.pgc" + + } + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0018 요약반영"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_commit(); + /* 6) 응답 구성 */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CL_OL_0018] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "AGGKEY", h_agg_key); + tx_buf_setlong(ctx->out, "AGGCNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "AGGSUM", h_sum); + tx_buf_sets(ctx->out, "AGGMODE", h_exist > 0 ? "UPDATE" : "INSERT"); + + tx_log(TX_LOG_INFO, "[CL_OL_0018] 집계반영 완료 date=%s 건수=%d 합계=%ld mode=%s", + bizdate, h_cnt, h_sum, h_exist > 0 ? "UPDATE" : "INSERT"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0018.pgc b/app/online/cl_ol_0018.pgc index bc3dee9..69de4df 100644 --- a/app/online/cl_ol_0018.pgc +++ b/app/online/cl_ol_0018.pgc @@ -2,13 +2,18 @@ /* * cl_ol_0018.pgc - 마감 온라인 서비스 (CL_OL_0018) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 집계 반영: 요청 영업일(BIZDATE)의 원장 전체를 Σamount/count 로 + * 집계한 뒤, 그 결과를 집계요약행으로 원장에 반영한다. 요약행은 + * merch_id='*AGG', key=BIZDATE+"AG", status='S'(정산완료) 로 식별하며 + * amount 에 합계금액, fee 에 집계건수를 적재한다. 이미 요약행이 있으면 + * 재집계값으로 UPDATE, 없으면 INSERT 하는 멱등 upsert 로 처리한다. + * 집계 조회 + 반영은 하나의 트랜잭션으로 커밋한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,83 +21,113 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0018, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_agg_key[16]; + long h_sum; + int h_cnt; + int h_exist; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0018] 마감 트랜잭션 오케스트레이션 진입"); + char bizdate[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0018] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0018] 마감 집계 반영 서비스 진입"); + + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0018] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0018] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0018] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + /* 집계 요약행 키 = 영업일 + "AG" (실집계 대상에서 요약행은 제외) */ + snprintf(h_agg_key, sizeof(h_agg_key), "%.8sAG", h_biz_date); + + /* 2) 트랜잭션 시작 */ if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0018] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 3) 당일 실거래 집계 (요약행 '*AGG' 는 제외) */ + EXEC SQL SELECT coalesce(sum(amount), 0), count(*) + INTO :h_sum, :h_cnt + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND merch_id <> '*AGG'; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0018] 원장반영 실패 rc=%d", rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0018 집계"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + /* 4) 기존 집계요약행 존재 여부 확인 */ + EXEC SQL SELECT count(*) + INTO :h_exist + FROM cl_ledger + WHERE key = :h_agg_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0018 요약존재확인"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CL_OL_0018] 후속연계 경고 rc=%d", rc); + /* 5) 멱등 upsert: 있으면 갱신, 없으면 삽입 (금액=합계, 수수료=건수) */ + if (h_exist > 0) { + EXEC SQL UPDATE cl_ledger + SET amount = :h_sum, fee = :h_cnt, status = 'S', upd_ts = now() + WHERE key = :h_agg_key; + } else { + EXEC SQL INSERT INTO cl_ledger + (key, merch_id, card_no, amount, biz_date, status, fee) + VALUES + (:h_agg_key, '*AGG', '*AGGREGATE*', :h_sum, :h_biz_date, 'S', :h_cnt); } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - /* 교차 검증: 당일 합계 재계산 */ - if (cl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0018 요약반영"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_commit(); + /* 6) 응답 구성 */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CL_OL_0018] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "AGGKEY", h_agg_key); + tx_buf_setlong(ctx->out, "AGGCNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "AGGSUM", h_sum); + tx_buf_sets(ctx->out, "AGGMODE", h_exist > 0 ? "UPDATE" : "INSERT"); + + tx_log(TX_LOG_INFO, "[CL_OL_0018] 집계반영 완료 date=%s 건수=%d 합계=%ld mode=%s", + bizdate, h_cnt, h_sum, h_exist > 0 ? "UPDATE" : "INSERT"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0019.c b/app/online/cl_ol_0019.c index d5fccbc..a60ea7e 100644 --- a/app/online/cl_ol_0019.c +++ b/app/online/cl_ol_0019.c @@ -10,13 +10,20 @@ /* * cl_ol_0019.pgc - 마감 온라인 서비스 (CL_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 취소 승인 + * ---------------------------------------------------------------------- + * 요청 KEY 의 원장이 취소요청 상태(status='U')인지 확인한 뒤, 승인되면 + * 처리완료(status='M') 로 재개방 갱신한다. 대상이 취소요청 상태가 + * 아니면 승인 불가로 반려한다. 상태 조회 후 갱신은 하나의 트랜잭션으로 + * 커밋하며, 승인/반려 결과와 원거래 요약(가맹점/금액/영업일)을 응답에 + * 함께 실어 상위 화면이 감사표시를 할 수 있게 한다. 감사추적을 위해 + * 선택 입력 REQID/OPER 를 로그·응답에 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +96,110 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0019.pgc" +#line 22 "app/online/cl_ol_0019.pgc" TX_SERVICE(CL_OL_0019, ctx) { cl_rec_t rec; char key[16]; + char reqid[32]; + char oper[32]; + char won[32]; int rc; - tx_log(TX_LOG_INFO, "[CL_OL_0019] 마감 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CL_OL_0019] 마감 취소 승인 서비스 진입"); + /* 1) 입력 수신 */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[CL_OL_0019] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + if (tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) + strncpy(oper, "SUPERV", sizeof(oper) - 1); + /* 2) 현재 상태 조회 */ memset(&rec, 0, sizeof(rec)); rc = cl_rec_select(key, &rec); + if (rc == TX_ENOENT) { + tx_log(TX_LOG_WARN, "[CL_OL_0019] 대상없음 key=%s reqid=%s", key, reqid); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "APPROVED", "N"); + tx_buf_sets(ctx->out, "MESSAGE", "취소승인 대상 원장 없음"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_ERROR, "[CL_OL_0019] 조회 실패 key=%s rc=%d", key, rc); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, rc, ctx->out); return; } + /* 3) 취소요청(U) 상태만 승인 가능 */ + if (strcmp(rec.status, CL_ST_FAIL) != 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0019] 승인불가 상태 key=%s status=%s reqid=%s", + key, rec.status, reqid); + amount_format_won(rec.amount, won, sizeof(won)); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "APPROVED", "N"); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", rec.status); + tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_sets(ctx->out, "AMOUNTWON", won); + tx_buf_sets(ctx->out, "MESSAGE", "취소요청(U) 상태가 아니어서 승인 불가"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 4) 재개방 승인: U -> M 갱신 (트랜잭션) */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0019] 트랜잭션 시작 실패 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + rc = cl_rec_update_status(key, CL_ST_DONE); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0019] 승인갱신 실패 key=%s rc=%d", key, rc); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_buf_sets(ctx->out, "APPROVED", "N"); + tx_return(ctx, rc, ctx->out); + return; + } + + tx_commit(); + + /* 5) 승인 결과 응답 (원거래 요약 포함) */ + amount_format_won(rec.amount, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "OPER", oper); + tx_buf_sets(ctx->out, "APPROVED", "Y"); + tx_buf_sets(ctx->out, "PREVSTATUS", CL_ST_FAIL); + tx_buf_sets(ctx->out, "STATUS", CL_ST_DONE); tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_sets(ctx->out, "BIZDATE", rec.biz_date); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "AMOUNTWON", won); + tx_buf_sets(ctx->out, "MESSAGE", "마감 취소 승인(재개방) 완료"); + + tx_log(TX_LOG_INFO, "[CL_OL_0019] 취소 승인 완료 key=%s U->M oper=%s reqid=%s", + key, oper, reqid); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0019.pgc b/app/online/cl_ol_0019.pgc index 3ce30e3..95517cf 100644 --- a/app/online/cl_ol_0019.pgc +++ b/app/online/cl_ol_0019.pgc @@ -2,13 +2,20 @@ /* * cl_ol_0019.pgc - 마감 온라인 서비스 (CL_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 취소 승인 + * ---------------------------------------------------------------------- + * 요청 KEY 의 원장이 취소요청 상태(status='U')인지 확인한 뒤, 승인되면 + * 처리완료(status='M') 로 재개방 갱신한다. 대상이 취소요청 상태가 + * 아니면 승인 불가로 반려한다. 상태 조회 후 갱신은 하나의 트랜잭션으로 + * 커밋하며, 승인/반려 결과와 원거래 요약(가맹점/금액/영업일)을 응답에 + * 함께 실어 상위 화면이 감사표시를 할 수 있게 한다. 감사추적을 위해 + * 선택 입력 REQID/OPER 를 로그·응답에 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -18,30 +25,103 @@ TX_SERVICE(CL_OL_0019, ctx) { cl_rec_t rec; char key[16]; + char reqid[32]; + char oper[32]; + char won[32]; int rc; - tx_log(TX_LOG_INFO, "[CL_OL_0019] 마감 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CL_OL_0019] 마감 취소 승인 서비스 진입"); + /* 1) 입력 수신 */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[CL_OL_0019] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + if (tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) + strncpy(oper, "SUPERV", sizeof(oper) - 1); + /* 2) 현재 상태 조회 */ memset(&rec, 0, sizeof(rec)); rc = cl_rec_select(key, &rec); + if (rc == TX_ENOENT) { + tx_log(TX_LOG_WARN, "[CL_OL_0019] 대상없음 key=%s reqid=%s", key, reqid); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "APPROVED", "N"); + tx_buf_sets(ctx->out, "MESSAGE", "취소승인 대상 원장 없음"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_ERROR, "[CL_OL_0019] 조회 실패 key=%s rc=%d", key, rc); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, rc, ctx->out); return; } + /* 3) 취소요청(U) 상태만 승인 가능 */ + if (strcmp(rec.status, CL_ST_FAIL) != 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0019] 승인불가 상태 key=%s status=%s reqid=%s", + key, rec.status, reqid); + amount_format_won(rec.amount, won, sizeof(won)); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "APPROVED", "N"); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", rec.status); + tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_sets(ctx->out, "AMOUNTWON", won); + tx_buf_sets(ctx->out, "MESSAGE", "취소요청(U) 상태가 아니어서 승인 불가"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 4) 재개방 승인: U -> M 갱신 (트랜잭션) */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0019] 트랜잭션 시작 실패 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + rc = cl_rec_update_status(key, CL_ST_DONE); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0019] 승인갱신 실패 key=%s rc=%d", key, rc); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_buf_sets(ctx->out, "APPROVED", "N"); + tx_return(ctx, rc, ctx->out); + return; + } + + tx_commit(); + + /* 5) 승인 결과 응답 (원거래 요약 포함) */ + amount_format_won(rec.amount, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "OPER", oper); + tx_buf_sets(ctx->out, "APPROVED", "Y"); + tx_buf_sets(ctx->out, "PREVSTATUS", CL_ST_FAIL); + tx_buf_sets(ctx->out, "STATUS", CL_ST_DONE); tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_sets(ctx->out, "BIZDATE", rec.biz_date); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "AMOUNTWON", won); + tx_buf_sets(ctx->out, "MESSAGE", "마감 취소 승인(재개방) 완료"); + + tx_log(TX_LOG_INFO, "[CL_OL_0019] 취소 승인 완료 key=%s U->M oper=%s reqid=%s", + key, oper, reqid); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0020.c b/app/online/cl_ol_0020.c index f8bafea..46764dc 100644 --- a/app/online/cl_ol_0020.c +++ b/app/online/cl_ol_0020.c @@ -10,13 +10,23 @@ /* * cl_ol_0020.pgc - 마감 온라인 서비스 (CL_OL_0020) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 재마감 검증 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)에서 이미 정산완료(S)된 건수와, 마감처리 대상 + * 전체(M+U+S) 건수를 비교하여 재마감 필요 여부를 판정한다. 정산완료 + * 건수가 전체 대상 건수보다 적으면 아직 정산되지 않은 마감건이 남은 + * 것이므로 재마감이 필요(REDO=Y)하다. + * + * 추가로 미처리(R) 잔량과 정산완료 금액 합계를 함께 조회하여 재마감 + * 판단의 근거를 응답에 제공하고, 정산 진척률(%)을 산출한다. 모든 집계 + * 는 하나의 조회 트랜잭션 문맥에서 수행한다. 감사추적을 위해 선택 입력 + * REQID/OPER 를 로그·응답에 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,88 +99,163 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0020.pgc" +#line 25 "app/online/cl_ol_0020.pgc" TX_SERVICE(CL_OL_0020, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + + +#line 30 "app/online/cl_ol_0020.pgc" + char h_biz_date [ 9 ] ; + +#line 31 "app/online/cl_ol_0020.pgc" + int h_settled ; + +#line 32 "app/online/cl_ol_0020.pgc" + int h_total ; + +#line 33 "app/online/cl_ol_0020.pgc" + int h_recv ; + +#line 34 "app/online/cl_ol_0020.pgc" + long h_settled_sum ; +/* exec sql end declare section */ +#line 35 "app/online/cl_ol_0020.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0020] 마감 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0020] 필수 필드 누락"); + char bizdate[16]; + char reqid[32]; + char oper[32]; + char won[32]; + long remain; + long progress; + int redo; + + tx_log(TX_LOG_INFO, "[CL_OL_0020] 재마감 검증 서비스 진입"); + + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0020] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + if (tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) + strncpy(oper, "BATCH", sizeof(oper) - 1); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0020] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0020] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + /* 2) 정산완료(S) 건수 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_settled),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 76 "app/online/cl_ol_0020.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0020 정산건수"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 3) 마감대상 전체(M+U+S) 건수 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date = $1 and status in ( 'M' , 'U' , 'S' )", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_total),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 91 "app/online/cl_ol_0020.pgc" - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0020] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0020 전체건수"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + /* 4) 미처리(R) 잔량 및 정산완료 금액 합계 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date = $1 and status = 'R'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_recv),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 106 "app/online/cl_ol_0020.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0020 미처리건수"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CL_OL_0020] 후속연계 경고 rc=%d", rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settled_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 120 "app/online/cl_ol_0020.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0020 정산합계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - /* 교차 검증: 당일 합계 재계산 */ - if (cl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 5) 재마감 필요 판정: 정산완료 < 전체대상 이면 잔여 존재 */ + remain = (long)h_total - (long)h_settled; + redo = (remain > 0) ? 1 : 0; + progress = (h_total > 0) ? ((long)h_settled * 100 / h_total) : 0; + amount_format_won(h_settled_sum, won, sizeof(won)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CL_OL_0020] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "OPER", oper); + tx_buf_setlong(ctx->out, "SETTLEDCNT", (long)h_settled); + tx_buf_setlong(ctx->out, "TOTALCNT", (long)h_total); + tx_buf_setlong(ctx->out, "RECVCNT", (long)h_recv); + tx_buf_setlong(ctx->out, "REMAINCNT", remain); + tx_buf_setlong(ctx->out, "SETTLEDSUM", h_settled_sum); + tx_buf_sets(ctx->out, "SETTLEDSUMWON", won); + tx_buf_setlong(ctx->out, "PROGRESS", progress); + tx_buf_sets(ctx->out, "REDO", redo ? "Y" : "N"); + tx_buf_sets(ctx->out, "MESSAGE", + redo ? "미정산 마감건 존재 - 재마감 필요" + : "전건 정산완료 - 재마감 불필요"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0020] 완료 date=%s S=%d 전체=%d R=%d 잔여=%ld 진척=%ld%% redo=%s", + bizdate, h_settled, h_total, h_recv, remain, progress, + redo ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0020.pgc b/app/online/cl_ol_0020.pgc index deb08c0..dbc62c5 100644 --- a/app/online/cl_ol_0020.pgc +++ b/app/online/cl_ol_0020.pgc @@ -2,13 +2,23 @@ /* * cl_ol_0020.pgc - 마감 온라인 서비스 (CL_OL_0020) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 재마감 검증 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)에서 이미 정산완료(S)된 건수와, 마감처리 대상 + * 전체(M+U+S) 건수를 비교하여 재마감 필요 여부를 판정한다. 정산완료 + * 건수가 전체 대상 건수보다 적으면 아직 정산되지 않은 마감건이 남은 + * 것이므로 재마감이 필요(REDO=Y)하다. + * + * 추가로 미처리(R) 잔량과 정산완료 금액 합계를 함께 조회하여 재마감 + * 판단의 근거를 응답에 제공하고, 정산 진척률(%)을 산출한다. 모든 집계 + * 는 하나의 조회 트랜잭션 문맥에서 수행한다. 감사추적을 위해 선택 입력 + * REQID/OPER 를 로그·응답에 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,83 +26,133 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0020, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + int h_settled; + int h_total; + int h_recv; + long h_settled_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0020] 마감 트랜잭션 오케스트레이션 진입"); + char bizdate[16]; + char reqid[32]; + char oper[32]; + char won[32]; + long remain; + long progress; + int redo; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0020] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0020] 재마감 검증 서비스 진입"); + + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0020] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + if (tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) + strncpy(oper, "BATCH", sizeof(oper) - 1); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0020] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0020] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + /* 2) 정산완료(S) 건수 조회 */ + EXEC SQL SELECT count(*) + INTO :h_settled + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'S'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0020 정산건수"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 3) 마감대상 전체(M+U+S) 건수 조회 */ + EXEC SQL SELECT count(*) + INTO :h_total + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status IN ('M', 'U', 'S'); - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0020] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0020 전체건수"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + /* 4) 미처리(R) 잔량 및 정산완료 금액 합계 조회 */ + EXEC SQL SELECT count(*) + INTO :h_recv + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'R'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0020 미처리건수"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CL_OL_0020] 후속연계 경고 rc=%d", rc); + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_settled_sum + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'S'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0020 정산합계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - /* 교차 검증: 당일 합계 재계산 */ - if (cl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 5) 재마감 필요 판정: 정산완료 < 전체대상 이면 잔여 존재 */ + remain = (long)h_total - (long)h_settled; + redo = (remain > 0) ? 1 : 0; + progress = (h_total > 0) ? ((long)h_settled * 100 / h_total) : 0; + amount_format_won(h_settled_sum, won, sizeof(won)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CL_OL_0020] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "OPER", oper); + tx_buf_setlong(ctx->out, "SETTLEDCNT", (long)h_settled); + tx_buf_setlong(ctx->out, "TOTALCNT", (long)h_total); + tx_buf_setlong(ctx->out, "RECVCNT", (long)h_recv); + tx_buf_setlong(ctx->out, "REMAINCNT", remain); + tx_buf_setlong(ctx->out, "SETTLEDSUM", h_settled_sum); + tx_buf_sets(ctx->out, "SETTLEDSUMWON", won); + tx_buf_setlong(ctx->out, "PROGRESS", progress); + tx_buf_sets(ctx->out, "REDO", redo ? "Y" : "N"); + tx_buf_sets(ctx->out, "MESSAGE", + redo ? "미정산 마감건 존재 - 재마감 필요" + : "전건 정산완료 - 재마감 불필요"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0020] 완료 date=%s S=%d 전체=%d R=%d 잔여=%ld 진척=%ld%% redo=%s", + bizdate, h_settled, h_total, h_recv, remain, progress, + redo ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0021.c b/app/online/cl_ol_0021.c index ab83683..2278a9d 100644 --- a/app/online/cl_ol_0021.c +++ b/app/online/cl_ol_0021.c @@ -10,13 +10,22 @@ /* * cl_ol_0021.pgc - 마감 온라인 서비스 (CL_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 잔액검증 재수행 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)의 정산완료(S) 합계금액과 당일 전체 합계금액을 + * 각각 산출한 뒤, 그 차액을 아직 정산되지 않은 미마감분 잔액으로 + * 응답한다. 차액이 0 이면 잔액검증 일치(MATCH=Y). + * + * 금액 차이의 근거를 제공하기 위해 정산완료 건수/전체 건수도 함께 + * 조회하여 미정산 건수를 도출하고, 각 금액을 원화 표기로 변환하여 + * 응답에 함께 싣는다. 감사추적을 위해 선택 입력 REQID/OPER 를 로그· + * 응답에 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +98,138 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0021.pgc" +#line 24 "app/online/cl_ol_0021.pgc" TX_SERVICE(CL_OL_0021, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 29 "app/online/cl_ol_0021.pgc" + char h_biz_date [ 9 ] ; + +#line 30 "app/online/cl_ol_0021.pgc" + long h_settled_sum ; + +#line 31 "app/online/cl_ol_0021.pgc" + int h_settled_cnt ; + +#line 32 "app/online/cl_ol_0021.pgc" + int h_total_cnt ; +/* exec sql end declare section */ +#line 33 "app/online/cl_ol_0021.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0021] 마감 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0021] KEY 누락"); + char bizdate[16]; + char reqid[32]; + char oper[32]; + char diff_won[32]; + char settled_won[32]; + char total_won[32]; + long total_sum = 0; + long diff; + long unsettled_cnt; + + tx_log(TX_LOG_INFO, "[CL_OL_0021] 잔액검증 재수행 서비스 진입"); + + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0021] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + if (tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) + strncpy(oper, "BATCH", sizeof(oper) - 1); + + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0021] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + /* 2) 정산완료(S) 합계금액 + 건수 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) from cl_ledger where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settled_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_settled_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 76 "app/online/cl_ol_0021.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0021 정산합계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3) 당일 전체 건수 조회 (요약행 제외) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date = $1 and merch_id <> '*AGG'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_total_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 91 "app/online/cl_ol_0021.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0021 전체건수"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 4) 당일 전체 합계금액 조회 (표준 DBIO) */ + if (cl_rec_sum(bizdate, &total_sum) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0021] 전체합계 조회 실패 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 5) 차액 = 미마감분 잔액, 미정산 건수 도출 */ + diff = total_sum - h_settled_sum; + unsettled_cnt = (long)h_total_cnt - (long)h_settled_cnt; + amount_format_won(diff, diff_won, sizeof(diff_won)); + amount_format_won(h_settled_sum, settled_won, sizeof(settled_won)); + amount_format_won(total_sum, total_won, sizeof(total_won)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "OPER", oper); + tx_buf_setlong(ctx->out, "SETTLEDSUM", h_settled_sum); + tx_buf_sets(ctx->out, "SETTLEDSUMWON", settled_won); + tx_buf_setlong(ctx->out, "TOTALSUM", total_sum); + tx_buf_sets(ctx->out, "TOTALSUMWON", total_won); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "DIFFWON", diff_won); + tx_buf_setlong(ctx->out, "SETTLEDCNT", (long)h_settled_cnt); + tx_buf_setlong(ctx->out, "TOTALCNT", (long)h_total_cnt); + tx_buf_setlong(ctx->out, "UNSETTLEDCNT", unsettled_cnt); + tx_buf_sets(ctx->out, "MATCH", (diff == 0) ? "Y" : "N"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0021] 완료 date=%s 정산=%ld 전체=%ld 차액=%ld 미정산건=%ld", + bizdate, h_settled_sum, total_sum, diff, unsettled_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0021.pgc b/app/online/cl_ol_0021.pgc index 985fb5b..e057b5b 100644 --- a/app/online/cl_ol_0021.pgc +++ b/app/online/cl_ol_0021.pgc @@ -2,13 +2,22 @@ /* * cl_ol_0021.pgc - 마감 온라인 서비스 (CL_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 잔액검증 재수행 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)의 정산완료(S) 합계금액과 당일 전체 합계금액을 + * 각각 산출한 뒤, 그 차액을 아직 정산되지 않은 미마감분 잔액으로 + * 응답한다. 차액이 0 이면 잔액검증 일치(MATCH=Y). + * + * 금액 차이의 근거를 제공하기 위해 정산완료 건수/전체 건수도 함께 + * 조회하여 미정산 건수를 도출하고, 각 금액을 원화 표기로 변환하여 + * 응답에 함께 싣는다. 감사추적을 위해 선택 입력 REQID/OPER 를 로그· + * 응답에 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,32 +25,113 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0021, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + long h_settled_sum; + int h_settled_cnt; + int h_total_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0021] 마감 단건조회 서비스 진입"); + char bizdate[16]; + char reqid[32]; + char oper[32]; + char diff_won[32]; + char settled_won[32]; + char total_won[32]; + long total_sum = 0; + long diff; + long unsettled_cnt; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0021] KEY 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0021] 잔액검증 재수행 서비스 진입"); + + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0021] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + if (tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) + strncpy(oper, "BATCH", sizeof(oper) - 1); + + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0021] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + /* 2) 정산완료(S) 합계금액 + 건수 조회 */ + EXEC SQL SELECT coalesce(sum(amount), 0), count(*) + INTO :h_settled_sum, :h_settled_cnt + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'S'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0021 정산합계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3) 당일 전체 건수 조회 (요약행 제외) */ + EXEC SQL SELECT count(*) + INTO :h_total_cnt + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND merch_id <> '*AGG'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0021 전체건수"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 4) 당일 전체 합계금액 조회 (표준 DBIO) */ + if (cl_rec_sum(bizdate, &total_sum) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0021] 전체합계 조회 실패 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 5) 차액 = 미마감분 잔액, 미정산 건수 도출 */ + diff = total_sum - h_settled_sum; + unsettled_cnt = (long)h_total_cnt - (long)h_settled_cnt; + amount_format_won(diff, diff_won, sizeof(diff_won)); + amount_format_won(h_settled_sum, settled_won, sizeof(settled_won)); + amount_format_won(total_sum, total_won, sizeof(total_won)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "OPER", oper); + tx_buf_setlong(ctx->out, "SETTLEDSUM", h_settled_sum); + tx_buf_sets(ctx->out, "SETTLEDSUMWON", settled_won); + tx_buf_setlong(ctx->out, "TOTALSUM", total_sum); + tx_buf_sets(ctx->out, "TOTALSUMWON", total_won); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "DIFFWON", diff_won); + tx_buf_setlong(ctx->out, "SETTLEDCNT", (long)h_settled_cnt); + tx_buf_setlong(ctx->out, "TOTALCNT", (long)h_total_cnt); + tx_buf_setlong(ctx->out, "UNSETTLEDCNT", unsettled_cnt); + tx_buf_sets(ctx->out, "MATCH", (diff == 0) ? "Y" : "N"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0021] 완료 date=%s 정산=%ld 전체=%ld 차액=%ld 미정산건=%ld", + bizdate, h_settled_sum, total_sum, diff, unsettled_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0022.c b/app/online/cl_ol_0022.c index 707a681..0561022 100644 --- a/app/online/cl_ol_0022.c +++ b/app/online/cl_ol_0022.c @@ -10,13 +10,22 @@ /* * cl_ol_0022.pgc - 마감 온라인 서비스 (CL_OL_0022) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 스냅샷 조회 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)의 마감 스냅샷 요약행을 key=BIZDATE+"SS" 로 표준 + * DBIO(cl_rec_select) 를 통해 조회한다. 스냅샷 요약행은 마감 시점의 + * 합계(amount)/건수(fee) 를 담고 있으며, 없으면 스냅샷 미생성으로 보아 + * ENOENT(FOUND=N) 를 응답한다. + * + * 스냅샷이 존재하면 현재 원장의 실집계(합계/건수)를 재산출하여 스냅샷 + * 시점 대비 변동분(DRIFT)을 함께 제공한다. 변동이 0 이면 스냅샷과 + * 현행이 일치(FRESH=Y). 감사추적을 위해 선택 입력 REQID 를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +98,119 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0022.pgc" +#line 24 "app/online/cl_ol_0022.pgc" TX_SERVICE(CL_OL_0022, ctx) { + /* exec sql begin declare section */ + + + + +#line 29 "app/online/cl_ol_0022.pgc" + char h_biz_date [ 9 ] ; + +#line 30 "app/online/cl_ol_0022.pgc" + long h_live_sum ; + +#line 31 "app/online/cl_ol_0022.pgc" + int h_live_cnt ; +/* exec sql end declare section */ +#line 32 "app/online/cl_ol_0022.pgc" + + cl_rec_t rec; - char key[16]; - int rc; + char bizdate[16]; + char reqid[32]; + char snap_key[16]; + char snap_won[32]; + char drift_won[32]; + long drift; - tx_log(TX_LOG_INFO, "[CL_OL_0022] 마감 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CL_OL_0022] 스냅샷 조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0022] KEY 누락"); + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0022] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0022] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2) 스냅샷 요약행 키 구성 = 영업일 + "SS" */ + snprintf(snap_key, sizeof(snap_key), "%.8sSS", bizdate); + + /* 3) 스냅샷 요약행 조회 */ memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0022] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (cl_rec_select(snap_key, &rec) == TX_ENOENT) { + tx_log(TX_LOG_WARN, "[CL_OL_0022] 스냅샷 없음 key=%s", snap_key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "SNAPKEY", snap_key); + tx_buf_sets(ctx->out, "FOUND", "N"); + tx_buf_sets(ctx->out, "MESSAGE", "마감 스냅샷 미생성"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + /* 4) 현행 실집계 재산출 (요약행 제외) */ + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) from cl_ledger where biz_date = $1 and merch_id <> '*AGG'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_live_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_live_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 88 "app/online/cl_ol_0022.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0022 현행집계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 5) 스냅샷 대비 변동분 산출 (amount=합계, fee=건수) */ + drift = h_live_sum - rec.amount; + amount_format_won(rec.amount, snap_won, sizeof(snap_won)); + amount_format_won(drift, drift_won, sizeof(drift_won)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "SNAPKEY", snap_key); + tx_buf_sets(ctx->out, "FOUND", "Y"); + tx_buf_setlong(ctx->out, "SNAPSUM", rec.amount); + tx_buf_sets(ctx->out, "SNAPSUMWON", snap_won); + tx_buf_setlong(ctx->out, "SNAPCNT", rec.fee); + tx_buf_setlong(ctx->out, "LIVESUM", h_live_sum); + tx_buf_setlong(ctx->out, "LIVECNT", (long)h_live_cnt); + tx_buf_setlong(ctx->out, "DRIFT", drift); + tx_buf_sets(ctx->out, "DRIFTWON", drift_won); + tx_buf_sets(ctx->out, "FRESH", (drift == 0) ? "Y" : "N"); tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0022] 조회완료 key=%s amount=%ld", key, rec.amount); + + tx_log(TX_LOG_INFO, + "[CL_OL_0022] 스냅샷 조회완료 key=%s 스냅=%ld 현행=%ld 변동=%ld", + snap_key, rec.amount, h_live_sum, drift); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0022.pgc b/app/online/cl_ol_0022.pgc index 00f290c..1848908 100644 --- a/app/online/cl_ol_0022.pgc +++ b/app/online/cl_ol_0022.pgc @@ -2,13 +2,22 @@ /* * cl_ol_0022.pgc - 마감 온라인 서비스 (CL_OL_0022) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 스냅샷 조회 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)의 마감 스냅샷 요약행을 key=BIZDATE+"SS" 로 표준 + * DBIO(cl_rec_select) 를 통해 조회한다. 스냅샷 요약행은 마감 시점의 + * 합계(amount)/건수(fee) 를 담고 있으며, 없으면 스냅샷 미생성으로 보아 + * ENOENT(FOUND=N) 를 응답한다. + * + * 스냅샷이 존재하면 현재 원장의 실집계(합계/건수)를 재산출하여 스냅샷 + * 시점 대비 변동분(DRIFT)을 함께 제공한다. 변동이 0 이면 스냅샷과 + * 현행이 일치(FRESH=Y). 감사추적을 위해 선택 입력 REQID 를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,32 +25,99 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0022, ctx) { + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + long h_live_sum; + int h_live_cnt; + EXEC SQL END DECLARE SECTION; + cl_rec_t rec; - char key[16]; - int rc; + char bizdate[16]; + char reqid[32]; + char snap_key[16]; + char snap_won[32]; + char drift_won[32]; + long drift; - tx_log(TX_LOG_INFO, "[CL_OL_0022] 마감 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CL_OL_0022] 스냅샷 조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0022] KEY 누락"); + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0022] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0022] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2) 스냅샷 요약행 키 구성 = 영업일 + "SS" */ + snprintf(snap_key, sizeof(snap_key), "%.8sSS", bizdate); + + /* 3) 스냅샷 요약행 조회 */ memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0022] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (cl_rec_select(snap_key, &rec) == TX_ENOENT) { + tx_log(TX_LOG_WARN, "[CL_OL_0022] 스냅샷 없음 key=%s", snap_key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "SNAPKEY", snap_key); + tx_buf_sets(ctx->out, "FOUND", "N"); + tx_buf_sets(ctx->out, "MESSAGE", "마감 스냅샷 미생성"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + /* 4) 현행 실집계 재산출 (요약행 제외) */ + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + EXEC SQL SELECT coalesce(sum(amount), 0), count(*) + INTO :h_live_sum, :h_live_cnt + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND merch_id <> '*AGG'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0022 현행집계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 5) 스냅샷 대비 변동분 산출 (amount=합계, fee=건수) */ + drift = h_live_sum - rec.amount; + amount_format_won(rec.amount, snap_won, sizeof(snap_won)); + amount_format_won(drift, drift_won, sizeof(drift_won)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "SNAPKEY", snap_key); + tx_buf_sets(ctx->out, "FOUND", "Y"); + tx_buf_setlong(ctx->out, "SNAPSUM", rec.amount); + tx_buf_sets(ctx->out, "SNAPSUMWON", snap_won); + tx_buf_setlong(ctx->out, "SNAPCNT", rec.fee); + tx_buf_setlong(ctx->out, "LIVESUM", h_live_sum); + tx_buf_setlong(ctx->out, "LIVECNT", (long)h_live_cnt); + tx_buf_setlong(ctx->out, "DRIFT", drift); + tx_buf_sets(ctx->out, "DRIFTWON", drift_won); + tx_buf_sets(ctx->out, "FRESH", (drift == 0) ? "Y" : "N"); tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0022] 조회완료 key=%s amount=%ld", key, rec.amount); + + tx_log(TX_LOG_INFO, + "[CL_OL_0022] 스냅샷 조회완료 key=%s 스냅=%ld 현행=%ld 변동=%ld", + snap_key, rec.amount, h_live_sum, drift); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0023.c b/app/online/cl_ol_0023.c index 2224842..91d2e61 100644 --- a/app/online/cl_ol_0023.c +++ b/app/online/cl_ol_0023.c @@ -10,13 +10,23 @@ /* * cl_ol_0023.pgc - 마감 온라인 서비스 (CL_OL_0023) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채번 조회 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)에 대해 현재까지 마감처리완료(M)된 건수를 채번 + * 소진수로 보고, 다음 부여 순번(=소진수+1)을 산출하여 응답한다. 원장을 + * 변경하지 않는 read-only 조회이며, 응답의 다음순번은 배치/온라인 채번 + * 발급의 힌트로 사용된다. + * + * 소진 현황의 근거로 접수(R)/정산완료(S) 건수도 함께 조회하여 잔여 + * 발급 여력(가용 채번 = 상한 - 소진)을 계산한다. 상한(CAP)은 선택 입력 + * 으로 받되 미지정 시 일 100만 건을 기본 상한으로 적용한다. 감사추적을 + * 위해 선택 입력 REQID/CHANNEL 을 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,60 +99,102 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0023.pgc" +#line 25 "app/online/cl_ol_0023.pgc" +#define CL_OL_0023_DEFAULT_CAP 1000000L + TX_SERVICE(CL_OL_0023, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char bizdate[16]; + char reqid[32]; + char channel[16]; + char nextseq[16]; + char summary[TX_VAL_LEN]; + long done_cnt, recv_cnt, settled_cnt; + long next_no; + long cap = 0; + long avail, usage_rate; - tx_log(TX_LOG_INFO, "[CL_OL_0023] 마감 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[CL_OL_0023] 채번 조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0023] 필수 필드 누락"); + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0023] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + if (tx_buf_get(ctx->in, "CHANNEL", channel, sizeof(channel)) != TX_OK) + strncpy(channel, "ON", sizeof(channel) - 1); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0023] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0023] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[CL_OL_0023] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + /* 채번 상한 보정 */ + tx_buf_getlong(ctx->in, "CAP", &cap); + if (cap <= 0) + cap = CL_OL_0023_DEFAULT_CAP; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0023] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 상태별 건수 조회 (표준 DBIO, read-only) */ + done_cnt = cl_rec_count(bizdate, CL_ST_DONE); + recv_cnt = cl_rec_count(bizdate, CL_ST_RECV); + settled_cnt = cl_rec_count(bizdate, CL_ST_SETTLED); + + if (done_cnt < 0 || recv_cnt < 0 || settled_cnt < 0) { + tx_log(TX_LOG_ERROR, "[CL_OL_0023] 채번 소진수 조회 실패 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* + * 3) 다음 순번 산출 및 채번 키 구성(YYYYMMDD + 6자리 일련) + * - 소진수(done_cnt)는 이미 마감완료(M)로 확정된 건수이므로 + * 다음 발급 순번은 소진수 + 1 이 된다. + * - 가용 여력(avail)은 일 상한(cap)에서 소진수를 뺀 값이며 + * 음수가 되지 않도록 0 하한으로 보정한다. + */ + next_no = done_cnt + 1; + avail = (cap > done_cnt) ? (cap - done_cnt) : 0; + usage_rate = (cap > 0) ? (done_cnt * 100 / cap) : 0; + snprintf(nextseq, sizeof(nextseq), "%.8s%06ld", bizdate, next_no); + + /* 4) 채번 현황 요약 문구 */ + snprintf(summary, sizeof(summary), + "영업일 %s 채번 소진 %ld / 상한 %ld (소진율 %ld%%), 다음순번 %ld", + bizdate, done_cnt, cap, usage_rate, next_no); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0023] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "CHANNEL", channel); + tx_buf_setlong(ctx->out, "USEDCNT", done_cnt); + tx_buf_setlong(ctx->out, "RECVCNT", recv_cnt); + tx_buf_setlong(ctx->out, "SETTLEDCNT", settled_cnt); + tx_buf_setlong(ctx->out, "CAP", cap); + tx_buf_setlong(ctx->out, "AVAIL", avail); + tx_buf_setlong(ctx->out, "USAGERATE", usage_rate); + tx_buf_setlong(ctx->out, "NEXTNO", next_no); + tx_buf_sets(ctx->out, "NEXTSEQ", nextseq); + tx_buf_sets(ctx->out, "SUMMARY", summary); + tx_buf_sets(ctx->out, "EXHAUSTED", (avail == 0) ? "Y" : "N"); + /* 발급 여력 임계(잔여 상한의 10% 미만) 사전 경고 플래그 */ + tx_buf_sets(ctx->out, "LOWALERT", + (cap > 0 && avail * 10 < cap) ? "Y" : "N"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0023] 완료 date=%s 소진=%ld 다음순번=%ld 가용=%ld ch=%s", + bizdate, done_cnt, next_no, avail, channel); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0023.pgc b/app/online/cl_ol_0023.pgc index 4a395cf..ebafe95 100644 --- a/app/online/cl_ol_0023.pgc +++ b/app/online/cl_ol_0023.pgc @@ -2,69 +2,121 @@ /* * cl_ol_0023.pgc - 마감 온라인 서비스 (CL_OL_0023) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채번 조회 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)에 대해 현재까지 마감처리완료(M)된 건수를 채번 + * 소진수로 보고, 다음 부여 순번(=소진수+1)을 산출하여 응답한다. 원장을 + * 변경하지 않는 read-only 조회이며, 응답의 다음순번은 배치/온라인 채번 + * 발급의 힌트로 사용된다. + * + * 소진 현황의 근거로 접수(R)/정산완료(S) 건수도 함께 조회하여 잔여 + * 발급 여력(가용 채번 = 상한 - 소진)을 계산한다. 상한(CAP)은 선택 입력 + * 으로 받되 미지정 시 일 100만 건을 기본 상한으로 적용한다. 감사추적을 + * 위해 선택 입력 REQID/CHANNEL 을 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" EXEC SQL INCLUDE sqlca; +#define CL_OL_0023_DEFAULT_CAP 1000000L + TX_SERVICE(CL_OL_0023, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char bizdate[16]; + char reqid[32]; + char channel[16]; + char nextseq[16]; + char summary[TX_VAL_LEN]; + long done_cnt, recv_cnt, settled_cnt; + long next_no; + long cap = 0; + long avail, usage_rate; - tx_log(TX_LOG_INFO, "[CL_OL_0023] 마감 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[CL_OL_0023] 채번 조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0023] 필수 필드 누락"); + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0023] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + if (tx_buf_get(ctx->in, "CHANNEL", channel, sizeof(channel)) != TX_OK) + strncpy(channel, "ON", sizeof(channel) - 1); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0023] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0023] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[CL_OL_0023] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + /* 채번 상한 보정 */ + tx_buf_getlong(ctx->in, "CAP", &cap); + if (cap <= 0) + cap = CL_OL_0023_DEFAULT_CAP; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0023] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 상태별 건수 조회 (표준 DBIO, read-only) */ + done_cnt = cl_rec_count(bizdate, CL_ST_DONE); + recv_cnt = cl_rec_count(bizdate, CL_ST_RECV); + settled_cnt = cl_rec_count(bizdate, CL_ST_SETTLED); + + if (done_cnt < 0 || recv_cnt < 0 || settled_cnt < 0) { + tx_log(TX_LOG_ERROR, "[CL_OL_0023] 채번 소진수 조회 실패 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* + * 3) 다음 순번 산출 및 채번 키 구성(YYYYMMDD + 6자리 일련) + * - 소진수(done_cnt)는 이미 마감완료(M)로 확정된 건수이므로 + * 다음 발급 순번은 소진수 + 1 이 된다. + * - 가용 여력(avail)은 일 상한(cap)에서 소진수를 뺀 값이며 + * 음수가 되지 않도록 0 하한으로 보정한다. + */ + next_no = done_cnt + 1; + avail = (cap > done_cnt) ? (cap - done_cnt) : 0; + usage_rate = (cap > 0) ? (done_cnt * 100 / cap) : 0; + snprintf(nextseq, sizeof(nextseq), "%.8s%06ld", bizdate, next_no); + + /* 4) 채번 현황 요약 문구 */ + snprintf(summary, sizeof(summary), + "영업일 %s 채번 소진 %ld / 상한 %ld (소진율 %ld%%), 다음순번 %ld", + bizdate, done_cnt, cap, usage_rate, next_no); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0023] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "CHANNEL", channel); + tx_buf_setlong(ctx->out, "USEDCNT", done_cnt); + tx_buf_setlong(ctx->out, "RECVCNT", recv_cnt); + tx_buf_setlong(ctx->out, "SETTLEDCNT", settled_cnt); + tx_buf_setlong(ctx->out, "CAP", cap); + tx_buf_setlong(ctx->out, "AVAIL", avail); + tx_buf_setlong(ctx->out, "USAGERATE", usage_rate); + tx_buf_setlong(ctx->out, "NEXTNO", next_no); + tx_buf_sets(ctx->out, "NEXTSEQ", nextseq); + tx_buf_sets(ctx->out, "SUMMARY", summary); + tx_buf_sets(ctx->out, "EXHAUSTED", (avail == 0) ? "Y" : "N"); + /* 발급 여력 임계(잔여 상한의 10% 미만) 사전 경고 플래그 */ + tx_buf_sets(ctx->out, "LOWALERT", + (cap > 0 && avail * 10 < cap) ? "Y" : "N"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0023] 완료 date=%s 소진=%ld 다음순번=%ld 가용=%ld ch=%s", + bizdate, done_cnt, next_no, avail, channel); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0024.c b/app/online/cl_ol_0024.c index 6d5182c..d4d2620 100644 --- a/app/online/cl_ol_0024.c +++ b/app/online/cl_ol_0024.c @@ -10,13 +10,21 @@ /* * cl_ol_0024.pgc - 마감 온라인 서비스 (CL_OL_0024) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 리포트 요약 + * ---------------------------------------------------------------------- + * 요청 가맹점(MERCHID) + 영업일(BIZDATE) 범위의 원장을 하나의 싱글턴 + * 집계 SELECT 로 건수/합계금액/합계수수료/최소·최대금액을 산출하고, + * 평균금액(정수 나눗셈, 0건 보호)과 실효 수수료율(bp, 만분율)을 + * 계산하여 요약 리포트로 응답한다. + * + * 금액 필드는 원화 표기를 병기하고, 감사추적을 위해 선택 입력 REQID 를 + * 반영한다. 대상 건이 없으면 건수 0 의 정상 리포트를 반환한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +97,139 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0024.pgc" +#line 23 "app/online/cl_ol_0024.pgc" TX_SERVICE(CL_OL_0024, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + + +#line 28 "app/online/cl_ol_0024.pgc" + char h_merch_id [ 16 ] ; + +#line 29 "app/online/cl_ol_0024.pgc" + char h_biz_date [ 9 ] ; + +#line 30 "app/online/cl_ol_0024.pgc" + int h_cnt ; + +#line 31 "app/online/cl_ol_0024.pgc" + long h_sum ; + +#line 32 "app/online/cl_ol_0024.pgc" + long h_fee ; + +#line 33 "app/online/cl_ol_0024.pgc" + long h_min ; + +#line 34 "app/online/cl_ol_0024.pgc" + long h_max ; +/* exec sql end declare section */ +#line 35 "app/online/cl_ol_0024.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0024] 마감 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0024] KEY 누락"); + char merch[16]; + char bizdate[16]; + char reqid[32]; + char sum_won[32]; + char fee_won[32]; + char summary[TX_VAL_LEN]; + long avg_amt; + long fee_bp; + long net_amt; + + tx_log(TX_LOG_INFO, "[CL_OL_0024] 리포트 요약 서비스 진입"); + + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0024] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0024] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0024] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_merch_id, 0, sizeof(h_merch_id)); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + /* 2) 가맹점+영업일 싱글턴 집계 (건수/합계/수수료/최소·최대) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) , coalesce ( min ( amount ) , 0 ) , coalesce ( max ( amount ) , 0 ) from cl_ledger where merch_id = $1 and biz_date = $2 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_min),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_max),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 80 "app/online/cl_ol_0024.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0024 리포트집계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3) 평균금액 및 실효 수수료율(bp) 산출 (0 보호) */ + avg_amt = (h_cnt > 0) ? (h_sum / h_cnt) : 0; + fee_bp = (h_sum > 0) ? (h_fee * 10000 / h_sum) : 0; + net_amt = h_sum - h_fee; /* 수수료 차감 후 정산 예상 순액 */ + amount_format_won(h_sum, sum_won, sizeof(sum_won)); + amount_format_won(h_fee, fee_won, sizeof(fee_won)); + + /* 4) 리포트 요약 문구 구성 */ + snprintf(summary, sizeof(summary), + "가맹점 %s / %s : %d건, 합계 %s, 수수료 %s, 평균 %ld원", + merch, bizdate, h_cnt, sum_won, fee_won, avg_amt); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0024] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_setlong(ctx->out, "CNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "SUMAMT", h_sum); + tx_buf_sets(ctx->out, "SUMAMTWON", sum_won); + tx_buf_setlong(ctx->out, "SUMFEE", h_fee); + tx_buf_sets(ctx->out, "SUMFEEWON", fee_won); + tx_buf_setlong(ctx->out, "MINAMT", h_min); + tx_buf_setlong(ctx->out, "MAXAMT", h_max); + tx_buf_setlong(ctx->out, "AVGAMT", avg_amt); + tx_buf_setlong(ctx->out, "FEEBP", fee_bp); + tx_buf_setlong(ctx->out, "NETAMT", net_amt); + tx_buf_sets(ctx->out, "SUMMARY", summary); + + tx_log(TX_LOG_INFO, + "[CL_OL_0024] 완료 merch=%s date=%s 건수=%d 합계=%ld 평균=%ld 수수료율=%ldbp", + merch, bizdate, h_cnt, h_sum, avg_amt, fee_bp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0024.pgc b/app/online/cl_ol_0024.pgc index 1c5276d..152ce7a 100644 --- a/app/online/cl_ol_0024.pgc +++ b/app/online/cl_ol_0024.pgc @@ -2,13 +2,21 @@ /* * cl_ol_0024.pgc - 마감 온라인 서비스 (CL_OL_0024) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 리포트 요약 + * ---------------------------------------------------------------------- + * 요청 가맹점(MERCHID) + 영업일(BIZDATE) 범위의 원장을 하나의 싱글턴 + * 집계 SELECT 로 건수/합계금액/합계수수료/최소·최대금액을 산출하고, + * 평균금액(정수 나눗셈, 0건 보호)과 실효 수수료율(bp, 만분율)을 + * 계산하여 요약 리포트로 응답한다. + * + * 금액 필드는 원화 표기를 병기하고, 감사추적을 위해 선택 입력 REQID 를 + * 반영한다. 대상 건이 없으면 건수 0 의 정상 리포트를 반환한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,32 +24,100 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0024, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_biz_date[9]; + int h_cnt; + long h_sum; + long h_fee; + long h_min; + long h_max; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0024] 마감 단건조회 서비스 진입"); + char merch[16]; + char bizdate[16]; + char reqid[32]; + char sum_won[32]; + char fee_won[32]; + char summary[TX_VAL_LEN]; + long avg_amt; + long fee_bp; + long net_amt; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0024] KEY 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0024] 리포트 요약 서비스 진입"); + + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0024] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0024] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0024] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_merch_id, 0, sizeof(h_merch_id)); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + /* 2) 가맹점+영업일 싱글턴 집계 (건수/합계/수수료/최소·최대) */ + EXEC SQL SELECT count(*), coalesce(sum(amount), 0), coalesce(sum(fee), 0), + coalesce(min(amount), 0), coalesce(max(amount), 0) + INTO :h_cnt, :h_sum, :h_fee, :h_min, :h_max + FROM cl_ledger + WHERE merch_id = :h_merch_id + AND biz_date = :h_biz_date; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0024 리포트집계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3) 평균금액 및 실효 수수료율(bp) 산출 (0 보호) */ + avg_amt = (h_cnt > 0) ? (h_sum / h_cnt) : 0; + fee_bp = (h_sum > 0) ? (h_fee * 10000 / h_sum) : 0; + net_amt = h_sum - h_fee; /* 수수료 차감 후 정산 예상 순액 */ + amount_format_won(h_sum, sum_won, sizeof(sum_won)); + amount_format_won(h_fee, fee_won, sizeof(fee_won)); + + /* 4) 리포트 요약 문구 구성 */ + snprintf(summary, sizeof(summary), + "가맹점 %s / %s : %d건, 합계 %s, 수수료 %s, 평균 %ld원", + merch, bizdate, h_cnt, sum_won, fee_won, avg_amt); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0024] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_setlong(ctx->out, "CNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "SUMAMT", h_sum); + tx_buf_sets(ctx->out, "SUMAMTWON", sum_won); + tx_buf_setlong(ctx->out, "SUMFEE", h_fee); + tx_buf_sets(ctx->out, "SUMFEEWON", fee_won); + tx_buf_setlong(ctx->out, "MINAMT", h_min); + tx_buf_setlong(ctx->out, "MAXAMT", h_max); + tx_buf_setlong(ctx->out, "AVGAMT", avg_amt); + tx_buf_setlong(ctx->out, "FEEBP", fee_bp); + tx_buf_setlong(ctx->out, "NETAMT", net_amt); + tx_buf_sets(ctx->out, "SUMMARY", summary); + + tx_log(TX_LOG_INFO, + "[CL_OL_0024] 완료 merch=%s date=%s 건수=%d 합계=%ld 평균=%ld 수수료율=%ldbp", + merch, bizdate, h_cnt, h_sum, avg_amt, fee_bp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0025.c b/app/online/cl_ol_0025.c index 29b3081..bdf4b06 100644 --- a/app/online/cl_ol_0025.c +++ b/app/online/cl_ol_0025.c @@ -10,13 +10,22 @@ /* * cl_ol_0025.pgc - 마감 온라인 서비스 (CL_OL_0025) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 알림 재발송 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)의 미마감(R) 건수와 불일치(U) 건수를 각각 개별 + * 조회한 뒤, 두 값을 바탕으로 마감 지연/불일치 알림 메시지를 구성하여 + * ALERT 유형 응답으로 반환한다. 미마감·불일치가 모두 0 이면 + * 정상(알림불필요)으로 표시한다. + * + * 미마감/불일치 규모에 따라 심각도(SEVERITY: NONE/WARN/CRIT)를 부여 + * 하고, 정산완료(S) 건수를 함께 조회하여 진행 맥락을 알림에 포함한다. + * 재발송 대상 수신처(TARGET)와 감사추적 REQID 는 선택 입력으로 받는다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,60 +98,105 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0025.pgc" +#line 24 "app/online/cl_ol_0025.pgc" +#define CL_OL_0025_CRIT_THRESHOLD 10 + TX_SERVICE(CL_OL_0025, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char bizdate[16]; + char reqid[32]; + char target[32]; + char alert[TX_VAL_LEN]; + const char *severity; + long recv_cnt, fail_cnt, settled_cnt, done_cnt; + long pending, total_cnt; + int need_alert; - tx_log(TX_LOG_INFO, "[CL_OL_0025] 마감 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[CL_OL_0025] 알림 재발송 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0025] 필수 필드 누락"); + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0025] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + if (tx_buf_get(ctx->in, "TARGET", target, sizeof(target)) != TX_OK) + strncpy(target, "OPS-DESK", sizeof(target) - 1); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0025] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0025] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[CL_OL_0025] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0025] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 미마감(R)/불일치(U)/정산완료(S) 건수 개별 조회 */ + recv_cnt = cl_rec_count(bizdate, CL_ST_RECV); + fail_cnt = cl_rec_count(bizdate, CL_ST_FAIL); + settled_cnt = cl_rec_count(bizdate, CL_ST_SETTLED); + done_cnt = cl_rec_count(bizdate, CL_ST_DONE); + if (recv_cnt < 0 || fail_cnt < 0 || settled_cnt < 0 || done_cnt < 0) { + tx_log(TX_LOG_ERROR, "[CL_OL_0025] 건수 조회 실패 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + total_cnt = recv_cnt + done_cnt + fail_cnt + settled_cnt; - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* + * 3) 심각도 판정 및 알림 메시지 구성 + * - pending(미마감+불일치)이 0 이면 알림 불필요(NONE) + * - 임계(CRIT_THRESHOLD) 이상이면 즉시 조치 대상(CRIT) + * - 그 외 잔량이 있으면 주의(WARN) + */ + pending = recv_cnt + fail_cnt; + need_alert = (pending > 0) ? 1 : 0; + if (pending == 0) + severity = "NONE"; + else if (pending >= CL_OL_0025_CRIT_THRESHOLD) + severity = "CRIT"; + else + severity = "WARN"; + + if (need_alert) + snprintf(alert, sizeof(alert), + "[마감알림/%s] %s 미마감 %ld건, 불일치 %ld건 (정산완료 %ld건) - 확인 요망", + severity, bizdate, recv_cnt, fail_cnt, settled_cnt); + else + snprintf(alert, sizeof(alert), + "[마감알림/%s] %s 마감 정상완료(정산완료 %ld건) - 조치 불필요", + severity, bizdate, settled_cnt); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0025] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "TYPE", "ALERT"); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "TARGET", target); + tx_buf_setlong(ctx->out, "RECVCNT", recv_cnt); + tx_buf_setlong(ctx->out, "FAILCNT", fail_cnt); + tx_buf_setlong(ctx->out, "DONECNT", done_cnt); + tx_buf_setlong(ctx->out, "SETTLEDCNT", settled_cnt); + tx_buf_setlong(ctx->out, "TOTALCNT", total_cnt); + tx_buf_setlong(ctx->out, "PENDING", pending); + tx_buf_sets(ctx->out, "SEVERITY", severity); + tx_buf_sets(ctx->out, "NEEDALERT", need_alert ? "Y" : "N"); + tx_buf_sets(ctx->out, "ALERTMSG", alert); + /* 미마감 우세/불일치 우세 구분 (조치 우선순위 힌트) */ + tx_buf_sets(ctx->out, "FOCUS", + (fail_cnt > recv_cnt) ? "UNMATCH" : "PENDING"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0025] 완료 date=%s R=%ld U=%ld sev=%s 발송=%s target=%s", + bizdate, recv_cnt, fail_cnt, severity, + need_alert ? "Y" : "N", target); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0025.pgc b/app/online/cl_ol_0025.pgc index 892b31b..245bc59 100644 --- a/app/online/cl_ol_0025.pgc +++ b/app/online/cl_ol_0025.pgc @@ -2,69 +2,123 @@ /* * cl_ol_0025.pgc - 마감 온라인 서비스 (CL_OL_0025) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 알림 재발송 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)의 미마감(R) 건수와 불일치(U) 건수를 각각 개별 + * 조회한 뒤, 두 값을 바탕으로 마감 지연/불일치 알림 메시지를 구성하여 + * ALERT 유형 응답으로 반환한다. 미마감·불일치가 모두 0 이면 + * 정상(알림불필요)으로 표시한다. + * + * 미마감/불일치 규모에 따라 심각도(SEVERITY: NONE/WARN/CRIT)를 부여 + * 하고, 정산완료(S) 건수를 함께 조회하여 진행 맥락을 알림에 포함한다. + * 재발송 대상 수신처(TARGET)와 감사추적 REQID 는 선택 입력으로 받는다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" EXEC SQL INCLUDE sqlca; +#define CL_OL_0025_CRIT_THRESHOLD 10 + TX_SERVICE(CL_OL_0025, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char bizdate[16]; + char reqid[32]; + char target[32]; + char alert[TX_VAL_LEN]; + const char *severity; + long recv_cnt, fail_cnt, settled_cnt, done_cnt; + long pending, total_cnt; + int need_alert; - tx_log(TX_LOG_INFO, "[CL_OL_0025] 마감 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[CL_OL_0025] 알림 재발송 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0025] 필수 필드 누락"); + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0025] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + if (tx_buf_get(ctx->in, "TARGET", target, sizeof(target)) != TX_OK) + strncpy(target, "OPS-DESK", sizeof(target) - 1); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0025] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0025] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[CL_OL_0025] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0025] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 미마감(R)/불일치(U)/정산완료(S) 건수 개별 조회 */ + recv_cnt = cl_rec_count(bizdate, CL_ST_RECV); + fail_cnt = cl_rec_count(bizdate, CL_ST_FAIL); + settled_cnt = cl_rec_count(bizdate, CL_ST_SETTLED); + done_cnt = cl_rec_count(bizdate, CL_ST_DONE); + if (recv_cnt < 0 || fail_cnt < 0 || settled_cnt < 0 || done_cnt < 0) { + tx_log(TX_LOG_ERROR, "[CL_OL_0025] 건수 조회 실패 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + total_cnt = recv_cnt + done_cnt + fail_cnt + settled_cnt; - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* + * 3) 심각도 판정 및 알림 메시지 구성 + * - pending(미마감+불일치)이 0 이면 알림 불필요(NONE) + * - 임계(CRIT_THRESHOLD) 이상이면 즉시 조치 대상(CRIT) + * - 그 외 잔량이 있으면 주의(WARN) + */ + pending = recv_cnt + fail_cnt; + need_alert = (pending > 0) ? 1 : 0; + if (pending == 0) + severity = "NONE"; + else if (pending >= CL_OL_0025_CRIT_THRESHOLD) + severity = "CRIT"; + else + severity = "WARN"; + + if (need_alert) + snprintf(alert, sizeof(alert), + "[마감알림/%s] %s 미마감 %ld건, 불일치 %ld건 (정산완료 %ld건) - 확인 요망", + severity, bizdate, recv_cnt, fail_cnt, settled_cnt); + else + snprintf(alert, sizeof(alert), + "[마감알림/%s] %s 마감 정상완료(정산완료 %ld건) - 조치 불필요", + severity, bizdate, settled_cnt); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0025] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "TYPE", "ALERT"); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "TARGET", target); + tx_buf_setlong(ctx->out, "RECVCNT", recv_cnt); + tx_buf_setlong(ctx->out, "FAILCNT", fail_cnt); + tx_buf_setlong(ctx->out, "DONECNT", done_cnt); + tx_buf_setlong(ctx->out, "SETTLEDCNT", settled_cnt); + tx_buf_setlong(ctx->out, "TOTALCNT", total_cnt); + tx_buf_setlong(ctx->out, "PENDING", pending); + tx_buf_sets(ctx->out, "SEVERITY", severity); + tx_buf_sets(ctx->out, "NEEDALERT", need_alert ? "Y" : "N"); + tx_buf_sets(ctx->out, "ALERTMSG", alert); + /* 미마감 우세/불일치 우세 구분 (조치 우선순위 힌트) */ + tx_buf_sets(ctx->out, "FOCUS", + (fail_cnt > recv_cnt) ? "UNMATCH" : "PENDING"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0025] 완료 date=%s R=%ld U=%ld sev=%s 발송=%s target=%s", + bizdate, recv_cnt, fail_cnt, severity, + need_alert ? "Y" : "N", target); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0026.c b/app/online/cl_ol_0026.c index 6a4a202..7c742f0 100644 --- a/app/online/cl_ol_0026.c +++ b/app/online/cl_ol_0026.c @@ -10,13 +10,16 @@ /* * cl_ol_0026.pgc - 마감 온라인 서비스 (CL_OL_0026) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 미마감 목록: 요청 영업일(BIZDATE)에서 미마감(R)/불일치(U) 상태의 + * 원장을 커서(cur_cl_ol_0026)로 순회하여 상위 N건의 처리키를 응답 + * 필드(KEY1..KEYN)로 나열하고, 총 미마감 대상 건수를 함께 반환한다. + * N(LIMIT)은 입력으로 받되 1..20 범위로 보정한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +92,133 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0026.pgc" +#line 18 "app/online/cl_ol_0026.pgc" +#define CL_OL_0026_MAX 20 + TX_SERVICE(CL_OL_0026, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 25 "app/online/cl_ol_0026.pgc" + char h_biz_date [ 9 ] ; + +#line 26 "app/online/cl_ol_0026.pgc" + char h_key [ 16 ] ; + +#line 27 "app/online/cl_ol_0026.pgc" + int h_total ; +/* exec sql end declare section */ +#line 28 "app/online/cl_ol_0026.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0026] 마감 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0026] KEY 누락"); + char bizdate[16]; + char fld[16]; + long limit = 0; + int listed = 0; + int cur_open = 0; + + tx_log(TX_LOG_INFO, "[CL_OL_0026] 미마감 목록 서비스 진입"); + + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0026] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0026] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0026] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* LIMIT 보정 (1..MAX) */ + tx_buf_getlong(ctx->in, "LIMIT", &limit); + if (limit <= 0) + limit = 10; + if (limit > CL_OL_0026_MAX) + limit = CL_OL_0026_MAX; + + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + /* 2) 총 미마감(R,U) 건수 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date = $1 and status in ( 'R' , 'U' )", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_total),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 69 "app/online/cl_ol_0026.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0026 총건수"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0026] 조회완료 key=%s amount=%ld", key, rec.amount); + + /* 3) 커서로 상위 N건 처리키 나열 */ + ECPGset_var( 0, ( h_biz_date ), __LINE__);\ + /* declare cur_cl_ol_0026 cursor for select key from cl_ledger where biz_date = $1 and status in ( 'R' , 'U' ) order by key */ +#line 87 "app/online/cl_ol_0026.pgc" + +#line 87 "app/online/cl_ol_0026.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cl_ol_0026 cursor for select key from cl_ledger where biz_date = $1 and status in ( 'R' , 'U' ) order by key", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 89 "app/online/cl_ol_0026.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0026 OPEN"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + cur_open = 1; + + while (listed < (int)limit) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cl_ol_0026", ECPGt_EOIT, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 99 "app/online/cl_ol_0026.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0026 FETCH"); + break; + } + listed++; + snprintf(fld, sizeof(fld), "KEY%d", listed); + tx_buf_sets(ctx->out, fld, h_key); + } + + if (cur_open) + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cl_ol_0026", ECPGt_EOIT, ECPGt_EORT);} +#line 112 "app/online/cl_ol_0026.pgc" + + + /* 4) 응답 요약 */ + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "TOTALCNT", (long)h_total); + tx_buf_setlong(ctx->out, "LISTED", (long)listed); + + tx_log(TX_LOG_INFO, "[CL_OL_0026] 완료 date=%s 총미마감=%d 목록=%d", + bizdate, h_total, listed); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0026.pgc b/app/online/cl_ol_0026.pgc index 6c2c88c..56cea49 100644 --- a/app/online/cl_ol_0026.pgc +++ b/app/online/cl_ol_0026.pgc @@ -2,46 +2,122 @@ /* * cl_ol_0026.pgc - 마감 온라인 서비스 (CL_OL_0026) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 미마감 목록: 요청 영업일(BIZDATE)에서 미마감(R)/불일치(U) 상태의 + * 원장을 커서(cur_cl_ol_0026)로 순회하여 상위 N건의 처리키를 응답 + * 필드(KEY1..KEYN)로 나열하고, 총 미마감 대상 건수를 함께 반환한다. + * N(LIMIT)은 입력으로 받되 1..20 범위로 보정한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" EXEC SQL INCLUDE sqlca; +#define CL_OL_0026_MAX 20 + TX_SERVICE(CL_OL_0026, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_key[16]; + int h_total; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0026] 마감 단건조회 서비스 진입"); + char bizdate[16]; + char fld[16]; + long limit = 0; + int listed = 0; + int cur_open = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0026] KEY 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0026] 미마감 목록 서비스 진입"); + + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0026] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0026] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0026] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* LIMIT 보정 (1..MAX) */ + tx_buf_getlong(ctx->in, "LIMIT", &limit); + if (limit <= 0) + limit = 10; + if (limit > CL_OL_0026_MAX) + limit = CL_OL_0026_MAX; + + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + /* 2) 총 미마감(R,U) 건수 */ + EXEC SQL SELECT count(*) + INTO :h_total + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status IN ('R', 'U'); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0026 총건수"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0026] 조회완료 key=%s amount=%ld", key, rec.amount); + + /* 3) 커서로 상위 N건 처리키 나열 */ + EXEC SQL DECLARE cur_cl_ol_0026 CURSOR FOR + SELECT key + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status IN ('R', 'U') + ORDER BY key; + + EXEC SQL OPEN cur_cl_ol_0026; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0026 OPEN"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + cur_open = 1; + + while (listed < (int)limit) { + EXEC SQL FETCH cur_cl_ol_0026 INTO :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0026 FETCH"); + break; + } + listed++; + snprintf(fld, sizeof(fld), "KEY%d", listed); + tx_buf_sets(ctx->out, fld, h_key); + } + + if (cur_open) + EXEC SQL CLOSE cur_cl_ol_0026; + + /* 4) 응답 요약 */ + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "TOTALCNT", (long)h_total); + tx_buf_setlong(ctx->out, "LISTED", (long)listed); + + tx_log(TX_LOG_INFO, "[CL_OL_0026] 완료 date=%s 총미마감=%d 목록=%d", + bizdate, h_total, listed); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0027.c b/app/online/cl_ol_0027.c index 9955f17..47d8f68 100644 --- a/app/online/cl_ol_0027.c +++ b/app/online/cl_ol_0027.c @@ -10,13 +10,22 @@ /* * cl_ol_0027.pgc - 마감 온라인 서비스 (CL_OL_0027) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 월마감 검증 + * ---------------------------------------------------------------------- + * 요청 월(YYYYMM)에 속한 모든 영업일의 미마감(R) 건이 하나도 없는지 + * 확인한다. C 에서 'YYYYMM%' LIKE 패턴을 구성하여 biz_date LIKE :pat + * AND status='R' 건수를 조회하고, 0 이면 월마감 유효(VALID=Y) 로 + * 응답한다. 입력월은 YYYYMM01 로 확장해 형식 검증한다. + * + * 월 전체 규모의 근거로 해당 월의 총 건수/합계금액과 마감 대상이 존재 + * 하는 영업일 수를 함께 조회하여 응답에 제공한다. 감사추적을 위해 선택 + * 입력 REQID 를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,60 +98,132 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0027.pgc" +#line 24 "app/online/cl_ol_0027.pgc" TX_SERVICE(CL_OL_0027, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 29 "app/online/cl_ol_0027.pgc" + char h_pat [ 16 ] ; + +#line 30 "app/online/cl_ol_0027.pgc" + int h_recv ; + +#line 31 "app/online/cl_ol_0027.pgc" + int h_total ; + +#line 32 "app/online/cl_ol_0027.pgc" + int h_days ; + +#line 33 "app/online/cl_ol_0027.pgc" + long h_sum ; +/* exec sql end declare section */ +#line 34 "app/online/cl_ol_0027.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0027] 마감 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0027] 필수 필드 누락"); + char yyyymm[16]; + char reqid[32]; + char probe[9]; + char sum_won[32]; + int valid; + + tx_log(TX_LOG_INFO, "[CL_OL_0027] 월마감 검증 서비스 진입"); + + /* 1) 입력 수신 (YYYYMM 6자리) */ + if (tx_buf_get(ctx->in, "YYYYMM", yyyymm, sizeof(yyyymm)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0027] YYYYMM 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0027] 금액 범위 오류 amount=%ld", amount); + /* 2) 형식 검증: 길이 6 + YYYYMM01 로 확장하여 일자 유효성 확인 */ + if (strlen(yyyymm) != 6) { + tx_log(TX_LOG_WARN, "[CL_OL_0027] 월 형식 오류 yyyymm=%s", yyyymm); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0027] 영업일 오류 date=%s", bizdate); + snprintf(probe, sizeof(probe), "%.6s01", yyyymm); + if (!date_is_valid(probe)) { + tx_log(TX_LOG_WARN, "[CL_OL_0027] 월 값 오류 yyyymm=%s", yyyymm); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + /* 3) LIKE 패턴 'YYYYMM%' 구성 */ + snprintf(h_pat, sizeof(h_pat), "%.6s%%", yyyymm); - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0027] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 4) 해당 월의 미마감(R) 건수 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date like $1 and status = 'R'", + ECPGt_char,(h_pat),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_recv),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 80 "app/online/cl_ol_0027.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0027 월미마감"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* 5) 월 전체 규모 및 대상 영업일 수 조회 (요약행 제외) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) , count ( distinct biz_date ) from cl_ledger where biz_date like $1 and merch_id <> '*AGG'", + ECPGt_char,(h_pat),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_total),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_days),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 95 "app/online/cl_ol_0027.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0027 월규모"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 6) 월마감 유효성 판정 */ + valid = (h_recv == 0) ? 1 : 0; + amount_format_won(h_sum, sum_won, sizeof(sum_won)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0027] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "YYYYMM", yyyymm); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_setlong(ctx->out, "RECVCNT", (long)h_recv); + tx_buf_setlong(ctx->out, "TOTALCNT", (long)h_total); + tx_buf_setlong(ctx->out, "DAYS", (long)h_days); + tx_buf_setlong(ctx->out, "SUMAMT", h_sum); + tx_buf_sets(ctx->out, "SUMAMTWON", sum_won); + tx_buf_sets(ctx->out, "VALID", valid ? "Y" : "N"); + tx_buf_sets(ctx->out, "MESSAGE", + valid ? "월마감 가능(미마감 없음)" : "월중 미마감 잔량 존재"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0027] 완료 yyyymm=%s 미마감=%d 총건=%d 영업일수=%d valid=%s", + yyyymm, h_recv, h_total, h_days, valid ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0027.pgc b/app/online/cl_ol_0027.pgc index 5d233db..c759bd7 100644 --- a/app/online/cl_ol_0027.pgc +++ b/app/online/cl_ol_0027.pgc @@ -2,13 +2,22 @@ /* * cl_ol_0027.pgc - 마감 온라인 서비스 (CL_OL_0027) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 월마감 검증 + * ---------------------------------------------------------------------- + * 요청 월(YYYYMM)에 속한 모든 영업일의 미마감(R) 건이 하나도 없는지 + * 확인한다. C 에서 'YYYYMM%' LIKE 패턴을 구성하여 biz_date LIKE :pat + * AND status='R' 건수를 조회하고, 0 이면 월마감 유효(VALID=Y) 로 + * 응답한다. 입력월은 YYYYMM01 로 확장해 형식 검증한다. + * + * 월 전체 규모의 근거로 해당 월의 총 건수/합계금액과 마감 대상이 존재 + * 하는 영업일 수를 함께 조회하여 응답에 제공한다. 감사추적을 위해 선택 + * 입력 REQID 를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,55 +25,102 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0027, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_pat[16]; + int h_recv; + int h_total; + int h_days; + long h_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0027] 마감 등록 서비스 진입"); + char yyyymm[16]; + char reqid[32]; + char probe[9]; + char sum_won[32]; + int valid; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0027] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0027] 월마감 검증 서비스 진입"); + + /* 1) 입력 수신 (YYYYMM 6자리) */ + if (tx_buf_get(ctx->in, "YYYYMM", yyyymm, sizeof(yyyymm)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0027] YYYYMM 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CL_OL_0027] 금액 범위 오류 amount=%ld", amount); + /* 2) 형식 검증: 길이 6 + YYYYMM01 로 확장하여 일자 유효성 확인 */ + if (strlen(yyyymm) != 6) { + tx_log(TX_LOG_WARN, "[CL_OL_0027] 월 형식 오류 yyyymm=%s", yyyymm); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0027] 영업일 오류 date=%s", bizdate); + snprintf(probe, sizeof(probe), "%.6s01", yyyymm); + if (!date_is_valid(probe)) { + tx_log(TX_LOG_WARN, "[CL_OL_0027] 월 값 오류 yyyymm=%s", yyyymm); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + /* 3) LIKE 패턴 'YYYYMM%' 구성 */ + snprintf(h_pat, sizeof(h_pat), "%.6s%%", yyyymm); - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0027] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 4) 해당 월의 미마감(R) 건수 조회 */ + EXEC SQL SELECT count(*) + INTO :h_recv + FROM cl_ledger + WHERE biz_date LIKE :h_pat + AND status = 'R'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0027 월미마감"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = cl_rec_count(bizdate, CL_ST_RECV); + /* 5) 월 전체 규모 및 대상 영업일 수 조회 (요약행 제외) */ + EXEC SQL SELECT count(*), coalesce(sum(amount), 0), count(distinct biz_date) + INTO :h_total, :h_sum, :h_days + FROM cl_ledger + WHERE biz_date LIKE :h_pat + AND merch_id <> '*AGG'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0027 월규모"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 6) 월마감 유효성 판정 */ + valid = (h_recv == 0) ? 1 : 0; + amount_format_won(h_sum, sum_won, sizeof(sum_won)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CL_OL_0027] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "YYYYMM", yyyymm); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_setlong(ctx->out, "RECVCNT", (long)h_recv); + tx_buf_setlong(ctx->out, "TOTALCNT", (long)h_total); + tx_buf_setlong(ctx->out, "DAYS", (long)h_days); + tx_buf_setlong(ctx->out, "SUMAMT", h_sum); + tx_buf_sets(ctx->out, "SUMAMTWON", sum_won); + tx_buf_sets(ctx->out, "VALID", valid ? "Y" : "N"); + tx_buf_sets(ctx->out, "MESSAGE", + valid ? "월마감 가능(미마감 없음)" : "월중 미마감 잔량 존재"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0027] 완료 yyyymm=%s 미마감=%d 총건=%d 영업일수=%d valid=%s", + yyyymm, h_recv, h_total, h_days, valid ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0028.c b/app/online/cl_ol_0028.c index 6ee1176..541911d 100644 --- a/app/online/cl_ol_0028.c +++ b/app/online/cl_ol_0028.c @@ -10,13 +10,22 @@ /* * cl_ol_0028.pgc - 마감 온라인 서비스 (CL_OL_0028) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 분기마감 검증 + * ---------------------------------------------------------------------- + * 요청 기간 시작일(D1)~종료일(D2) 범위에서 미마감(R) 건이 남아 있는지 + * 조회한다. biz_date BETWEEN :d1 AND :d2 AND status='R' 건수가 0 이면 + * 분기마감 가능(ABLE=Y) 으로 판정한다. 두 일자 모두 형식 검증하고 + * D1<=D2 순서를 확인한다. + * + * 분기 규모의 근거로 기간 내 전체 건수/정산완료 건수/합계금액과 대상 + * 영업일 수를 함께 조회하고, 정산 진척률(%)을 산출한다. 감사추적을 + * 위해 선택 입력 REQID 를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +98,147 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0028.pgc" +#line 24 "app/online/cl_ol_0028.pgc" TX_SERVICE(CL_OL_0028, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + + +#line 29 "app/online/cl_ol_0028.pgc" + char h_d1 [ 9 ] ; + +#line 30 "app/online/cl_ol_0028.pgc" + char h_d2 [ 9 ] ; + +#line 31 "app/online/cl_ol_0028.pgc" + int h_recv ; + +#line 32 "app/online/cl_ol_0028.pgc" + int h_total ; + +#line 33 "app/online/cl_ol_0028.pgc" + int h_settled ; + +#line 34 "app/online/cl_ol_0028.pgc" + int h_days ; + +#line 35 "app/online/cl_ol_0028.pgc" + long h_sum ; +/* exec sql end declare section */ +#line 36 "app/online/cl_ol_0028.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0028] 마감 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0028] KEY 누락"); + char d1[16]; + char d2[16]; + char reqid[32]; + char sum_won[32]; + long progress; + int able; + + tx_log(TX_LOG_INFO, "[CL_OL_0028] 분기마감 검증 서비스 진입"); + + /* 1) 입력 수신 */ + if (tx_buf_get(ctx->in, "D1", d1, sizeof(d1)) != TX_OK || + tx_buf_get(ctx->in, "D2", d2, sizeof(d2)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0028] D1/D2 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + + /* 2) 일자 형식 및 범위 순서 검증 */ + if (!date_is_valid(d1) || !date_is_valid(d2) || strcmp(d1, d2) > 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0028] 기간 오류 d1=%s d2=%s", d1, d2); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_d1, 0, sizeof(h_d1)); + memset(h_d2, 0, sizeof(h_d2)); + strncpy(h_d1, d1, sizeof(h_d1) - 1); + strncpy(h_d2, d2, sizeof(h_d2) - 1); + + /* 3) 기간 내 미마감(R) 건수 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cl_ledger where biz_date between $1 and $2 and status = 'R'", + ECPGt_char,(h_d1),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_d2),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_recv),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 78 "app/online/cl_ol_0028.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0028 분기미마감"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 4) 기간 규모/정산 현황 조회 (요약행 제외) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( case when status = 'S' then 1 else 0 end ) , 0 ) , coalesce ( sum ( amount ) , 0 ) , count ( distinct biz_date ) from cl_ledger where biz_date between $1 and $2 and merch_id <> '*AGG'", + ECPGt_char,(h_d1),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_d2),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_total),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_settled),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_days),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 96 "app/online/cl_ol_0028.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0028 분기규모"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 5) 분기마감 가능 여부 및 진척률 판정 */ + able = (h_recv == 0) ? 1 : 0; + progress = (h_total > 0) ? ((long)h_settled * 100 / h_total) : 0; + amount_format_won(h_sum, sum_won, sizeof(sum_won)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "D1", d1); + tx_buf_sets(ctx->out, "D2", d2); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_setlong(ctx->out, "RECVCNT", (long)h_recv); + tx_buf_setlong(ctx->out, "TOTALCNT", (long)h_total); + tx_buf_setlong(ctx->out, "SETTLEDCNT", (long)h_settled); + tx_buf_setlong(ctx->out, "DAYS", (long)h_days); + tx_buf_setlong(ctx->out, "SUMAMT", h_sum); + tx_buf_sets(ctx->out, "SUMAMTWON", sum_won); + tx_buf_setlong(ctx->out, "PROGRESS", progress); + tx_buf_sets(ctx->out, "ABLE", able ? "Y" : "N"); + tx_buf_sets(ctx->out, "MESSAGE", + able ? "분기마감 가능(기간내 미마감 없음)" + : "기간내 미마감 잔량 존재로 분기마감 불가"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0028] 완료 %s~%s 미마감=%d 총건=%d 정산=%d 진척=%ld%% able=%s", + d1, d2, h_recv, h_total, h_settled, progress, able ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0028.pgc b/app/online/cl_ol_0028.pgc index 51faa0a..e266d91 100644 --- a/app/online/cl_ol_0028.pgc +++ b/app/online/cl_ol_0028.pgc @@ -2,13 +2,22 @@ /* * cl_ol_0028.pgc - 마감 온라인 서비스 (CL_OL_0028) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 분기마감 검증 + * ---------------------------------------------------------------------- + * 요청 기간 시작일(D1)~종료일(D2) 범위에서 미마감(R) 건이 남아 있는지 + * 조회한다. biz_date BETWEEN :d1 AND :d2 AND status='R' 건수가 0 이면 + * 분기마감 가능(ABLE=Y) 으로 판정한다. 두 일자 모두 형식 검증하고 + * D1<=D2 순서를 확인한다. + * + * 분기 규모의 근거로 기간 내 전체 건수/정산완료 건수/합계금액과 대상 + * 영업일 수를 함께 조회하고, 정산 진척률(%)을 산출한다. 감사추적을 + * 위해 선택 입력 REQID 를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,32 +25,108 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0028, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_d1[9]; + char h_d2[9]; + int h_recv; + int h_total; + int h_settled; + int h_days; + long h_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0028] 마감 단건조회 서비스 진입"); + char d1[16]; + char d2[16]; + char reqid[32]; + char sum_won[32]; + long progress; + int able; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0028] KEY 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0028] 분기마감 검증 서비스 진입"); + + /* 1) 입력 수신 */ + if (tx_buf_get(ctx->in, "D1", d1, sizeof(d1)) != TX_OK || + tx_buf_get(ctx->in, "D2", d2, sizeof(d2)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0028] D1/D2 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + + /* 2) 일자 형식 및 범위 순서 검증 */ + if (!date_is_valid(d1) || !date_is_valid(d2) || strcmp(d1, d2) > 0) { + tx_log(TX_LOG_WARN, "[CL_OL_0028] 기간 오류 d1=%s d2=%s", d1, d2); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_d1, 0, sizeof(h_d1)); + memset(h_d2, 0, sizeof(h_d2)); + strncpy(h_d1, d1, sizeof(h_d1) - 1); + strncpy(h_d2, d2, sizeof(h_d2) - 1); + + /* 3) 기간 내 미마감(R) 건수 조회 */ + EXEC SQL SELECT count(*) + INTO :h_recv + FROM cl_ledger + WHERE biz_date BETWEEN :h_d1 AND :h_d2 + AND status = 'R'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0028 분기미마감"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 4) 기간 규모/정산 현황 조회 (요약행 제외) */ + EXEC SQL SELECT count(*), + coalesce(sum(case when status = 'S' then 1 else 0 end), 0), + coalesce(sum(amount), 0), + count(distinct biz_date) + INTO :h_total, :h_settled, :h_sum, :h_days + FROM cl_ledger + WHERE biz_date BETWEEN :h_d1 AND :h_d2 + AND merch_id <> '*AGG'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0028 분기규모"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 5) 분기마감 가능 여부 및 진척률 판정 */ + able = (h_recv == 0) ? 1 : 0; + progress = (h_total > 0) ? ((long)h_settled * 100 / h_total) : 0; + amount_format_won(h_sum, sum_won, sizeof(sum_won)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "D1", d1); + tx_buf_sets(ctx->out, "D2", d2); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_setlong(ctx->out, "RECVCNT", (long)h_recv); + tx_buf_setlong(ctx->out, "TOTALCNT", (long)h_total); + tx_buf_setlong(ctx->out, "SETTLEDCNT", (long)h_settled); + tx_buf_setlong(ctx->out, "DAYS", (long)h_days); + tx_buf_setlong(ctx->out, "SUMAMT", h_sum); + tx_buf_sets(ctx->out, "SUMAMTWON", sum_won); + tx_buf_setlong(ctx->out, "PROGRESS", progress); + tx_buf_sets(ctx->out, "ABLE", able ? "Y" : "N"); + tx_buf_sets(ctx->out, "MESSAGE", + able ? "분기마감 가능(기간내 미마감 없음)" + : "기간내 미마감 잔량 존재로 분기마감 불가"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0028] 완료 %s~%s 미마감=%d 총건=%d 정산=%d 진척=%ld%% able=%s", + d1, d2, h_recv, h_total, h_settled, progress, able ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0029.c b/app/online/cl_ol_0029.c index 5f54902..e7e9dc3 100644 --- a/app/online/cl_ol_0029.c +++ b/app/online/cl_ol_0029.c @@ -10,13 +10,22 @@ /* * cl_ol_0029.pgc - 마감 온라인 서비스 (CL_OL_0029) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 통계 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)의 원장을 상태별로 각각 조회하여 미마감(R)/ + * 처리완료(M)/불일치(U)/정산완료(S) 4종 건수 통계를 산출한다. 4건의 + * 개별 count 조회 결과를 합산해 전체 건수와 정산 진척률(%, 정수)을 + * 계산한다. + * + * 금액 관점의 통계로 당일 전체 합계와 정산완료 합계를 추가 조회하여 + * 정산금액 비중(%)을 산출하고, 건수 기준 최다 상태(DOMINANT)를 판별해 + * 응답에 제공한다. 감사추적을 위해 선택 입력 REQID 를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,88 +98,123 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0029.pgc" +#line 24 "app/online/cl_ol_0029.pgc" TX_SERVICE(CL_OL_0029, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + +#line 29 "app/online/cl_ol_0029.pgc" + char h_biz_date [ 9 ] ; + +#line 30 "app/online/cl_ol_0029.pgc" + long h_settled_sum ; +/* exec sql end declare section */ +#line 31 "app/online/cl_ol_0029.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0029] 마감 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0029] 필수 필드 누락"); + char bizdate[16]; + char reqid[32]; + char total_won[32]; + char settled_won[32]; + const char *dominant; + long c_recv, c_done, c_fail, c_settled; + long total, cnt_progress, amt_progress; + long total_sum = 0; + long topcnt; + + tx_log(TX_LOG_INFO, "[CL_OL_0029] 마감 통계 서비스 진입"); + + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0029] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0029] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0029] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + /* 2) 상태별 건수 개별 조회 (R/M/U/S) */ + c_recv = cl_rec_count(bizdate, CL_ST_RECV); + c_done = cl_rec_count(bizdate, CL_ST_DONE); + c_fail = cl_rec_count(bizdate, CL_ST_FAIL); + c_settled = cl_rec_count(bizdate, CL_ST_SETTLED); + if (c_recv < 0 || c_done < 0 || c_fail < 0 || c_settled < 0) { + tx_log(TX_LOG_ERROR, "[CL_OL_0029] 상태별 통계 조회 실패 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 3) 금액 통계: 전체 합계(표준 DBIO) + 정산완료 합계(직접 집계) */ + if (cl_rec_sum(bizdate, &total_sum) != TX_OK) + total_sum = 0; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0029] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from cl_ledger where biz_date = $1 and status = 'S'", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settled_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 88 "app/online/cl_ol_0029.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0029 정산합계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } + /* 4) 파생 지표: 전체건수/진척률/최다상태 판별 */ + total = c_recv + c_done + c_fail + c_settled; + cnt_progress = (total > 0) ? (c_settled * 100 / total) : 0; + amt_progress = (total_sum > 0) ? (h_settled_sum * 100 / total_sum) : 0; - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CL_OL_0029] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); + dominant = "R"; topcnt = c_recv; + if (c_done > topcnt) { dominant = "M"; topcnt = c_done; } + if (c_fail > topcnt) { dominant = "U"; topcnt = c_fail; } + if (c_settled > topcnt) { dominant = "S"; topcnt = c_settled; } - /* 교차 검증: 당일 합계 재계산 */ - if (cl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + amount_format_won(total_sum, total_won, sizeof(total_won)); + amount_format_won(h_settled_sum, settled_won, sizeof(settled_won)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CL_OL_0029] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_setlong(ctx->out, "CNTRECV", c_recv); + tx_buf_setlong(ctx->out, "CNTDONE", c_done); + tx_buf_setlong(ctx->out, "CNTFAIL", c_fail); + tx_buf_setlong(ctx->out, "CNTSETTLED", c_settled); + tx_buf_setlong(ctx->out, "TOTALCNT", total); + tx_buf_setlong(ctx->out, "TOTALSUM", total_sum); + tx_buf_sets(ctx->out, "TOTALSUMWON", total_won); + tx_buf_setlong(ctx->out, "SETTLEDSUM", h_settled_sum); + tx_buf_sets(ctx->out, "SETTLEDSUMWON", settled_won); + tx_buf_setlong(ctx->out, "CNTPROGRESS", cnt_progress); + tx_buf_setlong(ctx->out, "AMTPROGRESS", amt_progress); + tx_buf_sets(ctx->out, "DOMINANT", dominant); + + tx_log(TX_LOG_INFO, + "[CL_OL_0029] 완료 date=%s R=%ld M=%ld U=%ld S=%ld 진척=%ld%% 최다=%s", + bizdate, c_recv, c_done, c_fail, c_settled, cnt_progress, dominant); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0029.pgc b/app/online/cl_ol_0029.pgc index c319953..e5379f5 100644 --- a/app/online/cl_ol_0029.pgc +++ b/app/online/cl_ol_0029.pgc @@ -2,13 +2,22 @@ /* * cl_ol_0029.pgc - 마감 온라인 서비스 (CL_OL_0029) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 통계 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)의 원장을 상태별로 각각 조회하여 미마감(R)/ + * 처리완료(M)/불일치(U)/정산완료(S) 4종 건수 통계를 산출한다. 4건의 + * 개별 count 조회 결과를 합산해 전체 건수와 정산 진척률(%, 정수)을 + * 계산한다. + * + * 금액 관점의 통계로 당일 전체 합계와 정산완료 합계를 추가 조회하여 + * 정산금액 비중(%)을 산출하고, 건수 기준 최다 상태(DOMINANT)를 판별해 + * 응답에 제공한다. 감사추적을 위해 선택 입력 REQID 를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,83 +25,108 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0029, ctx) { - cl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + long h_settled_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0029] 마감 트랜잭션 오케스트레이션 진입"); + char bizdate[16]; + char reqid[32]; + char total_won[32]; + char settled_won[32]; + const char *dominant; + long c_recv, c_done, c_fail, c_settled; + long total, cnt_progress, amt_progress; + long total_sum = 0; + long topcnt; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0029] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0029] 마감 통계 서비스 진입"); + + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0029] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CL_OL_0029] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0029] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + /* 2) 상태별 건수 개별 조회 (R/M/U/S) */ + c_recv = cl_rec_count(bizdate, CL_ST_RECV); + c_done = cl_rec_count(bizdate, CL_ST_DONE); + c_fail = cl_rec_count(bizdate, CL_ST_FAIL); + c_settled = cl_rec_count(bizdate, CL_ST_SETTLED); + if (c_recv < 0 || c_done < 0 || c_fail < 0 || c_settled < 0) { + tx_log(TX_LOG_ERROR, "[CL_OL_0029] 상태별 통계 조회 실패 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 3) 금액 통계: 전체 합계(표준 DBIO) + 정산완료 합계(직접 집계) */ + if (cl_rec_sum(bizdate, &total_sum) != TX_OK) + total_sum = 0; - rc = cl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0029] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_settled_sum + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND status = 'S'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0029 정산합계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } + /* 4) 파생 지표: 전체건수/진척률/최다상태 판별 */ + total = c_recv + c_done + c_fail + c_settled; + cnt_progress = (total > 0) ? (c_settled * 100 / total) : 0; + amt_progress = (total_sum > 0) ? (h_settled_sum * 100 / total_sum) : 0; - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CL_OL_0029] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); + dominant = "R"; topcnt = c_recv; + if (c_done > topcnt) { dominant = "M"; topcnt = c_done; } + if (c_fail > topcnt) { dominant = "U"; topcnt = c_fail; } + if (c_settled > topcnt) { dominant = "S"; topcnt = c_settled; } - /* 교차 검증: 당일 합계 재계산 */ - if (cl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + amount_format_won(total_sum, total_won, sizeof(total_won)); + amount_format_won(h_settled_sum, settled_won, sizeof(settled_won)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CL_OL_0029] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_setlong(ctx->out, "CNTRECV", c_recv); + tx_buf_setlong(ctx->out, "CNTDONE", c_done); + tx_buf_setlong(ctx->out, "CNTFAIL", c_fail); + tx_buf_setlong(ctx->out, "CNTSETTLED", c_settled); + tx_buf_setlong(ctx->out, "TOTALCNT", total); + tx_buf_setlong(ctx->out, "TOTALSUM", total_sum); + tx_buf_sets(ctx->out, "TOTALSUMWON", total_won); + tx_buf_setlong(ctx->out, "SETTLEDSUM", h_settled_sum); + tx_buf_sets(ctx->out, "SETTLEDSUMWON", settled_won); + tx_buf_setlong(ctx->out, "CNTPROGRESS", cnt_progress); + tx_buf_setlong(ctx->out, "AMTPROGRESS", amt_progress); + tx_buf_sets(ctx->out, "DOMINANT", dominant); + + tx_log(TX_LOG_INFO, + "[CL_OL_0029] 완료 date=%s R=%ld M=%ld U=%ld S=%ld 진척=%ld%% 최다=%s", + bizdate, c_recv, c_done, c_fail, c_settled, cnt_progress, dominant); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0030.c b/app/online/cl_ol_0030.c index 8d87135..6754ad0 100644 --- a/app/online/cl_ol_0030.c +++ b/app/online/cl_ol_0030.c @@ -10,13 +10,21 @@ /* * cl_ol_0030.pgc - 마감 온라인 서비스 (CL_OL_0030) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 대사 상세 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)의 원장을 부호로 구분하여 대변(amount>=0) 합계와 + * 차변(amount<0) 합계를 각각 산출하고, 순액(net = 대변 + 차변) 을 + * 계산하여 대사 상세로 응답한다. net 이 0 이면 대차 일치(BALANCED=Y). + * + * 대변/차변 각각의 건수도 함께 조회하여 대사 명세를 구성하고, 총거래 + * 규모(대변 - 차변, 절대 합)와 각 금액의 원화 표기를 응답에 제공한다. + * 감사추적을 위해 선택 입력 REQID 를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +97,130 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0030.pgc" +#line 23 "app/online/cl_ol_0030.pgc" TX_SERVICE(CL_OL_0030, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 28 "app/online/cl_ol_0030.pgc" + char h_biz_date [ 9 ] ; + +#line 29 "app/online/cl_ol_0030.pgc" + long h_credit ; + +#line 30 "app/online/cl_ol_0030.pgc" + long h_debit ; + +#line 31 "app/online/cl_ol_0030.pgc" + int h_credit_cnt ; + +#line 32 "app/online/cl_ol_0030.pgc" + int h_debit_cnt ; +/* exec sql end declare section */ +#line 33 "app/online/cl_ol_0030.pgc" - tx_log(TX_LOG_INFO, "[CL_OL_0030] 마감 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0030] KEY 누락"); + char bizdate[16]; + char reqid[32]; + char credit_won[32]; + char debit_won[32]; + char net_won[32]; + long net; + long gross; + + tx_log(TX_LOG_INFO, "[CL_OL_0030] 마감 대사 상세 서비스 진입"); + + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0030] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0030] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + /* 2) 대변(amount>=0) 합계/건수 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) from cl_ledger where biz_date = $1 and amount >= 0", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_credit),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_credit_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 72 "app/online/cl_ol_0030.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0030 대변집계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3) 차변(amount<0) 합계/건수 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) from cl_ledger where biz_date = $1 and amount < 0", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_debit),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_debit_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 87 "app/online/cl_ol_0030.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0030 차변집계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 4) 순액 = 대변 + 차변(차변은 음수), 총거래규모 = 대변 - 차변 */ + net = h_credit + h_debit; + gross = h_credit - h_debit; + amount_format_won(h_credit, credit_won, sizeof(credit_won)); + amount_format_won(h_debit, debit_won, sizeof(debit_won)); + amount_format_won(net, net_won, sizeof(net_won)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_setlong(ctx->out, "CREDIT", h_credit); + tx_buf_sets(ctx->out, "CREDITWON", credit_won); + tx_buf_setlong(ctx->out, "CREDITCNT", (long)h_credit_cnt); + tx_buf_setlong(ctx->out, "DEBIT", h_debit); + tx_buf_sets(ctx->out, "DEBITWON", debit_won); + tx_buf_setlong(ctx->out, "DEBITCNT", (long)h_debit_cnt); + tx_buf_setlong(ctx->out, "NET", net); + tx_buf_sets(ctx->out, "NETWON", net_won); + tx_buf_setlong(ctx->out, "GROSS", gross); + tx_buf_sets(ctx->out, "BALANCED", (net == 0) ? "Y" : "N"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0030] 완료 date=%s 대변=%ld(%d) 차변=%ld(%d) 순액=%ld", + bizdate, h_credit, h_credit_cnt, h_debit, h_debit_cnt, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0030.pgc b/app/online/cl_ol_0030.pgc index 9a8f915..dccac4f 100644 --- a/app/online/cl_ol_0030.pgc +++ b/app/online/cl_ol_0030.pgc @@ -2,13 +2,21 @@ /* * cl_ol_0030.pgc - 마감 온라인 서비스 (CL_OL_0030) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 대사 상세 + * ---------------------------------------------------------------------- + * 요청 영업일(BIZDATE)의 원장을 부호로 구분하여 대변(amount>=0) 합계와 + * 차변(amount<0) 합계를 각각 산출하고, 순액(net = 대변 + 차변) 을 + * 계산하여 대사 상세로 응답한다. net 이 0 이면 대차 일치(BALANCED=Y). + * + * 대변/차변 각각의 건수도 함께 조회하여 대사 명세를 구성하고, 총거래 + * 규모(대변 - 차변, 절대 합)와 각 금액의 원화 표기를 응답에 제공한다. + * 감사추적을 위해 선택 입력 REQID 를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,32 +24,100 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0030, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + long h_credit; + long h_debit; + int h_credit_cnt; + int h_debit_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CL_OL_0030] 마감 단건조회 서비스 진입"); + char bizdate[16]; + char reqid[32]; + char credit_won[32]; + char debit_won[32]; + char net_won[32]; + long net; + long gross; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0030] KEY 누락"); + tx_log(TX_LOG_INFO, "[CL_OL_0030] 마감 대사 상세 서비스 진입"); + + /* 1) 입력 수신 및 검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0030] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); + + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CL_OL_0030] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + /* 2) 대변(amount>=0) 합계/건수 */ + EXEC SQL SELECT coalesce(sum(amount), 0), count(*) + INTO :h_credit, :h_credit_cnt + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND amount >= 0; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0030 대변집계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3) 차변(amount<0) 합계/건수 */ + EXEC SQL SELECT coalesce(sum(amount), 0), count(*) + INTO :h_debit, :h_debit_cnt + FROM cl_ledger + WHERE biz_date = :h_biz_date + AND amount < 0; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cl_ol_0030 차변집계"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 4) 순액 = 대변 + 차변(차변은 음수), 총거래규모 = 대변 - 차변 */ + net = h_credit + h_debit; + gross = h_credit - h_debit; + amount_format_won(h_credit, credit_won, sizeof(credit_won)); + amount_format_won(h_debit, debit_won, sizeof(debit_won)); + amount_format_won(net, net_won, sizeof(net_won)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_setlong(ctx->out, "CREDIT", h_credit); + tx_buf_sets(ctx->out, "CREDITWON", credit_won); + tx_buf_setlong(ctx->out, "CREDITCNT", (long)h_credit_cnt); + tx_buf_setlong(ctx->out, "DEBIT", h_debit); + tx_buf_sets(ctx->out, "DEBITWON", debit_won); + tx_buf_setlong(ctx->out, "DEBITCNT", (long)h_debit_cnt); + tx_buf_setlong(ctx->out, "NET", net); + tx_buf_sets(ctx->out, "NETWON", net_won); + tx_buf_setlong(ctx->out, "GROSS", gross); + tx_buf_sets(ctx->out, "BALANCED", (net == 0) ? "Y" : "N"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0030] 완료 date=%s 대변=%ld(%d) 차변=%ld(%d) 순액=%ld", + bizdate, h_credit, h_credit_cnt, h_debit, h_debit_cnt, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0031.c b/app/online/cl_ol_0031.c index 8752a19..30a7e62 100644 --- a/app/online/cl_ol_0031.c +++ b/app/online/cl_ol_0031.c @@ -10,13 +10,22 @@ /* * cl_ol_0031.pgc - 마감 온라인 서비스 (CL_OL_0031) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 스냅샷 비교 + * ---------------------------------------------------------------------- + * 두 스냅샷 처리키(SS1, SS2)를 표준 DBIO(cl_rec_select) 로 각각 조회 + * 하여, 두 스냅샷의 합계금액(amount) 차이와 일치 여부(MATCH=Y/N) 를 + * diff 응답으로 반환한다. 어느 한쪽 스냅샷이 없으면 비교 불가로 + * ENOENT 를 응답한다. + * + * 금액 차이에 더해 건수(fee 필드에 적재) 차이와 변동 방향(DIRECTION: + * UP/DOWN/FLAT), 기준(SS1) 대비 변동률(%)을 산출하여 제공한다. + * 감사추적을 위해 선택 입력 REQID 를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -89,37 +98,103 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cl_ol_0031.pgc" +#line 24 "app/online/cl_ol_0031.pgc" TX_SERVICE(CL_OL_0031, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + cl_rec_t rec1, rec2; + char ss1[16], ss2[16]; + char reqid[32]; + char diff_won[32]; + char summary[TX_VAL_LEN]; + const char *direction; + long diff, cnt_diff, abs_diff; + long rate; + int rc1, rc2; - tx_log(TX_LOG_INFO, "[CL_OL_0031] 마감 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CL_OL_0031] 마감 스냅샷 비교 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0031] KEY 누락"); + /* 1) 입력 수신 */ + if (tx_buf_get(ctx->in, "SS1", ss1, sizeof(ss1)) != TX_OK || + tx_buf_get(ctx->in, "SS2", ss2, sizeof(ss2)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0031] SS1/SS2 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0031] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 두 스냅샷 각각 조회 */ + memset(&rec1, 0, sizeof(rec1)); + memset(&rec2, 0, sizeof(rec2)); + rc1 = cl_rec_select(ss1, &rec1); + rc2 = cl_rec_select(ss2, &rec2); + + if (rc1 == TX_ENOENT || rc2 == TX_ENOENT) { + tx_log(TX_LOG_WARN, "[CL_OL_0031] 스냅샷 없음 ss1=%s(rc=%d) ss2=%s(rc=%d)", + ss1, rc1, ss2, rc2); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "FOUND1", rc1 == TX_OK ? "Y" : "N"); + tx_buf_sets(ctx->out, "FOUND2", rc2 == TX_OK ? "Y" : "N"); + tx_buf_sets(ctx->out, "MESSAGE", "비교 대상 스냅샷 누락"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (rc1 != TX_OK || rc2 != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0031] 스냅샷 조회 실패 rc1=%d rc2=%d", rc1, rc2); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3) 금액/건수 차이, 방향, 변동률(기준 SS1) 산출 */ + diff = rec2.amount - rec1.amount; /* SS1 -> SS2 변동 */ + cnt_diff = rec2.fee - rec1.fee; + if (diff > 0) + direction = "UP"; + else if (diff < 0) + direction = "DOWN"; + else + direction = "FLAT"; + rate = (rec1.amount != 0) + ? (diff * 100 / (rec1.amount < 0 ? -rec1.amount : rec1.amount)) + : 0; + abs_diff = (diff < 0) ? -diff : diff; + amount_format_won(diff, diff_won, sizeof(diff_won)); + + /* 4) 비교 결과 요약 문구 구성 */ + snprintf(summary, sizeof(summary), + "스냅샷 %s(%ld) -> %s(%ld) 변동 %ld원 (%s %ld%%)", + ss1, rec1.amount, ss2, rec2.amount, diff, direction, rate); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0031] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "SS1", ss1); + tx_buf_sets(ctx->out, "SS2", ss2); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_setlong(ctx->out, "AMT1", rec1.amount); + tx_buf_setlong(ctx->out, "AMT2", rec2.amount); + tx_buf_setlong(ctx->out, "CNT1", rec1.fee); + tx_buf_setlong(ctx->out, "CNT2", rec2.fee); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "DIFFWON", diff_won); + tx_buf_setlong(ctx->out, "ABSDIFF", abs_diff); + tx_buf_setlong(ctx->out, "CNTDIFF", cnt_diff); + tx_buf_setlong(ctx->out, "RATE", rate); + tx_buf_sets(ctx->out, "DIRECTION", direction); + tx_buf_sets(ctx->out, "SUMMARY", summary); + tx_buf_sets(ctx->out, "MATCH", (diff == 0) ? "Y" : "N"); + /* 유의미 변동 여부(절대변동이 기준의 1% 초과) */ + tx_buf_sets(ctx->out, "MATERIAL", + (rate > 1 || rate < -1) ? "Y" : "N"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0031] 완료 ss1=%s(%ld) ss2=%s(%ld) 차이=%ld 방향=%s", + ss1, rec1.amount, ss2, rec2.amount, diff, direction); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cl_ol_0031.pgc b/app/online/cl_ol_0031.pgc index 20e8267..d979252 100644 --- a/app/online/cl_ol_0031.pgc +++ b/app/online/cl_ol_0031.pgc @@ -2,13 +2,22 @@ /* * cl_ol_0031.pgc - 마감 온라인 서비스 (CL_OL_0031) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 마감 스냅샷 비교 + * ---------------------------------------------------------------------- + * 두 스냅샷 처리키(SS1, SS2)를 표준 DBIO(cl_rec_select) 로 각각 조회 + * 하여, 두 스냅샷의 합계금액(amount) 차이와 일치 여부(MATCH=Y/N) 를 + * diff 응답으로 반환한다. 어느 한쪽 스냅샷이 없으면 비교 불가로 + * ENOENT 를 응답한다. + * + * 금액 차이에 더해 건수(fee 필드에 적재) 차이와 변동 방향(DIRECTION: + * UP/DOWN/FLAT), 기준(SS1) 대비 변동률(%)을 산출하여 제공한다. + * 감사추적을 위해 선택 입력 REQID 를 반영한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cl_core.h" @@ -16,32 +25,98 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CL_OL_0031, ctx) { - cl_rec_t rec; - char key[16]; - int rc; + cl_rec_t rec1, rec2; + char ss1[16], ss2[16]; + char reqid[32]; + char diff_won[32]; + char summary[TX_VAL_LEN]; + const char *direction; + long diff, cnt_diff, abs_diff; + long rate; + int rc1, rc2; - tx_log(TX_LOG_INFO, "[CL_OL_0031] 마감 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CL_OL_0031] 마감 스냅샷 비교 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CL_OL_0031] KEY 누락"); + /* 1) 입력 수신 */ + if (tx_buf_get(ctx->in, "SS1", ss1, sizeof(ss1)) != TX_OK || + tx_buf_get(ctx->in, "SS2", ss2, sizeof(ss2)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0031] SS1/SS2 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_get(ctx->in, "REQID", reqid, sizeof(reqid)) != TX_OK) + strncpy(reqid, "-", sizeof(reqid) - 1); - memset(&rec, 0, sizeof(rec)); - rc = cl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CL_OL_0031] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 두 스냅샷 각각 조회 */ + memset(&rec1, 0, sizeof(rec1)); + memset(&rec2, 0, sizeof(rec2)); + rc1 = cl_rec_select(ss1, &rec1); + rc2 = cl_rec_select(ss2, &rec2); + + if (rc1 == TX_ENOENT || rc2 == TX_ENOENT) { + tx_log(TX_LOG_WARN, "[CL_OL_0031] 스냅샷 없음 ss1=%s(rc=%d) ss2=%s(rc=%d)", + ss1, rc1, ss2, rc2); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "FOUND1", rc1 == TX_OK ? "Y" : "N"); + tx_buf_sets(ctx->out, "FOUND2", rc2 == TX_OK ? "Y" : "N"); + tx_buf_sets(ctx->out, "MESSAGE", "비교 대상 스냅샷 누락"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (rc1 != TX_OK || rc2 != TX_OK) { + tx_log(TX_LOG_ERROR, "[CL_OL_0031] 스냅샷 조회 실패 rc1=%d rc2=%d", rc1, rc2); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3) 금액/건수 차이, 방향, 변동률(기준 SS1) 산출 */ + diff = rec2.amount - rec1.amount; /* SS1 -> SS2 변동 */ + cnt_diff = rec2.fee - rec1.fee; + if (diff > 0) + direction = "UP"; + else if (diff < 0) + direction = "DOWN"; + else + direction = "FLAT"; + rate = (rec1.amount != 0) + ? (diff * 100 / (rec1.amount < 0 ? -rec1.amount : rec1.amount)) + : 0; + abs_diff = (diff < 0) ? -diff : diff; + amount_format_won(diff, diff_won, sizeof(diff_won)); + + /* 4) 비교 결과 요약 문구 구성 */ + snprintf(summary, sizeof(summary), + "스냅샷 %s(%ld) -> %s(%ld) 변동 %ld원 (%s %ld%%)", + ss1, rec1.amount, ss2, rec2.amount, diff, direction, rate); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CL_OL_0031] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "SS1", ss1); + tx_buf_sets(ctx->out, "SS2", ss2); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_setlong(ctx->out, "AMT1", rec1.amount); + tx_buf_setlong(ctx->out, "AMT2", rec2.amount); + tx_buf_setlong(ctx->out, "CNT1", rec1.fee); + tx_buf_setlong(ctx->out, "CNT2", rec2.fee); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "DIFFWON", diff_won); + tx_buf_setlong(ctx->out, "ABSDIFF", abs_diff); + tx_buf_setlong(ctx->out, "CNTDIFF", cnt_diff); + tx_buf_setlong(ctx->out, "RATE", rate); + tx_buf_sets(ctx->out, "DIRECTION", direction); + tx_buf_sets(ctx->out, "SUMMARY", summary); + tx_buf_sets(ctx->out, "MATCH", (diff == 0) ? "Y" : "N"); + /* 유의미 변동 여부(절대변동이 기준의 1% 초과) */ + tx_buf_sets(ctx->out, "MATERIAL", + (rate > 1 || rate < -1) ? "Y" : "N"); + + tx_log(TX_LOG_INFO, + "[CL_OL_0031] 완료 ss1=%s(%ld) ss2=%s(%ld) 차이=%ld 방향=%s", + ss1, rec1.amount, ss2, rec2.amount, diff, direction); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0001.c b/app/online/cm_ol_0001.c index 6b3212a..98639cb 100644 --- a/app/online/cm_ol_0001.c +++ b/app/online/cm_ol_0001.c @@ -8,15 +8,17 @@ #line 1 "app/online/cm_ol_0001.pgc" /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0001.pgc - 공통 온라인 서비스 (CM_OL_0001) [tier=easy] + * cm_ol_0001.pgc - 공통코드 조회 서비스 (CM_OL_0001) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 코드그룹(GRP)/코드값(CODE)으로 공통코드 마스터(cm_code)를 + * 조회하여 코드명/속성값을 응답 TXBUF 로 반환한다. 캐시 미적용 직접조회. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,37 +91,87 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0001.pgc" +#line 17 "app/online/cm_ol_0001.pgc" TX_SERVICE(CM_OL_0001, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 22 "app/online/cm_ol_0001.pgc" + char h_grp [ 16 ] ; + +#line 23 "app/online/cm_ol_0001.pgc" + char h_code [ 16 ] ; + +#line 24 "app/online/cm_ol_0001.pgc" + char h_name [ 64 ] ; + +#line 25 "app/online/cm_ol_0001.pgc" + char h_attr [ 64 ] ; + +#line 26 "app/online/cm_ol_0001.pgc" + int h_sort ; +/* exec sql end declare section */ +#line 27 "app/online/cm_ol_0001.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0001] 공통 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0001] KEY 누락"); + char grp[16]; + char code[16]; + + tx_log(TX_LOG_INFO, "[CM_OL_0001] 공통코드 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "GRP", grp, sizeof(grp)) != TX_OK || + tx_buf_get(ctx->in, "CODE", code, sizeof(code)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0001] GRP/CODE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0001] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(&h_grp, 0, sizeof(h_grp)); + memset(&h_code, 0, sizeof(h_code)); + memset(&h_name, 0, sizeof(h_name)); + memset(&h_attr, 0, sizeof(h_attr)); + strncpy(h_grp, grp, sizeof(h_grp) - 1); + strncpy(h_code, code, sizeof(h_code) - 1); + h_sort = 0; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select code_name , attr_val , sort_no from cm_code where grp_cd = $1 and code_cd = $2 and use_yn = 'Y'", + ECPGt_char,(h_grp),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_code),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_name),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_attr),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_sort),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 55 "app/online/cm_ol_0001.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0001] 코드 없음 grp=%s code=%s", grp, code); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0001 SELECT cm_code"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0001] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "GRP", grp); + tx_buf_sets(ctx->out, "CODE", code); + tx_buf_sets(ctx->out, "NAME", h_name); + tx_buf_sets(ctx->out, "ATTR", h_attr); + tx_buf_setlong(ctx->out, "SORT", (long)h_sort); + tx_log(TX_LOG_INFO, "[CM_OL_0001] 코드 조회완료 grp=%s code=%s name=%s", grp, code, h_name); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0001.pgc b/app/online/cm_ol_0001.pgc index 6ff40dd..db9fed3 100644 --- a/app/online/cm_ol_0001.pgc +++ b/app/online/cm_ol_0001.pgc @@ -1,14 +1,16 @@ /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0001.pgc - 공통 온라인 서비스 (CM_OL_0001) [tier=easy] + * cm_ol_0001.pgc - 공통코드 조회 서비스 (CM_OL_0001) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 코드그룹(GRP)/코드값(CODE)으로 공통코드 마스터(cm_code)를 + * 조회하여 코드명/속성값을 응답 TXBUF 로 반환한다. 캐시 미적용 직접조회. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,32 +18,59 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0001, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_grp[16]; + char h_code[16]; + char h_name[64]; + char h_attr[64]; + int h_sort; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0001] 공통 단건조회 서비스 진입"); + char grp[16]; + char code[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0001] KEY 누락"); + tx_log(TX_LOG_INFO, "[CM_OL_0001] 공통코드 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "GRP", grp, sizeof(grp)) != TX_OK || + tx_buf_get(ctx->in, "CODE", code, sizeof(code)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0001] GRP/CODE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0001] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(&h_grp, 0, sizeof(h_grp)); + memset(&h_code, 0, sizeof(h_code)); + memset(&h_name, 0, sizeof(h_name)); + memset(&h_attr, 0, sizeof(h_attr)); + strncpy(h_grp, grp, sizeof(h_grp) - 1); + strncpy(h_code, code, sizeof(h_code) - 1); + h_sort = 0; + + EXEC SQL + SELECT code_name, attr_val, sort_no + INTO :h_name, :h_attr, :h_sort + FROM cm_code + WHERE grp_cd = :h_grp + AND code_cd = :h_code + AND use_yn = 'Y'; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0001] 코드 없음 grp=%s code=%s", grp, code); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0001 SELECT cm_code"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0001] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "GRP", grp); + tx_buf_sets(ctx->out, "CODE", code); + tx_buf_sets(ctx->out, "NAME", h_name); + tx_buf_sets(ctx->out, "ATTR", h_attr); + tx_buf_setlong(ctx->out, "SORT", (long)h_sort); + tx_log(TX_LOG_INFO, "[CM_OL_0001] 코드 조회완료 grp=%s code=%s name=%s", grp, code, h_name); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0002.c b/app/online/cm_ol_0002.c index d587e2e..fcbd827 100644 --- a/app/online/cm_ol_0002.c +++ b/app/online/cm_ol_0002.c @@ -8,10 +8,12 @@ #line 1 "app/online/cm_ol_0002.pgc" /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0002.pgc - 공통 온라인 서비스 (CM_OL_0002) [tier=easy] + * cm_ol_0002.pgc - 영업일 계산 서비스 (CM_OL_0002) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 기준일자(DATE, YYYYMMDD)를 검증한 뒤 date_next_business 로 + * 다음 영업일을 산출하고, 기준일의 요일번호/요일명/주말여부를 함께 + * 응답한다. DB 접근이 없는 순수 유틸리티 로직. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -89,37 +91,62 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0002.pgc" +#line 17 "app/online/cm_ol_0002.pgc" +/* 요일번호(0=일..6=토)를 한글 표기로 변환 */ +static const char *weekday_name(int wd) +{ + static const char *names[7] = { + "일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일" + }; + if (wd < 0 || wd > 6) { + return "미상"; + } + return names[wd]; +} + TX_SERVICE(CM_OL_0002, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + char base_date[9]; + char next_date[9]; + int wd; + int weekend; - tx_log(TX_LOG_INFO, "[CM_OL_0002] 공통 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0002] 영업일 계산 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0002] KEY 누락"); + memset(base_date, 0, sizeof(base_date)); + memset(next_date, 0, sizeof(next_date)); + + if (tx_buf_get(ctx->in, "DATE", base_date, sizeof(base_date)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0002] 기준일자(DATE) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0002] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(base_date)) { + tx_log(TX_LOG_WARN, "[CM_OL_0002] 유효하지 않은 일자 date=%s", base_date); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + wd = date_weekday(base_date); + weekend = date_is_weekend(base_date); + + if (date_next_business(base_date, next_date) != 0) { + tx_log(TX_LOG_ERROR, "[CM_OL_0002] 다음 영업일 계산 실패 date=%s", base_date); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0002] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "DATE", base_date); + tx_buf_sets(ctx->out, "NEXTBIZ", next_date); + tx_buf_setlong(ctx->out, "WEEKDAY", (long)wd); + tx_buf_sets(ctx->out, "WEEKDAYNM", weekday_name(wd)); + tx_buf_sets(ctx->out, "WEEKEND", weekend ? "Y" : "N"); + + tx_log(TX_LOG_INFO, "[CM_OL_0002] 계산완료 date=%s(%s) next=%s weekend=%s", + base_date, weekday_name(wd), next_date, weekend ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0002.pgc b/app/online/cm_ol_0002.pgc index 2469399..6f09b21 100644 --- a/app/online/cm_ol_0002.pgc +++ b/app/online/cm_ol_0002.pgc @@ -1,9 +1,11 @@ /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0002.pgc - 공통 온라인 서비스 (CM_OL_0002) [tier=easy] + * cm_ol_0002.pgc - 영업일 계산 서비스 (CM_OL_0002) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 기준일자(DATE, YYYYMMDD)를 검증한 뒤 date_next_business 로 + * 다음 영업일을 산출하고, 기준일의 요일번호/요일명/주말여부를 함께 + * 응답한다. DB 접근이 없는 순수 유틸리티 로직. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -14,34 +16,59 @@ EXEC SQL INCLUDE sqlca; +/* 요일번호(0=일..6=토)를 한글 표기로 변환 */ +static const char *weekday_name(int wd) +{ + static const char *names[7] = { + "일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일" + }; + if (wd < 0 || wd > 6) { + return "미상"; + } + return names[wd]; +} + TX_SERVICE(CM_OL_0002, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + char base_date[9]; + char next_date[9]; + int wd; + int weekend; - tx_log(TX_LOG_INFO, "[CM_OL_0002] 공통 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0002] 영업일 계산 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0002] KEY 누락"); + memset(base_date, 0, sizeof(base_date)); + memset(next_date, 0, sizeof(next_date)); + + if (tx_buf_get(ctx->in, "DATE", base_date, sizeof(base_date)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0002] 기준일자(DATE) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0002] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(base_date)) { + tx_log(TX_LOG_WARN, "[CM_OL_0002] 유효하지 않은 일자 date=%s", base_date); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + wd = date_weekday(base_date); + weekend = date_is_weekend(base_date); + + if (date_next_business(base_date, next_date) != 0) { + tx_log(TX_LOG_ERROR, "[CM_OL_0002] 다음 영업일 계산 실패 date=%s", base_date); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0002] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "DATE", base_date); + tx_buf_sets(ctx->out, "NEXTBIZ", next_date); + tx_buf_setlong(ctx->out, "WEEKDAY", (long)wd); + tx_buf_sets(ctx->out, "WEEKDAYNM", weekday_name(wd)); + tx_buf_sets(ctx->out, "WEEKEND", weekend ? "Y" : "N"); + + tx_log(TX_LOG_INFO, "[CM_OL_0002] 계산완료 date=%s(%s) next=%s weekend=%s", + base_date, weekday_name(wd), next_date, weekend ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0003.c b/app/online/cm_ol_0003.c index cdfb6bf..cbe819c 100644 --- a/app/online/cm_ol_0003.c +++ b/app/online/cm_ol_0003.c @@ -6,12 +6,14 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0003.pgc" -/* @tier medium @module cm @transform common-online */ +/* @tier easy @module cm @transform common-online */ /* - * cm_ol_0003.pgc - 공통 온라인 서비스 (CM_OL_0003) [tier=medium] + * cm_ol_0003.pgc - 금액 포맷 유틸 서비스 (CM_OL_0003) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 입력금액(AMOUNT)을 amount_format_won 으로 천단위 콤마/원 + * 표기 문자열로 변환하고, amount_is_valid 로 금액 유효범위 여부를 + * 함께 응답한다. DB 접근이 없는 순수 유틸리티 로직. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -89,60 +91,50 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0003.pgc" +#line 17 "app/online/cm_ol_0003.pgc" TX_SERVICE(CM_OL_0003, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char amt_field[32]; + char won_disp[48]; + char plain_disp[32]; + long amount = 0; + int valid; - tx_log(TX_LOG_INFO, "[CM_OL_0003] 공통 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0003] 금액 포맷 유틸 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0003] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + memset(amt_field, 0, sizeof(amt_field)); + memset(won_disp, 0, sizeof(won_disp)); + memset(plain_disp, 0, sizeof(plain_disp)); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0003] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0003] 영업일 오류 date=%s", bizdate); + /* AMOUNT 는 숫자 필드(문자열) 또는 정수 어느 쪽으로 와도 처리한다 */ + if (tx_buf_get(ctx->in, "AMOUNT", amt_field, sizeof(amt_field)) == TX_OK) { + amount = amount_parse(amt_field, (int)strlen(amt_field)); + } else if (tx_buf_getlong(ctx->in, "AMOUNT", &amount) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0003] 입력금액(AMOUNT) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0003] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + valid = amount_is_valid(amount); + if (!valid) { + tx_log(TX_LOG_WARN, "[CM_OL_0003] 금액 유효범위 초과 amount=%ld", amount); + /* 유효범위를 벗어나도 표기 자체는 제공하되 VALID=N 으로 통지 */ } - cnt = cm_rec_count(bizdate, CM_ST_RECV); + amount_format_won(amount, won_disp, sizeof(won_disp)); + if (amount_format(amount, plain_disp, sizeof(plain_disp)) != 0) { + strncpy(plain_disp, "0", sizeof(plain_disp) - 1); + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0003] 등록완료 key=%s 당일건수=%ld", key, cnt); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_sets(ctx->out, "WONDISP", won_disp); + tx_buf_sets(ctx->out, "PLAINDISP", plain_disp); + tx_buf_sets(ctx->out, "VALID", valid ? "Y" : "N"); + + tx_log(TX_LOG_INFO, "[CM_OL_0003] 포맷완료 amount=%ld disp=%s valid=%s", + amount, won_disp, valid ? "Y" : "N"); + tx_return(ctx, valid ? TX_OK : TX_EINVAL, ctx->out); } diff --git a/app/online/cm_ol_0003.pgc b/app/online/cm_ol_0003.pgc index b50b4ed..70b4c6e 100644 --- a/app/online/cm_ol_0003.pgc +++ b/app/online/cm_ol_0003.pgc @@ -1,9 +1,11 @@ -/* @tier medium @module cm @transform common-online */ +/* @tier easy @module cm @transform common-online */ /* - * cm_ol_0003.pgc - 공통 온라인 서비스 (CM_OL_0003) [tier=medium] + * cm_ol_0003.pgc - 금액 포맷 유틸 서비스 (CM_OL_0003) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 입력금액(AMOUNT)을 amount_format_won 으로 천단위 콤마/원 + * 표기 문자열로 변환하고, amount_is_valid 로 금액 유효범위 여부를 + * 함께 응답한다. DB 접근이 없는 순수 유틸리티 로직. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -16,55 +18,45 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0003, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char amt_field[32]; + char won_disp[48]; + char plain_disp[32]; + long amount = 0; + int valid; - tx_log(TX_LOG_INFO, "[CM_OL_0003] 공통 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0003] 금액 포맷 유틸 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0003] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + memset(amt_field, 0, sizeof(amt_field)); + memset(won_disp, 0, sizeof(won_disp)); + memset(plain_disp, 0, sizeof(plain_disp)); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0003] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0003] 영업일 오류 date=%s", bizdate); + /* AMOUNT 는 숫자 필드(문자열) 또는 정수 어느 쪽으로 와도 처리한다 */ + if (tx_buf_get(ctx->in, "AMOUNT", amt_field, sizeof(amt_field)) == TX_OK) { + amount = amount_parse(amt_field, (int)strlen(amt_field)); + } else if (tx_buf_getlong(ctx->in, "AMOUNT", &amount) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0003] 입력금액(AMOUNT) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0003] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + valid = amount_is_valid(amount); + if (!valid) { + tx_log(TX_LOG_WARN, "[CM_OL_0003] 금액 유효범위 초과 amount=%ld", amount); + /* 유효범위를 벗어나도 표기 자체는 제공하되 VALID=N 으로 통지 */ } - cnt = cm_rec_count(bizdate, CM_ST_RECV); + amount_format_won(amount, won_disp, sizeof(won_disp)); + if (amount_format(amount, plain_disp, sizeof(plain_disp)) != 0) { + strncpy(plain_disp, "0", sizeof(plain_disp) - 1); + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0003] 등록완료 key=%s 당일건수=%ld", key, cnt); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_sets(ctx->out, "WONDISP", won_disp); + tx_buf_sets(ctx->out, "PLAINDISP", plain_disp); + tx_buf_sets(ctx->out, "VALID", valid ? "Y" : "N"); + + tx_log(TX_LOG_INFO, "[CM_OL_0003] 포맷완료 amount=%ld disp=%s valid=%s", + amount, won_disp, valid ? "Y" : "N"); + tx_return(ctx, valid ? TX_OK : TX_EINVAL, ctx->out); } diff --git a/app/online/cm_ol_0004.c b/app/online/cm_ol_0004.c index a1f9933..36e9ec4 100644 --- a/app/online/cm_ol_0004.c +++ b/app/online/cm_ol_0004.c @@ -6,12 +6,14 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0004.pgc" -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0004.pgc - 공통 온라인 서비스 (CM_OL_0004) [tier=easy] + * cm_ol_0004.pgc - 전문 공통헤더 처리 서비스 (CM_OL_0004) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 공통헤더 원천 필드(전문길이/전문종별/채널/거래일시)를 파싱· + * 검증한 뒤, 고정 자리수의 표준 공통헤더 문자열로 재구성하여 응답한다. + * DB 접근이 없는 순수 전문 조립 로직. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -89,37 +91,78 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0004.pgc" +#line 17 "app/online/cm_ol_0004.pgc" +/* 전문종별 코드 → 표준 2자리 구분자 매핑 */ +static const char *header_kind_tag(const char *kind) +{ + if (strcmp(kind, MSG_TYPE_RECV) == 0) return "RQ"; /* 매입요청 */ + if (strcmp(kind, MSG_TYPE_RESP) == 0) return "RP"; /* 응답 */ + if (strcmp(kind, MSG_TYPE_RECON) == 0) return "RC"; /* 정산대사 */ + return "XX"; /* 미정의 */ +} + TX_SERVICE(CM_OL_0004, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + char msglen_f[8]; + char kind[8]; + char channel[4]; + char datetime[16]; + char std_header[64]; + const char *tag; + long msglen = 0; + int i; - tx_log(TX_LOG_INFO, "[CM_OL_0004] 공통 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0004] 전문 공통헤더 처리 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0004] KEY 누락"); + memset(msglen_f, 0, sizeof(msglen_f)); + memset(kind, 0, sizeof(kind)); + memset(channel, 0, sizeof(channel)); + memset(datetime, 0, sizeof(datetime)); + memset(std_header, 0, sizeof(std_header)); + + if (tx_buf_get(ctx->in, "MSGLEN", msglen_f, sizeof(msglen_f)) != TX_OK || + tx_buf_get(ctx->in, "MSGTYPE", kind, sizeof(kind)) != TX_OK || + tx_buf_get(ctx->in, "CHANNEL", channel, sizeof(channel)) != TX_OK || + tx_buf_get(ctx->in, "DATETIME", datetime, sizeof(datetime)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0004] 공통헤더 필수 필드 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0004] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 전문길이 숫자검증 */ + for (i = 0; msglen_f[i] != '\0'; i++) { + if (msglen_f[i] < '0' || msglen_f[i] > '9') { + tx_log(TX_LOG_WARN, "[CM_OL_0004] 전문길이 형식오류 msglen=%s", msglen_f); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } + msglen = amount_parse(msglen_f, (int)strlen(msglen_f)); + if (msglen <= 0 || msglen > 9999) { + tx_log(TX_LOG_WARN, "[CM_OL_0004] 전문길이 범위오류 msglen=%ld", msglen); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + tag = header_kind_tag(kind); + if (strcmp(tag, "XX") == 0) { + tx_log(TX_LOG_WARN, "[CM_OL_0004] 미정의 전문종별 kind=%s", kind); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 표준 공통헤더 = 길이(4) + 종별태그(2) + 채널(2) + 거래일시(14) */ + snprintf(std_header, sizeof(std_header), "%04ld%2s%-2.2s%-14.14s", + msglen, tag, channel, datetime); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0004] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "STDHEADER", std_header); + tx_buf_setlong(ctx->out, "MSGLEN", msglen); + tx_buf_sets(ctx->out, "KINDTAG", tag); + tx_buf_setlong(ctx->out, "HDRLEN", (long)strlen(std_header)); + + tx_log(TX_LOG_INFO, "[CM_OL_0004] 헤더조립 완료 tag=%s header=[%s]", tag, std_header); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0004.pgc b/app/online/cm_ol_0004.pgc index b664626..ed44e8f 100644 --- a/app/online/cm_ol_0004.pgc +++ b/app/online/cm_ol_0004.pgc @@ -1,9 +1,11 @@ -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0004.pgc - 공통 온라인 서비스 (CM_OL_0004) [tier=easy] + * cm_ol_0004.pgc - 전문 공통헤더 처리 서비스 (CM_OL_0004) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 공통헤더 원천 필드(전문길이/전문종별/채널/거래일시)를 파싱· + * 검증한 뒤, 고정 자리수의 표준 공통헤더 문자열로 재구성하여 응답한다. + * DB 접근이 없는 순수 전문 조립 로직. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -14,34 +16,75 @@ EXEC SQL INCLUDE sqlca; +/* 전문종별 코드 → 표준 2자리 구분자 매핑 */ +static const char *header_kind_tag(const char *kind) +{ + if (strcmp(kind, MSG_TYPE_RECV) == 0) return "RQ"; /* 매입요청 */ + if (strcmp(kind, MSG_TYPE_RESP) == 0) return "RP"; /* 응답 */ + if (strcmp(kind, MSG_TYPE_RECON) == 0) return "RC"; /* 정산대사 */ + return "XX"; /* 미정의 */ +} + TX_SERVICE(CM_OL_0004, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + char msglen_f[8]; + char kind[8]; + char channel[4]; + char datetime[16]; + char std_header[64]; + const char *tag; + long msglen = 0; + int i; - tx_log(TX_LOG_INFO, "[CM_OL_0004] 공통 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0004] 전문 공통헤더 처리 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0004] KEY 누락"); + memset(msglen_f, 0, sizeof(msglen_f)); + memset(kind, 0, sizeof(kind)); + memset(channel, 0, sizeof(channel)); + memset(datetime, 0, sizeof(datetime)); + memset(std_header, 0, sizeof(std_header)); + + if (tx_buf_get(ctx->in, "MSGLEN", msglen_f, sizeof(msglen_f)) != TX_OK || + tx_buf_get(ctx->in, "MSGTYPE", kind, sizeof(kind)) != TX_OK || + tx_buf_get(ctx->in, "CHANNEL", channel, sizeof(channel)) != TX_OK || + tx_buf_get(ctx->in, "DATETIME", datetime, sizeof(datetime)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0004] 공통헤더 필수 필드 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0004] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 전문길이 숫자검증 */ + for (i = 0; msglen_f[i] != '\0'; i++) { + if (msglen_f[i] < '0' || msglen_f[i] > '9') { + tx_log(TX_LOG_WARN, "[CM_OL_0004] 전문길이 형식오류 msglen=%s", msglen_f); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } + msglen = amount_parse(msglen_f, (int)strlen(msglen_f)); + if (msglen <= 0 || msglen > 9999) { + tx_log(TX_LOG_WARN, "[CM_OL_0004] 전문길이 범위오류 msglen=%ld", msglen); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + tag = header_kind_tag(kind); + if (strcmp(tag, "XX") == 0) { + tx_log(TX_LOG_WARN, "[CM_OL_0004] 미정의 전문종별 kind=%s", kind); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 표준 공통헤더 = 길이(4) + 종별태그(2) + 채널(2) + 거래일시(14) */ + snprintf(std_header, sizeof(std_header), "%04ld%2s%-2.2s%-14.14s", + msglen, tag, channel, datetime); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0004] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "STDHEADER", std_header); + tx_buf_setlong(ctx->out, "MSGLEN", msglen); + tx_buf_sets(ctx->out, "KINDTAG", tag); + tx_buf_setlong(ctx->out, "HDRLEN", (long)strlen(std_header)); + + tx_log(TX_LOG_INFO, "[CM_OL_0004] 헤더조립 완료 tag=%s header=[%s]", tag, std_header); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0005.c b/app/online/cm_ol_0005.c index 7de9dbe..6c7fb74 100644 --- a/app/online/cm_ol_0005.c +++ b/app/online/cm_ol_0005.c @@ -8,15 +8,18 @@ #line 1 "app/online/cm_ol_0005.pgc" /* @tier medium @module cm @transform common-online */ /* - * cm_ol_0005.pgc - 공통 온라인 서비스 (CM_OL_0005) [tier=medium] + * cm_ol_0005.pgc - 에러/메시지 공통 조회 서비스 (CM_OL_0005) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 에러코드(ERRCD)로 공통 메시지 마스터(cm_msg)를 직접 SELECT + * 하여 메시지문/심각도/처리구분을 응답한다. 심각도에 따라 응답코드를 + * 표준 응답코드로 매핑한다. 캐시 미적용 직접조회. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,60 +92,98 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0005.pgc" +#line 18 "app/online/cm_ol_0005.pgc" TX_SERVICE(CM_OL_0005, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/cm_ol_0005.pgc" + char h_errcd [ 8 ] ; + +#line 24 "app/online/cm_ol_0005.pgc" + char h_lang [ 4 ] ; + +#line 25 "app/online/cm_ol_0005.pgc" + char h_msgtext [ 128 ] ; + +#line 26 "app/online/cm_ol_0005.pgc" + char h_severity [ 2 ] ; + +#line 27 "app/online/cm_ol_0005.pgc" + char h_action [ 8 ] ; +/* exec sql end declare section */ +#line 28 "app/online/cm_ol_0005.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0005] 공통 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0005] 필수 필드 누락"); + char errcd[8]; + char lang[4]; + + tx_log(TX_LOG_INFO, "[CM_OL_0005] 에러메시지 조회 서비스 진입"); + + memset(errcd, 0, sizeof(errcd)); + memset(lang, 0, sizeof(lang)); + + if (tx_buf_get(ctx->in, "ERRCD", errcd, sizeof(errcd)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0005] 에러코드(ERRCD) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0005] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0005] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + /* 언어구분 미지정 시 한국어(KO) 기본 */ + if (tx_buf_get(ctx->in, "LANG", lang, sizeof(lang)) != TX_OK) { + strncpy(lang, "KO", sizeof(lang) - 1); } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + memset(h_errcd, 0, sizeof(h_errcd)); + memset(h_lang, 0, sizeof(h_lang)); + memset(h_msgtext, 0, sizeof(h_msgtext)); + memset(h_severity, 0, sizeof(h_severity)); + memset(h_action, 0, sizeof(h_action)); + strncpy(h_errcd, errcd, sizeof(h_errcd) - 1); + strncpy(h_lang, lang, sizeof(h_lang) - 1); - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0005] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select msg_text , severity , action_cd from cm_msg where err_cd = $1 and lang_cd = $2 and use_yn = 'Y'", + ECPGt_char,(h_errcd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_lang),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_msgtext),(long)128,(long)1,(128)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_severity),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_action),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 62 "app/online/cm_ol_0005.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0005] 메시지 없음 errcd=%s lang=%s", errcd, lang); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0005 SELECT cm_msg"); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = cm_rec_count(bizdate, CM_ST_RECV); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0005] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "ERRCD", errcd); + tx_buf_sets(ctx->out, "LANG", lang); + tx_buf_sets(ctx->out, "MSGTEXT", h_msgtext); + tx_buf_sets(ctx->out, "SEVERITY", h_severity); + tx_buf_sets(ctx->out, "ACTION", h_action); + /* 심각도 E(Error) 는 시스템오류 응답, 그 외는 정상 응답코드 */ + tx_buf_sets(ctx->out, "RESPCODE", + (h_severity[0] == 'E') ? RESP_SYSERR : RESP_OK); + + tx_log(TX_LOG_INFO, "[CM_OL_0005] 조회완료 errcd=%s sev=%s msg=%s", + errcd, h_severity, h_msgtext); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0005.pgc b/app/online/cm_ol_0005.pgc index 4d05d0b..34e3431 100644 --- a/app/online/cm_ol_0005.pgc +++ b/app/online/cm_ol_0005.pgc @@ -1,14 +1,17 @@ /* @tier medium @module cm @transform common-online */ /* - * cm_ol_0005.pgc - 공통 온라인 서비스 (CM_OL_0005) [tier=medium] + * cm_ol_0005.pgc - 에러/메시지 공통 조회 서비스 (CM_OL_0005) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 에러코드(ERRCD)로 공통 메시지 마스터(cm_msg)를 직접 SELECT + * 하여 메시지문/심각도/처리구분을 응답한다. 심각도에 따라 응답코드를 + * 표준 응답코드로 매핑한다. 캐시 미적용 직접조회. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,55 +19,70 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0005, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_errcd[8]; + char h_lang[4]; + char h_msgtext[128]; + char h_severity[2]; + char h_action[8]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0005] 공통 등록 서비스 진입"); + char errcd[8]; + char lang[4]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0005] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[CM_OL_0005] 에러메시지 조회 서비스 진입"); + + memset(errcd, 0, sizeof(errcd)); + memset(lang, 0, sizeof(lang)); + + if (tx_buf_get(ctx->in, "ERRCD", errcd, sizeof(errcd)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0005] 에러코드(ERRCD) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0005] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0005] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + /* 언어구분 미지정 시 한국어(KO) 기본 */ + if (tx_buf_get(ctx->in, "LANG", lang, sizeof(lang)) != TX_OK) { + strncpy(lang, "KO", sizeof(lang) - 1); } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + memset(h_errcd, 0, sizeof(h_errcd)); + memset(h_lang, 0, sizeof(h_lang)); + memset(h_msgtext, 0, sizeof(h_msgtext)); + memset(h_severity, 0, sizeof(h_severity)); + memset(h_action, 0, sizeof(h_action)); + strncpy(h_errcd, errcd, sizeof(h_errcd) - 1); + strncpy(h_lang, lang, sizeof(h_lang) - 1); - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0005] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL + SELECT msg_text, severity, action_cd + INTO :h_msgtext, :h_severity, :h_action + FROM cm_msg + WHERE err_cd = :h_errcd + AND lang_cd = :h_lang + AND use_yn = 'Y'; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0005] 메시지 없음 errcd=%s lang=%s", errcd, lang); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0005 SELECT cm_msg"); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = cm_rec_count(bizdate, CM_ST_RECV); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0005] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "ERRCD", errcd); + tx_buf_sets(ctx->out, "LANG", lang); + tx_buf_sets(ctx->out, "MSGTEXT", h_msgtext); + tx_buf_sets(ctx->out, "SEVERITY", h_severity); + tx_buf_sets(ctx->out, "ACTION", h_action); + /* 심각도 E(Error) 는 시스템오류 응답, 그 외는 정상 응답코드 */ + tx_buf_sets(ctx->out, "RESPCODE", + (h_severity[0] == 'E') ? RESP_SYSERR : RESP_OK); + + tx_log(TX_LOG_INFO, "[CM_OL_0005] 조회완료 errcd=%s sev=%s msg=%s", + errcd, h_severity, h_msgtext); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0006.c b/app/online/cm_ol_0006.c index d64535c..c678fa3 100644 --- a/app/online/cm_ol_0006.c +++ b/app/online/cm_ol_0006.c @@ -6,17 +6,21 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0006.pgc" -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0006.pgc - 공통 온라인 서비스 (CM_OL_0006) [tier=easy] + * cm_ol_0006.pgc - 순번 채번 서비스 (CM_OL_0006) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 채번키(SEQKEY)로 공통 순번 마스터(cm_seq)를 UPDATE 로 1 + * 증가시킨 뒤 증가된 순번을 SELECT 하여 발급한다. 채번은 트랜잭션 + * 경계 안에서 원자적으로 수행한다(tx_begin/commit). 순환최대치를 + * 넘으면 1 로 리셋한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,37 +93,126 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0006.pgc" +#line 19 "app/online/cm_ol_0006.pgc" TX_SERVICE(CM_OL_0006, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 24 "app/online/cm_ol_0006.pgc" + char h_seqkey [ 16 ] ; + +#line 25 "app/online/cm_ol_0006.pgc" + char h_prefix [ 8 ] ; + +#line 26 "app/online/cm_ol_0006.pgc" + long h_seqval ; + +#line 27 "app/online/cm_ol_0006.pgc" + long h_maxval ; + +#line 28 "app/online/cm_ol_0006.pgc" + int h_width ; +/* exec sql end declare section */ +#line 29 "app/online/cm_ol_0006.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0006] 공통 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0006] KEY 누락"); + char seqkey[16]; + char issued[32]; + + tx_log(TX_LOG_INFO, "[CM_OL_0006] 순번 채번 서비스 진입"); + + memset(seqkey, 0, sizeof(seqkey)); + memset(issued, 0, sizeof(issued)); + + if (tx_buf_get(ctx->in, "SEQKEY", seqkey, sizeof(seqkey)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0006] 채번키(SEQKEY) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0006] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0006] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); return; } + memset(h_seqkey, 0, sizeof(h_seqkey)); + memset(h_prefix, 0, sizeof(h_prefix)); + strncpy(h_seqkey, seqkey, sizeof(h_seqkey) - 1); + h_seqval = 0; + h_maxval = 0; + h_width = 0; + + /* 순환최대치/자리수/접두어 확보 (행잠금) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select cur_val , max_val , seq_width , prefix from cm_seq where seq_key = $1 for update", + ECPGt_char,(h_seqkey),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_seqval),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_maxval),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_width),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_prefix),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 64 "app/online/cm_ol_0006.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0006] 채번키 미등록 seqkey=%s", seqkey); + tx_abort(); + tx_return(ctx, TX_ENOKEY, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0006 SELECT cm_seq"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 다음 순번 산출 (순환) */ + h_seqval = h_seqval + 1; + if (h_maxval > 0 && h_seqval > h_maxval) { + h_seqval = 1; + tx_log(TX_LOG_INFO, "[CM_OL_0006] 순번 순환 리셋 seqkey=%s", seqkey); + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cm_seq set cur_val = $1 , upd_ts = current_timestamp where seq_key = $2 ", + ECPGt_long,&(h_seqval),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_seqkey),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 90 "app/online/cm_ol_0006.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0006 UPDATE cm_seq"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 접두어 + 자리수 zero-padding 으로 발급번호 조립 */ + if (h_width <= 0 || h_width > 18) { + h_width = 10; + } + snprintf(issued, sizeof(issued), "%s%0*ld", h_prefix, h_width, h_seqval); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0006] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "SEQKEY", seqkey); + tx_buf_setlong(ctx->out, "SEQVAL", h_seqval); + tx_buf_sets(ctx->out, "ISSUEDNO", issued); + + tx_log(TX_LOG_INFO, "[CM_OL_0006] 채번완료 seqkey=%s no=%s", seqkey, issued); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0006.pgc b/app/online/cm_ol_0006.pgc index c2a0fc6..202ecb1 100644 --- a/app/online/cm_ol_0006.pgc +++ b/app/online/cm_ol_0006.pgc @@ -1,14 +1,18 @@ -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0006.pgc - 공통 온라인 서비스 (CM_OL_0006) [tier=easy] + * cm_ol_0006.pgc - 순번 채번 서비스 (CM_OL_0006) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 채번키(SEQKEY)로 공통 순번 마스터(cm_seq)를 UPDATE 로 1 + * 증가시킨 뒤 증가된 순번을 SELECT 하여 발급한다. 채번은 트랜잭션 + * 경계 안에서 원자적으로 수행한다(tx_begin/commit). 순환최대치를 + * 넘으면 1 로 리셋한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,32 +20,95 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0006, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_seqkey[16]; + char h_prefix[8]; + long h_seqval; + long h_maxval; + int h_width; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0006] 공통 단건조회 서비스 진입"); + char seqkey[16]; + char issued[32]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0006] KEY 누락"); + tx_log(TX_LOG_INFO, "[CM_OL_0006] 순번 채번 서비스 진입"); + + memset(seqkey, 0, sizeof(seqkey)); + memset(issued, 0, sizeof(issued)); + + if (tx_buf_get(ctx->in, "SEQKEY", seqkey, sizeof(seqkey)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0006] 채번키(SEQKEY) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0006] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0006] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); return; } + memset(h_seqkey, 0, sizeof(h_seqkey)); + memset(h_prefix, 0, sizeof(h_prefix)); + strncpy(h_seqkey, seqkey, sizeof(h_seqkey) - 1); + h_seqval = 0; + h_maxval = 0; + h_width = 0; + + /* 순환최대치/자리수/접두어 확보 (행잠금) */ + EXEC SQL + SELECT cur_val, max_val, seq_width, prefix + INTO :h_seqval, :h_maxval, :h_width, :h_prefix + FROM cm_seq + WHERE seq_key = :h_seqkey + FOR UPDATE; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0006] 채번키 미등록 seqkey=%s", seqkey); + tx_abort(); + tx_return(ctx, TX_ENOKEY, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0006 SELECT cm_seq"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 다음 순번 산출 (순환) */ + h_seqval = h_seqval + 1; + if (h_maxval > 0 && h_seqval > h_maxval) { + h_seqval = 1; + tx_log(TX_LOG_INFO, "[CM_OL_0006] 순번 순환 리셋 seqkey=%s", seqkey); + } + + EXEC SQL + UPDATE cm_seq + SET cur_val = :h_seqval, + upd_ts = CURRENT_TIMESTAMP + WHERE seq_key = :h_seqkey; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0006 UPDATE cm_seq"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 접두어 + 자리수 zero-padding 으로 발급번호 조립 */ + if (h_width <= 0 || h_width > 18) { + h_width = 10; + } + snprintf(issued, sizeof(issued), "%s%0*ld", h_prefix, h_width, h_seqval); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0006] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "SEQKEY", seqkey); + tx_buf_setlong(ctx->out, "SEQVAL", h_seqval); + tx_buf_sets(ctx->out, "ISSUEDNO", issued); + + tx_log(TX_LOG_INFO, "[CM_OL_0006] 채번완료 seqkey=%s no=%s", seqkey, issued); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0007.c b/app/online/cm_ol_0007.c index a9d7271..82355fb 100644 --- a/app/online/cm_ol_0007.c +++ b/app/online/cm_ol_0007.c @@ -8,15 +8,18 @@ #line 1 "app/online/cm_ol_0007.pgc" /* @tier medium @module cm @transform common-online */ /* - * cm_ol_0007.pgc - 공통 온라인 서비스 (CM_OL_0007) [tier=medium] + * cm_ol_0007.pgc - 로그 공통 기록 서비스 (CM_OL_0007) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 로그레벨(LEVEL)/모듈(MODULE)/메시지(MESSAGE)를 받아 공통 + * 로그 테이블(cm_log)에 INSERT 하고, 채번된 로그일련번호를 응답한다. + * 일련번호는 당일 최대치 +1 로 산출하며 트랜잭션 안에서 기록한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,60 +92,138 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0007.pgc" +#line 18 "app/online/cm_ol_0007.pgc" +/* 로그레벨 문자열 → 유효성(허용레벨만 기록) */ +static int level_is_allowed(const char *lv) +{ + return (strcmp(lv, "DEBUG") == 0 || strcmp(lv, "INFO") == 0 || + strcmp(lv, "WARN") == 0 || strcmp(lv, "ERROR") == 0); +} + TX_SERVICE(CM_OL_0007, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 30 "app/online/cm_ol_0007.pgc" + char h_bizdate [ 9 ] ; + +#line 31 "app/online/cm_ol_0007.pgc" + char h_level [ 8 ] ; + +#line 32 "app/online/cm_ol_0007.pgc" + char h_module [ 16 ] ; + +#line 33 "app/online/cm_ol_0007.pgc" + char h_message [ 256 ] ; + +#line 34 "app/online/cm_ol_0007.pgc" + long h_logseq ; + +#line 35 "app/online/cm_ol_0007.pgc" + long h_xid ; +/* exec sql end declare section */ +#line 36 "app/online/cm_ol_0007.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0007] 공통 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0007] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char level[8]; + char module[16]; + char message[256]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0007] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0007] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[CM_OL_0007] 로그 공통 기록 서비스 진입"); + + memset(level, 0, sizeof(level)); + memset(module, 0, sizeof(module)); + memset(message, 0, sizeof(message)); + + if (tx_buf_get(ctx->in, "LEVEL", level, sizeof(level)) != TX_OK || + tx_buf_get(ctx->in, "MODULE", module, sizeof(module)) != TX_OK || + tx_buf_get(ctx->in, "MESSAGE", message, sizeof(message)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0007] 로그 필수 필드 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0007] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!level_is_allowed(level)) { + tx_log(TX_LOG_WARN, "[CM_OL_0007] 미허용 로그레벨 level=%s", level); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = cm_rec_count(bizdate, CM_ST_RECV); + memset(h_bizdate, 0, sizeof(h_bizdate)); + memset(h_level, 0, sizeof(h_level)); + memset(h_module, 0, sizeof(h_module)); + memset(h_message, 0, sizeof(h_message)); + date_today(h_bizdate); + strncpy(h_level, level, sizeof(h_level) - 1); + strncpy(h_module, module, sizeof(h_module) - 1); + strncpy(h_message, message, sizeof(h_message) - 1); + h_logseq = 0; + h_xid = tx_current_xid(); + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0007] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 당일 로그일련번호 채번 (당일 최대치 +1) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( MAX ( log_seq ) , 0 ) + 1 from cm_log where biz_date = $1 ", + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_logseq),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 84 "app/online/cm_ol_0007.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("CM_OL_0007 SELECT MAX cm_log"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_logseq <= 0) { + h_logseq = 1; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into cm_log ( biz_date , log_seq , log_level , module_id , log_msg , xid , reg_ts ) values ( $1 , $2 , $3 , $4 , $5 , $6 , current_timestamp )", + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_logseq),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_level),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_module),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_message),(long)256,(long)1,(256)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_xid),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 100 "app/online/cm_ol_0007.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0007 INSERT cm_log"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0007] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", h_bizdate); + tx_buf_setlong(ctx->out, "LOGSEQ", h_logseq); + tx_buf_setlong(ctx->out, "XID", h_xid); + + tx_log(TX_LOG_INFO, "[CM_OL_0007] 로그기록 완료 date=%s seq=%ld level=%s", + h_bizdate, h_logseq, level); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0007.pgc b/app/online/cm_ol_0007.pgc index c9f1942..4692b78 100644 --- a/app/online/cm_ol_0007.pgc +++ b/app/online/cm_ol_0007.pgc @@ -1,70 +1,119 @@ /* @tier medium @module cm @transform common-online */ /* - * cm_ol_0007.pgc - 공통 온라인 서비스 (CM_OL_0007) [tier=medium] + * cm_ol_0007.pgc - 로그 공통 기록 서비스 (CM_OL_0007) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 로그레벨(LEVEL)/모듈(MODULE)/메시지(MESSAGE)를 받아 공통 + * 로그 테이블(cm_log)에 INSERT 하고, 채번된 로그일련번호를 응답한다. + * 일련번호는 당일 최대치 +1 로 산출하며 트랜잭션 안에서 기록한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" EXEC SQL INCLUDE sqlca; +/* 로그레벨 문자열 → 유효성(허용레벨만 기록) */ +static int level_is_allowed(const char *lv) +{ + return (strcmp(lv, "DEBUG") == 0 || strcmp(lv, "INFO") == 0 || + strcmp(lv, "WARN") == 0 || strcmp(lv, "ERROR") == 0); +} + TX_SERVICE(CM_OL_0007, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[9]; + char h_level[8]; + char h_module[16]; + char h_message[256]; + long h_logseq; + long h_xid; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0007] 공통 등록 서비스 진입"); + char level[8]; + char module[16]; + char message[256]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0007] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[CM_OL_0007] 로그 공통 기록 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0007] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0007] 영업일 오류 date=%s", bizdate); + memset(level, 0, sizeof(level)); + memset(module, 0, sizeof(module)); + memset(message, 0, sizeof(message)); + + if (tx_buf_get(ctx->in, "LEVEL", level, sizeof(level)) != TX_OK || + tx_buf_get(ctx->in, "MODULE", module, sizeof(module)) != TX_OK || + tx_buf_get(ctx->in, "MESSAGE", message, sizeof(message)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0007] 로그 필수 필드 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0007] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!level_is_allowed(level)) { + tx_log(TX_LOG_WARN, "[CM_OL_0007] 미허용 로그레벨 level=%s", level); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = cm_rec_count(bizdate, CM_ST_RECV); + memset(h_bizdate, 0, sizeof(h_bizdate)); + memset(h_level, 0, sizeof(h_level)); + memset(h_module, 0, sizeof(h_module)); + memset(h_message, 0, sizeof(h_message)); + date_today(h_bizdate); + strncpy(h_level, level, sizeof(h_level) - 1); + strncpy(h_module, module, sizeof(h_module) - 1); + strncpy(h_message, message, sizeof(h_message) - 1); + h_logseq = 0; + h_xid = tx_current_xid(); + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0007] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 당일 로그일련번호 채번 (당일 최대치 +1) */ + EXEC SQL + SELECT COALESCE(MAX(log_seq), 0) + 1 + INTO :h_logseq + FROM cm_log + WHERE biz_date = :h_bizdate; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("CM_OL_0007 SELECT MAX cm_log"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_logseq <= 0) { + h_logseq = 1; + } + + EXEC SQL + INSERT INTO cm_log + (biz_date, log_seq, log_level, module_id, log_msg, xid, reg_ts) + VALUES (:h_bizdate, :h_logseq, :h_level, :h_module, :h_message, + :h_xid, CURRENT_TIMESTAMP); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0007 INSERT cm_log"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0007] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", h_bizdate); + tx_buf_setlong(ctx->out, "LOGSEQ", h_logseq); + tx_buf_setlong(ctx->out, "XID", h_xid); + + tx_log(TX_LOG_INFO, "[CM_OL_0007] 로그기록 완료 date=%s seq=%ld level=%s", + h_bizdate, h_logseq, level); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0008.c b/app/online/cm_ol_0008.c index 52d56f3..41d3568 100644 --- a/app/online/cm_ol_0008.c +++ b/app/online/cm_ol_0008.c @@ -6,17 +6,20 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0008.pgc" -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0008.pgc - 공통 온라인 서비스 (CM_OL_0008) [tier=easy] + * cm_ol_0008.pgc - 세션 공통 검증 서비스 (CM_OL_0008) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 세션ID(SESSID)로 공통 세션 테이블(cm_session)을 조회하여 + * 세션 소유자/상태/만료시각을 확인한다. 만료시각과 현재시각 비교는 + * DB 에서 수행하여 유효/만료 여부를 판정해 응답한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,37 +92,93 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0008.pgc" +#line 18 "app/online/cm_ol_0008.pgc" TX_SERVICE(CM_OL_0008, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/cm_ol_0008.pgc" + char h_sessid [ 40 ] ; + +#line 24 "app/online/cm_ol_0008.pgc" + char h_userid [ 16 ] ; + +#line 25 "app/online/cm_ol_0008.pgc" + char h_status [ 2 ] ; + +#line 26 "app/online/cm_ol_0008.pgc" + char h_expire [ 16 ] ; + +#line 27 "app/online/cm_ol_0008.pgc" + char h_expired [ 2 ] ; +/* exec sql end declare section */ +#line 28 "app/online/cm_ol_0008.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0008] 공통 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0008] KEY 누락"); + char sessid[40]; + int valid; + + tx_log(TX_LOG_INFO, "[CM_OL_0008] 세션 공통 검증 서비스 진입"); + + memset(sessid, 0, sizeof(sessid)); + + if (tx_buf_get(ctx->in, "SESSID", sessid, sizeof(sessid)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0008] 세션ID(SESSID) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0008] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_sessid, 0, sizeof(h_sessid)); + memset(h_userid, 0, sizeof(h_userid)); + memset(h_status, 0, sizeof(h_status)); + memset(h_expire, 0, sizeof(h_expire)); + memset(h_expired, 0, sizeof(h_expired)); + strncpy(h_sessid, sessid, sizeof(h_sessid) - 1); + + /* 만료판정은 DB 현재시각과 비교하여 산출 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select user_id , sess_status , TO_CHAR ( expire_ts , 'YYYYMMDDHH24MISS' ) , case when expire_ts < current_timestamp then 'Y' else 'N' end from cm_session where sess_id = $1 ", + ECPGt_char,(h_sessid),(long)40,(long)1,(40)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_userid),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_expire),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_expired),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 58 "app/online/cm_ol_0008.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0008] 세션 없음 sessid=%s", sessid); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0008 SELECT cm_session"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 유효 = 상태가 활성('A')이고 만료되지 않음 */ + valid = (h_status[0] == 'A' && h_expired[0] == 'N'); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "SESSID", sessid); + tx_buf_sets(ctx->out, "USERID", h_userid); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "EXPIRETS", h_expire); + tx_buf_sets(ctx->out, "VALID", valid ? "Y" : "N"); + + tx_log(TX_LOG_INFO, "[CM_OL_0008] 검증완료 sessid=%s user=%s valid=%s", + sessid, h_userid, valid ? "Y" : "N"); + tx_return(ctx, valid ? TX_OK : TX_FAIL, ctx->out); } diff --git a/app/online/cm_ol_0008.pgc b/app/online/cm_ol_0008.pgc index 772fce3..b5a9a29 100644 --- a/app/online/cm_ol_0008.pgc +++ b/app/online/cm_ol_0008.pgc @@ -1,14 +1,17 @@ -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0008.pgc - 공통 온라인 서비스 (CM_OL_0008) [tier=easy] + * cm_ol_0008.pgc - 세션 공통 검증 서비스 (CM_OL_0008) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 세션ID(SESSID)로 공통 세션 테이블(cm_session)을 조회하여 + * 세션 소유자/상태/만료시각을 확인한다. 만료시각과 현재시각 비교는 + * DB 에서 수행하여 유효/만료 여부를 판정해 응답한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,32 +19,66 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0008, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_sessid[40]; + char h_userid[16]; + char h_status[2]; + char h_expire[16]; + char h_expired[2]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0008] 공통 단건조회 서비스 진입"); + char sessid[40]; + int valid; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0008] KEY 누락"); + tx_log(TX_LOG_INFO, "[CM_OL_0008] 세션 공통 검증 서비스 진입"); + + memset(sessid, 0, sizeof(sessid)); + + if (tx_buf_get(ctx->in, "SESSID", sessid, sizeof(sessid)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0008] 세션ID(SESSID) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0008] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_sessid, 0, sizeof(h_sessid)); + memset(h_userid, 0, sizeof(h_userid)); + memset(h_status, 0, sizeof(h_status)); + memset(h_expire, 0, sizeof(h_expire)); + memset(h_expired, 0, sizeof(h_expired)); + strncpy(h_sessid, sessid, sizeof(h_sessid) - 1); + + /* 만료판정은 DB 현재시각과 비교하여 산출 */ + EXEC SQL + SELECT user_id, + sess_status, + TO_CHAR(expire_ts, 'YYYYMMDDHH24MISS'), + CASE WHEN expire_ts < CURRENT_TIMESTAMP THEN 'Y' ELSE 'N' END + INTO :h_userid, :h_status, :h_expire, :h_expired + FROM cm_session + WHERE sess_id = :h_sessid; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0008] 세션 없음 sessid=%s", sessid); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0008 SELECT cm_session"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 유효 = 상태가 활성('A')이고 만료되지 않음 */ + valid = (h_status[0] == 'A' && h_expired[0] == 'N'); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "SESSID", sessid); + tx_buf_sets(ctx->out, "USERID", h_userid); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "EXPIRETS", h_expire); + tx_buf_sets(ctx->out, "VALID", valid ? "Y" : "N"); + + tx_log(TX_LOG_INFO, "[CM_OL_0008] 검증완료 sessid=%s user=%s valid=%s", + sessid, h_userid, valid ? "Y" : "N"); + tx_return(ctx, valid ? TX_OK : TX_FAIL, ctx->out); } diff --git a/app/online/cm_ol_0009.c b/app/online/cm_ol_0009.c index 30ed70d..c7f41d5 100644 --- a/app/online/cm_ol_0009.c +++ b/app/online/cm_ol_0009.c @@ -6,17 +6,21 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0009.pgc" -/* @tier hard @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0009.pgc - 공통 온라인 서비스 (CM_OL_0009) [tier=hard] + * cm_ol_0009.pgc - 환율 조회 서비스 (CM_OL_0009) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 통화코드(CUR)/기준일자(BASEDATE)로 공통 환율 테이블(cm_rate) + * 에서 매매기준율을 조회한다. 소수 스케일은 정수(rate_scaled)+스케일 + * 자리수로 관리되므로, 기준일 이하 최근 고시분을 조회해 표기 문자열로 + * 재구성하여 응답한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,88 +93,127 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0009.pgc" +#line 19 "app/online/cm_ol_0009.pgc" TX_SERVICE(CM_OL_0009, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + + +#line 24 "app/online/cm_ol_0009.pgc" + char h_cur [ 4 ] ; + +#line 25 "app/online/cm_ol_0009.pgc" + char h_basedate [ 9 ] ; + +#line 26 "app/online/cm_ol_0009.pgc" + char h_quotedate [ 9 ] ; + +#line 27 "app/online/cm_ol_0009.pgc" + long h_scaled ; + +#line 28 "app/online/cm_ol_0009.pgc" + int h_scale ; +/* exec sql end declare section */ +#line 29 "app/online/cm_ol_0009.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0009] 공통 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0009] 필수 필드 누락"); + char cur[4]; + char basedate[9]; + char rate_disp[32]; + long ipart; + long fpart; + int i; + long divisor; + + tx_log(TX_LOG_INFO, "[CM_OL_0009] 환율 조회 서비스 진입"); + + memset(cur, 0, sizeof(cur)); + memset(basedate, 0, sizeof(basedate)); + memset(rate_disp, 0, sizeof(rate_disp)); + + if (tx_buf_get(ctx->in, "CUR", cur, sizeof(cur)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0009] 통화코드(CUR) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0009] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + /* 기준일자 미지정 시 당일 기준 */ + if (tx_buf_get(ctx->in, "BASEDATE", basedate, sizeof(basedate)) != TX_OK) { + date_today(basedate); + } + if (!date_is_valid(basedate)) { + tx_log(TX_LOG_WARN, "[CM_OL_0009] 기준일자 오류 date=%s", basedate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + memset(h_cur, 0, sizeof(h_cur)); + memset(h_basedate, 0, sizeof(h_basedate)); + memset(h_quotedate, 0, sizeof(h_quotedate)); + strncpy(h_cur, cur, sizeof(h_cur) - 1); + strncpy(h_basedate, basedate, sizeof(h_basedate) - 1); + h_scaled = 0; + h_scale = 0; + + /* 기준일 이하 최근 고시 1건 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select rate_scaled , rate_scale , quote_date from cm_rate where cur_cd = $1 and quote_date <= $2 and use_yn = 'Y' order by quote_date desc fetch first 1 row only", + ECPGt_char,(h_cur),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_basedate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_scaled),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_scale),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_quotedate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 77 "app/online/cm_ol_0009.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0009] 환율 없음 cur=%s date=%s", cur, basedate); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0009 SELECT cm_rate"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0009] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; + /* 스케일 방어 후 정수부/소수부 분리 표기 */ + if (h_scale < 0 || h_scale > 8) { + h_scale = 2; } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + divisor = 1; + for (i = 0; i < h_scale; i++) { + divisor *= 10; } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CM_OL_0009] 후속연계 경고 rc=%d", rc); + ipart = h_scaled / divisor; + fpart = h_scaled % divisor; + if (fpart < 0) { + fpart = -fpart; + } + if (h_scale > 0) { + snprintf(rate_disp, sizeof(rate_disp), "%ld.%0*ld", ipart, h_scale, fpart); + } else { + snprintf(rate_disp, sizeof(rate_disp), "%ld", ipart); } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (cm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CM_OL_0009] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "CUR", cur); + tx_buf_sets(ctx->out, "BASEDATE", basedate); + tx_buf_sets(ctx->out, "QUOTEDATE", h_quotedate); + tx_buf_setlong(ctx->out, "RATESCALED", h_scaled); + tx_buf_setlong(ctx->out, "SCALE", (long)h_scale); + tx_buf_sets(ctx->out, "RATE", rate_disp); + + tx_log(TX_LOG_INFO, "[CM_OL_0009] 조회완료 cur=%s rate=%s (고시일 %s)", + cur, rate_disp, h_quotedate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0009.pgc b/app/online/cm_ol_0009.pgc index 58caee6..d455f67 100644 --- a/app/online/cm_ol_0009.pgc +++ b/app/online/cm_ol_0009.pgc @@ -1,14 +1,18 @@ -/* @tier hard @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0009.pgc - 공통 온라인 서비스 (CM_OL_0009) [tier=hard] + * cm_ol_0009.pgc - 환율 조회 서비스 (CM_OL_0009) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 통화코드(CUR)/기준일자(BASEDATE)로 공통 환율 테이블(cm_rate) + * 에서 매매기준율을 조회한다. 소수 스케일은 정수(rate_scaled)+스케일 + * 자리수로 관리되므로, 기준일 이하 최근 고시분을 조회해 표기 문자열로 + * 재구성하여 응답한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,83 +20,101 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0009, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_cur[4]; + char h_basedate[9]; + char h_quotedate[9]; + long h_scaled; + int h_scale; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0009] 공통 트랜잭션 오케스트레이션 진입"); + char cur[4]; + char basedate[9]; + char rate_disp[32]; + long ipart; + long fpart; + int i; + long divisor; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0009] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[CM_OL_0009] 환율 조회 서비스 진입"); + + memset(cur, 0, sizeof(cur)); + memset(basedate, 0, sizeof(basedate)); + memset(rate_disp, 0, sizeof(rate_disp)); + + if (tx_buf_get(ctx->in, "CUR", cur, sizeof(cur)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0009] 통화코드(CUR) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0009] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + /* 기준일자 미지정 시 당일 기준 */ + if (tx_buf_get(ctx->in, "BASEDATE", basedate, sizeof(basedate)) != TX_OK) { + date_today(basedate); + } + if (!date_is_valid(basedate)) { + tx_log(TX_LOG_WARN, "[CM_OL_0009] 기준일자 오류 date=%s", basedate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + memset(h_cur, 0, sizeof(h_cur)); + memset(h_basedate, 0, sizeof(h_basedate)); + memset(h_quotedate, 0, sizeof(h_quotedate)); + strncpy(h_cur, cur, sizeof(h_cur) - 1); + strncpy(h_basedate, basedate, sizeof(h_basedate) - 1); + h_scaled = 0; + h_scale = 0; + + /* 기준일 이하 최근 고시 1건 */ + EXEC SQL + SELECT rate_scaled, rate_scale, quote_date + INTO :h_scaled, :h_scale, :h_quotedate + FROM cm_rate + WHERE cur_cd = :h_cur + AND quote_date <= :h_basedate + AND use_yn = 'Y' + ORDER BY quote_date DESC + FETCH FIRST 1 ROW ONLY; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0009] 환율 없음 cur=%s date=%s", cur, basedate); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0009 SELECT cm_rate"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0009] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; + /* 스케일 방어 후 정수부/소수부 분리 표기 */ + if (h_scale < 0 || h_scale > 8) { + h_scale = 2; } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + divisor = 1; + for (i = 0; i < h_scale; i++) { + divisor *= 10; } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CM_OL_0009] 후속연계 경고 rc=%d", rc); + ipart = h_scaled / divisor; + fpart = h_scaled % divisor; + if (fpart < 0) { + fpart = -fpart; + } + if (h_scale > 0) { + snprintf(rate_disp, sizeof(rate_disp), "%ld.%0*ld", ipart, h_scale, fpart); + } else { + snprintf(rate_disp, sizeof(rate_disp), "%ld", ipart); } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (cm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CM_OL_0009] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "CUR", cur); + tx_buf_sets(ctx->out, "BASEDATE", basedate); + tx_buf_sets(ctx->out, "QUOTEDATE", h_quotedate); + tx_buf_setlong(ctx->out, "RATESCALED", h_scaled); + tx_buf_setlong(ctx->out, "SCALE", (long)h_scale); + tx_buf_sets(ctx->out, "RATE", rate_disp); + + tx_log(TX_LOG_INFO, "[CM_OL_0009] 조회완료 cur=%s rate=%s (고시일 %s)", + cur, rate_disp, h_quotedate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0010.c b/app/online/cm_ol_0010.c index f93ae1d..b3b7392 100644 --- a/app/online/cm_ol_0010.c +++ b/app/online/cm_ol_0010.c @@ -6,17 +6,20 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0010.pgc" -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0010.pgc - 공통 온라인 서비스 (CM_OL_0010) [tier=easy] + * cm_ol_0010.pgc - 공통코드 캐시 로더 서비스 (CM_OL_0010) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 코드그룹(GRP)에 속한 유효 공통코드를 커서로 전량 순회하며 + * 로드 건수를 집계한다. 첫 코드/마지막 코드를 함께 반환하여 캐시 + * 적재 결과를 확인할 수 있게 한다. (기동 시 캐시 워밍업 용도) + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,37 +92,119 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0010.pgc" +#line 18 "app/online/cm_ol_0010.pgc" TX_SERVICE(CM_OL_0010, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 23 "app/online/cm_ol_0010.pgc" + char h_grp [ 16 ] ; + +#line 24 "app/online/cm_ol_0010.pgc" + char h_code [ 16 ] ; + +#line 25 "app/online/cm_ol_0010.pgc" + char h_name [ 64 ] ; +/* exec sql end declare section */ +#line 26 "app/online/cm_ol_0010.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0010] 공통 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0010] KEY 누락"); + char grp[16]; + char first_code[16]; + char last_code[16]; + long loaded; + int cur_open; + + tx_log(TX_LOG_INFO, "[CM_OL_0010] 공통코드 캐시 로더 서비스 진입"); + + memset(grp, 0, sizeof(grp)); + memset(first_code, 0, sizeof(first_code)); + memset(last_code, 0, sizeof(last_code)); + loaded = 0; + cur_open = 0; + + if (tx_buf_get(ctx->in, "GRP", grp, sizeof(grp)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0010] 코드그룹(GRP) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_grp, 0, sizeof(h_grp)); + strncpy(h_grp, grp, sizeof(h_grp) - 1); + + ECPGset_var( 0, ( h_grp ), __LINE__);\ + /* declare cur_code cursor for select code_cd , code_name from cm_code where grp_cd = $1 and use_yn = 'Y' order by sort_no , code_cd */ +#line 57 "app/online/cm_ol_0010.pgc" + +#line 57 "app/online/cm_ol_0010.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_code cursor for select code_cd , code_name from cm_code where grp_cd = $1 and use_yn = 'Y' order by sort_no , code_cd", + ECPGt_char,(h_grp),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 59 "app/online/cm_ol_0010.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0010 OPEN cur_code"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + cur_open = 1; + + for (;;) { + memset(h_code, 0, sizeof(h_code)); + memset(h_name, 0, sizeof(h_name)); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_code", ECPGt_EOIT, + ECPGt_char,(h_code),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_name),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 71 "app/online/cm_ol_0010.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + break; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0010 FETCH cur_code"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_code", ECPGt_EOIT, ECPGt_EORT);} +#line 78 "app/online/cm_ol_0010.pgc" + + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (loaded == 0) { + strncpy(first_code, h_code, sizeof(first_code) - 1); + } + strncpy(last_code, h_code, sizeof(last_code) - 1); + loaded++; + } + + if (cur_open) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_code", ECPGt_EOIT, ECPGt_EORT);} +#line 91 "app/online/cm_ol_0010.pgc" + + } + + if (loaded == 0) { + tx_log(TX_LOG_WARN, "[CM_OL_0010] 적재 대상 없음 grp=%s", grp); + tx_return(ctx, TX_ENOENT, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "GRP", grp); + tx_buf_setlong(ctx->out, "LOADCNT", loaded); + tx_buf_sets(ctx->out, "FIRSTCODE", first_code); + tx_buf_sets(ctx->out, "LASTCODE", last_code); + + tx_log(TX_LOG_INFO, "[CM_OL_0010] 캐시적재 완료 grp=%s 건수=%ld", grp, loaded); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0010.pgc b/app/online/cm_ol_0010.pgc index f7a8890..396cfb8 100644 --- a/app/online/cm_ol_0010.pgc +++ b/app/online/cm_ol_0010.pgc @@ -1,14 +1,17 @@ -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0010.pgc - 공통 온라인 서비스 (CM_OL_0010) [tier=easy] + * cm_ol_0010.pgc - 공통코드 캐시 로더 서비스 (CM_OL_0010) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 코드그룹(GRP)에 속한 유효 공통코드를 커서로 전량 순회하며 + * 로드 건수를 집계한다. 첫 코드/마지막 코드를 함께 반환하여 캐시 + * 적재 결과를 확인할 수 있게 한다. (기동 시 캐시 워밍업 용도) + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,32 +19,90 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0010, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_grp[16]; + char h_code[16]; + char h_name[64]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0010] 공통 단건조회 서비스 진입"); + char grp[16]; + char first_code[16]; + char last_code[16]; + long loaded; + int cur_open; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0010] KEY 누락"); + tx_log(TX_LOG_INFO, "[CM_OL_0010] 공통코드 캐시 로더 서비스 진입"); + + memset(grp, 0, sizeof(grp)); + memset(first_code, 0, sizeof(first_code)); + memset(last_code, 0, sizeof(last_code)); + loaded = 0; + cur_open = 0; + + if (tx_buf_get(ctx->in, "GRP", grp, sizeof(grp)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0010] 코드그룹(GRP) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_grp, 0, sizeof(h_grp)); + strncpy(h_grp, grp, sizeof(h_grp) - 1); + + EXEC SQL + DECLARE cur_code CURSOR FOR + SELECT code_cd, code_name + FROM cm_code + WHERE grp_cd = :h_grp + AND use_yn = 'Y' + ORDER BY sort_no, code_cd; + + EXEC SQL OPEN cur_code; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0010 OPEN cur_code"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + cur_open = 1; + + for (;;) { + memset(h_code, 0, sizeof(h_code)); + memset(h_name, 0, sizeof(h_name)); + + EXEC SQL FETCH cur_code INTO :h_code, :h_name; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + break; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0010 FETCH cur_code"); + EXEC SQL CLOSE cur_code; + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (loaded == 0) { + strncpy(first_code, h_code, sizeof(first_code) - 1); + } + strncpy(last_code, h_code, sizeof(last_code) - 1); + loaded++; + } + + if (cur_open) { + EXEC SQL CLOSE cur_code; + } + + if (loaded == 0) { + tx_log(TX_LOG_WARN, "[CM_OL_0010] 적재 대상 없음 grp=%s", grp); + tx_return(ctx, TX_ENOENT, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "GRP", grp); + tx_buf_setlong(ctx->out, "LOADCNT", loaded); + tx_buf_sets(ctx->out, "FIRSTCODE", first_code); + tx_buf_sets(ctx->out, "LASTCODE", last_code); + + tx_log(TX_LOG_INFO, "[CM_OL_0010] 캐시적재 완료 grp=%s 건수=%ld", grp, loaded); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0011.c b/app/online/cm_ol_0011.c index 8df0ee6..25367a9 100644 --- a/app/online/cm_ol_0011.c +++ b/app/online/cm_ol_0011.c @@ -6,17 +6,20 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0011.pgc" -/* @tier hard @module cm @transform common-online */ +/* @tier easy @module cm @transform common-online */ /* - * cm_ol_0011.pgc - 공통 온라인 서비스 (CM_OL_0011) [tier=hard] + * cm_ol_0011.pgc - 파라미터 조회 서비스 (CM_OL_0011) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 파라미터키(PKEY)로 공통 파라미터 테이블(cm_param)을 직접 + * SELECT 하여 파라미터값/데이터타입/설명을 응답한다. 데이터타입이 + * 숫자(N)인 경우 파싱 가능한 정수값도 함께 제공한다. 직접조회. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,88 +92,88 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0011.pgc" +#line 18 "app/online/cm_ol_0011.pgc" TX_SERVICE(CM_OL_0011, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + +#line 23 "app/online/cm_ol_0011.pgc" + char h_pkey [ 32 ] ; + +#line 24 "app/online/cm_ol_0011.pgc" + char h_pval [ 128 ] ; + +#line 25 "app/online/cm_ol_0011.pgc" + char h_dtype [ 2 ] ; + +#line 26 "app/online/cm_ol_0011.pgc" + char h_desc [ 128 ] ; +/* exec sql end declare section */ +#line 27 "app/online/cm_ol_0011.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0011] 공통 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0011] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char pkey[32]; + long numval; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0011] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + tx_log(TX_LOG_INFO, "[CM_OL_0011] 파라미터 조회 서비스 진입"); + + memset(pkey, 0, sizeof(pkey)); + + if (tx_buf_get(ctx->in, "PKEY", pkey, sizeof(pkey)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0011] 파라미터키(PKEY) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + memset(h_pkey, 0, sizeof(h_pkey)); + memset(h_pval, 0, sizeof(h_pval)); + memset(h_dtype, 0, sizeof(h_dtype)); + memset(h_desc, 0, sizeof(h_desc)); + strncpy(h_pkey, pkey, sizeof(h_pkey) - 1); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select param_val , data_type , param_desc from cm_param where param_key = $1 and use_yn = 'Y'", + ECPGt_char,(h_pkey),(long)32,(long)1,(32)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_pval),(long)128,(long)1,(128)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_dtype),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_desc),(long)128,(long)1,(128)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 53 "app/online/cm_ol_0011.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0011] 파라미터 없음 pkey=%s", pkey); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0011] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0011 SELECT cm_param"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CM_OL_0011] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (cm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CM_OL_0011] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "PKEY", pkey); + tx_buf_sets(ctx->out, "PVAL", h_pval); + tx_buf_sets(ctx->out, "DTYPE", h_dtype); + tx_buf_sets(ctx->out, "PDESC", h_desc); + + /* 숫자형(N) 파라미터는 정수값도 함께 제공 */ + if (h_dtype[0] == 'N') { + numval = amount_parse(h_pval, (int)strlen(h_pval)); + tx_buf_setlong(ctx->out, "NUMVAL", numval); + tx_log(TX_LOG_INFO, "[CM_OL_0011] 조회완료 pkey=%s numval=%ld", pkey, numval); + } else { + tx_log(TX_LOG_INFO, "[CM_OL_0011] 조회완료 pkey=%s val=%s", pkey, h_pval); + } + tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0011.pgc b/app/online/cm_ol_0011.pgc index df56a29..ddee1e3 100644 --- a/app/online/cm_ol_0011.pgc +++ b/app/online/cm_ol_0011.pgc @@ -1,14 +1,17 @@ -/* @tier hard @module cm @transform common-online */ +/* @tier easy @module cm @transform common-online */ /* - * cm_ol_0011.pgc - 공통 온라인 서비스 (CM_OL_0011) [tier=hard] + * cm_ol_0011.pgc - 파라미터 조회 서비스 (CM_OL_0011) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 파라미터키(PKEY)로 공통 파라미터 테이블(cm_param)을 직접 + * SELECT 하여 파라미터값/데이터타입/설명을 응답한다. 데이터타입이 + * 숫자(N)인 경우 파싱 가능한 정수값도 함께 제공한다. 직접조회. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,83 +19,64 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0011, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_pkey[32]; + char h_pval[128]; + char h_dtype[2]; + char h_desc[128]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0011] 공통 트랜잭션 오케스트레이션 진입"); + char pkey[32]; + long numval; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0011] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[CM_OL_0011] 파라미터 조회 서비스 진입"); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0011] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + memset(pkey, 0, sizeof(pkey)); + + if (tx_buf_get(ctx->in, "PKEY", pkey, sizeof(pkey)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0011] 파라미터키(PKEY) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + memset(h_pkey, 0, sizeof(h_pkey)); + memset(h_pval, 0, sizeof(h_pval)); + memset(h_dtype, 0, sizeof(h_dtype)); + memset(h_desc, 0, sizeof(h_desc)); + strncpy(h_pkey, pkey, sizeof(h_pkey) - 1); + + EXEC SQL + SELECT param_val, data_type, param_desc + INTO :h_pval, :h_dtype, :h_desc + FROM cm_param + WHERE param_key = :h_pkey + AND use_yn = 'Y'; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0011] 파라미터 없음 pkey=%s", pkey); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0011] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0011 SELECT cm_param"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CM_OL_0011] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (cm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CM_OL_0011] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "PKEY", pkey); + tx_buf_sets(ctx->out, "PVAL", h_pval); + tx_buf_sets(ctx->out, "DTYPE", h_dtype); + tx_buf_sets(ctx->out, "PDESC", h_desc); + + /* 숫자형(N) 파라미터는 정수값도 함께 제공 */ + if (h_dtype[0] == 'N') { + numval = amount_parse(h_pval, (int)strlen(h_pval)); + tx_buf_setlong(ctx->out, "NUMVAL", numval); + tx_log(TX_LOG_INFO, "[CM_OL_0011] 조회완료 pkey=%s numval=%ld", pkey, numval); + } else { + tx_log(TX_LOG_INFO, "[CM_OL_0011] 조회완료 pkey=%s val=%s", pkey, h_pval); + } + tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0012.c b/app/online/cm_ol_0012.c index ee976fe..b70c181 100644 --- a/app/online/cm_ol_0012.c +++ b/app/online/cm_ol_0012.c @@ -8,15 +8,18 @@ #line 1 "app/online/cm_ol_0012.pgc" /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0012.pgc - 공통 온라인 서비스 (CM_OL_0012) [tier=easy] + * cm_ol_0012.pgc - 코드 변환(내부→대외) 서비스 (CM_OL_0012) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 원천시스템(SRCSYS)/내부코드(SRCCD) 로 코드매핑 마스터 + * (cm_code_map)를 조회하여 대외 표준코드(대상시스템/대상코드/대상명)를 + * 응답 TXBUF 로 반환한다. 대외기관 연계용 코드 변환 게이트웨이. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,37 +92,88 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0012.pgc" +#line 18 "app/online/cm_ol_0012.pgc" TX_SERVICE(CM_OL_0012, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/cm_ol_0012.pgc" + char h_src_sys [ 16 ] ; + +#line 24 "app/online/cm_ol_0012.pgc" + char h_src_cd [ 16 ] ; + +#line 25 "app/online/cm_ol_0012.pgc" + char h_dst_sys [ 16 ] ; + +#line 26 "app/online/cm_ol_0012.pgc" + char h_dst_cd [ 20 ] ; + +#line 27 "app/online/cm_ol_0012.pgc" + char h_dst_name [ 64 ] ; +/* exec sql end declare section */ +#line 28 "app/online/cm_ol_0012.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0012] 공통 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0012] KEY 누락"); + char src_sys[16]; + char src_cd[16]; + + tx_log(TX_LOG_INFO, "[CM_OL_0012] 코드 변환 서비스 진입"); + + if (tx_buf_get(ctx->in, "SRCSYS", src_sys, sizeof(src_sys)) != TX_OK || + tx_buf_get(ctx->in, "SRCCD", src_cd, sizeof(src_cd)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0012] SRCSYS/SRCCD 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_src_sys, 0, sizeof(h_src_sys)); + memset(h_src_cd, 0, sizeof(h_src_cd)); + memset(h_dst_sys, 0, sizeof(h_dst_sys)); + memset(h_dst_cd, 0, sizeof(h_dst_cd)); + memset(h_dst_name, 0, sizeof(h_dst_name)); + strncpy(h_src_sys, src_sys, sizeof(h_src_sys) - 1); + strncpy(h_src_cd, src_cd, sizeof(h_src_cd) - 1); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select dst_sys , dst_cd , dst_name from cm_code_map where src_sys = $1 and src_cd = $2 and use_yn = 'Y'", + ECPGt_char,(h_src_sys),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_src_cd),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_dst_sys),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_dst_cd),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_dst_name),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 56 "app/online/cm_ol_0012.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0012] 매핑 없음 sys=%s cd=%s", src_sys, src_cd); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0012 SELECT cm_code_map"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "SRCSYS", src_sys); + tx_buf_sets(ctx->out, "SRCCD", src_cd); + tx_buf_sets(ctx->out, "DSTSYS", h_dst_sys); + tx_buf_sets(ctx->out, "DSTCD", h_dst_cd); + tx_buf_sets(ctx->out, "DSTNAME", h_dst_name); + tx_log(TX_LOG_INFO, "[CM_OL_0012] 변환완료 %s:%s -> %s:%s", + src_sys, src_cd, h_dst_sys, h_dst_cd); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0012.pgc b/app/online/cm_ol_0012.pgc index affd753..85b6f28 100644 --- a/app/online/cm_ol_0012.pgc +++ b/app/online/cm_ol_0012.pgc @@ -1,14 +1,17 @@ /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0012.pgc - 공통 온라인 서비스 (CM_OL_0012) [tier=easy] + * cm_ol_0012.pgc - 코드 변환(내부→대외) 서비스 (CM_OL_0012) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 원천시스템(SRCSYS)/내부코드(SRCCD) 로 코드매핑 마스터 + * (cm_code_map)를 조회하여 대외 표준코드(대상시스템/대상코드/대상명)를 + * 응답 TXBUF 로 반환한다. 대외기관 연계용 코드 변환 게이트웨이. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,32 +19,60 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0012, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_src_sys[16]; + char h_src_cd[16]; + char h_dst_sys[16]; + char h_dst_cd[20]; + char h_dst_name[64]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0012] 공통 단건조회 서비스 진입"); + char src_sys[16]; + char src_cd[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0012] KEY 누락"); + tx_log(TX_LOG_INFO, "[CM_OL_0012] 코드 변환 서비스 진입"); + + if (tx_buf_get(ctx->in, "SRCSYS", src_sys, sizeof(src_sys)) != TX_OK || + tx_buf_get(ctx->in, "SRCCD", src_cd, sizeof(src_cd)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0012] SRCSYS/SRCCD 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_src_sys, 0, sizeof(h_src_sys)); + memset(h_src_cd, 0, sizeof(h_src_cd)); + memset(h_dst_sys, 0, sizeof(h_dst_sys)); + memset(h_dst_cd, 0, sizeof(h_dst_cd)); + memset(h_dst_name, 0, sizeof(h_dst_name)); + strncpy(h_src_sys, src_sys, sizeof(h_src_sys) - 1); + strncpy(h_src_cd, src_cd, sizeof(h_src_cd) - 1); + + EXEC SQL + SELECT dst_sys, dst_cd, dst_name + INTO :h_dst_sys, :h_dst_cd, :h_dst_name + FROM cm_code_map + WHERE src_sys = :h_src_sys + AND src_cd = :h_src_cd + AND use_yn = 'Y'; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0012] 매핑 없음 sys=%s cd=%s", src_sys, src_cd); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0012 SELECT cm_code_map"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "SRCSYS", src_sys); + tx_buf_sets(ctx->out, "SRCCD", src_cd); + tx_buf_sets(ctx->out, "DSTSYS", h_dst_sys); + tx_buf_sets(ctx->out, "DSTCD", h_dst_cd); + tx_buf_sets(ctx->out, "DSTNAME", h_dst_name); + tx_log(TX_LOG_INFO, "[CM_OL_0012] 변환완료 %s:%s -> %s:%s", + src_sys, src_cd, h_dst_sys, h_dst_cd); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0013.c b/app/online/cm_ol_0013.c index 34afddf..c2b7c6c 100644 --- a/app/online/cm_ol_0013.c +++ b/app/online/cm_ol_0013.c @@ -8,10 +8,12 @@ #line 1 "app/online/cm_ol_0013.pgc" /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0013.pgc - 공통 온라인 서비스 (CM_OL_0013) [tier=easy] + * cm_ol_0013.pgc - 체크섬(LRC) 검증 서비스 (CM_OL_0013) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 원문(DATA) 에 대해 바이트 XOR 누적(LRC) 체크섬을 순수 로직으로 + * 계산하고, 기대값(EXPECT, 16진 2자리) 과 비교하여 일치여부(MATCH=Y/N)를 + * 응답한다. 대외 전문 무결성 검증 공통 루틴. DB 접근 없음. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -89,37 +91,63 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0013.pgc" +#line 17 "app/online/cm_ol_0013.pgc" +/* 문자열 전 구간 바이트 XOR 로 LRC(종방향 중복검사) 계산 */ +static unsigned char lrc_calc(const char *data, int len) +{ + unsigned char lrc = 0; + int i; + + for (i = 0; i < len; i++) + lrc ^= (unsigned char)data[i]; + + return lrc; +} + TX_SERVICE(CM_OL_0013, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + char data[256]; + char expect[8]; + unsigned char lrc; + unsigned int exp_val = 0; + char calc_hex[4]; + int match; - tx_log(TX_LOG_INFO, "[CM_OL_0013] 공통 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0013] 체크섬 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0013] KEY 누락"); + if (tx_buf_get(ctx->in, "DATA", data, sizeof(data)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0013] DATA 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0013] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + lrc = lrc_calc(data, (int)strlen(data)); + snprintf(calc_hex, sizeof(calc_hex), "%02X", (unsigned int)lrc); + + /* 기대값(EXPECT)은 선택 입력: 없으면 계산만 수행 */ + if (tx_buf_get(ctx->in, "EXPECT", expect, sizeof(expect)) == TX_OK) { + if (sscanf(expect, "%x", &exp_val) != 1) { + tx_log(TX_LOG_WARN, "[CM_OL_0013] EXPECT 형식 오류 val=%s", expect); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + match = ((unsigned int)lrc == (exp_val & 0xFF)) ? 1 : 0; + } else { + match = -1; /* 비교 미수행 */ } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0013] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "LRC", calc_hex); + tx_buf_setlong(ctx->out, "LRCDEC", (long)lrc); + if (match < 0) { + tx_buf_sets(ctx->out, "MATCH", "N/A"); + tx_log(TX_LOG_INFO, "[CM_OL_0013] LRC 계산 lrc=%s (비교 미수행)", calc_hex); + } else { + tx_buf_sets(ctx->out, "MATCH", match ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[CM_OL_0013] LRC=%s 기대=%02X 일치=%s", + calc_hex, exp_val & 0xFF, match ? "Y" : "N"); + } tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0013.pgc b/app/online/cm_ol_0013.pgc index 9619d43..55bdb2d 100644 --- a/app/online/cm_ol_0013.pgc +++ b/app/online/cm_ol_0013.pgc @@ -1,9 +1,11 @@ /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0013.pgc - 공통 온라인 서비스 (CM_OL_0013) [tier=easy] + * cm_ol_0013.pgc - 체크섬(LRC) 검증 서비스 (CM_OL_0013) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 원문(DATA) 에 대해 바이트 XOR 누적(LRC) 체크섬을 순수 로직으로 + * 계산하고, 기대값(EXPECT, 16진 2자리) 과 비교하여 일치여부(MATCH=Y/N)를 + * 응답한다. 대외 전문 무결성 검증 공통 루틴. DB 접근 없음. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -14,34 +16,60 @@ EXEC SQL INCLUDE sqlca; +/* 문자열 전 구간 바이트 XOR 로 LRC(종방향 중복검사) 계산 */ +static unsigned char lrc_calc(const char *data, int len) +{ + unsigned char lrc = 0; + int i; + + for (i = 0; i < len; i++) + lrc ^= (unsigned char)data[i]; + + return lrc; +} + TX_SERVICE(CM_OL_0013, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + char data[256]; + char expect[8]; + unsigned char lrc; + unsigned int exp_val = 0; + char calc_hex[4]; + int match; - tx_log(TX_LOG_INFO, "[CM_OL_0013] 공통 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0013] 체크섬 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0013] KEY 누락"); + if (tx_buf_get(ctx->in, "DATA", data, sizeof(data)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0013] DATA 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0013] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + lrc = lrc_calc(data, (int)strlen(data)); + snprintf(calc_hex, sizeof(calc_hex), "%02X", (unsigned int)lrc); + + /* 기대값(EXPECT)은 선택 입력: 없으면 계산만 수행 */ + if (tx_buf_get(ctx->in, "EXPECT", expect, sizeof(expect)) == TX_OK) { + if (sscanf(expect, "%x", &exp_val) != 1) { + tx_log(TX_LOG_WARN, "[CM_OL_0013] EXPECT 형식 오류 val=%s", expect); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + match = ((unsigned int)lrc == (exp_val & 0xFF)) ? 1 : 0; + } else { + match = -1; /* 비교 미수행 */ } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0013] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "LRC", calc_hex); + tx_buf_setlong(ctx->out, "LRCDEC", (long)lrc); + if (match < 0) { + tx_buf_sets(ctx->out, "MATCH", "N/A"); + tx_log(TX_LOG_INFO, "[CM_OL_0013] LRC 계산 lrc=%s (비교 미수행)", calc_hex); + } else { + tx_buf_sets(ctx->out, "MATCH", match ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[CM_OL_0013] LRC=%s 기대=%02X 일치=%s", + calc_hex, exp_val & 0xFF, match ? "Y" : "N"); + } tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0014.c b/app/online/cm_ol_0014.c index 5d1a7dc..5844f5f 100644 --- a/app/online/cm_ol_0014.c +++ b/app/online/cm_ol_0014.c @@ -6,12 +6,14 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0014.pgc" -/* @tier medium @module cm @transform common-online */ +/* @tier easy @module cm @transform common-online */ /* - * cm_ol_0014.pgc - 공통 온라인 서비스 (CM_OL_0014) [tier=medium] + * cm_ol_0014.pgc - 날짜 포맷 변환 서비스 (CM_OL_0014) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 영업일(DATE, YYYYMMDD)을 date_is_valid 로 검증한 뒤 + * YYYY.MM.DD 표기 + 한글 요일명을 포함한 표시문자열로 변환하여 + * 응답한다. 화면/리포트 공통 날짜표기 루틴. DB 접근 없음. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -89,60 +91,53 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0014.pgc" +#line 17 "app/online/cm_ol_0014.pgc" TX_SERVICE(CM_OL_0014, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + static const char *WDAY_KR[7] = { + "일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일" + }; + char date[16]; + char dotted[16]; + char display[48]; + int wd; - tx_log(TX_LOG_INFO, "[CM_OL_0014] 공통 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0014] 날짜 포맷 변환 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0014] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0014] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0014] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "DATE", date, sizeof(date)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0014] DATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0014] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(date)) { + tx_log(TX_LOG_WARN, "[CM_OL_0014] 유효하지 않은 일자 date=%s", date); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = cm_rec_count(bizdate, CM_ST_RECV); + /* YYYYMMDD -> YYYY.MM.DD */ + snprintf(dotted, sizeof(dotted), "%c%c%c%c.%c%c.%c%c", + date[0], date[1], date[2], date[3], + date[4], date[5], date[6], date[7]); + + wd = date_weekday(date); + if (wd < 0 || wd > 6) { + tx_log(TX_LOG_ERROR, "[CM_OL_0014] 요일 계산 오류 date=%s wd=%d", date, wd); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + snprintf(display, sizeof(display), "%s (%s)", dotted, WDAY_KR[wd]); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0014] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "DATE", date); + tx_buf_sets(ctx->out, "DOTTED", dotted); + tx_buf_sets(ctx->out, "WDAYNM", WDAY_KR[wd]); + tx_buf_setlong(ctx->out, "WDAY", (long)wd); + tx_buf_sets(ctx->out, "DISPLAY", display); + tx_log(TX_LOG_INFO, "[CM_OL_0014] 변환완료 %s -> %s", date, display); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0014.pgc b/app/online/cm_ol_0014.pgc index a8288e2..01c27c8 100644 --- a/app/online/cm_ol_0014.pgc +++ b/app/online/cm_ol_0014.pgc @@ -1,9 +1,11 @@ -/* @tier medium @module cm @transform common-online */ +/* @tier easy @module cm @transform common-online */ /* - * cm_ol_0014.pgc - 공통 온라인 서비스 (CM_OL_0014) [tier=medium] + * cm_ol_0014.pgc - 날짜 포맷 변환 서비스 (CM_OL_0014) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 영업일(DATE, YYYYMMDD)을 date_is_valid 로 검증한 뒤 + * YYYY.MM.DD 표기 + 한글 요일명을 포함한 표시문자열로 변환하여 + * 응답한다. 화면/리포트 공통 날짜표기 루틴. DB 접근 없음. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -16,55 +18,48 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0014, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + static const char *WDAY_KR[7] = { + "일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일" + }; + char date[16]; + char dotted[16]; + char display[48]; + int wd; - tx_log(TX_LOG_INFO, "[CM_OL_0014] 공통 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0014] 날짜 포맷 변환 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0014] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0014] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0014] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "DATE", date, sizeof(date)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0014] DATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0014] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(date)) { + tx_log(TX_LOG_WARN, "[CM_OL_0014] 유효하지 않은 일자 date=%s", date); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = cm_rec_count(bizdate, CM_ST_RECV); + /* YYYYMMDD -> YYYY.MM.DD */ + snprintf(dotted, sizeof(dotted), "%c%c%c%c.%c%c.%c%c", + date[0], date[1], date[2], date[3], + date[4], date[5], date[6], date[7]); + + wd = date_weekday(date); + if (wd < 0 || wd > 6) { + tx_log(TX_LOG_ERROR, "[CM_OL_0014] 요일 계산 오류 date=%s wd=%d", date, wd); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + snprintf(display, sizeof(display), "%s (%s)", dotted, WDAY_KR[wd]); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0014] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "DATE", date); + tx_buf_sets(ctx->out, "DOTTED", dotted); + tx_buf_sets(ctx->out, "WDAYNM", WDAY_KR[wd]); + tx_buf_setlong(ctx->out, "WDAY", (long)wd); + tx_buf_sets(ctx->out, "DISPLAY", display); + tx_log(TX_LOG_INFO, "[CM_OL_0014] 변환완료 %s -> %s", date, display); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0015.c b/app/online/cm_ol_0015.c index f923153..654d95e 100644 --- a/app/online/cm_ol_0015.c +++ b/app/online/cm_ol_0015.c @@ -8,10 +8,12 @@ #line 1 "app/online/cm_ol_0015.pgc" /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0015.pgc - 공통 온라인 서비스 (CM_OL_0015) [tier=easy] + * cm_ol_0015.pgc - 통계 공통 조회 서비스 (CM_OL_0015) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 영업일(DATE)에 대해 cm 표준 DBIO(cm_rec_count/cm_rec_sum)를 + * 조합하여 상태별 건수(접수/완료/실패/정산)와 총 거래금액을 집계해 + * 응답한다. 일마감/모니터링 대시보드용 공통 집계 서비스. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -89,37 +91,59 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0015.pgc" +#line 17 "app/online/cm_ol_0015.pgc" TX_SERVICE(CM_OL_0015, ctx) { - cm_rec_t rec; - char key[16]; + char date[16]; + long c_recv, c_done, c_fail, c_settled, c_total; + long sum_amt = 0; int rc; - tx_log(TX_LOG_INFO, "[CM_OL_0015] 공통 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0015] 통계 공통 조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0015] KEY 누락"); + if (tx_buf_get(ctx->in, "DATE", date, sizeof(date)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0015] DATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0015] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(date)) { + tx_log(TX_LOG_WARN, "[CM_OL_0015] 유효하지 않은 영업일 date=%s", date); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 상태별 건수 집계 (음수는 조회 오류 → 0 처리) */ + c_recv = cm_rec_count(date, CM_ST_RECV); + c_done = cm_rec_count(date, CM_ST_DONE); + c_fail = cm_rec_count(date, CM_ST_FAIL); + c_settled = cm_rec_count(date, CM_ST_SETTLED); + + if (c_recv < 0) c_recv = 0; + if (c_done < 0) c_done = 0; + if (c_fail < 0) c_fail = 0; + if (c_settled < 0) c_settled = 0; + c_total = c_recv + c_done + c_fail + c_settled; + + /* 당일 총 거래금액 합계 */ + rc = cm_rec_sum(date, &sum_amt); + if (rc != TX_OK) { + tx_log(TX_LOG_WARN, "[CM_OL_0015] 합계 조회 경고 date=%s rc=%d", date, rc); + sum_amt = 0; + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0015] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "DATE", date); + tx_buf_setlong(ctx->out, "CNTRECV", c_recv); + tx_buf_setlong(ctx->out, "CNTDONE", c_done); + tx_buf_setlong(ctx->out, "CNTFAIL", c_fail); + tx_buf_setlong(ctx->out, "CNTSETTLED", c_settled); + tx_buf_setlong(ctx->out, "CNTTOTAL", c_total); + tx_buf_setlong(ctx->out, "SUMAMT", sum_amt); + tx_log(TX_LOG_INFO, + "[CM_OL_0015] 집계완료 date=%s 총건=%ld 완료=%ld 합계=%ld", + date, c_total, c_done, sum_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0015.pgc b/app/online/cm_ol_0015.pgc index e999e5c..336eae8 100644 --- a/app/online/cm_ol_0015.pgc +++ b/app/online/cm_ol_0015.pgc @@ -1,9 +1,11 @@ /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0015.pgc - 공통 온라인 서비스 (CM_OL_0015) [tier=easy] + * cm_ol_0015.pgc - 통계 공통 조회 서비스 (CM_OL_0015) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 영업일(DATE)에 대해 cm 표준 DBIO(cm_rec_count/cm_rec_sum)를 + * 조합하여 상태별 건수(접수/완료/실패/정산)와 총 거래금액을 집계해 + * 응답한다. 일마감/모니터링 대시보드용 공통 집계 서비스. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -16,32 +18,54 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0015, ctx) { - cm_rec_t rec; - char key[16]; + char date[16]; + long c_recv, c_done, c_fail, c_settled, c_total; + long sum_amt = 0; int rc; - tx_log(TX_LOG_INFO, "[CM_OL_0015] 공통 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0015] 통계 공통 조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0015] KEY 누락"); + if (tx_buf_get(ctx->in, "DATE", date, sizeof(date)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0015] DATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0015] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(date)) { + tx_log(TX_LOG_WARN, "[CM_OL_0015] 유효하지 않은 영업일 date=%s", date); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 상태별 건수 집계 (음수는 조회 오류 → 0 처리) */ + c_recv = cm_rec_count(date, CM_ST_RECV); + c_done = cm_rec_count(date, CM_ST_DONE); + c_fail = cm_rec_count(date, CM_ST_FAIL); + c_settled = cm_rec_count(date, CM_ST_SETTLED); + + if (c_recv < 0) c_recv = 0; + if (c_done < 0) c_done = 0; + if (c_fail < 0) c_fail = 0; + if (c_settled < 0) c_settled = 0; + c_total = c_recv + c_done + c_fail + c_settled; + + /* 당일 총 거래금액 합계 */ + rc = cm_rec_sum(date, &sum_amt); + if (rc != TX_OK) { + tx_log(TX_LOG_WARN, "[CM_OL_0015] 합계 조회 경고 date=%s rc=%d", date, rc); + sum_amt = 0; + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0015] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "DATE", date); + tx_buf_setlong(ctx->out, "CNTRECV", c_recv); + tx_buf_setlong(ctx->out, "CNTDONE", c_done); + tx_buf_setlong(ctx->out, "CNTFAIL", c_fail); + tx_buf_setlong(ctx->out, "CNTSETTLED", c_settled); + tx_buf_setlong(ctx->out, "CNTTOTAL", c_total); + tx_buf_setlong(ctx->out, "SUMAMT", sum_amt); + tx_log(TX_LOG_INFO, + "[CM_OL_0015] 집계완료 date=%s 총건=%ld 완료=%ld 합계=%ld", + date, c_total, c_done, sum_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0016.c b/app/online/cm_ol_0016.c index 184c30b..4ea565c 100644 --- a/app/online/cm_ol_0016.c +++ b/app/online/cm_ol_0016.c @@ -6,17 +6,20 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0016.pgc" -/* @tier medium @module cm @transform common-online */ +/* @tier easy @module cm @transform common-online */ /* - * cm_ol_0016.pgc - 공통 온라인 서비스 (CM_OL_0016) [tier=medium] + * cm_ol_0016.pgc - 배치 컨트롤 조회 서비스 (CM_OL_0016) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 배치ID(BATCHID)로 배치 통제 테이블(cm_batch_ctl)을 조회하여 + * 최종수행일시/수행상태/수행건수/대상영업일을 응답한다. 운영 콘솔의 + * 배치 상태 모니터링용 공통 조회 서비스. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,60 +92,85 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0016.pgc" +#line 18 "app/online/cm_ol_0016.pgc" TX_SERVICE(CM_OL_0016, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/cm_ol_0016.pgc" + char h_batch_id [ 24 ] ; + +#line 24 "app/online/cm_ol_0016.pgc" + char h_last_run [ 20 ] ; + +#line 25 "app/online/cm_ol_0016.pgc" + char h_run_status [ 2 ] ; + +#line 26 "app/online/cm_ol_0016.pgc" + char h_biz_date [ 9 ] ; + +#line 27 "app/online/cm_ol_0016.pgc" + long h_run_cnt ; +/* exec sql end declare section */ +#line 28 "app/online/cm_ol_0016.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0016] 공통 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0016] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char batch_id[24]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0016] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0016] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[CM_OL_0016] 배치 컨트롤 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "BATCHID", batch_id, sizeof(batch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0016] BATCHID 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + memset(h_batch_id, 0, sizeof(h_batch_id)); + memset(h_last_run, 0, sizeof(h_last_run)); + memset(h_run_status, 0, sizeof(h_run_status)); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_batch_id, batch_id, sizeof(h_batch_id) - 1); + h_run_cnt = 0; - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0016] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select last_run_dt , run_status , run_cnt , biz_date from cm_batch_ctl where batch_id = $1 ", + ECPGt_char,(h_batch_id),(long)24,(long)1,(24)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_last_run),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_run_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_run_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 51 "app/online/cm_ol_0016.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0016] 배치 정보 없음 id=%s", batch_id); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0016 SELECT cm_batch_ctl"); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = cm_rec_count(bizdate, CM_ST_RECV); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0016] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BATCHID", batch_id); + tx_buf_sets(ctx->out, "LASTRUN", h_last_run); + tx_buf_sets(ctx->out, "RUNSTATUS", h_run_status); + tx_buf_sets(ctx->out, "BIZDATE", h_biz_date); + tx_buf_setlong(ctx->out, "RUNCNT", h_run_cnt); + tx_log(TX_LOG_INFO, "[CM_OL_0016] 조회완료 id=%s 상태=%s 최종=%s 건수=%ld", + batch_id, h_run_status, h_last_run, h_run_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0016.pgc b/app/online/cm_ol_0016.pgc index 9e023e3..2a2691c 100644 --- a/app/online/cm_ol_0016.pgc +++ b/app/online/cm_ol_0016.pgc @@ -1,14 +1,17 @@ -/* @tier medium @module cm @transform common-online */ +/* @tier easy @module cm @transform common-online */ /* - * cm_ol_0016.pgc - 공통 온라인 서비스 (CM_OL_0016) [tier=medium] + * cm_ol_0016.pgc - 배치 컨트롤 조회 서비스 (CM_OL_0016) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 배치ID(BATCHID)로 배치 통제 테이블(cm_batch_ctl)을 조회하여 + * 최종수행일시/수행상태/수행건수/대상영업일을 응답한다. 운영 콘솔의 + * 배치 상태 모니터링용 공통 조회 서비스. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,55 +19,55 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0016, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_batch_id[24]; + char h_last_run[20]; + char h_run_status[2]; + char h_biz_date[9]; + long h_run_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0016] 공통 등록 서비스 진입"); + char batch_id[24]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0016] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[CM_OL_0016] 배치 컨트롤 조회 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0016] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0016] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "BATCHID", batch_id, sizeof(batch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0016] BATCHID 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + memset(h_batch_id, 0, sizeof(h_batch_id)); + memset(h_last_run, 0, sizeof(h_last_run)); + memset(h_run_status, 0, sizeof(h_run_status)); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_batch_id, batch_id, sizeof(h_batch_id) - 1); + h_run_cnt = 0; - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0016] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL + SELECT last_run_dt, run_status, run_cnt, biz_date + INTO :h_last_run, :h_run_status, :h_run_cnt, :h_biz_date + FROM cm_batch_ctl + WHERE batch_id = :h_batch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0016] 배치 정보 없음 id=%s", batch_id); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0016 SELECT cm_batch_ctl"); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = cm_rec_count(bizdate, CM_ST_RECV); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0016] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BATCHID", batch_id); + tx_buf_sets(ctx->out, "LASTRUN", h_last_run); + tx_buf_sets(ctx->out, "RUNSTATUS", h_run_status); + tx_buf_sets(ctx->out, "BIZDATE", h_biz_date); + tx_buf_setlong(ctx->out, "RUNCNT", h_run_cnt); + tx_log(TX_LOG_INFO, "[CM_OL_0016] 조회완료 id=%s 상태=%s 최종=%s 건수=%ld", + batch_id, h_run_status, h_last_run, h_run_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0017.c b/app/online/cm_ol_0017.c index c53eb6d..242783c 100644 --- a/app/online/cm_ol_0017.c +++ b/app/online/cm_ol_0017.c @@ -6,17 +6,20 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0017.pgc" -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0017.pgc - 공통 온라인 서비스 (CM_OL_0017) [tier=easy] + * cm_ol_0017.pgc - 공통코드 목록 조회 서비스 (CM_OL_0017) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 코드그룹(GRP)에 속한 전체 공통코드(cm_code)를 커서로 순회하며 + * "코드:코드명" 항목을 세미콜론 구분 목록 문자열로 누적 구성하여 + * 목록(LIST)과 건수(CNT)를 응답한다. 코드박스/드롭다운 조회 공통 서비스. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,37 +92,116 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0017.pgc" +#line 18 "app/online/cm_ol_0017.pgc" TX_SERVICE(CM_OL_0017, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 23 "app/online/cm_ol_0017.pgc" + char h_grp [ 16 ] ; + +#line 24 "app/online/cm_ol_0017.pgc" + char h_code [ 16 ] ; + +#line 25 "app/online/cm_ol_0017.pgc" + char h_name [ 64 ] ; +/* exec sql end declare section */ +#line 26 "app/online/cm_ol_0017.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0017] 공통 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0017] KEY 누락"); + char grp[16]; + char list[1024]; + char item[96]; + long cnt = 0; + int truncated = 0; + + tx_log(TX_LOG_INFO, "[CM_OL_0017] 공통코드 목록 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "GRP", grp, sizeof(grp)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0017] GRP 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0017] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_grp, 0, sizeof(h_grp)); + strncpy(h_grp, grp, sizeof(h_grp) - 1); + list[0] = '\0'; + + ECPGset_var( 0, ( h_grp ), __LINE__);\ + /* declare cur_cm_ol_0017 cursor for select code_cd , code_name from cm_code where grp_cd = $1 and use_yn = 'Y' order by sort_no , code_cd */ +#line 51 "app/online/cm_ol_0017.pgc" + +#line 51 "app/online/cm_ol_0017.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_cm_ol_0017 cursor for select code_cd , code_name from cm_code where grp_cd = $1 and use_yn = 'Y' order by sort_no , code_cd", + ECPGt_char,(h_grp),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 53 "app/online/cm_ol_0017.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0017 OPEN cur_cm_ol_0017"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + for (;;) { + memset(h_code, 0, sizeof(h_code)); + memset(h_name, 0, sizeof(h_name)); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_cm_ol_0017", ECPGt_EOIT, + ECPGt_char,(h_code),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_name),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 64 "app/online/cm_ol_0017.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0017 FETCH cur_cm_ol_0017"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_ol_0017", ECPGt_EOIT, ECPGt_EORT);} +#line 70 "app/online/cm_ol_0017.pgc" + + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + snprintf(item, sizeof(item), "%s:%s", h_code, h_name); + + /* 목록 버퍼 오버플로 방지: 구분자 포함 여유 확인 */ + if (strlen(list) + strlen(item) + 2 >= sizeof(list)) { + truncated = 1; + break; + } + if (cnt > 0) + strncat(list, ";", sizeof(list) - strlen(list) - 1); + strncat(list, item, sizeof(list) - strlen(list) - 1); + cnt++; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_cm_ol_0017", ECPGt_EOIT, ECPGt_EORT);} +#line 88 "app/online/cm_ol_0017.pgc" + + + if (cnt == 0) { + tx_log(TX_LOG_WARN, "[CM_OL_0017] 코드 없음 grp=%s", grp); + tx_return(ctx, TX_ENOENT, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0017] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "GRP", grp); + tx_buf_sets(ctx->out, "LIST", list); + tx_buf_setlong(ctx->out, "CNT", cnt); + tx_buf_sets(ctx->out, "TRUNCATED", truncated ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[CM_OL_0017] 목록완료 grp=%s 건수=%ld 절단=%s", + grp, cnt, truncated ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0017.pgc b/app/online/cm_ol_0017.pgc index 8469151..952e2a1 100644 --- a/app/online/cm_ol_0017.pgc +++ b/app/online/cm_ol_0017.pgc @@ -1,14 +1,17 @@ -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0017.pgc - 공통 온라인 서비스 (CM_OL_0017) [tier=easy] + * cm_ol_0017.pgc - 공통코드 목록 조회 서비스 (CM_OL_0017) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 코드그룹(GRP)에 속한 전체 공통코드(cm_code)를 커서로 순회하며 + * "코드:코드명" 항목을 세미콜론 구분 목록 문자열로 누적 구성하여 + * 목록(LIST)과 건수(CNT)를 응답한다. 코드박스/드롭다운 조회 공통 서비스. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,32 +19,86 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0017, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_grp[16]; + char h_code[16]; + char h_name[64]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0017] 공통 단건조회 서비스 진입"); + char grp[16]; + char list[1024]; + char item[96]; + long cnt = 0; + int truncated = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0017] KEY 누락"); + tx_log(TX_LOG_INFO, "[CM_OL_0017] 공통코드 목록 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "GRP", grp, sizeof(grp)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0017] GRP 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0017] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_grp, 0, sizeof(h_grp)); + strncpy(h_grp, grp, sizeof(h_grp) - 1); + list[0] = '\0'; + + EXEC SQL DECLARE cur_cm_ol_0017 CURSOR FOR + SELECT code_cd, code_name + FROM cm_code + WHERE grp_cd = :h_grp + AND use_yn = 'Y' + ORDER BY sort_no, code_cd; + + EXEC SQL OPEN cur_cm_ol_0017; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0017 OPEN cur_cm_ol_0017"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + for (;;) { + memset(h_code, 0, sizeof(h_code)); + memset(h_name, 0, sizeof(h_name)); + + EXEC SQL FETCH cur_cm_ol_0017 INTO :h_code, :h_name; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0017 FETCH cur_cm_ol_0017"); + EXEC SQL CLOSE cur_cm_ol_0017; + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + snprintf(item, sizeof(item), "%s:%s", h_code, h_name); + + /* 목록 버퍼 오버플로 방지: 구분자 포함 여유 확인 */ + if (strlen(list) + strlen(item) + 2 >= sizeof(list)) { + truncated = 1; + break; + } + if (cnt > 0) + strncat(list, ";", sizeof(list) - strlen(list) - 1); + strncat(list, item, sizeof(list) - strlen(list) - 1); + cnt++; + } + + EXEC SQL CLOSE cur_cm_ol_0017; + + if (cnt == 0) { + tx_log(TX_LOG_WARN, "[CM_OL_0017] 코드 없음 grp=%s", grp); + tx_return(ctx, TX_ENOENT, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0017] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "GRP", grp); + tx_buf_sets(ctx->out, "LIST", list); + tx_buf_setlong(ctx->out, "CNT", cnt); + tx_buf_sets(ctx->out, "TRUNCATED", truncated ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[CM_OL_0017] 목록완료 grp=%s 건수=%ld 절단=%s", + grp, cnt, truncated ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0018.c b/app/online/cm_ol_0018.c index b14619a..d6bf573 100644 --- a/app/online/cm_ol_0018.c +++ b/app/online/cm_ol_0018.c @@ -8,15 +8,18 @@ #line 1 "app/online/cm_ol_0018.pgc" /* @tier medium @module cm @transform common-online */ /* - * cm_ol_0018.pgc - 공통 온라인 서비스 (CM_OL_0018) [tier=medium] + * cm_ol_0018.pgc - 영업일 여부 판정 서비스 (CM_OL_0018) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 일자(DATE)에 대해 (1) 주말여부(date_is_weekend) 판정과 + * (2) 공휴일 마스터(cm_holiday) 등록여부 조회를 종합하여 영업일 여부 + * (BIZYN=Y/N)와 비영업 사유를 응답한다. 정산일/마감일 산정 공통 서비스. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,60 +92,98 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0018.pgc" +#line 18 "app/online/cm_ol_0018.pgc" TX_SERVICE(CM_OL_0018, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + +#line 23 "app/online/cm_ol_0018.pgc" + char h_date [ 9 ] ; + +#line 24 "app/online/cm_ol_0018.pgc" + char h_holi_name [ 64 ] ; + +#line 25 "app/online/cm_ol_0018.pgc" + int h_holi_cnt ; +/* exec sql end declare section */ +#line 26 "app/online/cm_ol_0018.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0018] 공통 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0018] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char date[16]; + char reason[80]; + int is_weekend; + int is_holiday = 0; + int is_biz; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0018] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0018] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[CM_OL_0018] 영업일 여부 판정 서비스 진입"); + + if (tx_buf_get(ctx->in, "DATE", date, sizeof(date)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0018] DATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0018] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(date)) { + tx_log(TX_LOG_WARN, "[CM_OL_0018] 유효하지 않은 일자 date=%s", date); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = cm_rec_count(bizdate, CM_ST_RECV); + is_weekend = date_is_weekend(date); + + /* 공휴일 마스터 조회 (등록건수 + 공휴일명) */ + memset(h_date, 0, sizeof(h_date)); + memset(h_holi_name, 0, sizeof(h_holi_name)); + strncpy(h_date, date, sizeof(h_date) - 1); + h_holi_cnt = 0; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from cm_holiday where holi_date = $1 ", + ECPGt_char,(h_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_holi_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 60 "app/online/cm_ol_0018.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("CM_OL_0018 SELECT cm_holiday"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_holi_cnt > 0) { + is_holiday = 1; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select holi_name from cm_holiday where holi_date = $1 ", + ECPGt_char,(h_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_holi_name),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 73 "app/online/cm_ol_0018.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) + strncpy(h_holi_name, "공휴일", sizeof(h_holi_name) - 1); + } + + /* 영업일 = 주말 아님 AND 공휴일 아님 */ + is_biz = (!is_weekend && !is_holiday) ? 1 : 0; + if (is_biz) + strncpy(reason, "영업일", sizeof(reason) - 1); + else if (is_holiday) + snprintf(reason, sizeof(reason), "공휴일(%s)", h_holi_name); + else + strncpy(reason, "주말", sizeof(reason) - 1); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0018] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "DATE", date); + tx_buf_sets(ctx->out, "BIZYN", is_biz ? "Y" : "N"); + tx_buf_sets(ctx->out, "WEEKEND", is_weekend ? "Y" : "N"); + tx_buf_sets(ctx->out, "HOLIDAY", is_holiday ? "Y" : "N"); + tx_buf_sets(ctx->out, "REASON", reason); + tx_log(TX_LOG_INFO, "[CM_OL_0018] 판정완료 date=%s 영업일=%s 사유=%s", + date, is_biz ? "Y" : "N", reason); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0018.pgc b/app/online/cm_ol_0018.pgc index 9f278a3..d3c514d 100644 --- a/app/online/cm_ol_0018.pgc +++ b/app/online/cm_ol_0018.pgc @@ -1,14 +1,17 @@ /* @tier medium @module cm @transform common-online */ /* - * cm_ol_0018.pgc - 공통 온라인 서비스 (CM_OL_0018) [tier=medium] + * cm_ol_0018.pgc - 영업일 여부 판정 서비스 (CM_OL_0018) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 일자(DATE)에 대해 (1) 주말여부(date_is_weekend) 판정과 + * (2) 공휴일 마스터(cm_holiday) 등록여부 조회를 종합하여 영업일 여부 + * (BIZYN=Y/N)와 비영업 사유를 응답한다. 정산일/마감일 산정 공통 서비스. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,55 +19,78 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0018, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[9]; + char h_holi_name[64]; + int h_holi_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0018] 공통 등록 서비스 진입"); + char date[16]; + char reason[80]; + int is_weekend; + int is_holiday = 0; + int is_biz; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0018] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[CM_OL_0018] 영업일 여부 판정 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0018] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0018] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "DATE", date, sizeof(date)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0018] DATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0018] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(date)) { + tx_log(TX_LOG_WARN, "[CM_OL_0018] 유효하지 않은 일자 date=%s", date); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = cm_rec_count(bizdate, CM_ST_RECV); + is_weekend = date_is_weekend(date); + + /* 공휴일 마스터 조회 (등록건수 + 공휴일명) */ + memset(h_date, 0, sizeof(h_date)); + memset(h_holi_name, 0, sizeof(h_holi_name)); + strncpy(h_date, date, sizeof(h_date) - 1); + h_holi_cnt = 0; + + EXEC SQL + SELECT COUNT(*) + INTO :h_holi_cnt + FROM cm_holiday + WHERE holi_date = :h_date; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("CM_OL_0018 SELECT cm_holiday"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_holi_cnt > 0) { + is_holiday = 1; + EXEC SQL + SELECT holi_name + INTO :h_holi_name + FROM cm_holiday + WHERE holi_date = :h_date; + if (sqlca.sqlcode != TX_SQL_OK) + strncpy(h_holi_name, "공휴일", sizeof(h_holi_name) - 1); + } + + /* 영업일 = 주말 아님 AND 공휴일 아님 */ + is_biz = (!is_weekend && !is_holiday) ? 1 : 0; + if (is_biz) + strncpy(reason, "영업일", sizeof(reason) - 1); + else if (is_holiday) + snprintf(reason, sizeof(reason), "공휴일(%s)", h_holi_name); + else + strncpy(reason, "주말", sizeof(reason) - 1); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0018] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "DATE", date); + tx_buf_sets(ctx->out, "BIZYN", is_biz ? "Y" : "N"); + tx_buf_sets(ctx->out, "WEEKEND", is_weekend ? "Y" : "N"); + tx_buf_sets(ctx->out, "HOLIDAY", is_holiday ? "Y" : "N"); + tx_buf_sets(ctx->out, "REASON", reason); + tx_log(TX_LOG_INFO, "[CM_OL_0018] 판정완료 date=%s 영업일=%s 사유=%s", + date, is_biz ? "Y" : "N", reason); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0019.c b/app/online/cm_ol_0019.c index 04b7074..76c3e76 100644 --- a/app/online/cm_ol_0019.c +++ b/app/online/cm_ol_0019.c @@ -8,10 +8,12 @@ #line 1 "app/online/cm_ol_0019.pgc" /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0019.pgc - 공통 온라인 서비스 (CM_OL_0019) [tier=easy] + * cm_ol_0019.pgc - 요일 계산 서비스 (CM_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 일자(DATE, YYYYMMDD)에 대해 date_weekday 로 요일 인덱스 + * (0=일 ~ 6=토)를 구하고 한글 요일명(일~토)으로 매핑하여 요일 인덱스/ + * 요일명/주말여부를 응답한다. 순수 로직 요일 산출 공통 서비스. DB 접근 없음. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -89,37 +91,51 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0019.pgc" +#line 17 "app/online/cm_ol_0019.pgc" TX_SERVICE(CM_OL_0019, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + static const char *WDAY_SHORT[7] = { + "일", "월", "화", "수", "목", "금", "토" + }; + char date[16]; + char label[8]; + int wd; + int weekend; - tx_log(TX_LOG_INFO, "[CM_OL_0019] 공통 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0019] 요일 계산 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0019] KEY 누락"); + if (tx_buf_get(ctx->in, "DATE", date, sizeof(date)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0019] DATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(date)) { + tx_log(TX_LOG_WARN, "[CM_OL_0019] 유효하지 않은 일자 date=%s", date); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + wd = date_weekday(date); + if (wd < 0 || wd > 6) { + tx_log(TX_LOG_ERROR, "[CM_OL_0019] 요일 계산 실패 date=%s wd=%d", date, wd); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 주말 = 일(0) 또는 토(6) */ + weekend = (wd == 0 || wd == 6) ? 1 : 0; + snprintf(label, sizeof(label), "%s요일", WDAY_SHORT[wd]); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "DATE", date); + tx_buf_setlong(ctx->out, "WDAY", (long)wd); + tx_buf_sets(ctx->out, "WDAYNM", WDAY_SHORT[wd]); + tx_buf_sets(ctx->out, "WDAYLABEL", label); + tx_buf_sets(ctx->out, "WEEKEND", weekend ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[CM_OL_0019] 계산완료 date=%s 요일=%s 주말=%s", + date, label, weekend ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0019.pgc b/app/online/cm_ol_0019.pgc index aee6fd5..48585df 100644 --- a/app/online/cm_ol_0019.pgc +++ b/app/online/cm_ol_0019.pgc @@ -1,9 +1,11 @@ /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0019.pgc - 공통 온라인 서비스 (CM_OL_0019) [tier=easy] + * cm_ol_0019.pgc - 요일 계산 서비스 (CM_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 일자(DATE, YYYYMMDD)에 대해 date_weekday 로 요일 인덱스 + * (0=일 ~ 6=토)를 구하고 한글 요일명(일~토)으로 매핑하여 요일 인덱스/ + * 요일명/주말여부를 응답한다. 순수 로직 요일 산출 공통 서비스. DB 접근 없음. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -16,32 +18,46 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0019, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + static const char *WDAY_SHORT[7] = { + "일", "월", "화", "수", "목", "금", "토" + }; + char date[16]; + char label[8]; + int wd; + int weekend; - tx_log(TX_LOG_INFO, "[CM_OL_0019] 공통 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0019] 요일 계산 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0019] KEY 누락"); + if (tx_buf_get(ctx->in, "DATE", date, sizeof(date)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0019] DATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(date)) { + tx_log(TX_LOG_WARN, "[CM_OL_0019] 유효하지 않은 일자 date=%s", date); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + wd = date_weekday(date); + if (wd < 0 || wd > 6) { + tx_log(TX_LOG_ERROR, "[CM_OL_0019] 요일 계산 실패 date=%s wd=%d", date, wd); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 주말 = 일(0) 또는 토(6) */ + weekend = (wd == 0 || wd == 6) ? 1 : 0; + snprintf(label, sizeof(label), "%s요일", WDAY_SHORT[wd]); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "DATE", date); + tx_buf_setlong(ctx->out, "WDAY", (long)wd); + tx_buf_sets(ctx->out, "WDAYNM", WDAY_SHORT[wd]); + tx_buf_sets(ctx->out, "WDAYLABEL", label); + tx_buf_sets(ctx->out, "WEEKEND", weekend ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[CM_OL_0019] 계산완료 date=%s 요일=%s 주말=%s", + date, label, weekend ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0020.c b/app/online/cm_ol_0020.c index 217d7e7..0244f4b 100644 --- a/app/online/cm_ol_0020.c +++ b/app/online/cm_ol_0020.c @@ -6,12 +6,14 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0020.pgc" -/* @tier hard @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0020.pgc - 공통 온라인 서비스 (CM_OL_0020) [tier=hard] + * cm_ol_0020.pgc - 금액 한글 표기 서비스 (CM_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 금액(AMOUNT)을 만/억/조 단위 절(節)로 분해하여 한글 금액표기 + * (예: 123000000 -> "일억이천삼백만")로 변환하여 응답한다. 증서/전표 + * 출력용 순수 로직 변환 서비스. DB 접근 없음. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -89,88 +91,89 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0020.pgc" +#line 17 "app/online/cm_ol_0020.pgc" +/* 0~9999 한 절(節)을 천/백/십 단위 한글로 변환하여 out 에 누적 */ +static void hangeul_group(int val, char *out, int outlen) +{ + static const char *DIG[10] = { + "", "일", "이", "삼", "사", "오", "육", "칠", "팔", "구" + }; + static const char *POS[4] = { "", "십", "백", "천" }; + int unit[4]; + int i; + + unit[3] = (val / 1000) % 10; + unit[2] = (val / 100) % 10; + unit[1] = (val / 10) % 10; + unit[0] = val % 10; + + for (i = 3; i >= 0; i--) { + if (unit[i] == 0) + continue; + /* 십/백/천 자리의 '일'은 관용상 생략 (예: 15 -> 십오) */ + if (!(unit[i] == 1 && i > 0)) + strncat(out, DIG[unit[i]], outlen - strlen(out) - 1); + strncat(out, POS[i], outlen - strlen(out) - 1); + } +} + TX_SERVICE(CM_OL_0020, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + static const char *GUNIT[4] = { "", "만", "억", "조" }; + char hangeul[128]; + char won_fmt[48]; + long amount = 0; + long n; + int neg = 0; + int g; + int groups[4]; + int i; - tx_log(TX_LOG_INFO, "[CM_OL_0020] 공통 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0020] 금액 한글 표기 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0020] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0020] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (tx_buf_getlong(ctx->in, "AMOUNT", &amount) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0020] AMOUNT 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; + hangeul[0] = '\0'; + n = amount; + if (n < 0) { neg = 1; n = -n; } + + if (n == 0) { + strncpy(hangeul, "영", sizeof(hangeul) - 1); + } else { + /* 4자리 절 단위 분해: 일/만/억/조 (조 단위까지 지원) */ + for (i = 0; i < 4; i++) { + groups[i] = (int)(n % 10000); + n /= 10000; + } + for (i = 3; i >= 0; i--) { + if (groups[i] == 0) + continue; + hangeul_group(groups[i], hangeul, sizeof(hangeul)); + strncat(hangeul, GUNIT[i], sizeof(hangeul) - strlen(hangeul) - 1); + } + } + strncat(hangeul, "원", sizeof(hangeul) - strlen(hangeul) - 1); + + if (neg) { + char tmp[128]; + snprintf(tmp, sizeof(tmp), "마이너스%s", hangeul); + strncpy(hangeul, tmp, sizeof(hangeul) - 1); + hangeul[sizeof(hangeul) - 1] = '\0'; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0020] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CM_OL_0020] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (cm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 교차표시용: 콤마 구분 숫자표기 */ + amount_format_won(amount, won_fmt, sizeof(won_fmt)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CM_OL_0020] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_sets(ctx->out, "HANGEUL", hangeul); + tx_buf_sets(ctx->out, "WON", won_fmt); + tx_log(TX_LOG_INFO, "[CM_OL_0020] 변환완료 amount=%ld -> %s", amount, hangeul); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0020.pgc b/app/online/cm_ol_0020.pgc index fcdec9e..c0be1b2 100644 --- a/app/online/cm_ol_0020.pgc +++ b/app/online/cm_ol_0020.pgc @@ -1,9 +1,11 @@ -/* @tier hard @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0020.pgc - 공통 온라인 서비스 (CM_OL_0020) [tier=hard] + * cm_ol_0020.pgc - 금액 한글 표기 서비스 (CM_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 금액(AMOUNT)을 만/억/조 단위 절(節)로 분해하여 한글 금액표기 + * (예: 123000000 -> "일억이천삼백만")로 변환하여 응답한다. 증서/전표 + * 출력용 순수 로직 변환 서비스. DB 접근 없음. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -14,85 +16,86 @@ EXEC SQL INCLUDE sqlca; +/* 0~9999 한 절(節)을 천/백/십 단위 한글로 변환하여 out 에 누적 */ +static void hangeul_group(int val, char *out, int outlen) +{ + static const char *DIG[10] = { + "", "일", "이", "삼", "사", "오", "육", "칠", "팔", "구" + }; + static const char *POS[4] = { "", "십", "백", "천" }; + int unit[4]; + int i; + + unit[3] = (val / 1000) % 10; + unit[2] = (val / 100) % 10; + unit[1] = (val / 10) % 10; + unit[0] = val % 10; + + for (i = 3; i >= 0; i--) { + if (unit[i] == 0) + continue; + /* 십/백/천 자리의 '일'은 관용상 생략 (예: 15 -> 십오) */ + if (!(unit[i] == 1 && i > 0)) + strncat(out, DIG[unit[i]], outlen - strlen(out) - 1); + strncat(out, POS[i], outlen - strlen(out) - 1); + } +} + TX_SERVICE(CM_OL_0020, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + static const char *GUNIT[4] = { "", "만", "억", "조" }; + char hangeul[128]; + char won_fmt[48]; + long amount = 0; + long n; + int neg = 0; + int g; + int groups[4]; + int i; - tx_log(TX_LOG_INFO, "[CM_OL_0020] 공통 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0020] 금액 한글 표기 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0020] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0020] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (tx_buf_getlong(ctx->in, "AMOUNT", &amount) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0020] AMOUNT 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; + hangeul[0] = '\0'; + n = amount; + if (n < 0) { neg = 1; n = -n; } + + if (n == 0) { + strncpy(hangeul, "영", sizeof(hangeul) - 1); + } else { + /* 4자리 절 단위 분해: 일/만/억/조 (조 단위까지 지원) */ + for (i = 0; i < 4; i++) { + groups[i] = (int)(n % 10000); + n /= 10000; + } + for (i = 3; i >= 0; i--) { + if (groups[i] == 0) + continue; + hangeul_group(groups[i], hangeul, sizeof(hangeul)); + strncat(hangeul, GUNIT[i], sizeof(hangeul) - strlen(hangeul) - 1); + } + } + strncat(hangeul, "원", sizeof(hangeul) - strlen(hangeul) - 1); + + if (neg) { + char tmp[128]; + snprintf(tmp, sizeof(tmp), "마이너스%s", hangeul); + strncpy(hangeul, tmp, sizeof(hangeul) - 1); + hangeul[sizeof(hangeul) - 1] = '\0'; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0020] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CM_OL_0020] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (cm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 교차표시용: 콤마 구분 숫자표기 */ + amount_format_won(amount, won_fmt, sizeof(won_fmt)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CM_OL_0020] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_sets(ctx->out, "HANGEUL", hangeul); + tx_buf_sets(ctx->out, "WON", won_fmt); + tx_log(TX_LOG_INFO, "[CM_OL_0020] 변환완료 amount=%ld -> %s", amount, hangeul); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0021.c b/app/online/cm_ol_0021.c index 62b2780..a5512d3 100644 --- a/app/online/cm_ol_0021.c +++ b/app/online/cm_ol_0021.c @@ -6,17 +6,20 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0021.pgc" -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0021.pgc - 공통 온라인 서비스 (CM_OL_0021) [tier=easy] + * cm_ol_0021.pgc - 통화 환산 서비스 (CM_OL_0021) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 원화금액(AMOUNT)/통화코드(CUR)로 환율 마스터(cm_rate)를 조회하여 + * 외화 환산액을 정수 스케일 연산으로 산출해 응답한다. 환율은 스케일드 + * 정수(rate/rate_scale)로 보관되며 반올림 처리한다. 대외정산 환산 공통 서비스. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,37 +92,108 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0021.pgc" +#line 18 "app/online/cm_ol_0021.pgc" TX_SERVICE(CM_OL_0021, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 23 "app/online/cm_ol_0021.pgc" + char h_cur [ 8 ] ; + +#line 24 "app/online/cm_ol_0021.pgc" + char h_base [ 8 ] ; + +#line 25 "app/online/cm_ol_0021.pgc" + long h_rate ; + +#line 26 "app/online/cm_ol_0021.pgc" + long h_scale ; +/* exec sql end declare section */ +#line 27 "app/online/cm_ol_0021.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0021] 공통 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0021] KEY 누락"); + char cur[8]; + char rate_disp[24]; + long amount = 0; + long converted; + long numer; + + tx_log(TX_LOG_INFO, "[CM_OL_0021] 통화 환산 서비스 진입"); + + if (tx_buf_get(ctx->in, "CUR", cur, sizeof(cur)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0021] CUR 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_getlong(ctx->in, "AMOUNT", &amount) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0021] AMOUNT 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!amount_is_valid(amount)) { + tx_log(TX_LOG_WARN, "[CM_OL_0021] 금액 범위 오류 amount=%ld", amount); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_cur, 0, sizeof(h_cur)); + memset(h_base, 0, sizeof(h_base)); + strncpy(h_cur, cur, sizeof(h_cur) - 1); + h_rate = 0; + h_scale = 0; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select rate , rate_scale , base_cd from cm_rate where cur_cd = $1 and use_yn = 'Y'", + ECPGt_char,(h_cur),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_rate),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_scale),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_base),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 64 "app/online/cm_ol_0021.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0021] 환율 없음 cur=%s", cur); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0021 SELECT cm_rate"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_rate <= 0 || h_scale <= 0) { + tx_log(TX_LOG_ERROR, "[CM_OL_0021] 환율 데이터 오류 rate=%ld scale=%ld", + h_rate, h_scale); + tx_return(ctx, TX_FAIL, ctx->out); return; } + /* 외화 = 원화 * scale / rate (반올림). rate 는 base 1단위당 원화가격. */ + numer = amount * h_scale; + converted = (numer + (h_rate / 2)) / h_rate; + + /* 적용환율 표시: rate/scale 를 소수로 재구성 */ + snprintf(rate_disp, sizeof(rate_disp), "%ld.%0*ld", + h_rate / h_scale, 2, (h_rate % h_scale) * 100 / h_scale); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "CUR", cur); + tx_buf_sets(ctx->out, "BASE", h_base); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_setlong(ctx->out, "CONVERTED", converted); + tx_buf_setlong(ctx->out, "RATE", h_rate); + tx_buf_setlong(ctx->out, "SCALE", h_scale); + tx_buf_sets(ctx->out, "RATEDISP", rate_disp); + tx_log(TX_LOG_INFO, "[CM_OL_0021] 환산완료 %ld원 -> %s %ld (rate=%s)", + amount, cur, converted, rate_disp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0021.pgc b/app/online/cm_ol_0021.pgc index de09294..94179dc 100644 --- a/app/online/cm_ol_0021.pgc +++ b/app/online/cm_ol_0021.pgc @@ -1,14 +1,17 @@ -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0021.pgc - 공통 온라인 서비스 (CM_OL_0021) [tier=easy] + * cm_ol_0021.pgc - 통화 환산 서비스 (CM_OL_0021) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 원화금액(AMOUNT)/통화코드(CUR)로 환율 마스터(cm_rate)를 조회하여 + * 외화 환산액을 정수 스케일 연산으로 산출해 응답한다. 환율은 스케일드 + * 정수(rate/rate_scale)로 보관되며 반올림 처리한다. 대외정산 환산 공통 서비스. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,32 +19,84 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0021, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_cur[8]; + char h_base[8]; + long h_rate; + long h_scale; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0021] 공통 단건조회 서비스 진입"); + char cur[8]; + char rate_disp[24]; + long amount = 0; + long converted; + long numer; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0021] KEY 누락"); + tx_log(TX_LOG_INFO, "[CM_OL_0021] 통화 환산 서비스 진입"); + + if (tx_buf_get(ctx->in, "CUR", cur, sizeof(cur)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0021] CUR 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_getlong(ctx->in, "AMOUNT", &amount) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0021] AMOUNT 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!amount_is_valid(amount)) { + tx_log(TX_LOG_WARN, "[CM_OL_0021] 금액 범위 오류 amount=%ld", amount); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_cur, 0, sizeof(h_cur)); + memset(h_base, 0, sizeof(h_base)); + strncpy(h_cur, cur, sizeof(h_cur) - 1); + h_rate = 0; + h_scale = 0; + + EXEC SQL + SELECT rate, rate_scale, base_cd + INTO :h_rate, :h_scale, :h_base + FROM cm_rate + WHERE cur_cd = :h_cur + AND use_yn = 'Y'; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0021] 환율 없음 cur=%s", cur); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0021 SELECT cm_rate"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_rate <= 0 || h_scale <= 0) { + tx_log(TX_LOG_ERROR, "[CM_OL_0021] 환율 데이터 오류 rate=%ld scale=%ld", + h_rate, h_scale); + tx_return(ctx, TX_FAIL, ctx->out); return; } + /* 외화 = 원화 * scale / rate (반올림). rate 는 base 1단위당 원화가격. */ + numer = amount * h_scale; + converted = (numer + (h_rate / 2)) / h_rate; + + /* 적용환율 표시: rate/scale 를 소수로 재구성 */ + snprintf(rate_disp, sizeof(rate_disp), "%ld.%0*ld", + h_rate / h_scale, 2, (h_rate % h_scale) * 100 / h_scale); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "CUR", cur); + tx_buf_sets(ctx->out, "BASE", h_base); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_setlong(ctx->out, "CONVERTED", converted); + tx_buf_setlong(ctx->out, "RATE", h_rate); + tx_buf_setlong(ctx->out, "SCALE", h_scale); + tx_buf_sets(ctx->out, "RATEDISP", rate_disp); + tx_log(TX_LOG_INFO, "[CM_OL_0021] 환산완료 %ld원 -> %s %ld (rate=%s)", + amount, cur, converted, rate_disp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0022.c b/app/online/cm_ol_0022.c index 9f91db0..d4346a5 100644 --- a/app/online/cm_ol_0022.c +++ b/app/online/cm_ol_0022.c @@ -8,10 +8,11 @@ #line 1 "app/online/cm_ol_0022.pgc" /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0022.pgc - 공통 온라인 서비스 (CM_OL_0022) [tier=easy] + * cm_ol_0022.pgc - 카드번호 마스킹 유틸 서비스 (CM_OL_0022) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 카드번호(CARDNO)에서 숫자만 추출하여 앞 6자리/뒤 4자리만 + * 노출하고 가운데를 '*' 로 치환한 마스킹 결과와 자릿수를 응답한다. + * DB 접근 없는 순수 로직 서비스. (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -89,37 +90,61 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0022.pgc" +#line 16 "app/online/cm_ol_0022.pgc" +/* 카드번호 문자열에서 숫자만 추출해 dst 로 복사, 유효 자릿수 반환 */ +static int cm_digits_only(const char *src, char *dst, int dstlen) +{ + int i; + int n = 0; + + for (i = 0; src[i] != '\0' && n < dstlen - 1; i++) { + if (src[i] >= '0' && src[i] <= '9') { + dst[n++] = src[i]; + } + } + dst[n] = '\0'; + return n; +} + TX_SERVICE(CM_OL_0022, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + char cardno[32]; + char digits[24]; + char masked[24]; + int len; + int i; - tx_log(TX_LOG_INFO, "[CM_OL_0022] 공통 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0022] 카드번호 마스킹 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0022] KEY 누락"); + if (tx_buf_get(ctx->in, "CARDNO", cardno, sizeof(cardno)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0022] CARDNO 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0022] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + len = cm_digits_only(cardno, digits, sizeof(digits)); + if (len < 12 || len > 19) { + tx_log(TX_LOG_WARN, "[CM_OL_0022] 카드번호 자릿수 오류 len=%d", len); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 앞 6자리와 뒤 4자리는 원본 유지, 그 사이는 '*' 로 치환 */ + for (i = 0; i < len; i++) { + if (i < 6 || i >= len - 4) { + masked[i] = digits[i]; + } else { + masked[i] = '*'; + } + } + masked[len] = '\0'; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0022] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MASKED", masked); + tx_buf_setlong(ctx->out, "DIGITS", (long)len); + tx_buf_sets(ctx->out, "RESULT", "OK"); + tx_log(TX_LOG_INFO, "[CM_OL_0022] 마스킹 완료 masked=%s len=%d", masked, len); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0022.pgc b/app/online/cm_ol_0022.pgc index 1f76efb..c969db6 100644 --- a/app/online/cm_ol_0022.pgc +++ b/app/online/cm_ol_0022.pgc @@ -1,9 +1,10 @@ /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0022.pgc - 공통 온라인 서비스 (CM_OL_0022) [tier=easy] + * cm_ol_0022.pgc - 카드번호 마스킹 유틸 서비스 (CM_OL_0022) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 카드번호(CARDNO)에서 숫자만 추출하여 앞 6자리/뒤 4자리만 + * 노출하고 가운데를 '*' 로 치환한 마스킹 결과와 자릿수를 응답한다. + * DB 접근 없는 순수 로직 서비스. (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -14,34 +15,58 @@ EXEC SQL INCLUDE sqlca; +/* 카드번호 문자열에서 숫자만 추출해 dst 로 복사, 유효 자릿수 반환 */ +static int cm_digits_only(const char *src, char *dst, int dstlen) +{ + int i; + int n = 0; + + for (i = 0; src[i] != '\0' && n < dstlen - 1; i++) { + if (src[i] >= '0' && src[i] <= '9') { + dst[n++] = src[i]; + } + } + dst[n] = '\0'; + return n; +} + TX_SERVICE(CM_OL_0022, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + char cardno[32]; + char digits[24]; + char masked[24]; + int len; + int i; - tx_log(TX_LOG_INFO, "[CM_OL_0022] 공통 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0022] 카드번호 마스킹 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0022] KEY 누락"); + if (tx_buf_get(ctx->in, "CARDNO", cardno, sizeof(cardno)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0022] CARDNO 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0022] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + len = cm_digits_only(cardno, digits, sizeof(digits)); + if (len < 12 || len > 19) { + tx_log(TX_LOG_WARN, "[CM_OL_0022] 카드번호 자릿수 오류 len=%d", len); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 앞 6자리와 뒤 4자리는 원본 유지, 그 사이는 '*' 로 치환 */ + for (i = 0; i < len; i++) { + if (i < 6 || i >= len - 4) { + masked[i] = digits[i]; + } else { + masked[i] = '*'; + } + } + masked[len] = '\0'; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0022] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MASKED", masked); + tx_buf_setlong(ctx->out, "DIGITS", (long)len); + tx_buf_sets(ctx->out, "RESULT", "OK"); + tx_log(TX_LOG_INFO, "[CM_OL_0022] 마스킹 완료 masked=%s len=%d", masked, len); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0023.c b/app/online/cm_ol_0023.c index 13a94ae..84c32c6 100644 --- a/app/online/cm_ol_0023.c +++ b/app/online/cm_ol_0023.c @@ -6,17 +6,19 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0023.pgc" -/* @tier medium @module cm @transform common-online */ +/* @tier easy @module cm @transform common-online */ /* - * cm_ol_0023.pgc - 공통 온라인 서비스 (CM_OL_0023) [tier=medium] + * cm_ol_0023.pgc - 코드명 조회 서비스 (CM_OL_0023) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 코드그룹(GRP)/코드값(CODE)으로 공통코드 마스터(cm_code)를 + * 조회하여 코드명(NAME)만 단건으로 응답한다. 부가속성은 반환하지 않는 + * 경량 조회. (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,60 +91,72 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0023.pgc" +#line 17 "app/online/cm_ol_0023.pgc" TX_SERVICE(CM_OL_0023, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + +#line 22 "app/online/cm_ol_0023.pgc" + char h_grp [ 16 ] ; + +#line 23 "app/online/cm_ol_0023.pgc" + char h_code [ 16 ] ; + +#line 24 "app/online/cm_ol_0023.pgc" + char h_name [ 64 ] ; +/* exec sql end declare section */ +#line 25 "app/online/cm_ol_0023.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0023] 공통 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0023] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char grp[16]; + char code[16]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0023] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0023] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[CM_OL_0023] 코드명 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "GRP", grp, sizeof(grp)) != TX_OK || + tx_buf_get(ctx->in, "CODE", code, sizeof(code)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0023] GRP/CODE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + memset(&h_grp, 0, sizeof(h_grp)); + memset(&h_code, 0, sizeof(h_code)); + memset(&h_name, 0, sizeof(h_name)); + strncpy(h_grp, grp, sizeof(h_grp) - 1); + strncpy(h_code, code, sizeof(h_code) - 1); - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0023] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select code_name from cm_code where grp_cd = $1 and code_cd = $2 and use_yn = 'Y'", + ECPGt_char,(h_grp),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_code),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_name),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 51 "app/online/cm_ol_0023.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0023] 코드 없음 grp=%s code=%s", grp, code); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0023 SELECT cm_code"); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = cm_rec_count(bizdate, CM_ST_RECV); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0023] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "GRP", grp); + tx_buf_sets(ctx->out, "CODE", code); + tx_buf_sets(ctx->out, "NAME", h_name); + tx_log(TX_LOG_INFO, "[CM_OL_0023] 코드명 조회완료 grp=%s code=%s name=%s", + grp, code, h_name); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0023.pgc b/app/online/cm_ol_0023.pgc index f25cea9..5604a86 100644 --- a/app/online/cm_ol_0023.pgc +++ b/app/online/cm_ol_0023.pgc @@ -1,14 +1,16 @@ -/* @tier medium @module cm @transform common-online */ +/* @tier easy @module cm @transform common-online */ /* - * cm_ol_0023.pgc - 공통 온라인 서비스 (CM_OL_0023) [tier=medium] + * cm_ol_0023.pgc - 코드명 조회 서비스 (CM_OL_0023) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 코드그룹(GRP)/코드값(CODE)으로 공통코드 마스터(cm_code)를 + * 조회하여 코드명(NAME)만 단건으로 응답한다. 부가속성은 반환하지 않는 + * 경량 조회. (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,55 +18,54 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0023, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_grp[16]; + char h_code[16]; + char h_name[64]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0023] 공통 등록 서비스 진입"); + char grp[16]; + char code[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0023] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[CM_OL_0023] 코드명 조회 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0023] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0023] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "GRP", grp, sizeof(grp)) != TX_OK || + tx_buf_get(ctx->in, "CODE", code, sizeof(code)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0023] GRP/CODE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + memset(&h_grp, 0, sizeof(h_grp)); + memset(&h_code, 0, sizeof(h_code)); + memset(&h_name, 0, sizeof(h_name)); + strncpy(h_grp, grp, sizeof(h_grp) - 1); + strncpy(h_code, code, sizeof(h_code) - 1); - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0023] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL + SELECT code_name + INTO :h_name + FROM cm_code + WHERE grp_cd = :h_grp + AND code_cd = :h_code + AND use_yn = 'Y'; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0023] 코드 없음 grp=%s code=%s", grp, code); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0023 SELECT cm_code"); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = cm_rec_count(bizdate, CM_ST_RECV); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0023] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "GRP", grp); + tx_buf_sets(ctx->out, "CODE", code); + tx_buf_sets(ctx->out, "NAME", h_name); + tx_log(TX_LOG_INFO, "[CM_OL_0023] 코드명 조회완료 grp=%s code=%s name=%s", + grp, code, h_name); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0024.c b/app/online/cm_ol_0024.c index 998ac7e..a43a792 100644 --- a/app/online/cm_ol_0024.c +++ b/app/online/cm_ol_0024.c @@ -8,15 +8,17 @@ #line 1 "app/online/cm_ol_0024.pgc" /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0024.pgc - 공통 온라인 서비스 (CM_OL_0024) [tier=easy] + * cm_ol_0024.pgc - 다음 순번 미리보기 서비스 (CM_OL_0024) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채번키(SEQKEY)로 채번 마스터(cm_seq)의 현재값/증분을 조회(갱신 없음)하여 + * 다음에 발급될 순번을 미리 계산해 응답한다. 실제 채번 확정은 별도 서비스가 + * 수행하며 본 서비스는 read-only 미리보기다. (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,37 +91,89 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0024.pgc" +#line 17 "app/online/cm_ol_0024.pgc" TX_SERVICE(CM_OL_0024, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 22 "app/online/cm_ol_0024.pgc" + char h_seqkey [ 24 ] ; + +#line 23 "app/online/cm_ol_0024.pgc" + long h_curval ; + +#line 24 "app/online/cm_ol_0024.pgc" + long h_incby ; + +#line 25 "app/online/cm_ol_0024.pgc" + long h_maxval ; +/* exec sql end declare section */ +#line 26 "app/online/cm_ol_0024.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0024] 공통 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0024] KEY 누락"); + char seqkey[24]; + long nextval; + + tx_log(TX_LOG_INFO, "[CM_OL_0024] 다음 순번 미리보기 서비스 진입"); + + if (tx_buf_get(ctx->in, "SEQKEY", seqkey, sizeof(seqkey)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0024] SEQKEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0024] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(&h_seqkey, 0, sizeof(h_seqkey)); + strncpy(h_seqkey, seqkey, sizeof(h_seqkey) - 1); + h_curval = 0; + h_incby = 1; + h_maxval = 0; + + /* 현재값만 읽고 갱신하지 않는다 (미리보기) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select cur_val , inc_by , max_val from cm_seq where seq_key = $1 ", + ECPGt_char,(h_seqkey),(long)24,(long)1,(24)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_curval),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_incby),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_maxval),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 50 "app/online/cm_ol_0024.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0024] 채번키 없음 seqkey=%s", seqkey); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0024 SELECT cm_seq"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (h_incby <= 0) + h_incby = 1; + + nextval = h_curval + h_incby; + + /* 상한 초과 시 순환(wrap-around) 예측 */ + if (h_maxval > 0 && nextval > h_maxval) { + nextval = 1; + tx_log(TX_LOG_WARN, "[CM_OL_0024] 순번 상한 초과 → 순환 예측 seqkey=%s", seqkey); + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0024] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "SEQKEY", seqkey); + tx_buf_setlong(ctx->out, "CURVAL", h_curval); + tx_buf_setlong(ctx->out, "NEXTVAL", nextval); + tx_log(TX_LOG_INFO, "[CM_OL_0024] 미리보기 완료 seqkey=%s cur=%ld next=%ld", + seqkey, h_curval, nextval); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0024.pgc b/app/online/cm_ol_0024.pgc index 2b9e37e..7dbc480 100644 --- a/app/online/cm_ol_0024.pgc +++ b/app/online/cm_ol_0024.pgc @@ -1,14 +1,16 @@ /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0024.pgc - 공통 온라인 서비스 (CM_OL_0024) [tier=easy] + * cm_ol_0024.pgc - 다음 순번 미리보기 서비스 (CM_OL_0024) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채번키(SEQKEY)로 채번 마스터(cm_seq)의 현재값/증분을 조회(갱신 없음)하여 + * 다음에 발급될 순번을 미리 계산해 응답한다. 실제 채번 확정은 별도 서비스가 + * 수행하며 본 서비스는 read-only 미리보기다. (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,32 +18,64 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0024, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_seqkey[24]; + long h_curval; + long h_incby; + long h_maxval; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0024] 공통 단건조회 서비스 진입"); + char seqkey[24]; + long nextval; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0024] KEY 누락"); + tx_log(TX_LOG_INFO, "[CM_OL_0024] 다음 순번 미리보기 서비스 진입"); + + if (tx_buf_get(ctx->in, "SEQKEY", seqkey, sizeof(seqkey)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0024] SEQKEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0024] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(&h_seqkey, 0, sizeof(h_seqkey)); + strncpy(h_seqkey, seqkey, sizeof(h_seqkey) - 1); + h_curval = 0; + h_incby = 1; + h_maxval = 0; + + /* 현재값만 읽고 갱신하지 않는다 (미리보기) */ + EXEC SQL + SELECT cur_val, inc_by, max_val + INTO :h_curval, :h_incby, :h_maxval + FROM cm_seq + WHERE seq_key = :h_seqkey; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0024] 채번키 없음 seqkey=%s", seqkey); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0024 SELECT cm_seq"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (h_incby <= 0) + h_incby = 1; + + nextval = h_curval + h_incby; + + /* 상한 초과 시 순환(wrap-around) 예측 */ + if (h_maxval > 0 && nextval > h_maxval) { + nextval = 1; + tx_log(TX_LOG_WARN, "[CM_OL_0024] 순번 상한 초과 → 순환 예측 seqkey=%s", seqkey); + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0024] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "SEQKEY", seqkey); + tx_buf_setlong(ctx->out, "CURVAL", h_curval); + tx_buf_setlong(ctx->out, "NEXTVAL", nextval); + tx_log(TX_LOG_INFO, "[CM_OL_0024] 미리보기 완료 seqkey=%s cur=%ld next=%ld", + seqkey, h_curval, nextval); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0025.c b/app/online/cm_ol_0025.c index f440c54..0c46c0c 100644 --- a/app/online/cm_ol_0025.c +++ b/app/online/cm_ol_0025.c @@ -8,15 +8,18 @@ #line 1 "app/online/cm_ol_0025.pgc" /* @tier medium @module cm @transform common-online */ /* - * cm_ol_0025.pgc - 공통 온라인 서비스 (CM_OL_0025) [tier=medium] + * cm_ol_0025.pgc - 파라미터 갱신 확인 서비스 (CM_OL_0025) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 파라미터키(PKEY)로 운영 파라미터(cm_param)를 조회하여 최종수정시각 + * (upd_ts)을 요청 기준시각(SINCE, YYYYMMDDHH24MISS)과 비교, 기준시각 이후 + * 변경 여부(CHANGED=Y/N)와 현재값을 응답한다. 캐시 갱신 판단에 사용한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,60 +92,76 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0025.pgc" +#line 18 "app/online/cm_ol_0025.pgc" TX_SERVICE(CM_OL_0025, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + +#line 23 "app/online/cm_ol_0025.pgc" + char h_pkey [ 32 ] ; + +#line 24 "app/online/cm_ol_0025.pgc" + char h_pval [ 128 ] ; + +#line 25 "app/online/cm_ol_0025.pgc" + char h_updts [ 16 ] ; +/* exec sql end declare section */ +#line 26 "app/online/cm_ol_0025.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0025] 공통 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0025] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char pkey[32]; + char since[16]; + int changed; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0025] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0025] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[CM_OL_0025] 파라미터 갱신 확인 서비스 진입"); + + if (tx_buf_get(ctx->in, "PKEY", pkey, sizeof(pkey)) != TX_OK || + tx_buf_get(ctx->in, "SINCE", since, sizeof(since)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0025] PKEY/SINCE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + memset(&h_pkey, 0, sizeof(h_pkey)); + memset(&h_pval, 0, sizeof(h_pval)); + memset(&h_updts, 0, sizeof(h_updts)); + strncpy(h_pkey, pkey, sizeof(h_pkey) - 1); - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0025] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select param_val , TO_CHAR ( upd_ts , 'YYYYMMDDHH24MISS' ) from cm_param where param_key = $1 ", + ECPGt_char,(h_pkey),(long)32,(long)1,(32)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_pval),(long)128,(long)1,(128)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_updts),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 50 "app/online/cm_ol_0025.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0025] 파라미터 없음 pkey=%s", pkey); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0025 SELECT cm_param"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = cm_rec_count(bizdate, CM_ST_RECV); + /* 최종수정시각 문자열 사전식 비교: 기준시각보다 크면 변경됨 */ + changed = (strncmp(h_updts, since, sizeof(h_updts)) > 0) ? 1 : 0; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0025] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "PKEY", pkey); + tx_buf_sets(ctx->out, "PVAL", h_pval); + tx_buf_sets(ctx->out, "UPDTS", h_updts); + tx_buf_sets(ctx->out, "CHANGED", changed ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[CM_OL_0025] 확인완료 pkey=%s upd=%s since=%s changed=%s", + pkey, h_updts, since, changed ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0025.pgc b/app/online/cm_ol_0025.pgc index c41d991..04a3d90 100644 --- a/app/online/cm_ol_0025.pgc +++ b/app/online/cm_ol_0025.pgc @@ -1,14 +1,17 @@ /* @tier medium @module cm @transform common-online */ /* - * cm_ol_0025.pgc - 공통 온라인 서비스 (CM_OL_0025) [tier=medium] + * cm_ol_0025.pgc - 파라미터 갱신 확인 서비스 (CM_OL_0025) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 파라미터키(PKEY)로 운영 파라미터(cm_param)를 조회하여 최종수정시각 + * (upd_ts)을 요청 기준시각(SINCE, YYYYMMDDHH24MISS)과 비교, 기준시각 이후 + * 변경 여부(CHANGED=Y/N)와 현재값을 응답한다. 캐시 갱신 판단에 사용한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,55 +19,56 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0025, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_pkey[32]; + char h_pval[128]; + char h_updts[16]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0025] 공통 등록 서비스 진입"); + char pkey[32]; + char since[16]; + int changed; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0025] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[CM_OL_0025] 파라미터 갱신 확인 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0025] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0025] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "PKEY", pkey, sizeof(pkey)) != TX_OK || + tx_buf_get(ctx->in, "SINCE", since, sizeof(since)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0025] PKEY/SINCE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + memset(&h_pkey, 0, sizeof(h_pkey)); + memset(&h_pval, 0, sizeof(h_pval)); + memset(&h_updts, 0, sizeof(h_updts)); + strncpy(h_pkey, pkey, sizeof(h_pkey) - 1); - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0025] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL + SELECT param_val, TO_CHAR(upd_ts, 'YYYYMMDDHH24MISS') + INTO :h_pval, :h_updts + FROM cm_param + WHERE param_key = :h_pkey; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0025] 파라미터 없음 pkey=%s", pkey); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0025 SELECT cm_param"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = cm_rec_count(bizdate, CM_ST_RECV); + /* 최종수정시각 문자열 사전식 비교: 기준시각보다 크면 변경됨 */ + changed = (strncmp(h_updts, since, sizeof(h_updts)) > 0) ? 1 : 0; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0025] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "PKEY", pkey); + tx_buf_sets(ctx->out, "PVAL", h_pval); + tx_buf_sets(ctx->out, "UPDTS", h_updts); + tx_buf_sets(ctx->out, "CHANGED", changed ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[CM_OL_0025] 확인완료 pkey=%s upd=%s since=%s changed=%s", + pkey, h_updts, since, changed ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0026.c b/app/online/cm_ol_0026.c index 9da3022..fde1bb7 100644 --- a/app/online/cm_ol_0026.c +++ b/app/online/cm_ol_0026.c @@ -6,17 +6,20 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0026.pgc" -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0026.pgc - 공통 온라인 서비스 (CM_OL_0026) [tier=easy] + * cm_ol_0026.pgc - 캐시 무효화 요청 처리 서비스 (CM_OL_0026) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 캐시키(CKEY) 유효성을 검증한 뒤 무효화 이벤트를 로그로 남기고, 운영 + * 파라미터(cm_param)의 캐시버전(cache_ver)을 1 증가시켜 하위 노드의 캐시 + * 재적재를 유도한다. 증가된 버전과 무효화 결과를 응답한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,37 +92,118 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0026.pgc" +#line 18 "app/online/cm_ol_0026.pgc" +/* 캐시키 형식 검증: 영숫자/'_'/'.' 만 허용, 길이 3~30 */ +static int cm_ckey_is_valid(const char *k) +{ + int i; + int n = (int)strlen(k); + + if (n < 3 || n > 30) + return 0; + for (i = 0; i < n; i++) { + char c = k[i]; + if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || c == '_' || c == '.')) { + return 0; + } + } + return 1; +} + TX_SERVICE(CM_OL_0026, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 41 "app/online/cm_ol_0026.pgc" + char h_ckey [ 32 ] ; + +#line 42 "app/online/cm_ol_0026.pgc" + long h_ver ; + +#line 43 "app/online/cm_ol_0026.pgc" + long newver ; +/* exec sql end declare section */ +#line 44 "app/online/cm_ol_0026.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0026] 공통 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0026] KEY 누락"); + char ckey[32]; + + tx_log(TX_LOG_INFO, "[CM_OL_0026] 캐시 무효화 요청 처리 진입"); + + if (tx_buf_get(ctx->in, "CKEY", ckey, sizeof(ckey)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0026] CKEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0026] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!cm_ckey_is_valid(ckey)) { + tx_log(TX_LOG_WARN, "[CM_OL_0026] 캐시키 형식 오류 ckey=%s", ckey); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + memset(&h_ckey, 0, sizeof(h_ckey)); + strncpy(h_ckey, ckey, sizeof(h_ckey) - 1); + h_ver = 0; + + /* 현재 캐시버전 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select cache_ver from cm_param where param_key = $1 ", + ECPGt_char,(h_ckey),(long)32,(long)1,(32)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_ver),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 71 "app/online/cm_ol_0026.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0026] 캐시 대상 없음 ckey=%s", ckey); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0026 SELECT cm_param"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + newver = h_ver + 1; + + /* 무효화 이벤트 기록 후 버전 증가 반영 */ + tx_log(TX_LOG_INFO, "[CM_OL_0026] 캐시 무효화 이벤트 ckey=%s %ld->%ld xid=%ld", + ckey, h_ver, newver, tx_current_xid()); + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update cm_param set cache_ver = $1 , upd_ts = SYSTIMESTAMP where param_key = $2 ", + ECPGt_long,&(newver),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_ckey),(long)32,(long)1,(32)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 99 "app/online/cm_ol_0026.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0026 UPDATE cm_param"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0026] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "CKEY", ckey); + tx_buf_setlong(ctx->out, "NEWVER", newver); + tx_buf_sets(ctx->out, "RESULT", "INVALIDATED"); + tx_log(TX_LOG_INFO, "[CM_OL_0026] 무효화 완료 ckey=%s newver=%ld", ckey, newver); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0026.pgc b/app/online/cm_ol_0026.pgc index 358da18..6990aa4 100644 --- a/app/online/cm_ol_0026.pgc +++ b/app/online/cm_ol_0026.pgc @@ -1,47 +1,116 @@ -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0026.pgc - 공통 온라인 서비스 (CM_OL_0026) [tier=easy] + * cm_ol_0026.pgc - 캐시 무효화 요청 처리 서비스 (CM_OL_0026) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 캐시키(CKEY) 유효성을 검증한 뒤 무효화 이벤트를 로그로 남기고, 운영 + * 파라미터(cm_param)의 캐시버전(cache_ver)을 1 증가시켜 하위 노드의 캐시 + * 재적재를 유도한다. 증가된 버전과 무효화 결과를 응답한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" EXEC SQL INCLUDE sqlca; +/* 캐시키 형식 검증: 영숫자/'_'/'.' 만 허용, 길이 3~30 */ +static int cm_ckey_is_valid(const char *k) +{ + int i; + int n = (int)strlen(k); + + if (n < 3 || n > 30) + return 0; + for (i = 0; i < n; i++) { + char c = k[i]; + if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || c == '_' || c == '.')) { + return 0; + } + } + return 1; +} + TX_SERVICE(CM_OL_0026, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_ckey[32]; + long h_ver; + long newver; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0026] 공통 단건조회 서비스 진입"); + char ckey[32]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0026] KEY 누락"); + tx_log(TX_LOG_INFO, "[CM_OL_0026] 캐시 무효화 요청 처리 진입"); + + if (tx_buf_get(ctx->in, "CKEY", ckey, sizeof(ckey)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0026] CKEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0026] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!cm_ckey_is_valid(ckey)) { + tx_log(TX_LOG_WARN, "[CM_OL_0026] 캐시키 형식 오류 ckey=%s", ckey); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + memset(&h_ckey, 0, sizeof(h_ckey)); + strncpy(h_ckey, ckey, sizeof(h_ckey) - 1); + h_ver = 0; + + /* 현재 캐시버전 조회 */ + EXEC SQL + SELECT cache_ver + INTO :h_ver + FROM cm_param + WHERE param_key = :h_ckey; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0026] 캐시 대상 없음 ckey=%s", ckey); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0026 SELECT cm_param"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + newver = h_ver + 1; + + /* 무효화 이벤트 기록 후 버전 증가 반영 */ + tx_log(TX_LOG_INFO, "[CM_OL_0026] 캐시 무효화 이벤트 ckey=%s %ld->%ld xid=%ld", + ckey, h_ver, newver, tx_current_xid()); + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL + UPDATE cm_param + SET cache_ver = :newver, + upd_ts = SYSTIMESTAMP + WHERE param_key = :h_ckey; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0026 UPDATE cm_param"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0026] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "CKEY", ckey); + tx_buf_setlong(ctx->out, "NEWVER", newver); + tx_buf_sets(ctx->out, "RESULT", "INVALIDATED"); + tx_log(TX_LOG_INFO, "[CM_OL_0026] 무효화 완료 ckey=%s newver=%ld", ckey, newver); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0027.c b/app/online/cm_ol_0027.c index 0bc430a..fd67617 100644 --- a/app/online/cm_ol_0027.c +++ b/app/online/cm_ol_0027.c @@ -8,10 +8,11 @@ #line 1 "app/online/cm_ol_0027.pgc" /* @tier medium @module cm @transform common-online */ /* - * cm_ol_0027.pgc - 공통 온라인 서비스 (CM_OL_0027) [tier=medium] + * cm_ol_0027.pgc - 체크디짓 검증 서비스 (CM_OL_0027) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 번호(NUMBER: 카드/계좌 등)에 Luhn(모드10) 알고리즘을 적용하여 + * 유효성(VALID=Y/N)과 계산된 체크디짓을 응답한다. DB 접근 없는 순수 로직. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -89,60 +90,81 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0027.pgc" +#line 16 "app/online/cm_ol_0027.pgc" +/* 숫자만 추출 후 자릿수 반환 */ +static int cm_pack_digits(const char *src, char *dst, int dstlen) +{ + int i; + int n = 0; + + for (i = 0; src[i] != '\0' && n < dstlen - 1; i++) { + if (src[i] >= '0' && src[i] <= '9') + dst[n++] = src[i]; + } + dst[n] = '\0'; + return n; +} + +/* + * 오른쪽에서 왼쪽으로 이동하며 짝수번째(offset 기준) 자리를 2배, + * 9 초과 시 자릿수 합으로 환산한 뒤 총합의 mod 10 을 반환한다. + * offset=1: 마지막 자리 포함(전체 검증), offset=0: 체크디짓 산출용. + */ +static int cm_luhn_mod(const char *digits, int len, int offset) +{ + int sum = 0; + int i; + int pos = 0; + + for (i = len - 1; i >= 0; i--) { + int d = digits[i] - '0'; + if (((pos + offset) & 1) == 1) { + d *= 2; + if (d > 9) + d -= 9; + } + sum += d; + pos++; + } + return sum % 10; +} + TX_SERVICE(CM_OL_0027, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char number[40]; + char digits[36]; + int len; + int valid; + int checkdigit; - tx_log(TX_LOG_INFO, "[CM_OL_0027] 공통 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0027] 체크디짓 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0027] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0027] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0027] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "NUMBER", number, sizeof(number)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0027] NUMBER 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0027] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + len = cm_pack_digits(number, digits, sizeof(digits)); + if (len < 2) { + tx_log(TX_LOG_WARN, "[CM_OL_0027] 검증대상 자릿수 부족 len=%d", len); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = cm_rec_count(bizdate, CM_ST_RECV); + /* 전체 번호(체크디짓 포함) 합이 mod10==0 이면 유효 */ + valid = (cm_luhn_mod(digits, len, 1) == 0) ? 1 : 0; + + /* 본체(마지막 자리 제외)에 대해 산출되는 정상 체크디짓 */ + checkdigit = (10 - cm_luhn_mod(digits, len - 1, 0)) % 10; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0027] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "DIGITS", (long)len); + tx_buf_sets(ctx->out, "VALID", valid ? "Y" : "N"); + tx_buf_setlong(ctx->out, "CHECKDIGIT", (long)checkdigit); + tx_log(TX_LOG_INFO, "[CM_OL_0027] 검증완료 len=%d valid=%s cd=%d", + len, valid ? "Y" : "N", checkdigit); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0027.pgc b/app/online/cm_ol_0027.pgc index 941d559..add74aa 100644 --- a/app/online/cm_ol_0027.pgc +++ b/app/online/cm_ol_0027.pgc @@ -1,9 +1,10 @@ /* @tier medium @module cm @transform common-online */ /* - * cm_ol_0027.pgc - 공통 온라인 서비스 (CM_OL_0027) [tier=medium] + * cm_ol_0027.pgc - 체크디짓 검증 서비스 (CM_OL_0027) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 번호(NUMBER: 카드/계좌 등)에 Luhn(모드10) 알고리즘을 적용하여 + * 유효성(VALID=Y/N)과 계산된 체크디짓을 응답한다. DB 접근 없는 순수 로직. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -14,57 +15,78 @@ EXEC SQL INCLUDE sqlca; +/* 숫자만 추출 후 자릿수 반환 */ +static int cm_pack_digits(const char *src, char *dst, int dstlen) +{ + int i; + int n = 0; + + for (i = 0; src[i] != '\0' && n < dstlen - 1; i++) { + if (src[i] >= '0' && src[i] <= '9') + dst[n++] = src[i]; + } + dst[n] = '\0'; + return n; +} + +/* + * 오른쪽에서 왼쪽으로 이동하며 짝수번째(offset 기준) 자리를 2배, + * 9 초과 시 자릿수 합으로 환산한 뒤 총합의 mod 10 을 반환한다. + * offset=1: 마지막 자리 포함(전체 검증), offset=0: 체크디짓 산출용. + */ +static int cm_luhn_mod(const char *digits, int len, int offset) +{ + int sum = 0; + int i; + int pos = 0; + + for (i = len - 1; i >= 0; i--) { + int d = digits[i] - '0'; + if (((pos + offset) & 1) == 1) { + d *= 2; + if (d > 9) + d -= 9; + } + sum += d; + pos++; + } + return sum % 10; +} + TX_SERVICE(CM_OL_0027, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char number[40]; + char digits[36]; + int len; + int valid; + int checkdigit; - tx_log(TX_LOG_INFO, "[CM_OL_0027] 공통 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0027] 체크디짓 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0027] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[CM_OL_0027] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0027] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "NUMBER", number, sizeof(number)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0027] NUMBER 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0027] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + len = cm_pack_digits(number, digits, sizeof(digits)); + if (len < 2) { + tx_log(TX_LOG_WARN, "[CM_OL_0027] 검증대상 자릿수 부족 len=%d", len); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = cm_rec_count(bizdate, CM_ST_RECV); + /* 전체 번호(체크디짓 포함) 합이 mod10==0 이면 유효 */ + valid = (cm_luhn_mod(digits, len, 1) == 0) ? 1 : 0; + + /* 본체(마지막 자리 제외)에 대해 산출되는 정상 체크디짓 */ + checkdigit = (10 - cm_luhn_mod(digits, len - 1, 0)) % 10; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[CM_OL_0027] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "DIGITS", (long)len); + tx_buf_sets(ctx->out, "VALID", valid ? "Y" : "N"); + tx_buf_setlong(ctx->out, "CHECKDIGIT", (long)checkdigit); + tx_log(TX_LOG_INFO, "[CM_OL_0027] 검증완료 len=%d valid=%s cd=%d", + len, valid ? "Y" : "N", checkdigit); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0028.c b/app/online/cm_ol_0028.c index f0a3c6c..a0392f9 100644 --- a/app/online/cm_ol_0028.c +++ b/app/online/cm_ol_0028.c @@ -8,10 +8,11 @@ #line 1 "app/online/cm_ol_0028.pgc" /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0028.pgc - 공통 온라인 서비스 (CM_OL_0028) [tier=easy] + * cm_ol_0028.pgc - 시간 포맷 변환 서비스 (CM_OL_0028) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 시각(HHMMSS 6자리)을 HH:MM:SS 로 변환하고 각 필드의 범위를 + * 검증(시 0~23, 분/초 0~59)하여 포맷 결과와 총 경과초를 응답한다. + * DB 접근 없는 순수 로직 서비스. (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -89,37 +90,58 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0028.pgc" +#line 16 "app/online/cm_ol_0028.pgc" TX_SERVICE(CM_OL_0028, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + char hms[16]; + char formatted[16]; + int hh; + int mm; + int ss; + int i; + long secs; - tx_log(TX_LOG_INFO, "[CM_OL_0028] 공통 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0028] 시간 포맷 변환 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0028] KEY 누락"); + if (tx_buf_get(ctx->in, "HMS", hms, sizeof(hms)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0028] HMS 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 정확히 6자리 숫자여야 한다 */ + if (strlen(hms) != 6) { + tx_log(TX_LOG_WARN, "[CM_OL_0028] 길이 오류 hms=%s", hms); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + for (i = 0; i < 6; i++) { + if (hms[i] < '0' || hms[i] > '9') { + tx_log(TX_LOG_WARN, "[CM_OL_0028] 숫자 아님 hms=%s", hms); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } + + hh = (hms[0] - '0') * 10 + (hms[1] - '0'); + mm = (hms[2] - '0') * 10 + (hms[3] - '0'); + ss = (hms[4] - '0') * 10 + (hms[5] - '0'); + + if (hh > 23 || mm > 59 || ss > 59) { + tx_log(TX_LOG_WARN, "[CM_OL_0028] 시간 범위 오류 %02d:%02d:%02d", hh, mm, ss); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + sprintf(formatted, "%02d:%02d:%02d", hh, mm, ss); + secs = (long)hh * 3600 + (long)mm * 60 + ss; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "HMS", hms); + tx_buf_sets(ctx->out, "FORMATTED", formatted); + tx_buf_setlong(ctx->out, "SECONDS", secs); + tx_log(TX_LOG_INFO, "[CM_OL_0028] 변환완료 %s -> %s (%ld초)", hms, formatted, secs); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0028.pgc b/app/online/cm_ol_0028.pgc index 6314ef0..f3295de 100644 --- a/app/online/cm_ol_0028.pgc +++ b/app/online/cm_ol_0028.pgc @@ -1,9 +1,10 @@ /* @tier easy @module cm @transform common-online */ /* - * cm_ol_0028.pgc - 공통 온라인 서비스 (CM_OL_0028) [tier=easy] + * cm_ol_0028.pgc - 시간 포맷 변환 서비스 (CM_OL_0028) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 시각(HHMMSS 6자리)을 HH:MM:SS 로 변환하고 각 필드의 범위를 + * 검증(시 0~23, 분/초 0~59)하여 포맷 결과와 총 경과초를 응답한다. + * DB 접근 없는 순수 로직 서비스. (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -16,32 +17,53 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0028, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + char hms[16]; + char formatted[16]; + int hh; + int mm; + int ss; + int i; + long secs; - tx_log(TX_LOG_INFO, "[CM_OL_0028] 공통 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0028] 시간 포맷 변환 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0028] KEY 누락"); + if (tx_buf_get(ctx->in, "HMS", hms, sizeof(hms)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0028] HMS 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 정확히 6자리 숫자여야 한다 */ + if (strlen(hms) != 6) { + tx_log(TX_LOG_WARN, "[CM_OL_0028] 길이 오류 hms=%s", hms); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + for (i = 0; i < 6; i++) { + if (hms[i] < '0' || hms[i] > '9') { + tx_log(TX_LOG_WARN, "[CM_OL_0028] 숫자 아님 hms=%s", hms); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } + + hh = (hms[0] - '0') * 10 + (hms[1] - '0'); + mm = (hms[2] - '0') * 10 + (hms[3] - '0'); + ss = (hms[4] - '0') * 10 + (hms[5] - '0'); + + if (hh > 23 || mm > 59 || ss > 59) { + tx_log(TX_LOG_WARN, "[CM_OL_0028] 시간 범위 오류 %02d:%02d:%02d", hh, mm, ss); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + sprintf(formatted, "%02d:%02d:%02d", hh, mm, ss); + secs = (long)hh * 3600 + (long)mm * 60 + ss; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "HMS", hms); + tx_buf_sets(ctx->out, "FORMATTED", formatted); + tx_buf_setlong(ctx->out, "SECONDS", secs); + tx_log(TX_LOG_INFO, "[CM_OL_0028] 변환완료 %s -> %s (%ld초)", hms, formatted, secs); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0029.c b/app/online/cm_ol_0029.c index 675efb8..8901a8e 100644 --- a/app/online/cm_ol_0029.c +++ b/app/online/cm_ol_0029.c @@ -6,17 +6,19 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0029.pgc" -/* @tier hard @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0029.pgc - 공통 온라인 서비스 (CM_OL_0029) [tier=hard] + * cm_ol_0029.pgc - 통계 요약 조회 서비스 (CM_OL_0029) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 영업일(DATE)로 일 집계 뷰(cm_daily_v)를 조회하여 당일 거래 건수 합계와 + * 금액 총계를 응답한다. 뷰는 상태별 행을 보유하므로 SUM 집계로 단건화한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,88 +91,82 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0029.pgc" +#line 17 "app/online/cm_ol_0029.pgc" TX_SERVICE(CM_OL_0029, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + +#line 22 "app/online/cm_ol_0029.pgc" + char h_date [ 9 ] ; + +#line 23 "app/online/cm_ol_0029.pgc" + long h_cnt ; + +#line 24 "app/online/cm_ol_0029.pgc" + long h_total ; +/* exec sql end declare section */ +#line 25 "app/online/cm_ol_0029.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0029] 공통 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0029] 필수 필드 누락"); + char bizdate[16]; + char wonbuf[32]; + long avg; + + tx_log(TX_LOG_INFO, "[CM_OL_0029] 통계 요약 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "DATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0029] DATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0029] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CM_OL_0029] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + memset(&h_date, 0, sizeof(h_date)); + strncpy(h_date, bizdate, sizeof(h_date) - 1); + h_cnt = 0; + h_total = 0; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select NVL ( SUM ( cnt ) , 0 ) , NVL ( SUM ( total_amt ) , 0 ) from cm_daily_v where biz_date = $1 ", + ECPGt_char,(h_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_total),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 53 "app/online/cm_ol_0029.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_INFO, "[CM_OL_0029] 집계 없음 date=%s", bizdate); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0029 SELECT cm_daily_v"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0029] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CM_OL_0029] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (cm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 평균 거래금액 파생 계산 */ + avg = (h_cnt > 0) ? (h_total / h_cnt) : 0; + amount_format_won(h_total, wonbuf, sizeof(wonbuf)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CM_OL_0029] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "DATE", bizdate); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_setlong(ctx->out, "TOTAL", h_total); + tx_buf_setlong(ctx->out, "AVG", avg); + tx_buf_sets(ctx->out, "TOTALWON", wonbuf); + tx_log(TX_LOG_INFO, "[CM_OL_0029] 요약완료 date=%s cnt=%ld total=%ld avg=%ld", + bizdate, h_cnt, h_total, avg); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0029.pgc b/app/online/cm_ol_0029.pgc index 2699366..1ecb610 100644 --- a/app/online/cm_ol_0029.pgc +++ b/app/online/cm_ol_0029.pgc @@ -1,14 +1,16 @@ -/* @tier hard @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0029.pgc - 공통 온라인 서비스 (CM_OL_0029) [tier=hard] + * cm_ol_0029.pgc - 통계 요약 조회 서비스 (CM_OL_0029) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 영업일(DATE)로 일 집계 뷰(cm_daily_v)를 조회하여 당일 거래 건수 합계와 + * 금액 총계를 응답한다. 뷰는 상태별 행을 보유하므로 SUM 집계로 단건화한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,83 +18,62 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0029, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_date[9]; + long h_cnt; + long h_total; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0029] 공통 트랜잭션 오케스트레이션 진입"); + char bizdate[16]; + char wonbuf[32]; + long avg; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0029] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[CM_OL_0029] 통계 요약 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "DATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0029] DATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0029] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[CM_OL_0029] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + memset(&h_date, 0, sizeof(h_date)); + strncpy(h_date, bizdate, sizeof(h_date) - 1); + h_cnt = 0; + h_total = 0; + + EXEC SQL + SELECT NVL(SUM(cnt), 0), NVL(SUM(total_amt), 0) + INTO :h_cnt, :h_total + FROM cm_daily_v + WHERE biz_date = :h_date; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_INFO, "[CM_OL_0029] 집계 없음 date=%s", bizdate); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0029 SELECT cm_daily_v"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0029] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CM_OL_0029] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (cm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 평균 거래금액 파생 계산 */ + avg = (h_cnt > 0) ? (h_total / h_cnt) : 0; + amount_format_won(h_total, wonbuf, sizeof(wonbuf)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CM_OL_0029] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "DATE", bizdate); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_setlong(ctx->out, "TOTAL", h_total); + tx_buf_setlong(ctx->out, "AVG", avg); + tx_buf_sets(ctx->out, "TOTALWON", wonbuf); + tx_log(TX_LOG_INFO, "[CM_OL_0029] 요약완료 date=%s cnt=%ld total=%ld avg=%ld", + bizdate, h_cnt, h_total, avg); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0030.c b/app/online/cm_ol_0030.c index 5897fef..c922d72 100644 --- a/app/online/cm_ol_0030.c +++ b/app/online/cm_ol_0030.c @@ -6,17 +6,20 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0030.pgc" -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0030.pgc - 공통 온라인 서비스 (CM_OL_0030) [tier=easy] + * cm_ol_0030.pgc - 세션 유효성 만료 확인 서비스 (CM_OL_0030) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 세션ID(SID)로 세션 마스터(cm_session)를 조회하여 만료시각(epoch)과 상태를 + * 얻고, 요청 현재시각(NOW, epoch 초)과 비교해 잔여 유효시간(초)을 계산한다. + * 잔여시간 > 0 이고 상태가 활성('A')이면 유효로 응답한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -89,37 +92,86 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0030.pgc" +#line 18 "app/online/cm_ol_0030.pgc" TX_SERVICE(CM_OL_0030, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 23 "app/online/cm_ol_0030.pgc" + char h_sid [ 40 ] ; + +#line 24 "app/online/cm_ol_0030.pgc" + long h_exp ; + +#line 25 "app/online/cm_ol_0030.pgc" + char h_state [ 2 ] ; +/* exec sql end declare section */ +#line 26 "app/online/cm_ol_0030.pgc" - tx_log(TX_LOG_INFO, "[CM_OL_0030] 공통 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0030] KEY 누락"); + char sid[40]; + long now; + long remain; + int valid; + + tx_log(TX_LOG_INFO, "[CM_OL_0030] 세션 유효성 만료 확인 서비스 진입"); + + if (tx_buf_get(ctx->in, "SID", sid, sizeof(sid)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0030] SID 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + now = 0; + if (tx_buf_getlong(ctx->in, "NOW", &now) != TX_OK || now <= 0) { + tx_log(TX_LOG_ERROR, "[CM_OL_0030] NOW(현재시각) 누락/오류"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(&h_sid, 0, sizeof(h_sid)); + memset(&h_state, 0, sizeof(h_state)); + strncpy(h_sid, sid, sizeof(h_sid) - 1); + h_exp = 0; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select expire_epoch , sess_state from cm_session where sess_id = $1 ", + ECPGt_char,(h_sid),(long)40,(long)1,(40)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_exp),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_state),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 56 "app/online/cm_ol_0030.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0030] 세션 없음 sid=%s", sid); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0030 SELECT cm_session"); + tx_return(ctx, TX_EDB, ctx->out); return; } + remain = h_exp - now; + if (remain < 0) + remain = 0; + + /* 활성 상태 + 잔여시간 존재 시에만 유효 */ + valid = (h_state[0] == 'A' && remain > 0) ? 1 : 0; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "SID", sid); + tx_buf_sets(ctx->out, "STATE", h_state); + tx_buf_setlong(ctx->out, "REMAIN", remain); + tx_buf_sets(ctx->out, "VALID", valid ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[CM_OL_0030] 확인완료 sid=%s remain=%ld valid=%s", + sid, remain, valid ? "Y" : "N"); + tx_return(ctx, valid ? TX_OK : TX_ENOENT, ctx->out); } diff --git a/app/online/cm_ol_0030.pgc b/app/online/cm_ol_0030.pgc index 924ad74..72e4c3b 100644 --- a/app/online/cm_ol_0030.pgc +++ b/app/online/cm_ol_0030.pgc @@ -1,14 +1,17 @@ -/* @tier easy @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0030.pgc - 공통 온라인 서비스 (CM_OL_0030) [tier=easy] + * cm_ol_0030.pgc - 세션 유효성 만료 확인 서비스 (CM_OL_0030) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 세션ID(SID)로 세션 마스터(cm_session)를 조회하여 만료시각(epoch)과 상태를 + * 얻고, 요청 현재시각(NOW, epoch 초)과 비교해 잔여 유효시간(초)을 계산한다. + * 잔여시간 > 0 이고 상태가 활성('A')이면 유효로 응답한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "cm_core.h" @@ -16,32 +19,66 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(CM_OL_0030, ctx) { - cm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_sid[40]; + long h_exp; + char h_state[2]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[CM_OL_0030] 공통 단건조회 서비스 진입"); + char sid[40]; + long now; + long remain; + int valid; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0030] KEY 누락"); + tx_log(TX_LOG_INFO, "[CM_OL_0030] 세션 유효성 만료 확인 서비스 진입"); + + if (tx_buf_get(ctx->in, "SID", sid, sizeof(sid)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0030] SID 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + now = 0; + if (tx_buf_getlong(ctx->in, "NOW", &now) != TX_OK || now <= 0) { + tx_log(TX_LOG_ERROR, "[CM_OL_0030] NOW(현재시각) 누락/오류"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = cm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[CM_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(&h_sid, 0, sizeof(h_sid)); + memset(&h_state, 0, sizeof(h_state)); + strncpy(h_sid, sid, sizeof(h_sid) - 1); + h_exp = 0; + + EXEC SQL + SELECT expire_epoch, sess_state + INTO :h_exp, :h_state + FROM cm_session + WHERE sess_id = :h_sid; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[CM_OL_0030] 세션 없음 sid=%s", sid); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("CM_OL_0030 SELECT cm_session"); + tx_return(ctx, TX_EDB, ctx->out); return; } + remain = h_exp - now; + if (remain < 0) + remain = 0; + + /* 활성 상태 + 잔여시간 존재 시에만 유효 */ + valid = (h_state[0] == 'A' && remain > 0) ? 1 : 0; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[CM_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "SID", sid); + tx_buf_sets(ctx->out, "STATE", h_state); + tx_buf_setlong(ctx->out, "REMAIN", remain); + tx_buf_sets(ctx->out, "VALID", valid ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[CM_OL_0030] 확인완료 sid=%s remain=%ld valid=%s", + sid, remain, valid ? "Y" : "N"); + tx_return(ctx, valid ? TX_OK : TX_ENOENT, ctx->out); } diff --git a/app/online/cm_ol_0031.c b/app/online/cm_ol_0031.c index b5a1c54..dbc87fc 100644 --- a/app/online/cm_ol_0031.c +++ b/app/online/cm_ol_0031.c @@ -6,12 +6,14 @@ /* End of automatic include section */ #line 1 "app/online/cm_ol_0031.pgc" -/* @tier hard @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0031.pgc - 공통 온라인 서비스 (CM_OL_0031) [tier=hard] + * cm_ol_0031.pgc - 공통 응답코드 매핑 서비스 (CM_OL_0031) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 내부 처리결과코드(RC, 정수)를 대외 표준 응답코드(4자리 문자열)로 매핑한다. + * TxCore 오류상수(TX_OK/TX_ENOENT/...) 및 업무 결과코드를 msg_layout.h 의 + * 표준 대외코드(RESP_OK/RESP_INVALID/RESP_NOMERCH/RESP_SYSERR)로 환산해 + * 응답한다. DB 접근 없는 in-code 규칙 매핑. (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -89,88 +91,59 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/cm_ol_0031.pgc" +#line 17 "app/online/cm_ol_0031.pgc" +/* 내부 RC -> 대외 표준 응답코드 문자열 매핑 규칙 */ +static const char *cm_map_resp(int rc, const char **desc) +{ + switch (rc) { + case TX_OK: + *desc = "정상처리"; + return RESP_OK; + case TX_EINVAL: + *desc = "입력항목 오류"; + return RESP_INVALID; + case TX_ENOKEY: + *desc = "필수키 누락"; + return RESP_INVALID; + case TX_ENOENT: + *desc = "대상 없음(가맹점/거래)"; + return RESP_NOMERCH; + case TX_EDB: + *desc = "DB 처리 오류"; + return RESP_SYSERR; + case TX_ENOMEM: + *desc = "자원 부족"; + return RESP_SYSERR; + case TX_FAIL: + default: + *desc = "시스템 오류"; + return RESP_SYSERR; + } +} + TX_SERVICE(CM_OL_0031, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + long rc = 0; + const char *resp; + const char *desc = ""; - tx_log(TX_LOG_INFO, "[CM_OL_0031] 공통 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0031] 공통 응답코드 매핑 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0031] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0031] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (tx_buf_getlong(ctx->in, "RC", &rc) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0031] RC 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0031] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CM_OL_0031] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (cm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + resp = cm_map_resp((int)rc, &desc); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CM_OL_0031] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_setlong(ctx->out, "RC", rc); + tx_buf_sets(ctx->out, "RESPCODE", resp); + tx_buf_sets(ctx->out, "RESPDESC", desc); + tx_buf_sets(ctx->out, "SUCCESS", (strcmp(resp, RESP_OK) == 0) ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[CM_OL_0031] 매핑완료 rc=%ld -> resp=%s(%s)", rc, resp, desc); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/cm_ol_0031.pgc b/app/online/cm_ol_0031.pgc index e3ef8d7..b9fb50a 100644 --- a/app/online/cm_ol_0031.pgc +++ b/app/online/cm_ol_0031.pgc @@ -1,9 +1,11 @@ -/* @tier hard @module cm @transform common-online */ +/* @tier medium @module cm @transform common-online */ /* - * cm_ol_0031.pgc - 공통 온라인 서비스 (CM_OL_0031) [tier=hard] + * cm_ol_0031.pgc - 공통 응답코드 매핑 서비스 (CM_OL_0031) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 cm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 내부 처리결과코드(RC, 정수)를 대외 표준 응답코드(4자리 문자열)로 매핑한다. + * TxCore 오류상수(TX_OK/TX_ENOENT/...) 및 업무 결과코드를 msg_layout.h 의 + * 표준 대외코드(RESP_OK/RESP_INVALID/RESP_NOMERCH/RESP_SYSERR)로 환산해 + * 응답한다. DB 접근 없는 in-code 규칙 매핑. (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -14,85 +16,56 @@ EXEC SQL INCLUDE sqlca; +/* 내부 RC -> 대외 표준 응답코드 문자열 매핑 규칙 */ +static const char *cm_map_resp(int rc, const char **desc) +{ + switch (rc) { + case TX_OK: + *desc = "정상처리"; + return RESP_OK; + case TX_EINVAL: + *desc = "입력항목 오류"; + return RESP_INVALID; + case TX_ENOKEY: + *desc = "필수키 누락"; + return RESP_INVALID; + case TX_ENOENT: + *desc = "대상 없음(가맹점/거래)"; + return RESP_NOMERCH; + case TX_EDB: + *desc = "DB 처리 오류"; + return RESP_SYSERR; + case TX_ENOMEM: + *desc = "자원 부족"; + return RESP_SYSERR; + case TX_FAIL: + default: + *desc = "시스템 오류"; + return RESP_SYSERR; + } +} + TX_SERVICE(CM_OL_0031, ctx) { - cm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + long rc = 0; + const char *resp; + const char *desc = ""; - tx_log(TX_LOG_INFO, "[CM_OL_0031] 공통 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[CM_OL_0031] 공통 응답코드 매핑 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0031] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[CM_OL_0031] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (tx_buf_getlong(ctx->in, "RC", &rc) != TX_OK) { + tx_log(TX_LOG_ERROR, "[CM_OL_0031] RC 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, CM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = cm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[CM_OL_0031] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("CM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[CM_OL_0031] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (cm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + resp = cm_map_resp((int)rc, &desc); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[CM_OL_0031] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_setlong(ctx->out, "RC", rc); + tx_buf_sets(ctx->out, "RESPCODE", resp); + tx_buf_sets(ctx->out, "RESPDESC", desc); + tx_buf_sets(ctx->out, "SUCCESS", (strcmp(resp, RESP_OK) == 0) ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[CM_OL_0031] 매핑완료 rc=%ld -> resp=%s(%s)", rc, resp, desc); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0003.c b/app/online/lg_ol_0003.c index 2103e7e..9da667f 100644 --- a/app/online/lg_ol_0003.c +++ b/app/online/lg_ol_0003.c @@ -10,13 +10,19 @@ /* * lg_ol_0003.pgc - 원장 온라인 서비스 (LG_OL_0003) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인원장 정리: 특정 가맹점(MERCHID)의 승인 접수건(status='R')을 + * 일괄 확정(status='M') 처리하고 upd_ts 를 갱신한다. 마감 전에 접수 + * 상태로 남아 있는 승인건을 확정 상태로 끌어올리는 정리성 서비스로, + * 반영된 건수(AFFECTED)를 응답으로 돌려준다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(UPDATE) 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +95,134 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0003.pgc" +#line 21 "app/online/lg_ol_0003.pgc" TX_SERVICE(LG_OL_0003, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 26 "app/online/lg_ol_0003.pgc" + char h_merch_id [ 16 ] ; + +#line 27 "app/online/lg_ol_0003.pgc" + char h_st_recv [ 2 ] ; + +#line 28 "app/online/lg_ol_0003.pgc" + char h_st_done [ 2 ] ; + +#line 29 "app/online/lg_ol_0003.pgc" + int h_pending ; +/* exec sql end declare section */ +#line 30 "app/online/lg_ol_0003.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0003] 원장 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0003] KEY 누락"); + char merch[16]; + long affected = 0; + + tx_log(TX_LOG_INFO, "[LG_OL_0003] 승인원장 정리 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + merch[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0003] MERCHID 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_st_recv, LG_ST_RECV, sizeof(h_st_recv) - 1); + h_st_recv[sizeof(h_st_recv) - 1] = '\0'; + strncpy(h_st_done, LG_ST_DONE, sizeof(h_st_done) - 1); + h_st_done[sizeof(h_st_done) - 1] = '\0'; + + /* ---- 2. 정리 대상(접수 상태) 건수 사전 확인 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from lg_ledger where merch_id = $1 and status = $2 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_st_recv),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_pending),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 59 "app/online/lg_ol_0003.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0003 count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + tx_log(TX_LOG_INFO, "[LG_OL_0003] 가맹점 %s 접수(R) 대상 %d 건", + merch, h_pending); + + if (h_pending == 0) { + /* 정리할 접수건이 없음 - 정상(0건) 처리 응답 */ + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "AFFECTED", 0); + tx_log(TX_LOG_INFO, "[LG_OL_0003] 정리 대상 없음 merch=%s", merch); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* ---- 3. 트랜잭션 시작 → 접수(R) 건 확정(M) 일괄 전이 ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0003] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set status = $1 , upd_ts = now ( ) where merch_id = $2 and status = $3 ", + ECPGt_char,(h_st_done),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_st_recv),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 95 "app/online/lg_ol_0003.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0003 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + affected = (long)sqlca.sqlerrd[2]; + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0003] 커밋 실패 merch=%s", merch); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 4. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "AFFECTED", affected); + tx_buf_sets(ctx->out, "STATUS", LG_ST_DONE); + + tx_log(TX_LOG_INFO, "[LG_OL_0003] 승인원장 정리 완료 merch=%s 확정=%ld 건", + merch, affected); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0003.pgc b/app/online/lg_ol_0003.pgc index 9b2bb26..0f603fd 100644 --- a/app/online/lg_ol_0003.pgc +++ b/app/online/lg_ol_0003.pgc @@ -2,13 +2,19 @@ /* * lg_ol_0003.pgc - 원장 온라인 서비스 (LG_OL_0003) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인원장 정리: 특정 가맹점(MERCHID)의 승인 접수건(status='R')을 + * 일괄 확정(status='M') 처리하고 upd_ts 를 갱신한다. 마감 전에 접수 + * 상태로 남아 있는 승인건을 확정 상태로 끌어올리는 정리성 서비스로, + * 반영된 건수(AFFECTED)를 응답으로 돌려준다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(UPDATE) 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,32 +22,106 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0003, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_st_recv[2]; + char h_st_done[2]; + int h_pending; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0003] 원장 단건조회 서비스 진입"); + char merch[16]; + long affected = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0003] KEY 누락"); + tx_log(TX_LOG_INFO, "[LG_OL_0003] 승인원장 정리 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + merch[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0003] MERCHID 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_st_recv, LG_ST_RECV, sizeof(h_st_recv) - 1); + h_st_recv[sizeof(h_st_recv) - 1] = '\0'; + strncpy(h_st_done, LG_ST_DONE, sizeof(h_st_done) - 1); + h_st_done[sizeof(h_st_done) - 1] = '\0'; + + /* ---- 2. 정리 대상(접수 상태) 건수 사전 확인 ---- */ + EXEC SQL SELECT count(*) + INTO :h_pending + FROM lg_ledger + WHERE merch_id = :h_merch_id + AND status = :h_st_recv; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0003 count"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + tx_log(TX_LOG_INFO, "[LG_OL_0003] 가맹점 %s 접수(R) 대상 %d 건", + merch, h_pending); + + if (h_pending == 0) { + /* 정리할 접수건이 없음 - 정상(0건) 처리 응답 */ + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "AFFECTED", 0); + tx_log(TX_LOG_INFO, "[LG_OL_0003] 정리 대상 없음 merch=%s", merch); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* ---- 3. 트랜잭션 시작 → 접수(R) 건 확정(M) 일괄 전이 ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0003] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE lg_ledger + SET status = :h_st_done, upd_ts = now() + WHERE merch_id = :h_merch_id + AND status = :h_st_recv; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0003 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + affected = (long)sqlca.sqlerrd[2]; + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0003] 커밋 실패 merch=%s", merch); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 4. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "AFFECTED", affected); + tx_buf_sets(ctx->out, "STATUS", LG_ST_DONE); + + tx_log(TX_LOG_INFO, "[LG_OL_0003] 승인원장 정리 완료 merch=%s 확정=%ld 건", + merch, affected); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0004.c b/app/online/lg_ol_0004.c index 7fc0ec7..8bd757a 100644 --- a/app/online/lg_ol_0004.c +++ b/app/online/lg_ol_0004.c @@ -10,13 +10,19 @@ /* * lg_ol_0004.pgc - 원장 온라인 서비스 (LG_OL_0004) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산원장 반영: 요청 영업일(BIZDATE)의 승인완료(status='M') 건을 + * 정산완료(status='S')로 전이한다. 전이 전 SUM(amount)/COUNT 로 + * 정산 대상 금액을 확정 조회한 뒤 UPDATE 하며, 정산 반영 건수와 + * 정산금액 합계를 응답한다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +95,141 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0004.pgc" +#line 21 "app/online/lg_ol_0004.pgc" TX_SERVICE(LG_OL_0004, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 26 "app/online/lg_ol_0004.pgc" + char h_biz_date [ 9 ] ; + +#line 27 "app/online/lg_ol_0004.pgc" + char h_st_done [ 2 ] ; + +#line 28 "app/online/lg_ol_0004.pgc" + char h_st_settled [ 2 ] ; + +#line 29 "app/online/lg_ol_0004.pgc" + long h_sum ; + +#line 30 "app/online/lg_ol_0004.pgc" + int h_cnt ; +/* exec sql end declare section */ +#line 31 "app/online/lg_ol_0004.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0004] 원장 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0004] KEY 누락"); + char bizdate[16]; + long affected = 0; + + tx_log(TX_LOG_INFO, "[LG_OL_0004] 정산원장 반영 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_ERROR, "[LG_OL_0004] BIZDATE 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0004] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_st_done, LG_ST_DONE, sizeof(h_st_done) - 1); + h_st_done[sizeof(h_st_done) - 1] = '\0'; + strncpy(h_st_settled, LG_ST_SETTLED, sizeof(h_st_settled) - 1); + h_st_settled[sizeof(h_st_settled) - 1] = '\0'; + + /* ---- 2. 정산대상(M) 금액/건수 사전 집계 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) from lg_ledger where biz_date = $1 and status = $2 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_st_done),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 60 "app/online/lg_ol_0004.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0004 sum"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + tx_log(TX_LOG_INFO, "[LG_OL_0004] 정산대상 date=%s 건수=%d 금액=%ld", + bizdate, h_cnt, h_sum); + + if (h_cnt == 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SETTLED", 0); + tx_buf_setlong(ctx->out, "SUMAMT", 0); + tx_log(TX_LOG_INFO, "[LG_OL_0004] 정산대상 없음 date=%s", bizdate); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* ---- 3. 트랜잭션 시작 → 승인완료(M) → 정산완료(S) 전이 ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0004] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set status = $1 , upd_ts = now ( ) where biz_date = $2 and status = $3 ", + ECPGt_char,(h_st_settled),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_st_done),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 96 "app/online/lg_ol_0004.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0004 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + affected = (long)sqlca.sqlerrd[2]; + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0004] 커밋 실패 date=%s", bizdate); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 4. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0004] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SETTLED", affected); + tx_buf_setlong(ctx->out, "SUMAMT", h_sum); + tx_buf_sets(ctx->out, "STATUS", LG_ST_SETTLED); + + tx_log(TX_LOG_INFO, "[LG_OL_0004] 정산원장 반영 완료 date=%s 정산=%ld건 금액=%ld", + bizdate, affected, h_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0004.pgc b/app/online/lg_ol_0004.pgc index c100331..ce0b027 100644 --- a/app/online/lg_ol_0004.pgc +++ b/app/online/lg_ol_0004.pgc @@ -2,13 +2,19 @@ /* * lg_ol_0004.pgc - 원장 온라인 서비스 (LG_OL_0004) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산원장 반영: 요청 영업일(BIZDATE)의 승인완료(status='M') 건을 + * 정산완료(status='S')로 전이한다. 전이 전 SUM(amount)/COUNT 로 + * 정산 대상 금액을 확정 조회한 뒤 UPDATE 하며, 정산 반영 건수와 + * 정산금액 합계를 응답한다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,32 +22,108 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0004, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_st_done[2]; + char h_st_settled[2]; + long h_sum; + int h_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0004] 원장 단건조회 서비스 진입"); + char bizdate[16]; + long affected = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0004] KEY 누락"); + tx_log(TX_LOG_INFO, "[LG_OL_0004] 정산원장 반영 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_ERROR, "[LG_OL_0004] BIZDATE 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0004] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_st_done, LG_ST_DONE, sizeof(h_st_done) - 1); + h_st_done[sizeof(h_st_done) - 1] = '\0'; + strncpy(h_st_settled, LG_ST_SETTLED, sizeof(h_st_settled) - 1); + h_st_settled[sizeof(h_st_settled) - 1] = '\0'; + + /* ---- 2. 정산대상(M) 금액/건수 사전 집계 ---- */ + EXEC SQL SELECT coalesce(sum(amount), 0), count(*) + INTO :h_sum, :h_cnt + FROM lg_ledger + WHERE biz_date = :h_biz_date + AND status = :h_st_done; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0004 sum"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + tx_log(TX_LOG_INFO, "[LG_OL_0004] 정산대상 date=%s 건수=%d 금액=%ld", + bizdate, h_cnt, h_sum); + + if (h_cnt == 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SETTLED", 0); + tx_buf_setlong(ctx->out, "SUMAMT", 0); + tx_log(TX_LOG_INFO, "[LG_OL_0004] 정산대상 없음 date=%s", bizdate); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* ---- 3. 트랜잭션 시작 → 승인완료(M) → 정산완료(S) 전이 ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0004] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE lg_ledger + SET status = :h_st_settled, upd_ts = now() + WHERE biz_date = :h_biz_date + AND status = :h_st_done; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0004 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + affected = (long)sqlca.sqlerrd[2]; + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0004] 커밋 실패 date=%s", bizdate); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 4. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0004] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SETTLED", affected); + tx_buf_setlong(ctx->out, "SUMAMT", h_sum); + tx_buf_sets(ctx->out, "STATUS", LG_ST_SETTLED); + + tx_log(TX_LOG_INFO, "[LG_OL_0004] 정산원장 반영 완료 date=%s 정산=%ld건 금액=%ld", + bizdate, affected, h_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0005.c b/app/online/lg_ol_0005.c index 703d943..5602789 100644 --- a/app/online/lg_ol_0005.c +++ b/app/online/lg_ol_0005.c @@ -10,13 +10,20 @@ /* * lg_ol_0005.pgc - 원장 온라인 서비스 (LG_OL_0005) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 대사(對査) 잔액검증: 요청 영업일(BIZDATE)의 원장 합계(Σamount) + * 를 집계하고, 요청자가 제시한 기대값(EXPECT)과 대조한다. 두 값이 + * 일치하면 정상(RESP_OK), 불일치하면 차액과 함께 RESP_INVALID 를 + * 반환한다. 불일치 진단을 돕기 위해 처리완료(M)/정산완료(S) 상태별 + * 부분합도 함께 산출한다. 조회 전용 서비스로 원장을 변경하지 않는다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(SUM/COUNT) 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,60 +96,159 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0005.pgc" +#line 22 "app/online/lg_ol_0005.pgc" TX_SERVICE(LG_OL_0005, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 27 "app/online/lg_ol_0005.pgc" + char h_biz_date [ 9 ] ; + +#line 28 "app/online/lg_ol_0005.pgc" + char h_status [ 2 ] ; + +#line 29 "app/online/lg_ol_0005.pgc" + long h_sum ; + +#line 30 "app/online/lg_ol_0005.pgc" + int h_cnt ; + +#line 31 "app/online/lg_ol_0005.pgc" + long h_done_sum ; + +#line 32 "app/online/lg_ol_0005.pgc" + long h_settled_sum ; +/* exec sql end declare section */ +#line 33 "app/online/lg_ol_0005.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0005] 원장 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0005] 필수 필드 누락"); + char bizdate[16]; + char wonbuf[32]; + long expect = 0, diff = 0; + + tx_log(TX_LOG_INFO, "[LG_OL_0005] 원장 대사 잔액검증 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_ERROR, "[LG_OL_0005] BIZDATE 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0005] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0005] 영업일 오류 date=%s", bizdate); + if (tx_buf_getlong(ctx->in, "EXPECT", &expect) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0005] EXPECT 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0005] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* ---- 2. 당일 원장 전체 합계/건수 집계 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) from lg_ledger where biz_date = $1 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 65 "app/online/lg_ol_0005.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0005 sum"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + /* ---- 3. 상태별 부분합 (처리완료 M) ---- */ + strncpy(h_status, LG_ST_DONE, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from lg_ledger where biz_date = $1 and status = $2 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_done_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 83 "app/online/lg_ol_0005.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0005 done"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* ---- 4. 상태별 부분합 (정산완료 S) ---- */ + strncpy(h_status, LG_ST_SETTLED, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from lg_ledger where biz_date = $1 and status = $2 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settled_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 101 "app/online/lg_ol_0005.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0005 settled"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* ---- 5. 기대값과 대조 ---- */ + diff = h_sum - expect; + amount_format_won(diff, wonbuf, sizeof(wonbuf)); + tx_log(TX_LOG_INFO, + "[LG_OL_0005] 대사 date=%s 실제=%ld 기대=%ld 차액=%ld 건수=%d", + bizdate, h_sum, expect, diff, h_cnt); tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "ACTUAL", h_sum); + tx_buf_setlong(ctx->out, "EXPECT", expect); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "DIFFWON", wonbuf); + tx_buf_setlong(ctx->out, "DAYCNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "DONESUM", h_done_sum); + tx_buf_setlong(ctx->out, "SETTLEDSUM", h_settled_sum); + + if (diff != 0) { + /* 불일치: 대사 실패로 판정 */ + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "MATCHED", "N"); + tx_log(TX_LOG_WARN, "[LG_OL_0005] 대사 불일치 date=%s 차액=%ld", + bizdate, diff); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 일치: 대사 성공 */ tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0005] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MATCHED", "Y"); + tx_log(TX_LOG_INFO, "[LG_OL_0005] 대사 일치 date=%s 합계=%ld", bizdate, h_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0005.pgc b/app/online/lg_ol_0005.pgc index 7f4de59..a73b125 100644 --- a/app/online/lg_ol_0005.pgc +++ b/app/online/lg_ol_0005.pgc @@ -2,13 +2,20 @@ /* * lg_ol_0005.pgc - 원장 온라인 서비스 (LG_OL_0005) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 대사(對査) 잔액검증: 요청 영업일(BIZDATE)의 원장 합계(Σamount) + * 를 집계하고, 요청자가 제시한 기대값(EXPECT)과 대조한다. 두 값이 + * 일치하면 정상(RESP_OK), 불일치하면 차액과 함께 RESP_INVALID 를 + * 반환한다. 불일치 진단을 돕기 위해 처리완료(M)/정산완료(S) 상태별 + * 부분합도 함께 산출한다. 조회 전용 서비스로 원장을 변경하지 않는다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(SUM/COUNT) 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,55 +23,121 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0005, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_status[2]; + long h_sum; + int h_cnt; + long h_done_sum; + long h_settled_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0005] 원장 등록 서비스 진입"); + char bizdate[16]; + char wonbuf[32]; + long expect = 0, diff = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0005] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[LG_OL_0005] 원장 대사 잔액검증 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_ERROR, "[LG_OL_0005] BIZDATE 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0005] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0005] 영업일 오류 date=%s", bizdate); + if (tx_buf_getlong(ctx->in, "EXPECT", &expect) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0005] EXPECT 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0005] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* ---- 2. 당일 원장 전체 합계/건수 집계 ---- */ + EXEC SQL SELECT coalesce(sum(amount), 0), count(*) + INTO :h_sum, :h_cnt + FROM lg_ledger + WHERE biz_date = :h_biz_date; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0005 sum"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + /* ---- 3. 상태별 부분합 (처리완료 M) ---- */ + strncpy(h_status, LG_ST_DONE, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_done_sum + FROM lg_ledger + WHERE biz_date = :h_biz_date + AND status = :h_status; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0005 done"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* ---- 4. 상태별 부분합 (정산완료 S) ---- */ + strncpy(h_status, LG_ST_SETTLED, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_settled_sum + FROM lg_ledger + WHERE biz_date = :h_biz_date + AND status = :h_status; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0005 settled"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* ---- 5. 기대값과 대조 ---- */ + diff = h_sum - expect; + amount_format_won(diff, wonbuf, sizeof(wonbuf)); + tx_log(TX_LOG_INFO, + "[LG_OL_0005] 대사 date=%s 실제=%ld 기대=%ld 차액=%ld 건수=%d", + bizdate, h_sum, expect, diff, h_cnt); tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "ACTUAL", h_sum); + tx_buf_setlong(ctx->out, "EXPECT", expect); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "DIFFWON", wonbuf); + tx_buf_setlong(ctx->out, "DAYCNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "DONESUM", h_done_sum); + tx_buf_setlong(ctx->out, "SETTLEDSUM", h_settled_sum); + + if (diff != 0) { + /* 불일치: 대사 실패로 판정 */ + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "MATCHED", "N"); + tx_log(TX_LOG_WARN, "[LG_OL_0005] 대사 불일치 date=%s 차액=%ld", + bizdate, diff); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 일치: 대사 성공 */ tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0005] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MATCHED", "Y"); + tx_log(TX_LOG_INFO, "[LG_OL_0005] 대사 일치 date=%s 합계=%ld", bizdate, h_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0006.c b/app/online/lg_ol_0006.c index 02be96b..8cfd845 100644 --- a/app/online/lg_ol_0006.c +++ b/app/online/lg_ol_0006.c @@ -10,13 +10,18 @@ /* * lg_ol_0006.pgc - 원장 온라인 서비스 (LG_OL_0006) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 정정: 단건 원장(KEY)의 금액/수수료를 정정한다. 기존 행을 + * 먼저 조회해 정정 전 금액을 확보한 뒤, 요청받은 새 금액(AMOUNT)과 + * 재산정 수수료로 UPDATE 하고 정정 전/후 금액을 응답한다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +94,147 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0006.pgc" +#line 20 "app/online/lg_ol_0006.pgc" TX_SERVICE(LG_OL_0006, ctx) { - lg_rec_t rec; + /* exec sql begin declare section */ + + + + + + +#line 25 "app/online/lg_ol_0006.pgc" + char h_key [ 16 ] ; + +#line 26 "app/online/lg_ol_0006.pgc" + long h_old_amount ; + +#line 27 "app/online/lg_ol_0006.pgc" + long h_old_fee ; + +#line 28 "app/online/lg_ol_0006.pgc" + long h_new_amount ; + +#line 29 "app/online/lg_ol_0006.pgc" + long h_new_fee ; +/* exec sql end declare section */ +#line 30 "app/online/lg_ol_0006.pgc" + + char key[16]; - int rc; + long new_amount = 0; - tx_log(TX_LOG_INFO, "[LG_OL_0006] 원장 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0006] 원장 정정 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { tx_log(TX_LOG_ERROR, "[LG_OL_0006] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_getlong(ctx->in, "AMOUNT", &new_amount) != TX_OK || + !amount_is_valid(new_amount)) { + tx_log(TX_LOG_WARN, "[LG_OL_0006] 정정금액 오류 amount=%ld", new_amount); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0006] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + /* ---- 2. 정정 대상 기존 행 조회 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , fee from lg_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_old_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_old_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 61 "app/online/lg_ol_0006.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0006] 정정 대상 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0006 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + h_new_amount = new_amount; + h_new_fee = new_amount / 100; /* 정정 수수료 재산정(1%) */ + + tx_log(TX_LOG_INFO, "[LG_OL_0006] 정정 key=%s 금액 %ld→%ld 수수료 %ld→%ld", + key, h_old_amount, h_new_amount, h_old_fee, h_new_fee); + + /* ---- 3. 트랜잭션 시작 → 금액/수수료 정정 ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0006] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set amount = $1 , fee = $2 , upd_ts = now ( ) where key = $3 ", + ECPGt_long,&(h_new_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_new_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 95 "app/online/lg_ol_0006.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0006 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0006] 정정 반영 0건 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0006] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 4. 정정 전/후 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0006] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "OLDAMT", h_old_amount); + tx_buf_setlong(ctx->out, "NEWAMT", h_new_amount); + tx_buf_setlong(ctx->out, "OLDFEE", h_old_fee); + tx_buf_setlong(ctx->out, "NEWFEE", h_new_fee); + + tx_log(TX_LOG_INFO, "[LG_OL_0006] 원장 정정 완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0006.pgc b/app/online/lg_ol_0006.pgc index e82ebfd..59cf15f 100644 --- a/app/online/lg_ol_0006.pgc +++ b/app/online/lg_ol_0006.pgc @@ -2,13 +2,18 @@ /* * lg_ol_0006.pgc - 원장 온라인 서비스 (LG_OL_0006) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 정정: 단건 원장(KEY)의 금액/수수료를 정정한다. 기존 행을 + * 먼저 조회해 정정 전 금액을 확보한 뒤, 요청받은 새 금액(AMOUNT)과 + * 재산정 수수료로 UPDATE 하고 정정 전/후 금액을 응답한다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,32 +21,114 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0006, ctx) { - lg_rec_t rec; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_old_amount; + long h_old_fee; + long h_new_amount; + long h_new_fee; + EXEC SQL END DECLARE SECTION; + char key[16]; - int rc; + long new_amount = 0; - tx_log(TX_LOG_INFO, "[LG_OL_0006] 원장 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0006] 원장 정정 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { tx_log(TX_LOG_ERROR, "[LG_OL_0006] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_getlong(ctx->in, "AMOUNT", &new_amount) != TX_OK || + !amount_is_valid(new_amount)) { + tx_log(TX_LOG_WARN, "[LG_OL_0006] 정정금액 오류 amount=%ld", new_amount); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0006] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + /* ---- 2. 정정 대상 기존 행 조회 ---- */ + EXEC SQL SELECT amount, fee + INTO :h_old_amount, :h_old_fee + FROM lg_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0006] 정정 대상 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0006 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + h_new_amount = new_amount; + h_new_fee = new_amount / 100; /* 정정 수수료 재산정(1%) */ + + tx_log(TX_LOG_INFO, "[LG_OL_0006] 정정 key=%s 금액 %ld→%ld 수수료 %ld→%ld", + key, h_old_amount, h_new_amount, h_old_fee, h_new_fee); + + /* ---- 3. 트랜잭션 시작 → 금액/수수료 정정 ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0006] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE lg_ledger + SET amount = :h_new_amount, fee = :h_new_fee, upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0006 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0006] 정정 반영 0건 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0006] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 4. 정정 전/후 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0006] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "OLDAMT", h_old_amount); + tx_buf_setlong(ctx->out, "NEWAMT", h_new_amount); + tx_buf_setlong(ctx->out, "OLDFEE", h_old_fee); + tx_buf_setlong(ctx->out, "NEWFEE", h_new_fee); + + tx_log(TX_LOG_INFO, "[LG_OL_0006] 원장 정정 완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0007.c b/app/online/lg_ol_0007.c index 1e5597c..50ef248 100644 --- a/app/online/lg_ol_0007.c +++ b/app/online/lg_ol_0007.c @@ -10,13 +10,19 @@ /* * lg_ol_0007.pgc - 원장 온라인 서비스 (LG_OL_0007) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 대변(貸邊) 전표 생성: 요청 정보로 신규 원장 전표행을 생성한다. + * 대변 전표는 금액을 양수(amount>0)로, 상태를 처리완료(status='M') + * 로 기표하며, 수수료는 금액의 1%(amount/100)로 산정한다. 신규 키가 + * 이미 존재하면 중복으로 거부한다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(INSERT) 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,60 +95,177 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0007.pgc" +#line 21 "app/online/lg_ol_0007.pgc" TX_SERVICE(LG_OL_0007, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + + + + +#line 26 "app/online/lg_ol_0007.pgc" + char h_key [ 16 ] ; + +#line 27 "app/online/lg_ol_0007.pgc" + char h_merch_id [ 16 ] ; + +#line 28 "app/online/lg_ol_0007.pgc" + char h_card_no [ 20 ] ; + +#line 29 "app/online/lg_ol_0007.pgc" + long h_amount ; + +#line 30 "app/online/lg_ol_0007.pgc" + char h_biz_date [ 9 ] ; + +#line 31 "app/online/lg_ol_0007.pgc" + char h_status [ 2 ] ; + +#line 32 "app/online/lg_ol_0007.pgc" + long h_fee ; + +#line 33 "app/online/lg_ol_0007.pgc" + int h_dup ; +/* exec sql end declare section */ +#line 34 "app/online/lg_ol_0007.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0007] 원장 등록 서비스 진입"); + char key[16], merch[16], bizdate[16], cardno[20]; + long amount = 0; + + tx_log(TX_LOG_INFO, "[LG_OL_0007] 대변 전표생성 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { tx_log(TX_LOG_ERROR, "[LG_OL_0007] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_get(ctx->in, "CARDNO", cardno, sizeof(cardno)) != TX_OK) + cardno[0] = '\0'; tx_buf_getlong(ctx->in, "AMOUNT", &amount); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0007] 금액 범위 오류 amount=%ld", amount); + /* 대변 전표는 반드시 양수 금액 */ + if (!amount_is_valid(amount) || amount <= 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0007] 대변금액 오류 amount=%ld", amount); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { tx_log(TX_LOG_WARN, "[LG_OL_0007] 영업일 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0007] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* ---- 2. 신규 키 중복 확인 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from lg_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_dup),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 78 "app/online/lg_ol_0007.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0007 dupchk"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0007] 전표키 중복 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + /* ---- 3. 호스트 변수 채우기 (대변: amount>0, status='M') ---- */ + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_card_no, cardno, sizeof(h_card_no) - 1); + h_card_no[sizeof(h_card_no) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, LG_ST_DONE, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + h_amount = amount; + h_fee = amount / 100; + /* ---- 4. 트랜잭션 시작 → 대변 전표 INSERT ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0007] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into lg_ledger ( key , merch_id , card_no , amount , biz_date , status , fee , upd_ts ) values ( $1 , $2 , $3 , $4 , $5 , $6 , $7 , now ( ) )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 120 "app/online/lg_ol_0007.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0007 insert"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0007] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0007] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "DRCR", "CR"); /* 대변 표시 */ + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_buf_sets(ctx->out, "STATUS", LG_ST_DONE); + + tx_log(TX_LOG_INFO, "[LG_OL_0007] 대변 전표생성 완료 key=%s 금액=%ld", + key, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0007.pgc b/app/online/lg_ol_0007.pgc index a87742f..44b665c 100644 --- a/app/online/lg_ol_0007.pgc +++ b/app/online/lg_ol_0007.pgc @@ -2,13 +2,19 @@ /* * lg_ol_0007.pgc - 원장 온라인 서비스 (LG_OL_0007) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 대변(貸邊) 전표 생성: 요청 정보로 신규 원장 전표행을 생성한다. + * 대변 전표는 금액을 양수(amount>0)로, 상태를 처리완료(status='M') + * 로 기표하며, 수수료는 금액의 1%(amount/100)로 산정한다. 신규 키가 + * 이미 존재하면 중복으로 거부한다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(INSERT) 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,55 +22,131 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0007, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_merch_id[16]; + char h_card_no[20]; + long h_amount; + char h_biz_date[9]; + char h_status[2]; + long h_fee; + int h_dup; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0007] 원장 등록 서비스 진입"); + char key[16], merch[16], bizdate[16], cardno[20]; + long amount = 0; + tx_log(TX_LOG_INFO, "[LG_OL_0007] 대변 전표생성 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { tx_log(TX_LOG_ERROR, "[LG_OL_0007] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_get(ctx->in, "CARDNO", cardno, sizeof(cardno)) != TX_OK) + cardno[0] = '\0'; tx_buf_getlong(ctx->in, "AMOUNT", &amount); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0007] 금액 범위 오류 amount=%ld", amount); + /* 대변 전표는 반드시 양수 금액 */ + if (!amount_is_valid(amount) || amount <= 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0007] 대변금액 오류 amount=%ld", amount); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { tx_log(TX_LOG_WARN, "[LG_OL_0007] 영업일 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0007] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* ---- 2. 신규 키 중복 확인 ---- */ + EXEC SQL SELECT count(*) + INTO :h_dup + FROM lg_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0007 dupchk"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0007] 전표키 중복 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + /* ---- 3. 호스트 변수 채우기 (대변: amount>0, status='M') ---- */ + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_card_no, cardno, sizeof(h_card_no) - 1); + h_card_no[sizeof(h_card_no) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, LG_ST_DONE, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + h_amount = amount; + h_fee = amount / 100; + /* ---- 4. 트랜잭션 시작 → 대변 전표 INSERT ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0007] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL INSERT INTO lg_ledger + (key, merch_id, card_no, amount, biz_date, status, fee, upd_ts) + VALUES + (:h_key, :h_merch_id, :h_card_no, :h_amount, :h_biz_date, + :h_status, :h_fee, now()); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0007 insert"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0007] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0007] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "DRCR", "CR"); /* 대변 표시 */ + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_buf_sets(ctx->out, "STATUS", LG_ST_DONE); + + tx_log(TX_LOG_INFO, "[LG_OL_0007] 대변 전표생성 완료 key=%s 금액=%ld", + key, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0008.c b/app/online/lg_ol_0008.c index 684a8ef..8fc1f68 100644 --- a/app/online/lg_ol_0008.c +++ b/app/online/lg_ol_0008.c @@ -10,13 +10,19 @@ /* * lg_ol_0008.pgc - 원장 온라인 서비스 (LG_OL_0008) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 차변(借邊) 전표 생성: 요청 정보로 신규 원장 전표행을 생성한다. + * 차변 전표는 금액을 음수(amount<0)로 기표하며, 상태를 처리완료 + * (status='M')로 둔다. 요청 금액은 양수 크기로 받아 내부에서 부호를 + * 뒤집고, 수수료는 금액 절대값의 1%로 산정한다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(INSERT) 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +95,177 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0008.pgc" +#line 21 "app/online/lg_ol_0008.pgc" TX_SERVICE(LG_OL_0008, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + + + +#line 26 "app/online/lg_ol_0008.pgc" + char h_key [ 16 ] ; + +#line 27 "app/online/lg_ol_0008.pgc" + char h_merch_id [ 16 ] ; + +#line 28 "app/online/lg_ol_0008.pgc" + char h_card_no [ 20 ] ; + +#line 29 "app/online/lg_ol_0008.pgc" + long h_amount ; + +#line 30 "app/online/lg_ol_0008.pgc" + char h_biz_date [ 9 ] ; + +#line 31 "app/online/lg_ol_0008.pgc" + char h_status [ 2 ] ; + +#line 32 "app/online/lg_ol_0008.pgc" + long h_fee ; + +#line 33 "app/online/lg_ol_0008.pgc" + int h_dup ; +/* exec sql end declare section */ +#line 34 "app/online/lg_ol_0008.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0008] 원장 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0008] KEY 누락"); + char key[16], merch[16], bizdate[16], cardno[20]; + long amount = 0; + + tx_log(TX_LOG_INFO, "[LG_OL_0008] 차변 전표생성 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0008] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "CARDNO", cardno, sizeof(cardno)) != TX_OK) + cardno[0] = '\0'; + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + /* 요청 금액은 양수 크기로 받는다 (부호는 내부에서 부여) */ + if (!amount_is_valid(amount) || amount <= 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0008] 차변금액 오류 amount=%ld", amount); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[LG_OL_0008] 영업일 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0008] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + /* ---- 2. 신규 키 중복 확인 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from lg_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_dup),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 78 "app/online/lg_ol_0008.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0008 dupchk"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0008] 전표키 중복 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* ---- 3. 호스트 변수 채우기 (차변: amount<0) ---- */ + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_card_no, cardno, sizeof(h_card_no) - 1); + h_card_no[sizeof(h_card_no) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, LG_ST_DONE, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + h_amount = -amount; /* 차변 → 음수 기표 */ + h_fee = amount / 100; /* 수수료는 절대값 기준 */ + + /* ---- 4. 트랜잭션 시작 → 차변 전표 INSERT ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0008] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into lg_ledger ( key , merch_id , card_no , amount , biz_date , status , fee , upd_ts ) values ( $1 , $2 , $3 , $4 , $5 , $6 , $7 , now ( ) )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 120 "app/online/lg_ol_0008.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0008 insert"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0008] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "DRCR", "DR"); /* 차변 표시 */ + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_buf_sets(ctx->out, "STATUS", LG_ST_DONE); + + tx_log(TX_LOG_INFO, "[LG_OL_0008] 차변 전표생성 완료 key=%s 금액=%ld", + key, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0008.pgc b/app/online/lg_ol_0008.pgc index 4f5ccc2..f257ee7 100644 --- a/app/online/lg_ol_0008.pgc +++ b/app/online/lg_ol_0008.pgc @@ -2,13 +2,19 @@ /* * lg_ol_0008.pgc - 원장 온라인 서비스 (LG_OL_0008) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 차변(借邊) 전표 생성: 요청 정보로 신규 원장 전표행을 생성한다. + * 차변 전표는 금액을 음수(amount<0)로 기표하며, 상태를 처리완료 + * (status='M')로 둔다. 요청 금액은 양수 크기로 받아 내부에서 부호를 + * 뒤집고, 수수료는 금액 절대값의 1%로 산정한다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(INSERT) 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,32 +22,131 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0008, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_merch_id[16]; + char h_card_no[20]; + long h_amount; + char h_biz_date[9]; + char h_status[2]; + long h_fee; + int h_dup; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0008] 원장 단건조회 서비스 진입"); + char key[16], merch[16], bizdate[16], cardno[20]; + long amount = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0008] KEY 누락"); + tx_log(TX_LOG_INFO, "[LG_OL_0008] 차변 전표생성 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0008] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "CARDNO", cardno, sizeof(cardno)) != TX_OK) + cardno[0] = '\0'; + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + /* 요청 금액은 양수 크기로 받는다 (부호는 내부에서 부여) */ + if (!amount_is_valid(amount) || amount <= 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0008] 차변금액 오류 amount=%ld", amount); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[LG_OL_0008] 영업일 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0008] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + /* ---- 2. 신규 키 중복 확인 ---- */ + EXEC SQL SELECT count(*) + INTO :h_dup + FROM lg_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0008 dupchk"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0008] 전표키 중복 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* ---- 3. 호스트 변수 채우기 (차변: amount<0) ---- */ + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_card_no, cardno, sizeof(h_card_no) - 1); + h_card_no[sizeof(h_card_no) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, LG_ST_DONE, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + h_amount = -amount; /* 차변 → 음수 기표 */ + h_fee = amount / 100; /* 수수료는 절대값 기준 */ + + /* ---- 4. 트랜잭션 시작 → 차변 전표 INSERT ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0008] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL INSERT INTO lg_ledger + (key, merch_id, card_no, amount, biz_date, status, fee, upd_ts) + VALUES + (:h_key, :h_merch_id, :h_card_no, :h_amount, :h_biz_date, + :h_status, :h_fee, now()); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0008 insert"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0008] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "DRCR", "DR"); /* 차변 표시 */ + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_buf_sets(ctx->out, "STATUS", LG_ST_DONE); + + tx_log(TX_LOG_INFO, "[LG_OL_0008] 차변 전표생성 완료 key=%s 금액=%ld", + key, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0009.c b/app/online/lg_ol_0009.c index f3bd7a7..70e96fd 100644 --- a/app/online/lg_ol_0009.c +++ b/app/online/lg_ol_0009.c @@ -10,13 +10,20 @@ /* * lg_ol_0009.pgc - 원장 온라인 서비스 (LG_OL_0009) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 계정별 집계 조회: 요청 가맹점(MERCHID)을 하나의 계정으로 보고 + * 해당 계정의 원장 총 건수, 총 거래금액(Σamount), 총 수수료(Σfee)를 + * 단일 집계 SELECT 로 산출한다. 여기에 정산완료(status='S') 금액과 + * 미정산(그 외) 금액을 분리 집계해 정산 진행 상태를 함께 보여주고, + * 순매출(net = Σamount - Σfee)과 원화 표기를 응답한다 (조회 전용). + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(singleton 집계) 을 수행한 뒤 응답을 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,60 +96,132 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0009.pgc" +#line 22 "app/online/lg_ol_0009.pgc" TX_SERVICE(LG_OL_0009, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 27 "app/online/lg_ol_0009.pgc" + char h_merch_id [ 16 ] ; + +#line 28 "app/online/lg_ol_0009.pgc" + char h_st_settled [ 2 ] ; + +#line 29 "app/online/lg_ol_0009.pgc" + int h_cnt ; + +#line 30 "app/online/lg_ol_0009.pgc" + long h_sum_amt ; + +#line 31 "app/online/lg_ol_0009.pgc" + long h_sum_fee ; + +#line 32 "app/online/lg_ol_0009.pgc" + long h_settled_sum ; +/* exec sql end declare section */ +#line 33 "app/online/lg_ol_0009.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0009] 원장 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0009] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char merch[16]; + char wonbuf[32]; + long net = 0, unsettled = 0; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0009] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0009] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[LG_OL_0009] 계정별 집계 조회 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + merch[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0009] MERCHID 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0009] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* ---- 2. 단일 가맹점 계정 전체 집계 (singleton) ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from lg_ledger where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 60 "app/online/lg_ol_0009.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0009 agg"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + if (h_cnt == 0) { + /* 해당 가맹점 원장이 전혀 없음 */ + tx_log(TX_LOG_WARN, "[LG_OL_0009] 계정 원장 없음 merch=%s", merch); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "CNT", 0); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + /* ---- 3. 정산완료(S) 금액 분리 집계 ---- */ + strncpy(h_st_settled, LG_ST_SETTLED, sizeof(h_st_settled) - 1); + h_st_settled[sizeof(h_st_settled) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from lg_ledger where merch_id = $1 and status = $2 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_st_settled),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settled_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 89 "app/online/lg_ol_0009.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0009 settled"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* ---- 4. 파생값 산출 ---- */ + net = h_sum_amt - h_sum_fee; /* 순매출 */ + unsettled = h_sum_amt - h_settled_sum; /* 미정산 잔여금액 */ + amount_format_won(net, wonbuf, sizeof(wonbuf)); + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0009] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "CNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "SUMAMT", h_sum_amt); + tx_buf_setlong(ctx->out, "SUMFEE", h_sum_fee); + tx_buf_setlong(ctx->out, "NETAMT", net); + tx_buf_setlong(ctx->out, "SETTLED", h_settled_sum); + tx_buf_setlong(ctx->out, "UNSETTLED", unsettled); + tx_buf_sets(ctx->out, "NETWON", wonbuf); + + tx_log(TX_LOG_INFO, + "[LG_OL_0009] 계정집계 merch=%s 건수=%d 금액=%ld 수수료=%ld 순액=%ld 미정산=%ld", + merch, h_cnt, h_sum_amt, h_sum_fee, net, unsettled); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0009.pgc b/app/online/lg_ol_0009.pgc index 92cc88a..94bed98 100644 --- a/app/online/lg_ol_0009.pgc +++ b/app/online/lg_ol_0009.pgc @@ -2,13 +2,20 @@ /* * lg_ol_0009.pgc - 원장 온라인 서비스 (LG_OL_0009) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 계정별 집계 조회: 요청 가맹점(MERCHID)을 하나의 계정으로 보고 + * 해당 계정의 원장 총 건수, 총 거래금액(Σamount), 총 수수료(Σfee)를 + * 단일 집계 SELECT 로 산출한다. 여기에 정산완료(status='S') 금액과 + * 미정산(그 외) 금액을 분리 집계해 정산 진행 상태를 함께 보여주고, + * 순매출(net = Σamount - Σfee)과 원화 표기를 응답한다 (조회 전용). + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(singleton 집계) 을 수행한 뒤 응답을 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,55 +23,98 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0009, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_st_settled[2]; + int h_cnt; + long h_sum_amt; + long h_sum_fee; + long h_settled_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0009] 원장 등록 서비스 진입"); + char merch[16]; + char wonbuf[32]; + long net = 0, unsettled = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0009] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[LG_OL_0009] 계정별 집계 조회 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0009] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0009] 영업일 오류 date=%s", bizdate); + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + merch[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0009] MERCHID 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0009] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* ---- 2. 단일 가맹점 계정 전체 집계 (singleton) ---- */ + EXEC SQL SELECT count(*), + coalesce(sum(amount), 0), + coalesce(sum(fee), 0) + INTO :h_cnt, :h_sum_amt, :h_sum_fee + FROM lg_ledger + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0009 agg"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + if (h_cnt == 0) { + /* 해당 가맹점 원장이 전혀 없음 */ + tx_log(TX_LOG_WARN, "[LG_OL_0009] 계정 원장 없음 merch=%s", merch); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "CNT", 0); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + /* ---- 3. 정산완료(S) 금액 분리 집계 ---- */ + strncpy(h_st_settled, LG_ST_SETTLED, sizeof(h_st_settled) - 1); + h_st_settled[sizeof(h_st_settled) - 1] = '\0'; + + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_settled_sum + FROM lg_ledger + WHERE merch_id = :h_merch_id + AND status = :h_st_settled; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0009 settled"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* ---- 4. 파생값 산출 ---- */ + net = h_sum_amt - h_sum_fee; /* 순매출 */ + unsettled = h_sum_amt - h_settled_sum; /* 미정산 잔여금액 */ + amount_format_won(net, wonbuf, sizeof(wonbuf)); + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0009] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "CNT", (long)h_cnt); + tx_buf_setlong(ctx->out, "SUMAMT", h_sum_amt); + tx_buf_setlong(ctx->out, "SUMFEE", h_sum_fee); + tx_buf_setlong(ctx->out, "NETAMT", net); + tx_buf_setlong(ctx->out, "SETTLED", h_settled_sum); + tx_buf_setlong(ctx->out, "UNSETTLED", unsettled); + tx_buf_sets(ctx->out, "NETWON", wonbuf); + + tx_log(TX_LOG_INFO, + "[LG_OL_0009] 계정집계 merch=%s 건수=%d 금액=%ld 수수료=%ld 순액=%ld 미정산=%ld", + merch, h_cnt, h_sum_amt, h_sum_fee, net, unsettled); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0010.c b/app/online/lg_ol_0010.c index 7595edb..6a4168c 100644 --- a/app/online/lg_ol_0010.c +++ b/app/online/lg_ol_0010.c @@ -10,13 +10,19 @@ /* * lg_ol_0010.pgc - 원장 온라인 서비스 (LG_OL_0010) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 잔액 재계산: 단건 원장(KEY)의 거래금액을 기준으로 수수료를 + * 다시 산정(fee = amount/100)해 UPDATE 하고, 순액(net = amount - fee) + * 을 응답한다. 수수료율 변경이나 초기 적재 오류를 바로잡기 위한 + * 재계산 서비스이다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +95,135 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0010.pgc" +#line 21 "app/online/lg_ol_0010.pgc" TX_SERVICE(LG_OL_0010, ctx) { - lg_rec_t rec; + /* exec sql begin declare section */ + + + + + +#line 26 "app/online/lg_ol_0010.pgc" + char h_key [ 16 ] ; + +#line 27 "app/online/lg_ol_0010.pgc" + long h_amount ; + +#line 28 "app/online/lg_ol_0010.pgc" + long h_old_fee ; + +#line 29 "app/online/lg_ol_0010.pgc" + long h_new_fee ; +/* exec sql end declare section */ +#line 30 "app/online/lg_ol_0010.pgc" + + char key[16]; - int rc; + long net = 0; - tx_log(TX_LOG_INFO, "[LG_OL_0010] 원장 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0010] 원장 잔액 재계산 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { tx_log(TX_LOG_ERROR, "[LG_OL_0010] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + /* ---- 2. 현재 금액/수수료 조회 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , fee from lg_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_old_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 53 "app/online/lg_ol_0010.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0010] 재계산 대상 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0010 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 재산정 수수료 (금액 부호를 따라감) */ + h_new_fee = h_amount / 100; + net = h_amount - h_new_fee; + + tx_log(TX_LOG_INFO, "[LG_OL_0010] 재계산 key=%s 금액=%ld 수수료 %ld→%ld 순액=%ld", + key, h_amount, h_old_fee, h_new_fee, net); + + /* ---- 3. 트랜잭션 시작 → 수수료 재산정 UPDATE ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0010] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set fee = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_long,&(h_new_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 88 "app/online/lg_ol_0010.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0010 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0010] 재계산 반영 0건 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0010] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 4. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "OLDFEE", h_old_fee); + tx_buf_setlong(ctx->out, "NEWFEE", h_new_fee); + tx_buf_setlong(ctx->out, "NETAMT", net); + + tx_log(TX_LOG_INFO, "[LG_OL_0010] 원장 잔액 재계산 완료 key=%s 순액=%ld", + key, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0010.pgc b/app/online/lg_ol_0010.pgc index b950dc8..b0869cd 100644 --- a/app/online/lg_ol_0010.pgc +++ b/app/online/lg_ol_0010.pgc @@ -2,13 +2,19 @@ /* * lg_ol_0010.pgc - 원장 온라인 서비스 (LG_OL_0010) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 잔액 재계산: 단건 원장(KEY)의 거래금액을 기준으로 수수료를 + * 다시 산정(fee = amount/100)해 UPDATE 하고, 순액(net = amount - fee) + * 을 응답한다. 수수료율 변경이나 초기 적재 오류를 바로잡기 위한 + * 재계산 서비스이다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,32 +22,107 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0010, ctx) { - lg_rec_t rec; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_amount; + long h_old_fee; + long h_new_fee; + EXEC SQL END DECLARE SECTION; + char key[16]; - int rc; + long net = 0; - tx_log(TX_LOG_INFO, "[LG_OL_0010] 원장 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0010] 원장 잔액 재계산 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { tx_log(TX_LOG_ERROR, "[LG_OL_0010] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + /* ---- 2. 현재 금액/수수료 조회 ---- */ + EXEC SQL SELECT amount, fee + INTO :h_amount, :h_old_fee + FROM lg_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0010] 재계산 대상 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0010 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 재산정 수수료 (금액 부호를 따라감) */ + h_new_fee = h_amount / 100; + net = h_amount - h_new_fee; + + tx_log(TX_LOG_INFO, "[LG_OL_0010] 재계산 key=%s 금액=%ld 수수료 %ld→%ld 순액=%ld", + key, h_amount, h_old_fee, h_new_fee, net); + + /* ---- 3. 트랜잭션 시작 → 수수료 재산정 UPDATE ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0010] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE lg_ledger + SET fee = :h_new_fee, upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0010 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0010] 재계산 반영 0건 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0010] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 4. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "OLDFEE", h_old_fee); + tx_buf_setlong(ctx->out, "NEWFEE", h_new_fee); + tx_buf_setlong(ctx->out, "NETAMT", net); + + tx_log(TX_LOG_INFO, "[LG_OL_0010] 원장 잔액 재계산 완료 key=%s 순액=%ld", + key, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0011.c b/app/online/lg_ol_0011.c index f4b1a7c..2dff901 100644 --- a/app/online/lg_ol_0011.c +++ b/app/online/lg_ol_0011.c @@ -10,13 +10,19 @@ /* * lg_ol_0011.pgc - 원장 온라인 서비스 (LG_OL_0011) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 마감연계: 요청 영업일(BIZDATE)의 처리완료(status='M') 건을 + * 정산완료(status='S')로 일괄 전이해 당일 마감을 확정하고, 다음 + * 영업일을 기준으로 후속 마감연계 서비스(LG_CLOSE)를 디스패치한다. + * 마감연계 건수와 마감금액 합계를 응답하는 오케스트레이션 서비스. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL 을 수행한 뒤 후속 서비스로 연계한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,88 +95,164 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0011.pgc" +#line 21 "app/online/lg_ol_0011.pgc" TX_SERVICE(LG_OL_0011, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; + /* exec sql begin declare section */ + + + + + + +#line 26 "app/online/lg_ol_0011.pgc" + char h_biz_date [ 9 ] ; + +#line 27 "app/online/lg_ol_0011.pgc" + char h_st_done [ 2 ] ; + +#line 28 "app/online/lg_ol_0011.pgc" + char h_st_settled [ 2 ] ; + +#line 29 "app/online/lg_ol_0011.pgc" + long h_sum ; + +#line 30 "app/online/lg_ol_0011.pgc" + int h_cnt ; +/* exec sql end declare section */ +#line 31 "app/online/lg_ol_0011.pgc" + + + char bizdate[16], nextbiz[9]; + long closed = 0; int rc; TXBUF *sub_in; TXBUF *sub_out; - tx_log(TX_LOG_INFO, "[LG_OL_0011] 원장 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0011] 원장 마감연계 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0011] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0011] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_ERROR, "[LG_OL_0011] BIZDATE 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_st_done, LG_ST_DONE, sizeof(h_st_done) - 1); + h_st_done[sizeof(h_st_done) - 1] = '\0'; + strncpy(h_st_settled, LG_ST_SETTLED, sizeof(h_st_settled) - 1); + h_st_settled[sizeof(h_st_settled) - 1] = '\0'; + + /* ---- 2. 마감대상(M) 금액/건수 사전 집계 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) from lg_ledger where biz_date = $1 and status = $2 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_st_done),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 63 "app/online/lg_ol_0011.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0011 presum"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_log(TX_LOG_INFO, "[LG_OL_0011] 마감대상 date=%s 건수=%d 금액=%ld", + bizdate, h_cnt, h_sum); + + if (h_cnt == 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CLOSED", 0); + tx_buf_setlong(ctx->out, "SUMAMT", 0); + tx_log(TX_LOG_INFO, "[LG_OL_0011] 마감대상 없음 date=%s", bizdate); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* ---- 3. 트랜잭션 시작 → M→S 일괄 마감 전이 ---- */ if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0011] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set status = $1 , upd_ts = now ( ) where biz_date = $2 and status = $3 ", + ECPGt_char,(h_st_settled),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_st_done),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 99 "app/online/lg_ol_0011.pgc" - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0011] 원장반영 실패 rc=%d", rc); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0011 close"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + closed = (long)sqlca.sqlerrd[2]; - /* 정산 영업일 = 거래일의 다음 영업일 */ + /* ---- 4. 다음 영업일 산출 → 후속 마감연계 서비스 디스패치 ---- */ if (date_next_business(bizdate, nextbiz) != 0) { memcpy(nextbiz, bizdate, 8); nextbiz[8] = '\0'; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ sub_in = tx_buf_alloc(); sub_out = tx_buf_alloc(); if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("LG_POST", sub_in, sub_out); + tx_buf_sets(sub_in, "BIZDATE", bizdate); + tx_buf_sets(sub_in, "NEXTBIZ", nextbiz); + tx_buf_setlong(sub_in, "CLOSED", closed); + tx_buf_setlong(sub_in, "SUMAMT", h_sum); + rc = tx_call("LG_CLOSE", sub_in, sub_out); if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[LG_OL_0011] 후속연계 경고 rc=%d", rc); + tx_log(TX_LOG_WARN, "[LG_OL_0011] 후속 마감연계 경고 rc=%d", rc); } tx_buf_free(sub_in); tx_buf_free(sub_out); - /* 교차 검증: 당일 합계 재계산 */ - if (lg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0011] 커밋 실패 date=%s", bizdate); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[LG_OL_0011] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "NEXTBIZ", nextbiz); + tx_buf_setlong(ctx->out, "CLOSED", closed); + tx_buf_setlong(ctx->out, "SUMAMT", h_sum); + + tx_log(TX_LOG_INFO, + "[LG_OL_0011] 원장 마감연계 완료 date=%s 마감=%ld건 금액=%ld 익일=%s", + bizdate, closed, h_sum, nextbiz); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0011.pgc b/app/online/lg_ol_0011.pgc index 5e6ff3e..120e4e0 100644 --- a/app/online/lg_ol_0011.pgc +++ b/app/online/lg_ol_0011.pgc @@ -2,13 +2,19 @@ /* * lg_ol_0011.pgc - 원장 온라인 서비스 (LG_OL_0011) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 마감연계: 요청 영업일(BIZDATE)의 처리완료(status='M') 건을 + * 정산완료(status='S')로 일괄 전이해 당일 마감을 확정하고, 다음 + * 영업일을 기준으로 후속 마감연계 서비스(LG_CLOSE)를 디스패치한다. + * 마감연계 건수와 마감금액 합계를 응답하는 오케스트레이션 서비스. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL 을 수행한 뒤 후속 서비스로 연계한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,83 +22,131 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0011, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_st_done[2]; + char h_st_settled[2]; + long h_sum; + int h_cnt; + EXEC SQL END DECLARE SECTION; + + char bizdate[16], nextbiz[9]; + long closed = 0; int rc; TXBUF *sub_in; TXBUF *sub_out; - tx_log(TX_LOG_INFO, "[LG_OL_0011] 원장 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0011] 원장 마감연계 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0011] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0011] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_ERROR, "[LG_OL_0011] BIZDATE 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_st_done, LG_ST_DONE, sizeof(h_st_done) - 1); + h_st_done[sizeof(h_st_done) - 1] = '\0'; + strncpy(h_st_settled, LG_ST_SETTLED, sizeof(h_st_settled) - 1); + h_st_settled[sizeof(h_st_settled) - 1] = '\0'; + + /* ---- 2. 마감대상(M) 금액/건수 사전 집계 ---- */ + EXEC SQL SELECT coalesce(sum(amount), 0), count(*) + INTO :h_sum, :h_cnt + FROM lg_ledger + WHERE biz_date = :h_biz_date + AND status = :h_st_done; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0011 presum"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_log(TX_LOG_INFO, "[LG_OL_0011] 마감대상 date=%s 건수=%d 금액=%ld", + bizdate, h_cnt, h_sum); + + if (h_cnt == 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CLOSED", 0); + tx_buf_setlong(ctx->out, "SUMAMT", 0); + tx_log(TX_LOG_INFO, "[LG_OL_0011] 마감대상 없음 date=%s", bizdate); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* ---- 3. 트랜잭션 시작 → M→S 일괄 마감 전이 ---- */ if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0011] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + EXEC SQL UPDATE lg_ledger + SET status = :h_st_settled, upd_ts = now() + WHERE biz_date = :h_biz_date + AND status = :h_st_done; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0011] 원장반영 실패 rc=%d", rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0011 close"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + closed = (long)sqlca.sqlerrd[2]; - /* 정산 영업일 = 거래일의 다음 영업일 */ + /* ---- 4. 다음 영업일 산출 → 후속 마감연계 서비스 디스패치 ---- */ if (date_next_business(bizdate, nextbiz) != 0) { memcpy(nextbiz, bizdate, 8); nextbiz[8] = '\0'; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ sub_in = tx_buf_alloc(); sub_out = tx_buf_alloc(); if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("LG_POST", sub_in, sub_out); + tx_buf_sets(sub_in, "BIZDATE", bizdate); + tx_buf_sets(sub_in, "NEXTBIZ", nextbiz); + tx_buf_setlong(sub_in, "CLOSED", closed); + tx_buf_setlong(sub_in, "SUMAMT", h_sum); + rc = tx_call("LG_CLOSE", sub_in, sub_out); if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[LG_OL_0011] 후속연계 경고 rc=%d", rc); + tx_log(TX_LOG_WARN, "[LG_OL_0011] 후속 마감연계 경고 rc=%d", rc); } tx_buf_free(sub_in); tx_buf_free(sub_out); - /* 교차 검증: 당일 합계 재계산 */ - if (lg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0011] 커밋 실패 date=%s", bizdate); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[LG_OL_0011] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "NEXTBIZ", nextbiz); + tx_buf_setlong(ctx->out, "CLOSED", closed); + tx_buf_setlong(ctx->out, "SUMAMT", h_sum); + + tx_log(TX_LOG_INFO, + "[LG_OL_0011] 원장 마감연계 완료 date=%s 마감=%ld건 금액=%ld 익일=%s", + bizdate, closed, h_sum, nextbiz); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0012.c b/app/online/lg_ol_0012.c index a4bb194..9a0a57b 100644 --- a/app/online/lg_ol_0012.c +++ b/app/online/lg_ol_0012.c @@ -10,13 +10,20 @@ /* * lg_ol_0012.pgc - 원장 온라인 서비스 (LG_OL_0012) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 미결원장 조회: 요청 가맹점(MERCHID)의 미결 상태 원장을 집계한다. + * 미결이란 접수(status='R') 또는 불일치/실패(status='U') 상태로, + * 아직 확정/정산으로 넘어가지 못한 건을 뜻한다. 접수 미결과 실패 + * 미결을 각각 구분 집계하고, 두 상태를 합한 전체 미결 건수/금액과 + * 원화 표기 문자열을 함께 응답한다 (조회 전용). + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(singleton 집계) 을 수행한 뒤 응답을 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +96,129 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0012.pgc" +#line 22 "app/online/lg_ol_0012.pgc" TX_SERVICE(LG_OL_0012, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 27 "app/online/lg_ol_0012.pgc" + char h_merch_id [ 16 ] ; + +#line 28 "app/online/lg_ol_0012.pgc" + char h_status [ 2 ] ; + +#line 29 "app/online/lg_ol_0012.pgc" + int h_cnt ; + +#line 30 "app/online/lg_ol_0012.pgc" + long h_sum ; +/* exec sql end declare section */ +#line 31 "app/online/lg_ol_0012.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0012] 원장 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0012] KEY 누락"); + char merch[16]; + char wonbuf[32]; + long recv_cnt = 0, recv_sum = 0; + long fail_cnt = 0, fail_sum = 0; + long open_cnt = 0, open_sum = 0; + long open_avg = 0; + + tx_log(TX_LOG_INFO, "[LG_OL_0012] 미결원장 조회 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + merch[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0012] MERCHID 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + + /* ---- 2. 접수(R) 미결 집계 ---- */ + strncpy(h_status, LG_ST_RECV, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from lg_ledger where merch_id = $1 and status = $2 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 63 "app/online/lg_ol_0012.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0012 recv"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + recv_cnt = (long)h_cnt; + recv_sum = h_sum; + /* ---- 3. 불일치/실패(U) 미결 집계 ---- */ + strncpy(h_status, LG_ST_FAIL, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from lg_ledger where merch_id = $1 and status = $2 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 83 "app/online/lg_ol_0012.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0012 fail"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + fail_cnt = (long)h_cnt; + fail_sum = h_sum; + + /* ---- 4. 전체 미결 합산, 건당 평균, 원화 표기 ---- */ + open_cnt = recv_cnt + fail_cnt; + open_sum = recv_sum + fail_sum; + if (open_cnt > 0) + open_avg = open_sum / open_cnt; /* 미결 건당 평균금액 */ + amount_format_won(open_sum, wonbuf, sizeof(wonbuf)); + + /* ---- 5. 응답 구성 (미결 0건도 정상 응답) ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "RECVCNT", recv_cnt); + tx_buf_setlong(ctx->out, "RECVSUM", recv_sum); + tx_buf_setlong(ctx->out, "FAILCNT", fail_cnt); + tx_buf_setlong(ctx->out, "FAILSUM", fail_sum); + tx_buf_setlong(ctx->out, "OPENCNT", open_cnt); + tx_buf_setlong(ctx->out, "OPENSUM", open_sum); + tx_buf_setlong(ctx->out, "OPENAVG", open_avg); + tx_buf_sets(ctx->out, "OPENWON", wonbuf); + tx_buf_sets(ctx->out, "HASOPEN", (open_cnt > 0) ? "Y" : "N"); + + tx_log(TX_LOG_INFO, + "[LG_OL_0012] 미결조회 merch=%s 접수=%ld건 실패=%ld건 계=%ld건 금액=%s", + merch, recv_cnt, fail_cnt, open_cnt, wonbuf); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0012.pgc b/app/online/lg_ol_0012.pgc index d835094..cb2d324 100644 --- a/app/online/lg_ol_0012.pgc +++ b/app/online/lg_ol_0012.pgc @@ -2,13 +2,20 @@ /* * lg_ol_0012.pgc - 원장 온라인 서비스 (LG_OL_0012) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 미결원장 조회: 요청 가맹점(MERCHID)의 미결 상태 원장을 집계한다. + * 미결이란 접수(status='R') 또는 불일치/실패(status='U') 상태로, + * 아직 확정/정산으로 넘어가지 못한 건을 뜻한다. 접수 미결과 실패 + * 미결을 각각 구분 집계하고, 두 상태를 합한 전체 미결 건수/금액과 + * 원화 표기 문자열을 함께 응답한다 (조회 전용). + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(singleton 집계) 을 수행한 뒤 응답을 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,32 +23,98 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0012, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_status[2]; + int h_cnt; + long h_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0012] 원장 단건조회 서비스 진입"); + char merch[16]; + char wonbuf[32]; + long recv_cnt = 0, recv_sum = 0; + long fail_cnt = 0, fail_sum = 0; + long open_cnt = 0, open_sum = 0; + long open_avg = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0012] KEY 누락"); + tx_log(TX_LOG_INFO, "[LG_OL_0012] 미결원장 조회 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + merch[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0012] MERCHID 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + + /* ---- 2. 접수(R) 미결 집계 ---- */ + strncpy(h_status, LG_ST_RECV, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_cnt, :h_sum + FROM lg_ledger + WHERE merch_id = :h_merch_id + AND status = :h_status; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0012 recv"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + recv_cnt = (long)h_cnt; + recv_sum = h_sum; + /* ---- 3. 불일치/실패(U) 미결 집계 ---- */ + strncpy(h_status, LG_ST_FAIL, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_cnt, :h_sum + FROM lg_ledger + WHERE merch_id = :h_merch_id + AND status = :h_status; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0012 fail"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + fail_cnt = (long)h_cnt; + fail_sum = h_sum; + + /* ---- 4. 전체 미결 합산, 건당 평균, 원화 표기 ---- */ + open_cnt = recv_cnt + fail_cnt; + open_sum = recv_sum + fail_sum; + if (open_cnt > 0) + open_avg = open_sum / open_cnt; /* 미결 건당 평균금액 */ + amount_format_won(open_sum, wonbuf, sizeof(wonbuf)); + + /* ---- 5. 응답 구성 (미결 0건도 정상 응답) ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "RECVCNT", recv_cnt); + tx_buf_setlong(ctx->out, "RECVSUM", recv_sum); + tx_buf_setlong(ctx->out, "FAILCNT", fail_cnt); + tx_buf_setlong(ctx->out, "FAILSUM", fail_sum); + tx_buf_setlong(ctx->out, "OPENCNT", open_cnt); + tx_buf_setlong(ctx->out, "OPENSUM", open_sum); + tx_buf_setlong(ctx->out, "OPENAVG", open_avg); + tx_buf_sets(ctx->out, "OPENWON", wonbuf); + tx_buf_sets(ctx->out, "HASOPEN", (open_cnt > 0) ? "Y" : "N"); + + tx_log(TX_LOG_INFO, + "[LG_OL_0012] 미결조회 merch=%s 접수=%ld건 실패=%ld건 계=%ld건 금액=%s", + merch, recv_cnt, fail_cnt, open_cnt, wonbuf); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0013.c b/app/online/lg_ol_0013.c index 3f33340..b3ac844 100644 --- a/app/online/lg_ol_0013.c +++ b/app/online/lg_ol_0013.c @@ -10,13 +10,20 @@ /* * lg_ol_0013.pgc - 원장 온라인 서비스 (LG_OL_0013) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 통계 조회: 요청 영업일(BIZDATE)의 원장을 상태코드별로 집계해 + * 접수(R)/처리완료(M)/불일치(U)/정산완료(S) 각 건수와 상태별 금액 + * 부분합, 전체 건수, 당일 금액 합계, 그리고 정산 진행률(정산건/전체)을 + * 한 번에 응답한다. 상태코드 배열을 순회하며 상태별 count/sum 을 + * 개별 집계 SELECT 로 산출한다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL 을 반복 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,88 +96,123 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0013.pgc" +#line 22 "app/online/lg_ol_0013.pgc" TX_SERVICE(LG_OL_0013, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + +#line 27 "app/online/lg_ol_0013.pgc" + char h_biz_date [ 9 ] ; + +#line 28 "app/online/lg_ol_0013.pgc" + char h_status [ 2 ] ; + +#line 29 "app/online/lg_ol_0013.pgc" + int h_cnt ; + +#line 30 "app/online/lg_ol_0013.pgc" + long h_sum ; +/* exec sql end declare section */ +#line 31 "app/online/lg_ol_0013.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0013] 원장 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0013] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char bizdate[16]; + char cntkey[8], sumkey[8]; + char wonbuf[32]; + const char *st_codes[4]; + const char *st_tag[4]; + long counts[4]; + long sums[4]; + long total = 0, totamt = 0, rate = 0, avgamt = 0; + int i; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0013] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + tx_log(TX_LOG_INFO, "[LG_OL_0013] 원장 통계 조회 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_ERROR, "[LG_OL_0013] BIZDATE 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 상태코드 → 응답 필드 접미 매핑 (CNT/SUM) */ + st_codes[0] = LG_ST_RECV; st_tag[0] = "R"; /* 접수 */ + st_codes[1] = LG_ST_DONE; st_tag[1] = "M"; /* 처리완료 */ + st_codes[2] = LG_ST_FAIL; st_tag[2] = "U"; /* 불일치 */ + st_codes[3] = LG_ST_SETTLED; st_tag[3] = "S"; /* 정산완료 */ + + /* ---- 2. 상태코드별 건수/금액 반복 집계 ---- */ + for (i = 0; i < 4; i++) { + strncpy(h_status, st_codes[i], sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from lg_ledger where biz_date = $1 and status = $2 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 73 "app/online/lg_ol_0013.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0013 stat"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + counts[i] = (long)h_cnt; + sums[i] = h_sum; + total += counts[i]; + totamt += sums[i]; + tx_log(TX_LOG_DEBUG, "[LG_OL_0013] date=%s status=%s cnt=%ld sum=%ld", + bizdate, st_codes[i], counts[i], sums[i]); } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0013] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; + /* ---- 3. 파생 지표 산출 ---- */ + /* 정산 진행률(%) = 정산완료건 / 전체건 * 100 */ + if (total > 0) { + rate = (counts[3] * 100) / total; + avgamt = totamt / total; /* 건당 평균금액 */ } + amount_format_won(totamt, wonbuf, sizeof(wonbuf)); - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("LG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[LG_OL_0013] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (lg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - + /* ---- 4. 응답 구성 (상태별 필드 동적 생성) ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[LG_OL_0013] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + for (i = 0; i < 4; i++) { + snprintf(cntkey, sizeof(cntkey), "CNT%s", st_tag[i]); + snprintf(sumkey, sizeof(sumkey), "SUM%s", st_tag[i]); + tx_buf_setlong(ctx->out, cntkey, counts[i]); + tx_buf_setlong(ctx->out, sumkey, sums[i]); + } + tx_buf_setlong(ctx->out, "TOTCNT", total); + tx_buf_setlong(ctx->out, "TOTAMT", totamt); + tx_buf_sets(ctx->out, "TOTWON", wonbuf); + tx_buf_setlong(ctx->out, "AVGAMT", avgamt); + tx_buf_setlong(ctx->out, "SETRATE", rate); + tx_buf_sets(ctx->out, "CLOSED", (counts[0] == 0 && counts[1] == 0) ? "Y" : "N"); + + tx_log(TX_LOG_INFO, + "[LG_OL_0013] 통계 date=%s R=%ld M=%ld U=%ld S=%ld 계=%ld 금액=%ld 정산율=%ld%%", + bizdate, counts[0], counts[1], counts[2], counts[3], total, totamt, rate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0013.pgc b/app/online/lg_ol_0013.pgc index 840d978..8ee1706 100644 --- a/app/online/lg_ol_0013.pgc +++ b/app/online/lg_ol_0013.pgc @@ -2,13 +2,20 @@ /* * lg_ol_0013.pgc - 원장 온라인 서비스 (LG_OL_0013) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 통계 조회: 요청 영업일(BIZDATE)의 원장을 상태코드별로 집계해 + * 접수(R)/처리완료(M)/불일치(U)/정산완료(S) 각 건수와 상태별 금액 + * 부분합, 전체 건수, 당일 금액 합계, 그리고 정산 진행률(정산건/전체)을 + * 한 번에 응답한다. 상태코드 배열을 순회하며 상태별 count/sum 을 + * 개별 집계 SELECT 로 산출한다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL 을 반복 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,83 +23,98 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0013, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_status[2]; + int h_cnt; + long h_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0013] 원장 트랜잭션 오케스트레이션 진입"); + char bizdate[16]; + char cntkey[8], sumkey[8]; + char wonbuf[32]; + const char *st_codes[4]; + const char *st_tag[4]; + long counts[4]; + long sums[4]; + long total = 0, totamt = 0, rate = 0, avgamt = 0; + int i; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0013] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[LG_OL_0013] 원장 통계 조회 서비스 진입"); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0013] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_ERROR, "[LG_OL_0013] BIZDATE 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 상태코드 → 응답 필드 접미 매핑 (CNT/SUM) */ + st_codes[0] = LG_ST_RECV; st_tag[0] = "R"; /* 접수 */ + st_codes[1] = LG_ST_DONE; st_tag[1] = "M"; /* 처리완료 */ + st_codes[2] = LG_ST_FAIL; st_tag[2] = "U"; /* 불일치 */ + st_codes[3] = LG_ST_SETTLED; st_tag[3] = "S"; /* 정산완료 */ + + /* ---- 2. 상태코드별 건수/금액 반복 집계 ---- */ + for (i = 0; i < 4; i++) { + strncpy(h_status, st_codes[i], sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_cnt, :h_sum + FROM lg_ledger + WHERE biz_date = :h_biz_date + AND status = :h_status; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0013 stat"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + counts[i] = (long)h_cnt; + sums[i] = h_sum; + total += counts[i]; + totamt += sums[i]; + tx_log(TX_LOG_DEBUG, "[LG_OL_0013] date=%s status=%s cnt=%ld sum=%ld", + bizdate, st_codes[i], counts[i], sums[i]); } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0013] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; + /* ---- 3. 파생 지표 산출 ---- */ + /* 정산 진행률(%) = 정산완료건 / 전체건 * 100 */ + if (total > 0) { + rate = (counts[3] * 100) / total; + avgamt = totamt / total; /* 건당 평균금액 */ } + amount_format_won(totamt, wonbuf, sizeof(wonbuf)); - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("LG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[LG_OL_0013] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (lg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - + /* ---- 4. 응답 구성 (상태별 필드 동적 생성) ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[LG_OL_0013] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + for (i = 0; i < 4; i++) { + snprintf(cntkey, sizeof(cntkey), "CNT%s", st_tag[i]); + snprintf(sumkey, sizeof(sumkey), "SUM%s", st_tag[i]); + tx_buf_setlong(ctx->out, cntkey, counts[i]); + tx_buf_setlong(ctx->out, sumkey, sums[i]); + } + tx_buf_setlong(ctx->out, "TOTCNT", total); + tx_buf_setlong(ctx->out, "TOTAMT", totamt); + tx_buf_sets(ctx->out, "TOTWON", wonbuf); + tx_buf_setlong(ctx->out, "AVGAMT", avgamt); + tx_buf_setlong(ctx->out, "SETRATE", rate); + tx_buf_sets(ctx->out, "CLOSED", (counts[0] == 0 && counts[1] == 0) ? "Y" : "N"); + + tx_log(TX_LOG_INFO, + "[LG_OL_0013] 통계 date=%s R=%ld M=%ld U=%ld S=%ld 계=%ld 금액=%ld 정산율=%ld%%", + bizdate, counts[0], counts[1], counts[2], counts[3], total, totamt, rate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0014.c b/app/online/lg_ol_0014.c index f92cb6e..f9eaf45 100644 --- a/app/online/lg_ol_0014.c +++ b/app/online/lg_ol_0014.c @@ -10,13 +10,20 @@ /* * lg_ol_0014.pgc - 원장 온라인 서비스 (LG_OL_0014) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 diff 검증: 두 영업일(D1, D2)의 원장 합계(Σamount)와 건수를 + * 각각 집계해 두 일자 간 금액 차이(DIFF = ΣD1 - ΣD2)와 건수 차이를 + * 산출한다. 증감 방향(TREND: UP/DOWN/FLAT)과 차액의 원화 표기를 + * 함께 응답한다. 전일 대비 증감이나 두 마감 스냅샷 간 편차를 확인 + * 하는 조회 전용 서비스이다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(SUM/COUNT) 을 두 번 수행한 뒤 응답을 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +96,131 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0014.pgc" +#line 22 "app/online/lg_ol_0014.pgc" TX_SERVICE(LG_OL_0014, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 27 "app/online/lg_ol_0014.pgc" + char h_biz_date [ 9 ] ; + +#line 28 "app/online/lg_ol_0014.pgc" + long h_sum1 ; + +#line 29 "app/online/lg_ol_0014.pgc" + int h_cnt1 ; + +#line 30 "app/online/lg_ol_0014.pgc" + long h_sum2 ; + +#line 31 "app/online/lg_ol_0014.pgc" + int h_cnt2 ; +/* exec sql end declare section */ +#line 32 "app/online/lg_ol_0014.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0014] 원장 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0014] KEY 누락"); + char d1[16], d2[16]; + char wonbuf[32]; + long diff = 0; + long absdiff = 0; + long cntdiff = 0; + const char *trend; + + tx_log(TX_LOG_INFO, "[LG_OL_0014] 원장 diff 검증 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "D1", d1, sizeof(d1)) != TX_OK || + tx_buf_get(ctx->in, "D2", d2, sizeof(d2)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0014] D1/D2 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(d1) || !date_is_valid(d2)) { + tx_log(TX_LOG_WARN, "[LG_OL_0014] 영업일 오류 d1=%s d2=%s", d1, d2); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* ---- 2. D1 합계/건수 집계 ---- */ + strncpy(h_biz_date, d1, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) from lg_ledger where biz_date = $1 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_sum1),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_cnt1),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 67 "app/online/lg_ol_0014.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0014 sum-d1"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* ---- 3. D2 합계/건수 집계 ---- */ + strncpy(h_biz_date, d2, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) from lg_ledger where biz_date = $1 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_sum2),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_cnt2),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 84 "app/online/lg_ol_0014.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0014 sum-d2"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* ---- 4. 차이/방향 산출 ---- */ + diff = h_sum1 - h_sum2; + absdiff = (diff < 0) ? -diff : diff; /* 편차 절대값 */ + cntdiff = (long)h_cnt1 - (long)h_cnt2; + trend = (diff > 0) ? "UP" : (diff < 0) ? "DOWN" : "FLAT"; + amount_format_won(diff, wonbuf, sizeof(wonbuf)); + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "D1", d1); + tx_buf_sets(ctx->out, "D2", d2); + tx_buf_setlong(ctx->out, "SUM1", h_sum1); + tx_buf_setlong(ctx->out, "SUM2", h_sum2); + tx_buf_setlong(ctx->out, "CNT1", (long)h_cnt1); + tx_buf_setlong(ctx->out, "CNT2", (long)h_cnt2); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_setlong(ctx->out, "ABSDIFF", absdiff); + tx_buf_setlong(ctx->out, "CNTDIFF", cntdiff); + tx_buf_sets(ctx->out, "DIFFWON", wonbuf); + tx_buf_sets(ctx->out, "TREND", trend); + tx_buf_sets(ctx->out, "MATCHED", (diff == 0) ? "Y" : "N"); + + tx_log(TX_LOG_INFO, + "[LG_OL_0014] diff 검증 d1=%s(%ld) d2=%s(%ld) 차이=%ld 방향=%s", + d1, h_sum1, d2, h_sum2, diff, trend); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0014.pgc b/app/online/lg_ol_0014.pgc index 1f4d670..6119352 100644 --- a/app/online/lg_ol_0014.pgc +++ b/app/online/lg_ol_0014.pgc @@ -2,13 +2,20 @@ /* * lg_ol_0014.pgc - 원장 온라인 서비스 (LG_OL_0014) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 diff 검증: 두 영업일(D1, D2)의 원장 합계(Σamount)와 건수를 + * 각각 집계해 두 일자 간 금액 차이(DIFF = ΣD1 - ΣD2)와 건수 차이를 + * 산출한다. 증감 방향(TREND: UP/DOWN/FLAT)과 차액의 원화 표기를 + * 함께 응답한다. 전일 대비 증감이나 두 마감 스냅샷 간 편차를 확인 + * 하는 조회 전용 서비스이다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(SUM/COUNT) 을 두 번 수행한 뒤 응답을 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,32 +23,99 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0014, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + long h_sum1; + int h_cnt1; + long h_sum2; + int h_cnt2; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0014] 원장 단건조회 서비스 진입"); + char d1[16], d2[16]; + char wonbuf[32]; + long diff = 0; + long absdiff = 0; + long cntdiff = 0; + const char *trend; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0014] KEY 누락"); + tx_log(TX_LOG_INFO, "[LG_OL_0014] 원장 diff 검증 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "D1", d1, sizeof(d1)) != TX_OK || + tx_buf_get(ctx->in, "D2", d2, sizeof(d2)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0014] D1/D2 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(d1) || !date_is_valid(d2)) { + tx_log(TX_LOG_WARN, "[LG_OL_0014] 영업일 오류 d1=%s d2=%s", d1, d2); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* ---- 2. D1 합계/건수 집계 ---- */ + strncpy(h_biz_date, d1, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + EXEC SQL SELECT coalesce(sum(amount), 0), count(*) + INTO :h_sum1, :h_cnt1 + FROM lg_ledger + WHERE biz_date = :h_biz_date; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0014 sum-d1"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* ---- 3. D2 합계/건수 집계 ---- */ + strncpy(h_biz_date, d2, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + EXEC SQL SELECT coalesce(sum(amount), 0), count(*) + INTO :h_sum2, :h_cnt2 + FROM lg_ledger + WHERE biz_date = :h_biz_date; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0014 sum-d2"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* ---- 4. 차이/방향 산출 ---- */ + diff = h_sum1 - h_sum2; + absdiff = (diff < 0) ? -diff : diff; /* 편차 절대값 */ + cntdiff = (long)h_cnt1 - (long)h_cnt2; + trend = (diff > 0) ? "UP" : (diff < 0) ? "DOWN" : "FLAT"; + amount_format_won(diff, wonbuf, sizeof(wonbuf)); + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "D1", d1); + tx_buf_sets(ctx->out, "D2", d2); + tx_buf_setlong(ctx->out, "SUM1", h_sum1); + tx_buf_setlong(ctx->out, "SUM2", h_sum2); + tx_buf_setlong(ctx->out, "CNT1", (long)h_cnt1); + tx_buf_setlong(ctx->out, "CNT2", (long)h_cnt2); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_setlong(ctx->out, "ABSDIFF", absdiff); + tx_buf_setlong(ctx->out, "CNTDIFF", cntdiff); + tx_buf_sets(ctx->out, "DIFFWON", wonbuf); + tx_buf_sets(ctx->out, "TREND", trend); + tx_buf_sets(ctx->out, "MATCHED", (diff == 0) ? "Y" : "N"); + + tx_log(TX_LOG_INFO, + "[LG_OL_0014] diff 검증 d1=%s(%ld) d2=%s(%ld) 차이=%ld 방향=%s", + d1, h_sum1, d2, h_sum2, diff, trend); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0015.c b/app/online/lg_ol_0015.c index ee7ecc0..0da38a0 100644 --- a/app/online/lg_ol_0015.c +++ b/app/online/lg_ol_0015.c @@ -10,13 +10,18 @@ /* * lg_ol_0015.pgc - 원장 온라인 서비스 (LG_OL_0015) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 전기(posting) 확정: 단건 원장(KEY)의 상태를 확인해 접수(status='R') + * 상태이면 처리완료(status='M')로 전기 확정한다. 이미 전기된(M 이상) + * 건은 재전기하지 않고 경고와 함께 현재 상태를 반환한다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +94,137 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0015.pgc" +#line 20 "app/online/lg_ol_0015.pgc" TX_SERVICE(LG_OL_0015, ctx) { - lg_rec_t rec; + /* exec sql begin declare section */ + + + + +#line 25 "app/online/lg_ol_0015.pgc" + char h_key [ 16 ] ; + +#line 26 "app/online/lg_ol_0015.pgc" + char h_status [ 2 ] ; + +#line 27 "app/online/lg_ol_0015.pgc" + char h_st_done [ 2 ] ; +/* exec sql end declare section */ +#line 28 "app/online/lg_ol_0015.pgc" + + char key[16]; - int rc; - tx_log(TX_LOG_INFO, "[LG_OL_0015] 원장 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0015] 전기(posting) 확정 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { tx_log(TX_LOG_ERROR, "[LG_OL_0015] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0015] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + /* ---- 2. 현재 상태 조회 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select status from lg_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 50 "app/online/lg_ol_0015.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0015] 전기 대상 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0015 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* ---- 3. 접수(R) 가 아니면 이미 전기된 것으로 경고 ---- */ + if (strncmp(h_status, LG_ST_RECV, 1) != 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0015] 이미 전기됨 key=%s status=%s", + key, h_status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "POSTED", "N"); /* 이번 호출로는 전기 안 함 */ + tx_return(ctx, TX_OK, ctx->out); + return; + } + + strncpy(h_st_done, LG_ST_DONE, sizeof(h_st_done) - 1); + h_st_done[sizeof(h_st_done) - 1] = '\0'; + + /* ---- 4. 트랜잭션 시작 → R→M 전기 확정 ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0015] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set status = $1 , upd_ts = now ( ) where key = $2 and status = $3 ", + ECPGt_char,(h_st_done),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 95 "app/online/lg_ol_0015.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0015 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + /* 조회~갱신 사이 상태 변경(경합) → 전기 미수행 */ + tx_log(TX_LOG_WARN, "[LG_OL_0015] 전기 반영 0건(경합) key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0015] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0015] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", LG_ST_DONE); + tx_buf_sets(ctx->out, "POSTED", "Y"); + + tx_log(TX_LOG_INFO, "[LG_OL_0015] 전기 확정 완료 key=%s R→M", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0015.pgc b/app/online/lg_ol_0015.pgc index cd6e3aa..1ff1646 100644 --- a/app/online/lg_ol_0015.pgc +++ b/app/online/lg_ol_0015.pgc @@ -2,13 +2,18 @@ /* * lg_ol_0015.pgc - 원장 온라인 서비스 (LG_OL_0015) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 전기(posting) 확정: 단건 원장(KEY)의 상태를 확인해 접수(status='R') + * 상태이면 처리완료(status='M')로 전기 확정한다. 이미 전기된(M 이상) + * 건은 재전기하지 않고 경고와 함께 현재 상태를 반환한다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL 을 수행한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,32 +21,113 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0015, ctx) { - lg_rec_t rec; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_status[2]; + char h_st_done[2]; + EXEC SQL END DECLARE SECTION; + char key[16]; - int rc; - tx_log(TX_LOG_INFO, "[LG_OL_0015] 원장 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0015] 전기(posting) 확정 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { tx_log(TX_LOG_ERROR, "[LG_OL_0015] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0015] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + /* ---- 2. 현재 상태 조회 ---- */ + EXEC SQL SELECT status + INTO :h_status + FROM lg_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0015] 전기 대상 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0015 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* ---- 3. 접수(R) 가 아니면 이미 전기된 것으로 경고 ---- */ + if (strncmp(h_status, LG_ST_RECV, 1) != 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0015] 이미 전기됨 key=%s status=%s", + key, h_status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "POSTED", "N"); /* 이번 호출로는 전기 안 함 */ + tx_return(ctx, TX_OK, ctx->out); + return; + } + + strncpy(h_st_done, LG_ST_DONE, sizeof(h_st_done) - 1); + h_st_done[sizeof(h_st_done) - 1] = '\0'; + + /* ---- 4. 트랜잭션 시작 → R→M 전기 확정 ---- */ + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0015] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE lg_ledger + SET status = :h_st_done, upd_ts = now() + WHERE key = :h_key + AND status = :h_status; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0015 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + /* 조회~갱신 사이 상태 변경(경합) → 전기 미수행 */ + tx_log(TX_LOG_WARN, "[LG_OL_0015] 전기 반영 0건(경합) key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0015] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 5. 응답 구성 ---- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0015] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", LG_ST_DONE); + tx_buf_sets(ctx->out, "POSTED", "Y"); + + tx_log(TX_LOG_INFO, "[LG_OL_0015] 전기 확정 완료 key=%s R→M", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0016.c b/app/online/lg_ol_0016.c index ea48eb1..8f91da2 100644 --- a/app/online/lg_ol_0016.c +++ b/app/online/lg_ol_0016.c @@ -10,13 +10,19 @@ /* * lg_ol_0016.pgc - 원장 온라인 서비스 (LG_OL_0016) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 역분개(逆分介): 원거래(KEY)를 조회해 동일 가맹점/카드/영업일로 + * 부호를 반대로 뒤집은(amount, fee 부호 반전) 새 역분개 원장행을 + * 생성한다. 역분개 키는 원거래 키에 'R' 접미를 붙여 구성하며 상태는 + * 처리완료(status='M')로 기표한다. 원거래 취소/상계 처리에 쓰인다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(SELECT/INSERT) 을 수행한 뒤 응답을 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,60 +95,197 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0016.pgc" +#line 21 "app/online/lg_ol_0016.pgc" TX_SERVICE(LG_OL_0016, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + + + + + +#line 26 "app/online/lg_ol_0016.pgc" + char h_key [ 16 ] ; + +#line 27 "app/online/lg_ol_0016.pgc" + char h_rev_key [ 16 ] ; + +#line 28 "app/online/lg_ol_0016.pgc" + char h_merch_id [ 16 ] ; + +#line 29 "app/online/lg_ol_0016.pgc" + char h_card_no [ 20 ] ; + +#line 30 "app/online/lg_ol_0016.pgc" + long h_amount ; + +#line 31 "app/online/lg_ol_0016.pgc" + char h_biz_date [ 9 ] ; + +#line 32 "app/online/lg_ol_0016.pgc" + char h_status [ 2 ] ; + +#line 33 "app/online/lg_ol_0016.pgc" + long h_fee ; + +#line 34 "app/online/lg_ol_0016.pgc" + int h_dup ; +/* exec sql end declare section */ +#line 35 "app/online/lg_ol_0016.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0016] 원장 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0016] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char key[16], revkey[16]; + int n; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0016] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0016] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[LG_OL_0016] 원장 역분개 서비스 진입"); + + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0016] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0016] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* ---- 2. 원거래 조회 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select merch_id , card_no , amount , biz_date , fee from lg_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 58 "app/online/lg_ol_0016.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0016] 원거래 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0016 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + /* ---- 3. 역분개 키 구성 (원키[최대14] + 'R') ---- */ + n = (int)strlen(key); + if (n > (int)sizeof(revkey) - 2) + n = (int)sizeof(revkey) - 2; + memcpy(revkey, key, n); + revkey[n] = 'R'; + revkey[n + 1] = '\0'; + strncpy(h_rev_key, revkey, sizeof(h_rev_key) - 1); + h_rev_key[sizeof(h_rev_key) - 1] = '\0'; + + /* ---- 4. 역분개 키 중복 확인 ---- */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from lg_ledger where key = $1 ", + ECPGt_char,(h_rev_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_dup),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 90 "app/online/lg_ol_0016.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0016 dupchk"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0016] 이미 역분개됨 revkey=%s", revkey); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "REVKEY", revkey); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* ---- 5. 부호 반전 후 역분개행 INSERT ---- */ + h_amount = -h_amount; + h_fee = -h_fee; + strncpy(h_status, LG_ST_DONE, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0016] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into lg_ledger ( key , merch_id , card_no , amount , biz_date , status , fee , upd_ts ) values ( $1 , $2 , $3 , $4 , $5 , $6 , $7 , now ( ) )", + ECPGt_char,(h_rev_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 126 "app/online/lg_ol_0016.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0016 insert"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0016] 커밋 실패 revkey=%s", revkey); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 6. 응답 구성 ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0016] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "REVKEY", revkey); + tx_buf_setlong(ctx->out, "REVAMT", h_amount); + tx_buf_setlong(ctx->out, "REVFEE", h_fee); + tx_buf_sets(ctx->out, "STATUS", LG_ST_DONE); + + tx_log(TX_LOG_INFO, "[LG_OL_0016] 역분개 완료 원키=%s 역키=%s 역금액=%ld", + key, revkey, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0016.pgc b/app/online/lg_ol_0016.pgc index bba9657..f511434 100644 --- a/app/online/lg_ol_0016.pgc +++ b/app/online/lg_ol_0016.pgc @@ -2,13 +2,19 @@ /* * lg_ol_0016.pgc - 원장 온라인 서비스 (LG_OL_0016) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 역분개(逆分介): 원거래(KEY)를 조회해 동일 가맹점/카드/영업일로 + * 부호를 반대로 뒤집은(amount, fee 부호 반전) 새 역분개 원장행을 + * 생성한다. 역분개 키는 원거래 키에 'R' 접미를 붙여 구성하며 상태는 + * 처리완료(status='M')로 기표한다. 원거래 취소/상계 처리에 쓰인다. + * + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg_ledger 에 + * 직접 임베디드 SQL(SELECT/INSERT) 을 수행한 뒤 응답을 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,55 +22,137 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0016, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_rev_key[16]; + char h_merch_id[16]; + char h_card_no[20]; + long h_amount; + char h_biz_date[9]; + char h_status[2]; + long h_fee; + int h_dup; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0016] 원장 등록 서비스 진입"); + char key[16], revkey[16]; + int n; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0016] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[LG_OL_0016] 원장 역분개 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0016] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0016] 영업일 오류 date=%s", bizdate); + /* ---- 1. 요청 필드 수신 및 검증 ---- */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0016] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0016] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* ---- 2. 원거래 조회 ---- */ + EXEC SQL SELECT merch_id, card_no, amount, biz_date, fee + INTO :h_merch_id, :h_card_no, :h_amount, :h_biz_date, :h_fee + FROM lg_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0016] 원거래 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0016 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + /* ---- 3. 역분개 키 구성 (원키[최대14] + 'R') ---- */ + n = (int)strlen(key); + if (n > (int)sizeof(revkey) - 2) + n = (int)sizeof(revkey) - 2; + memcpy(revkey, key, n); + revkey[n] = 'R'; + revkey[n + 1] = '\0'; + strncpy(h_rev_key, revkey, sizeof(h_rev_key) - 1); + h_rev_key[sizeof(h_rev_key) - 1] = '\0'; + + /* ---- 4. 역분개 키 중복 확인 ---- */ + EXEC SQL SELECT count(*) + INTO :h_dup + FROM lg_ledger + WHERE key = :h_rev_key; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("LG_OL_0016 dupchk"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0016] 이미 역분개됨 revkey=%s", revkey); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "REVKEY", revkey); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* ---- 5. 부호 반전 후 역분개행 INSERT ---- */ + h_amount = -h_amount; + h_fee = -h_fee; + strncpy(h_status, LG_ST_DONE, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0016] 트랜잭션 시작 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL INSERT INTO lg_ledger + (key, merch_id, card_no, amount, biz_date, status, fee, upd_ts) + VALUES + (:h_rev_key, :h_merch_id, :h_card_no, :h_amount, :h_biz_date, + :h_status, :h_fee, now()); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("LG_OL_0016 insert"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0016] 커밋 실패 revkey=%s", revkey); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* ---- 6. 응답 구성 ---- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0016] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "REVKEY", revkey); + tx_buf_setlong(ctx->out, "REVAMT", h_amount); + tx_buf_setlong(ctx->out, "REVFEE", h_fee); + tx_buf_sets(ctx->out, "STATUS", LG_ST_DONE); + + tx_log(TX_LOG_INFO, "[LG_OL_0016] 역분개 완료 원키=%s 역키=%s 역금액=%ld", + key, revkey, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0017.c b/app/online/lg_ol_0017.c index 67f22d2..f79fcbf 100644 --- a/app/online/lg_ol_0017.c +++ b/app/online/lg_ol_0017.c @@ -10,13 +10,14 @@ /* * lg_ol_0017.pgc - 원장 온라인 서비스 (LG_OL_0017) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 매출 일괄반영: 특정 가맹점의 접수(R) 상태 전표를 커서로 순회하며 + * 각 건을 처리완료(M) 상태로 UPDATE 하고, 반영 건수와 합계금액을 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +90,150 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0017.pgc" +#line 16 "app/online/lg_ol_0017.pgc" TX_SERVICE(LG_OL_0017, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 21 "app/online/lg_ol_0017.pgc" + char h_merch_id [ 16 ] ; + +#line 22 "app/online/lg_ol_0017.pgc" + char h_status_r [ 2 ] ; + +#line 23 "app/online/lg_ol_0017.pgc" + char h_status_m [ 2 ] ; + +#line 24 "app/online/lg_ol_0017.pgc" + char h_key [ 16 ] ; + +#line 25 "app/online/lg_ol_0017.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 26 "app/online/lg_ol_0017.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0017] 원장 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0017] KEY 누락"); + char merch[16]; + long total = 0; + long applied = 0; + + tx_log(TX_LOG_INFO, "[LG_OL_0017] 가맹점 매출 일괄반영 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + merch[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0017] MERCHID 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0017] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_status_r, LG_ST_RECV, sizeof(h_status_r) - 1); + h_status_r[sizeof(h_status_r) - 1] = '\0'; + strncpy(h_status_m, LG_ST_DONE, sizeof(h_status_m) - 1); + h_status_m[sizeof(h_status_m) - 1] = '\0'; + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0017] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + ECPGset_var( 0, ( h_status_r ), __LINE__);\ + ECPGset_var( 1, ( h_merch_id ), __LINE__);\ + /* declare cur_lg_ol_0017 cursor for select key , amount from lg_ledger where merch_id = $1 and status = $2 order by key */ +#line 61 "app/online/lg_ol_0017.pgc" + +#line 61 "app/online/lg_ol_0017.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_lg_ol_0017 cursor for select key , amount from lg_ledger where merch_id = $1 and status = $2 order by key", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status_r),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 63 "app/online/lg_ol_0017.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_lg_ol_0017 open"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_lg_ol_0017", ECPGt_EOIT, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 74 "app/online/lg_ol_0017.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_lg_ol_0017 fetch"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_ol_0017", ECPGt_EOIT, ECPGt_EORT);} +#line 79 "app/online/lg_ol_0017.pgc" + + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set status = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_char,(h_status_m),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 89 "app/online/lg_ol_0017.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_lg_ol_0017 update"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_ol_0017", ECPGt_EOIT, ECPGt_EORT);} +#line 92 "app/online/lg_ol_0017.pgc" + + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + applied += 1; + total += h_amount; + tx_log(TX_LOG_DEBUG, "[LG_OL_0017] 반영 key=%s amount=%ld", h_key, h_amount); + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_lg_ol_0017", ECPGt_EOIT, ECPGt_EORT);} +#line 104 "app/online/lg_ol_0017.pgc" + + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0017] 커밋 실패"); + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0017] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "CNT", applied); + tx_buf_setlong(ctx->out, "SUMAMT", total); + tx_log(TX_LOG_INFO, "[LG_OL_0017] 일괄반영 완료 merch=%s 건수=%ld 합계=%ld", + merch, applied, total); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0017.pgc b/app/online/lg_ol_0017.pgc index 97264f1..8797d19 100644 --- a/app/online/lg_ol_0017.pgc +++ b/app/online/lg_ol_0017.pgc @@ -2,13 +2,14 @@ /* * lg_ol_0017.pgc - 원장 온라인 서비스 (LG_OL_0017) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 매출 일괄반영: 특정 가맹점의 접수(R) 상태 전표를 커서로 순회하며 + * 각 건을 처리완료(M) 상태로 UPDATE 하고, 반영 건수와 합계금액을 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,32 +17,105 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0017, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_status_r[2]; + char h_status_m[2]; + char h_key[16]; + long h_amount; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0017] 원장 단건조회 서비스 진입"); + char merch[16]; + long total = 0; + long applied = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0017] KEY 누락"); + tx_log(TX_LOG_INFO, "[LG_OL_0017] 가맹점 매출 일괄반영 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + merch[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0017] MERCHID 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0017] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_status_r, LG_ST_RECV, sizeof(h_status_r) - 1); + h_status_r[sizeof(h_status_r) - 1] = '\0'; + strncpy(h_status_m, LG_ST_DONE, sizeof(h_status_m) - 1); + h_status_m[sizeof(h_status_m) - 1] = '\0'; + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0017] 트랜잭션 시작 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL DECLARE cur_lg_ol_0017 CURSOR FOR + SELECT key, amount + FROM lg_ledger + WHERE merch_id = :h_merch_id + AND status = :h_status_r + ORDER BY key; + + EXEC SQL OPEN cur_lg_ol_0017; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_lg_ol_0017 open"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + for (;;) { + EXEC SQL FETCH cur_lg_ol_0017 INTO :h_key, :h_amount; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_lg_ol_0017 fetch"); + EXEC SQL CLOSE cur_lg_ol_0017; + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL UPDATE lg_ledger + SET status = :h_status_m, upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("cur_lg_ol_0017 update"); + EXEC SQL CLOSE cur_lg_ol_0017; + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + applied += 1; + total += h_amount; + tx_log(TX_LOG_DEBUG, "[LG_OL_0017] 반영 key=%s amount=%ld", h_key, h_amount); + } + + EXEC SQL CLOSE cur_lg_ol_0017; + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0017] 커밋 실패"); + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0017] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "CNT", applied); + tx_buf_setlong(ctx->out, "SUMAMT", total); + tx_log(TX_LOG_INFO, "[LG_OL_0017] 일괄반영 완료 merch=%s 건수=%ld 합계=%ld", + merch, applied, total); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0018.c b/app/online/lg_ol_0018.c index ab47261..1974d19 100644 --- a/app/online/lg_ol_0018.c +++ b/app/online/lg_ol_0018.c @@ -10,13 +10,14 @@ /* * lg_ol_0018.pgc - 원장 온라인 서비스 (LG_OL_0018) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인취소 반영: 원거래 key 를 조회하여 취소 가능한 상태인지 확인한 뒤 + * 상태를 불일치/취소(U) 로 UPDATE 하고, 취소된 원거래 금액을 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,60 +90,151 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0018.pgc" +#line 16 "app/online/lg_ol_0018.pgc" +/* + * [업무 개요] + * 앞서 접수/처리된 원거래 승인을 취소한다. 원거래 key 를 조회하여 취소가 + * 가능한 상태인지 확인한 뒤 상태를 불일치/취소(U) 로 전환하고, 취소된 + * 원거래 금액을 반환한다. + * + * [취소 가능 판정] + * 이미 취소(U) 또는 정산완료(S) 인 전표는 취소 불가(RESP_INVALID). + * 접수(R)/처리완료(M) 전표만 취소 대상으로 허용한다. + * + * [처리 절차] + * 1) KEY 추출/검증. + * 2) 단건 SELECT 로 amount, 현재 status 확보(없으면 RESP_NOMERCH). + * 3) 취소 가능 상태 검증. + * 4) tx_begin 후 status='U' 로 UPDATE, tx_commit. + * + * [응답 필드] + * RESPCODE 처리결과코드 KEY 처리키(에코) + * CANCELAMT 취소금액 CANCELWON 취소금액 원화표기 STATUS 취소후 상태(U) + */ TX_SERVICE(LG_OL_0018, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + +#line 41 "app/online/lg_ol_0018.pgc" + char h_key [ 16 ] ; + +#line 42 "app/online/lg_ol_0018.pgc" + char h_status_u [ 2 ] ; + +#line 43 "app/online/lg_ol_0018.pgc" + char h_cur_status [ 2 ] ; + +#line 44 "app/online/lg_ol_0018.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 45 "app/online/lg_ol_0018.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0018] 원장 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0018] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char key[16]; + char wonbuf[32]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0018] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0018] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[LG_OL_0018] 승인취소 반영 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0018] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_status_u, LG_ST_FAIL, sizeof(h_status_u) - 1); + h_status_u[sizeof(h_status_u) - 1] = '\0'; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0018] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 원거래 조회 (단건 PK) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , status from lg_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_cur_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 69 "app/online/lg_ol_0018.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0018] 원거래 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0018 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + /* 이미 취소(U)/정산(S) 상태면 취소 불가 */ + if (h_cur_status[0] == LG_ST_FAIL[0] || h_cur_status[0] == LG_ST_SETTLED[0]) { + tx_log(TX_LOG_WARN, "[LG_OL_0018] 취소 불가 상태 key=%s status=%s", + key, h_cur_status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "STATUS", h_cur_status); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 취소플래그성 처리: 상태를 U 로 전환 (취소 반영) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set status = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_char,(h_status_u),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 105 "app/online/lg_ol_0018.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0018 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0018] 취소 대상 없음 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + amount_format_won(h_amount, wonbuf, sizeof(wonbuf)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0018] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "CANCELAMT", h_amount); + tx_buf_sets(ctx->out, "CANCELWON", wonbuf); + tx_buf_sets(ctx->out, "STATUS", LG_ST_FAIL); + tx_log(TX_LOG_INFO, "[LG_OL_0018] 승인취소 완료 key=%s 취소금액=%ld", key, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0018.pgc b/app/online/lg_ol_0018.pgc index 7918cb5..2a5148f 100644 --- a/app/online/lg_ol_0018.pgc +++ b/app/online/lg_ol_0018.pgc @@ -2,69 +2,138 @@ /* * lg_ol_0018.pgc - 원장 온라인 서비스 (LG_OL_0018) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인취소 반영: 원거래 key 를 조회하여 취소 가능한 상태인지 확인한 뒤 + * 상태를 불일치/취소(U) 로 UPDATE 하고, 취소된 원거래 금액을 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" EXEC SQL INCLUDE sqlca; +/* + * [업무 개요] + * 앞서 접수/처리된 원거래 승인을 취소한다. 원거래 key 를 조회하여 취소가 + * 가능한 상태인지 확인한 뒤 상태를 불일치/취소(U) 로 전환하고, 취소된 + * 원거래 금액을 반환한다. + * + * [취소 가능 판정] + * 이미 취소(U) 또는 정산완료(S) 인 전표는 취소 불가(RESP_INVALID). + * 접수(R)/처리완료(M) 전표만 취소 대상으로 허용한다. + * + * [처리 절차] + * 1) KEY 추출/검증. + * 2) 단건 SELECT 로 amount, 현재 status 확보(없으면 RESP_NOMERCH). + * 3) 취소 가능 상태 검증. + * 4) tx_begin 후 status='U' 로 UPDATE, tx_commit. + * + * [응답 필드] + * RESPCODE 처리결과코드 KEY 처리키(에코) + * CANCELAMT 취소금액 CANCELWON 취소금액 원화표기 STATUS 취소후 상태(U) + */ TX_SERVICE(LG_OL_0018, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_status_u[2]; + char h_cur_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0018] 원장 등록 서비스 진입"); + char key[16]; + char wonbuf[32]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0018] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[LG_OL_0018] 승인취소 반영 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0018] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0018] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0018] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_status_u, LG_ST_FAIL, sizeof(h_status_u) - 1); + h_status_u[sizeof(h_status_u) - 1] = '\0'; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0018] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 원거래 조회 (단건 PK) */ + EXEC SQL SELECT amount, status + INTO :h_amount, :h_cur_status + FROM lg_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0018] 원거래 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0018 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + /* 이미 취소(U)/정산(S) 상태면 취소 불가 */ + if (h_cur_status[0] == LG_ST_FAIL[0] || h_cur_status[0] == LG_ST_SETTLED[0]) { + tx_log(TX_LOG_WARN, "[LG_OL_0018] 취소 불가 상태 key=%s status=%s", + key, h_cur_status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "STATUS", h_cur_status); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 취소플래그성 처리: 상태를 U 로 전환 (취소 반영) */ + EXEC SQL UPDATE lg_ledger + SET status = :h_status_u, upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0018 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (sqlca.sqlerrd[2] == 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0018] 취소 대상 없음 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + amount_format_won(h_amount, wonbuf, sizeof(wonbuf)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0018] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "CANCELAMT", h_amount); + tx_buf_sets(ctx->out, "CANCELWON", wonbuf); + tx_buf_sets(ctx->out, "STATUS", LG_ST_FAIL); + tx_log(TX_LOG_INFO, "[LG_OL_0018] 승인취소 완료 key=%s 취소금액=%ld", key, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0019.c b/app/online/lg_ol_0019.c index 870e665..95d7024 100644 --- a/app/online/lg_ol_0019.c +++ b/app/online/lg_ol_0019.c @@ -10,13 +10,14 @@ /* * lg_ol_0019.pgc - 원장 온라인 서비스 (LG_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산확정 처리: 단건 key 의 처리완료(M) 전표를 정산완료(S) 로 확정한다. + * 정산액 = 거래금액 - 수수료 를 계산하여 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +90,150 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0019.pgc" +#line 16 "app/online/lg_ol_0019.pgc" +/* + * [업무 개요] + * 처리완료(M) 상태의 승인 전표 한 건을 정산확정(S) 으로 전이한다. 정산 + * 확정 시점에 가맹점에 실제 지급되는 금액(정산액) 을 산출하여 반환한다. + * + * [상태 전이] + * M(처리완료) → S(정산완료) 그 외 상태는 정산확정 불가(RESP_INVALID) + * + * [정산액 산식] + * SETTLEAMT = amount - fee (거래금액에서 수수료를 차감한 실지급액) + * + * [처리 절차] + * 1) KEY 추출/검증. + * 2) 단건 SELECT 로 amount, fee, 현재 status 확보. + * 3) 상태가 M 이 아니면 거부. + * 4) tx_begin 후 status='S' 로 UPDATE, tx_commit. + * 5) 정산액 산출 후 응답. + * + * [응답 필드] + * RESPCODE 처리결과코드 KEY 처리키(에코) AMOUNT 거래금액 + * FEE 수수료 SETTLEAMT 정산액 STATUS 확정후 상태(S) + */ TX_SERVICE(LG_OL_0019, ctx) { - lg_rec_t rec; + /* exec sql begin declare section */ + + + + + + +#line 43 "app/online/lg_ol_0019.pgc" + char h_key [ 16 ] ; + +#line 44 "app/online/lg_ol_0019.pgc" + char h_status_s [ 2 ] ; + +#line 45 "app/online/lg_ol_0019.pgc" + char h_cur_status [ 2 ] ; + +#line 46 "app/online/lg_ol_0019.pgc" + long h_amount ; + +#line 47 "app/online/lg_ol_0019.pgc" + long h_fee ; +/* exec sql end declare section */ +#line 48 "app/online/lg_ol_0019.pgc" + + char key[16]; - int rc; + long settle_amt; - tx_log(TX_LOG_INFO, "[LG_OL_0019] 원장 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0019] 정산확정 처리 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { tx_log(TX_LOG_ERROR, "[LG_OL_0019] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_status_s, LG_ST_SETTLED, sizeof(h_status_s) - 1); + h_status_s[sizeof(h_status_s) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , fee , status from lg_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_cur_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 71 "app/online/lg_ol_0019.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0019] 전표 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0019 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 처리완료(M) 상태만 정산확정 가능 */ + if (h_cur_status[0] != LG_ST_DONE[0]) { + tx_log(TX_LOG_WARN, "[LG_OL_0019] 정산확정 불가 상태 key=%s status=%s", + key, h_cur_status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "STATUS", h_cur_status); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set status = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_char,(h_status_s),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 106 "app/online/lg_ol_0019.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0019 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + settle_amt = h_amount - h_fee; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_buf_setlong(ctx->out, "SETTLEAMT", settle_amt); + tx_buf_sets(ctx->out, "STATUS", LG_ST_SETTLED); + tx_log(TX_LOG_INFO, "[LG_OL_0019] 정산확정 완료 key=%s 정산액=%ld", key, settle_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0019.pgc b/app/online/lg_ol_0019.pgc index 09cc963..9a41a5a 100644 --- a/app/online/lg_ol_0019.pgc +++ b/app/online/lg_ol_0019.pgc @@ -2,46 +2,132 @@ /* * lg_ol_0019.pgc - 원장 온라인 서비스 (LG_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산확정 처리: 단건 key 의 처리완료(M) 전표를 정산완료(S) 로 확정한다. + * 정산액 = 거래금액 - 수수료 를 계산하여 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" EXEC SQL INCLUDE sqlca; +/* + * [업무 개요] + * 처리완료(M) 상태의 승인 전표 한 건을 정산확정(S) 으로 전이한다. 정산 + * 확정 시점에 가맹점에 실제 지급되는 금액(정산액) 을 산출하여 반환한다. + * + * [상태 전이] + * M(처리완료) → S(정산완료) 그 외 상태는 정산확정 불가(RESP_INVALID) + * + * [정산액 산식] + * SETTLEAMT = amount - fee (거래금액에서 수수료를 차감한 실지급액) + * + * [처리 절차] + * 1) KEY 추출/검증. + * 2) 단건 SELECT 로 amount, fee, 현재 status 확보. + * 3) 상태가 M 이 아니면 거부. + * 4) tx_begin 후 status='S' 로 UPDATE, tx_commit. + * 5) 정산액 산출 후 응답. + * + * [응답 필드] + * RESPCODE 처리결과코드 KEY 처리키(에코) AMOUNT 거래금액 + * FEE 수수료 SETTLEAMT 정산액 STATUS 확정후 상태(S) + */ TX_SERVICE(LG_OL_0019, ctx) { - lg_rec_t rec; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_status_s[2]; + char h_cur_status[2]; + long h_amount; + long h_fee; + EXEC SQL END DECLARE SECTION; + char key[16]; - int rc; + long settle_amt; - tx_log(TX_LOG_INFO, "[LG_OL_0019] 원장 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0019] 정산확정 처리 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { tx_log(TX_LOG_ERROR, "[LG_OL_0019] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_status_s, LG_ST_SETTLED, sizeof(h_status_s) - 1); + h_status_s[sizeof(h_status_s) - 1] = '\0'; + + EXEC SQL SELECT amount, fee, status + INTO :h_amount, :h_fee, :h_cur_status + FROM lg_ledger + WHERE key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0019] 전표 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0019 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 처리완료(M) 상태만 정산확정 가능 */ + if (h_cur_status[0] != LG_ST_DONE[0]) { + tx_log(TX_LOG_WARN, "[LG_OL_0019] 정산확정 불가 상태 key=%s status=%s", + key, h_cur_status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "STATUS", h_cur_status); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE lg_ledger + SET status = :h_status_s, upd_ts = now() + WHERE key = :h_key; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0019 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + settle_amt = h_amount - h_fee; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_buf_setlong(ctx->out, "SETTLEAMT", settle_amt); + tx_buf_sets(ctx->out, "STATUS", LG_ST_SETTLED); + tx_log(TX_LOG_INFO, "[LG_OL_0019] 정산확정 완료 key=%s 정산액=%ld", key, settle_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0020.c b/app/online/lg_ol_0020.c index 19ec808..3931143 100644 --- a/app/online/lg_ol_0020.c +++ b/app/online/lg_ol_0020.c @@ -10,13 +10,14 @@ /* * lg_ol_0020.pgc - 원장 온라인 서비스 (LG_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 이월: 지정 영업일의 미결(접수 R / 불일치 U) 전표를 다음 영업일로 + * biz_date 를 이월(UPDATE) 하고, 이월된 건수를 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,60 +90,138 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0020.pgc" +#line 16 "app/online/lg_ol_0020.pgc" +/* + * [업무 개요] + * 당일 마감 시점까지 종결되지 못한 미결 전표(접수 R / 불일치 U) 를 다음 + * 영업일로 이월(carry-over) 한다. biz_date 를 다음 영업일로 갱신함으로써 + * 해당 전표가 익영업일 처리 대상 집합에 포함되도록 한다. + * + * [이월 대상] + * status IN ('R','U') (처리완료 M / 정산완료 S 는 종결건이므로 제외) + * + * [다음 영업일 산출] + * date_next_business() 로 주말을 건너뛴 익영업일을 계산한다. + * + * [처리 절차] + * 1) BIZDATE 추출 및 형식 검증. + * 2) 다음 영업일 산출(실패 시 시스템오류). + * 3) tx_begin 후 미결 전표의 biz_date 를 집합 UPDATE. + * 4) 이월 건수 = sqlca.sqlerrd[2]. + * 5) tx_commit — 실패 시 tx_abort. + * + * [응답 필드] + * RESPCODE 처리결과코드 + * BIZDATE 원 영업일(에코) + * NEXTDATE 이월 대상 다음 영업일 + * MOVEDCNT 이월된 전표 건수 + */ TX_SERVICE(LG_OL_0020, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 46 "app/online/lg_ol_0020.pgc" + char h_biz_date [ 9 ] ; + +#line 47 "app/online/lg_ol_0020.pgc" + char h_next_date [ 9 ] ; + +#line 48 "app/online/lg_ol_0020.pgc" + char h_status_r [ 2 ] ; + +#line 49 "app/online/lg_ol_0020.pgc" + char h_status_u [ 2 ] ; + +#line 50 "app/online/lg_ol_0020.pgc" + long h_moved ; +/* exec sql end declare section */ +#line 51 "app/online/lg_ol_0020.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0020] 원장 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0020] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char bizdate[16]; + char nextbiz[9]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0020] 금액 범위 오류 amount=%ld", amount); + tx_log(TX_LOG_INFO, "[LG_OL_0020] 원장 이월 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0020] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0020] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[LG_OL_0020] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0020] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 다음 영업일 산출 (주말 스킵) */ + if (date_next_business(bizdate, nextbiz) != 0) { + tx_log(TX_LOG_ERROR, "[LG_OL_0020] 다음영업일 산출 실패 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_next_date, nextbiz, sizeof(h_next_date) - 1); + h_next_date[sizeof(h_next_date) - 1] = '\0'; + strncpy(h_status_r, LG_ST_RECV, sizeof(h_status_r) - 1); + h_status_r[sizeof(h_status_r) - 1] = '\0'; + strncpy(h_status_u, LG_ST_FAIL, sizeof(h_status_u) - 1); + h_status_u[sizeof(h_status_u) - 1] = '\0'; + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set biz_date = $1 , upd_ts = now ( ) where biz_date = $2 and status in ( $3 , $4 )", + ECPGt_char,(h_next_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status_r),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status_u),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 99 "app/online/lg_ol_0020.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0020 carryover"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + h_moved = (long)sqlca.sqlerrd[2]; + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0020] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "NEXTDATE", nextbiz); + tx_buf_setlong(ctx->out, "MOVEDCNT", h_moved); + tx_log(TX_LOG_INFO, "[LG_OL_0020] 이월 완료 %s→%s 이월건수=%ld", + bizdate, nextbiz, h_moved); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0020.pgc b/app/online/lg_ol_0020.pgc index 3c0a915..0853e3c 100644 --- a/app/online/lg_ol_0020.pgc +++ b/app/online/lg_ol_0020.pgc @@ -2,69 +2,124 @@ /* * lg_ol_0020.pgc - 원장 온라인 서비스 (LG_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 이월: 지정 영업일의 미결(접수 R / 불일치 U) 전표를 다음 영업일로 + * biz_date 를 이월(UPDATE) 하고, 이월된 건수를 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" EXEC SQL INCLUDE sqlca; +/* + * [업무 개요] + * 당일 마감 시점까지 종결되지 못한 미결 전표(접수 R / 불일치 U) 를 다음 + * 영업일로 이월(carry-over) 한다. biz_date 를 다음 영업일로 갱신함으로써 + * 해당 전표가 익영업일 처리 대상 집합에 포함되도록 한다. + * + * [이월 대상] + * status IN ('R','U') (처리완료 M / 정산완료 S 는 종결건이므로 제외) + * + * [다음 영업일 산출] + * date_next_business() 로 주말을 건너뛴 익영업일을 계산한다. + * + * [처리 절차] + * 1) BIZDATE 추출 및 형식 검증. + * 2) 다음 영업일 산출(실패 시 시스템오류). + * 3) tx_begin 후 미결 전표의 biz_date 를 집합 UPDATE. + * 4) 이월 건수 = sqlca.sqlerrd[2]. + * 5) tx_commit — 실패 시 tx_abort. + * + * [응답 필드] + * RESPCODE 처리결과코드 + * BIZDATE 원 영업일(에코) + * NEXTDATE 이월 대상 다음 영업일 + * MOVEDCNT 이월된 전표 건수 + */ TX_SERVICE(LG_OL_0020, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_next_date[9]; + char h_status_r[2]; + char h_status_u[2]; + long h_moved; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0020] 원장 등록 서비스 진입"); + char bizdate[16]; + char nextbiz[9]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0020] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[LG_OL_0020] 원장 이월 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0020] 금액 범위 오류 amount=%ld", amount); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0020] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0020] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[LG_OL_0020] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0020] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 다음 영업일 산출 (주말 스킵) */ + if (date_next_business(bizdate, nextbiz) != 0) { + tx_log(TX_LOG_ERROR, "[LG_OL_0020] 다음영업일 산출 실패 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_next_date, nextbiz, sizeof(h_next_date) - 1); + h_next_date[sizeof(h_next_date) - 1] = '\0'; + strncpy(h_status_r, LG_ST_RECV, sizeof(h_status_r) - 1); + h_status_r[sizeof(h_status_r) - 1] = '\0'; + strncpy(h_status_u, LG_ST_FAIL, sizeof(h_status_u) - 1); + h_status_u[sizeof(h_status_u) - 1] = '\0'; + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE lg_ledger + SET biz_date = :h_next_date, upd_ts = now() + WHERE biz_date = :h_biz_date + AND status IN (:h_status_r, :h_status_u); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0020 carryover"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + h_moved = (long)sqlca.sqlerrd[2]; + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0020] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "NEXTDATE", nextbiz); + tx_buf_setlong(ctx->out, "MOVEDCNT", h_moved); + tx_log(TX_LOG_INFO, "[LG_OL_0020] 이월 완료 %s→%s 이월건수=%ld", + bizdate, nextbiz, h_moved); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0021.c b/app/online/lg_ol_0021.c index 99e14e3..53f126a 100644 --- a/app/online/lg_ol_0021.c +++ b/app/online/lg_ol_0021.c @@ -10,13 +10,14 @@ /* * lg_ol_0021.pgc - 원장 온라인 서비스 (LG_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 복식 전표생성: 하나의 거래에 대해 차변행(음수)과 대변행(양수) 두 행을 + * 한 트랜잭션으로 INSERT 하고, 두 행 금액 합(Σ=0) 균형검증 결과를 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +90,155 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0021.pgc" +#line 16 "app/online/lg_ol_0021.pgc" TX_SERVICE(LG_OL_0021, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + + +#line 21 "app/online/lg_ol_0021.pgc" + char h_key_dr [ 16 ] ; + +#line 22 "app/online/lg_ol_0021.pgc" + char h_key_cr [ 16 ] ; + +#line 23 "app/online/lg_ol_0021.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/lg_ol_0021.pgc" + char h_biz_date [ 9 ] ; + +#line 25 "app/online/lg_ol_0021.pgc" + char h_status [ 2 ] ; + +#line 26 "app/online/lg_ol_0021.pgc" + long h_debit ; + +#line 27 "app/online/lg_ol_0021.pgc" + long h_credit ; +/* exec sql end declare section */ +#line 28 "app/online/lg_ol_0021.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0021] 원장 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0021] KEY 누락"); + char key[16], merch[16], bizdate[16]; + long amount = 0; + long balance; + + tx_log(TX_LOG_INFO, "[LG_OL_0021] 복식 전표생성 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0021] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[LG_OL_0021] 입력 검증 실패 amount=%ld date=%s", + amount, bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 차변/대변 처리키 파생 (원거래키 + 접미) */ + snprintf(h_key_dr, sizeof(h_key_dr), "%.13sDR", key); + snprintf(h_key_cr, sizeof(h_key_cr), "%.13sCR", key); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, LG_ST_RECV, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + h_debit = -amount; /* 차변: 음수 */ + h_credit = amount; /* 대변: 양수 */ + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into lg_ledger ( key , merch_id , card_no , amount , biz_date , status , fee ) values ( $1 , $2 , '' , $3 , $4 , $5 , 0 )", + ECPGt_char,(h_key_dr),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_debit),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 76 "app/online/lg_ol_0021.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0021 debit insert"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into lg_ledger ( key , merch_id , card_no , amount , biz_date , status , fee ) values ( $1 , $2 , '' , $3 , $4 , $5 , 0 )", + ECPGt_char,(h_key_cr),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_credit),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 89 "app/online/lg_ol_0021.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0021 credit insert"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 복식부기 균형검증: 차변 + 대변 = 0 */ + balance = h_debit + h_credit; + if (balance != 0) { + tx_log(TX_LOG_ERROR, "[LG_OL_0021] 균형검증 실패 balance=%ld", balance); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEYDR", h_key_dr); + tx_buf_sets(ctx->out, "KEYCR", h_key_cr); + tx_buf_setlong(ctx->out, "DEBIT", h_debit); + tx_buf_setlong(ctx->out, "CREDIT", h_credit); + tx_buf_setlong(ctx->out, "BALANCE", balance); + tx_log(TX_LOG_INFO, "[LG_OL_0021] 복식전표 생성완료 key=%s 균형=%ld", key, balance); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0021.pgc b/app/online/lg_ol_0021.pgc index 4cd4b4e..d3aab14 100644 --- a/app/online/lg_ol_0021.pgc +++ b/app/online/lg_ol_0021.pgc @@ -2,13 +2,14 @@ /* * lg_ol_0021.pgc - 원장 온라인 서비스 (LG_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 복식 전표생성: 하나의 거래에 대해 차변행(음수)과 대변행(양수) 두 행을 + * 한 트랜잭션으로 INSERT 하고, 두 행 금액 합(Σ=0) 균형검증 결과를 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,32 +17,109 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0021, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key_dr[16]; + char h_key_cr[16]; + char h_merch_id[16]; + char h_biz_date[9]; + char h_status[2]; + long h_debit; + long h_credit; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0021] 원장 단건조회 서비스 진입"); + char key[16], merch[16], bizdate[16]; + long amount = 0; + long balance; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0021] KEY 누락"); + tx_log(TX_LOG_INFO, "[LG_OL_0021] 복식 전표생성 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0021] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[LG_OL_0021] 입력 검증 실패 amount=%ld date=%s", + amount, bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 차변/대변 처리키 파생 (원거래키 + 접미) */ + snprintf(h_key_dr, sizeof(h_key_dr), "%.13sDR", key); + snprintf(h_key_cr, sizeof(h_key_cr), "%.13sCR", key); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status, LG_ST_RECV, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + h_debit = -amount; /* 차변: 음수 */ + h_credit = amount; /* 대변: 양수 */ + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL INSERT INTO lg_ledger + (key, merch_id, card_no, amount, biz_date, status, fee) + VALUES + (:h_key_dr, :h_merch_id, '', :h_debit, :h_biz_date, :h_status, 0); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0021 debit insert"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL INSERT INTO lg_ledger + (key, merch_id, card_no, amount, biz_date, status, fee) + VALUES + (:h_key_cr, :h_merch_id, '', :h_credit, :h_biz_date, :h_status, 0); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0021 credit insert"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 복식부기 균형검증: 차변 + 대변 = 0 */ + balance = h_debit + h_credit; + if (balance != 0) { + tx_log(TX_LOG_ERROR, "[LG_OL_0021] 균형검증 실패 balance=%ld", balance); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEYDR", h_key_dr); + tx_buf_sets(ctx->out, "KEYCR", h_key_cr); + tx_buf_setlong(ctx->out, "DEBIT", h_debit); + tx_buf_setlong(ctx->out, "CREDIT", h_credit); + tx_buf_setlong(ctx->out, "BALANCE", balance); + tx_log(TX_LOG_INFO, "[LG_OL_0021] 복식전표 생성완료 key=%s 균형=%ld", key, balance); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0022.c b/app/online/lg_ol_0022.c index 2023f88..d8dcd9c 100644 --- a/app/online/lg_ol_0022.c +++ b/app/online/lg_ol_0022.c @@ -10,13 +10,14 @@ /* * lg_ol_0022.pgc - 원장 온라인 서비스 (LG_OL_0022) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 잔액 조회: 특정 가맹점의 원장 순액(Σamount)과 수수료 합(Σfee), + * 전표 건수를 단일 집계 SELECT 로 조회하여 잔액을 응답한다. (조회 전용) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,88 +90,131 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0022.pgc" +#line 16 "app/online/lg_ol_0022.pgc" +/* + * [업무 개요] + * 가맹점 정산 담당자가 특정 가맹점의 현재 원장 잔액을 실시간으로 확인할 때 + * 호출한다. 원장(lg_ledger) 은 매출/취소/이월/정산 전표가 상태코드와 금액 + * 부호로 누적되므로, 단순 Σamount 는 "가맹점에 귀속되는 순거래액" 을 뜻한다. + * + * [잔액 산식] + * 순거래액(NETAMT) = Σ amount (취소·역분개는 음수로 이미 반영) + * 누적수수료(FEESUM) = Σ fee + * 지급가능잔액(BALANCE) = NETAMT - FEESUM (가맹점 실지급 기준액) + * + * [처리 절차] + * 1) 요청 TXBUF 에서 MERCHID 를 추출/검증한다. + * 2) lg_ledger 를 가맹점 단위로 단일 집계(SELECT) 하여 순액/수수료/건수를 + * 한 번에 조회한다. (singleton — 집계는 항상 1행) + * 3) 건수가 0 이면 미등록 가맹점으로 보아 RESP_NOMERCH 로 응답한다. + * 4) 지급가능잔액을 산출하고 원화 표기 문자열을 함께 구성한다. + * + * [응답 필드] + * RESPCODE 처리결과코드 + * MERCHID 가맹점번호(에코) + * NETAMT 순거래액 합계 + * FEESUM 누적 수수료 합계 + * BALANCE 지급가능 잔액(순액-수수료) + * CNT 원장 전표 건수 + * BALANCEWON 잔액 원화표기(예: 1,234,000원) + * + * [주의] + * 본 서비스는 조회 전용이므로 tx_begin/tx_commit 을 사용하지 않는다. + * 집계 결과가 없어도(NOTFOUND) coalesce 로 0 이 보장된다. + * + * [연계] + * 가맹점 지급 배치(정산) 는 본 잔액을 사전 검증값으로 참조하며, 잔액이 + * 음수이면 미수(未收) 발생 가맹점으로 분류되어 별도 리스크 리포트에 + * 집계된다. 따라서 순액/수수료를 각각 분리 반환하여 상위 화면이 지급가능 + * 잔액과 수수료 부담을 구분 표기할 수 있도록 한다. + */ TX_SERVICE(LG_OL_0022, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + +#line 58 "app/online/lg_ol_0022.pgc" + char h_merch_id [ 16 ] ; + +#line 59 "app/online/lg_ol_0022.pgc" + long h_net ; + +#line 60 "app/online/lg_ol_0022.pgc" + long h_fee_sum ; + +#line 61 "app/online/lg_ol_0022.pgc" + long h_cnt ; +/* exec sql end declare section */ +#line 62 "app/online/lg_ol_0022.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0022] 원장 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0022] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char merch[16]; + char wonbuf[32]; + long net_balance; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0022] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + tx_log(TX_LOG_INFO, "[LG_OL_0022] 가맹점 잔액 조회 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + merch[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0022] MERCHID 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + + /* 순액/수수료/건수 단일 집계 (singleton) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) , count ( * ) from lg_ledger where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_net),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 88 "app/online/lg_ol_0022.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0022 balance"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0022] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + if (h_cnt == 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0022] 가맹점 원장 없음 merch=%s", merch); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "BALANCE", 0); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("LG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[LG_OL_0022] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (lg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 잔액 = 순거래액 - 누적 수수료 */ + net_balance = h_net - h_fee_sum; + amount_format_won(net_balance, wonbuf, sizeof(wonbuf)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[LG_OL_0022] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "NETAMT", h_net); + tx_buf_setlong(ctx->out, "FEESUM", h_fee_sum); + tx_buf_setlong(ctx->out, "BALANCE", net_balance); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_sets(ctx->out, "BALANCEWON", wonbuf); + tx_log(TX_LOG_INFO, "[LG_OL_0022] 잔액조회 완료 merch=%s 잔액=%ld 건수=%ld", + merch, net_balance, h_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0022.pgc b/app/online/lg_ol_0022.pgc index f761256..eb847ec 100644 --- a/app/online/lg_ol_0022.pgc +++ b/app/online/lg_ol_0022.pgc @@ -2,97 +2,122 @@ /* * lg_ol_0022.pgc - 원장 온라인 서비스 (LG_OL_0022) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 잔액 조회: 특정 가맹점의 원장 순액(Σamount)과 수수료 합(Σfee), + * 전표 건수를 단일 집계 SELECT 로 조회하여 잔액을 응답한다. (조회 전용) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" EXEC SQL INCLUDE sqlca; +/* + * [업무 개요] + * 가맹점 정산 담당자가 특정 가맹점의 현재 원장 잔액을 실시간으로 확인할 때 + * 호출한다. 원장(lg_ledger) 은 매출/취소/이월/정산 전표가 상태코드와 금액 + * 부호로 누적되므로, 단순 Σamount 는 "가맹점에 귀속되는 순거래액" 을 뜻한다. + * + * [잔액 산식] + * 순거래액(NETAMT) = Σ amount (취소·역분개는 음수로 이미 반영) + * 누적수수료(FEESUM) = Σ fee + * 지급가능잔액(BALANCE) = NETAMT - FEESUM (가맹점 실지급 기준액) + * + * [처리 절차] + * 1) 요청 TXBUF 에서 MERCHID 를 추출/검증한다. + * 2) lg_ledger 를 가맹점 단위로 단일 집계(SELECT) 하여 순액/수수료/건수를 + * 한 번에 조회한다. (singleton — 집계는 항상 1행) + * 3) 건수가 0 이면 미등록 가맹점으로 보아 RESP_NOMERCH 로 응답한다. + * 4) 지급가능잔액을 산출하고 원화 표기 문자열을 함께 구성한다. + * + * [응답 필드] + * RESPCODE 처리결과코드 + * MERCHID 가맹점번호(에코) + * NETAMT 순거래액 합계 + * FEESUM 누적 수수료 합계 + * BALANCE 지급가능 잔액(순액-수수료) + * CNT 원장 전표 건수 + * BALANCEWON 잔액 원화표기(예: 1,234,000원) + * + * [주의] + * 본 서비스는 조회 전용이므로 tx_begin/tx_commit 을 사용하지 않는다. + * 집계 결과가 없어도(NOTFOUND) coalesce 로 0 이 보장된다. + * + * [연계] + * 가맹점 지급 배치(정산) 는 본 잔액을 사전 검증값으로 참조하며, 잔액이 + * 음수이면 미수(未收) 발생 가맹점으로 분류되어 별도 리스크 리포트에 + * 집계된다. 따라서 순액/수수료를 각각 분리 반환하여 상위 화면이 지급가능 + * 잔액과 수수료 부담을 구분 표기할 수 있도록 한다. + */ TX_SERVICE(LG_OL_0022, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + long h_net; + long h_fee_sum; + long h_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0022] 원장 트랜잭션 오케스트레이션 진입"); + char merch[16]; + char wonbuf[32]; + long net_balance; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0022] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[LG_OL_0022] 가맹점 잔액 조회 진입"); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0022] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + merch[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0022] MERCHID 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + + /* 순액/수수료/건수 단일 집계 (singleton) */ + EXEC SQL SELECT coalesce(sum(amount), 0), + coalesce(sum(fee), 0), + count(*) + INTO :h_net, :h_fee_sum, :h_cnt + FROM lg_ledger + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0022 balance"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0022] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + if (h_cnt == 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0022] 가맹점 원장 없음 merch=%s", merch); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "BALANCE", 0); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("LG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[LG_OL_0022] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (lg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 잔액 = 순거래액 - 누적 수수료 */ + net_balance = h_net - h_fee_sum; + amount_format_won(net_balance, wonbuf, sizeof(wonbuf)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[LG_OL_0022] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "NETAMT", h_net); + tx_buf_setlong(ctx->out, "FEESUM", h_fee_sum); + tx_buf_setlong(ctx->out, "BALANCE", net_balance); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_sets(ctx->out, "BALANCEWON", wonbuf); + tx_log(TX_LOG_INFO, "[LG_OL_0022] 잔액조회 완료 merch=%s 잔액=%ld 건수=%ld", + merch, net_balance, h_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0023.c b/app/online/lg_ol_0023.c index c7ef9d1..7edc755 100644 --- a/app/online/lg_ol_0023.c +++ b/app/online/lg_ol_0023.c @@ -10,13 +10,14 @@ /* * lg_ol_0023.pgc - 원장 온라인 서비스 (LG_OL_0023) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 수수료 재계산 반영: 지정 영업일의 모든 전표에 대해 수수료를 거래금액의 + * 1%(amount/100) 로 일괄 재계산하여 UPDATE 하고, 반영 건수를 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +90,127 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0023.pgc" +#line 16 "app/online/lg_ol_0023.pgc" +/* + * [업무 개요] + * 수수료율 정책이 변경되었거나, 접수 단계에서 수수료가 미계산/오계산된 + * 경우, 특정 영업일 전체 전표의 수수료를 일괄 재계산한다. 기준율은 1% + * (거래금액 절대값의 1/100) 이며, 음수 전표(취소·역분개)도 절대 크기로 + * 수수료를 산정한다. + * + * [재계산 규칙] + * fee = abs(amount) / 100 (정수 나눗셈, 원 단위 절사) + * + * [처리 절차] + * 1) BIZDATE 추출 및 형식 검증. + * 2) tx_begin 후 당일 전표 전체를 한 UPDATE 문으로 재계산. + * 3) 반영 건수 = sqlca.sqlerrd[2]. + * 4) 재계산 직후 당일 수수료 총액을 재집계하여 검산. + * 5) tx_commit — 실패 시 tx_abort. + * + * [응답 필드] + * RESPCODE 처리결과코드 + * BIZDATE 대상 영업일(에코) + * UPDCNT 수수료 재계산 반영 건수 + * FEETOTAL 재계산 후 당일 수수료 총액 + * + * [주의] + * 집합 UPDATE 이므로 대량 전표에 대해 원자적으로 처리된다. 대상이 0건이면 + * UPDCNT=0 으로 정상 응답한다. + * + * [연계] + * 수수료 재계산은 정산확정(LG_OL_0019) 이전에 선행되어야 정산액이 올바르게 + * 산출된다. 이미 정산완료(S) 된 전표까지 포함해 재계산되므로, 정책 소급 + * 적용이 필요한 경우에만 운영 승인 하에 호출한다. + */ TX_SERVICE(LG_OL_0023, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 53 "app/online/lg_ol_0023.pgc" + char h_biz_date [ 9 ] ; + +#line 54 "app/online/lg_ol_0023.pgc" + long h_updated ; + +#line 55 "app/online/lg_ol_0023.pgc" + long h_fee_total ; +/* exec sql end declare section */ +#line 56 "app/online/lg_ol_0023.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0023] 원장 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0023] KEY 누락"); + char bizdate[16]; + + tx_log(TX_LOG_INFO, "[LG_OL_0023] 수수료 재계산 반영 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0023] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[LG_OL_0023] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 수수료 = 거래금액의 1% (음수 전표는 절대 크기 기준) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set fee = abs ( amount ) / 100 , upd_ts = now ( ) where biz_date = $1 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 88 "app/online/lg_ol_0023.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0023 recalc"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + h_updated = (long)sqlca.sqlerrd[2]; + + /* 재계산 후 당일 수수료 총액 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( fee ) , 0 ) from lg_ledger where biz_date = $1 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_fee_total),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 104 "app/online/lg_ol_0023.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) + h_fee_total = 0; + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "UPDCNT", h_updated); + tx_buf_setlong(ctx->out, "FEETOTAL", h_fee_total); + tx_log(TX_LOG_INFO, "[LG_OL_0023] 수수료 재계산 완료 date=%s 반영건수=%ld 수수료합=%ld", + bizdate, h_updated, h_fee_total); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0023.pgc b/app/online/lg_ol_0023.pgc index 87e9458..1533969 100644 --- a/app/online/lg_ol_0023.pgc +++ b/app/online/lg_ol_0023.pgc @@ -2,46 +2,121 @@ /* * lg_ol_0023.pgc - 원장 온라인 서비스 (LG_OL_0023) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 수수료 재계산 반영: 지정 영업일의 모든 전표에 대해 수수료를 거래금액의 + * 1%(amount/100) 로 일괄 재계산하여 UPDATE 하고, 반영 건수를 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" EXEC SQL INCLUDE sqlca; +/* + * [업무 개요] + * 수수료율 정책이 변경되었거나, 접수 단계에서 수수료가 미계산/오계산된 + * 경우, 특정 영업일 전체 전표의 수수료를 일괄 재계산한다. 기준율은 1% + * (거래금액 절대값의 1/100) 이며, 음수 전표(취소·역분개)도 절대 크기로 + * 수수료를 산정한다. + * + * [재계산 규칙] + * fee = abs(amount) / 100 (정수 나눗셈, 원 단위 절사) + * + * [처리 절차] + * 1) BIZDATE 추출 및 형식 검증. + * 2) tx_begin 후 당일 전표 전체를 한 UPDATE 문으로 재계산. + * 3) 반영 건수 = sqlca.sqlerrd[2]. + * 4) 재계산 직후 당일 수수료 총액을 재집계하여 검산. + * 5) tx_commit — 실패 시 tx_abort. + * + * [응답 필드] + * RESPCODE 처리결과코드 + * BIZDATE 대상 영업일(에코) + * UPDCNT 수수료 재계산 반영 건수 + * FEETOTAL 재계산 후 당일 수수료 총액 + * + * [주의] + * 집합 UPDATE 이므로 대량 전표에 대해 원자적으로 처리된다. 대상이 0건이면 + * UPDCNT=0 으로 정상 응답한다. + * + * [연계] + * 수수료 재계산은 정산확정(LG_OL_0019) 이전에 선행되어야 정산액이 올바르게 + * 산출된다. 이미 정산완료(S) 된 전표까지 포함해 재계산되므로, 정책 소급 + * 적용이 필요한 경우에만 운영 승인 하에 호출한다. + */ TX_SERVICE(LG_OL_0023, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + long h_updated; + long h_fee_total; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0023] 원장 단건조회 서비스 진입"); + char bizdate[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0023] KEY 누락"); + tx_log(TX_LOG_INFO, "[LG_OL_0023] 수수료 재계산 반영 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0023] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[LG_OL_0023] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 수수료 = 거래금액의 1% (음수 전표는 절대 크기 기준) */ + EXEC SQL UPDATE lg_ledger + SET fee = abs(amount) / 100, upd_ts = now() + WHERE biz_date = :h_biz_date; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0023 recalc"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + h_updated = (long)sqlca.sqlerrd[2]; + + /* 재계산 후 당일 수수료 총액 확인 */ + EXEC SQL SELECT coalesce(sum(fee), 0) + INTO :h_fee_total + FROM lg_ledger + WHERE biz_date = :h_biz_date; + if (sqlca.sqlcode != TX_SQL_OK) + h_fee_total = 0; + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "UPDCNT", h_updated); + tx_buf_setlong(ctx->out, "FEETOTAL", h_fee_total); + tx_log(TX_LOG_INFO, "[LG_OL_0023] 수수료 재계산 완료 date=%s 반영건수=%ld 수수료합=%ld", + bizdate, h_updated, h_fee_total); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0024.c b/app/online/lg_ol_0024.c index c17a80d..db7cff6 100644 --- a/app/online/lg_ol_0024.c +++ b/app/online/lg_ol_0024.c @@ -10,13 +10,14 @@ /* * lg_ol_0024.pgc - 원장 온라인 서비스 (LG_OL_0024) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 미결 완료전이: 단건 key 의 접수(R) 전표를 처리완료(M) 로 전이한다. + * 이미 완료/정산 상태이면 전이하지 않고 경고 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +90,146 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0024.pgc" +#line 16 "app/online/lg_ol_0024.pgc" +/* + * [업무 개요] + * 접수(R) 상태로 대기 중인 미결 전표 한 건을 처리완료(M) 로 전이한다. + * 전표가 이미 완료/정산 등 다른 상태이면 전이하지 않고 경고(WARN) 만 + * 덧붙여 정상 응답한다. (멱등적 재시도 허용) + * + * [상태 전이] + * R(접수) → M(처리완료) 이미 R 이 아니면 전이 없음 + WARN=ALREADY_DONE + * + * [동시성] + * 조건부 UPDATE(WHERE status='R') 로 이중 처리 경합을 방지한다. 실제 전이 + * 여부는 sqlca.sqlerrd[2](affected rows) 로 판정한다. + * + * [처리 절차] + * 1) KEY 추출/검증. + * 2) 현재 status 조회(없으면 RESP_NOMERCH). + * 3) R 이 아니면 경고 응답(정상 종료). + * 4) tx_begin 후 R→M 조건부 UPDATE, tx_commit. + * + * [응답 필드] + * RESPCODE 처리결과코드 KEY 처리키(에코) + * STATUS 전이후 상태 MOVED 실제 전이 건수(0/1) WARN 경고사유(옵션) + */ TX_SERVICE(LG_OL_0024, ctx) { - lg_rec_t rec; + /* exec sql begin declare section */ + + + + + +#line 44 "app/online/lg_ol_0024.pgc" + char h_key [ 16 ] ; + +#line 45 "app/online/lg_ol_0024.pgc" + char h_status_r [ 2 ] ; + +#line 46 "app/online/lg_ol_0024.pgc" + char h_status_m [ 2 ] ; + +#line 47 "app/online/lg_ol_0024.pgc" + char h_cur_status [ 2 ] ; +/* exec sql end declare section */ +#line 48 "app/online/lg_ol_0024.pgc" + + char key[16]; - int rc; + long affected; - tx_log(TX_LOG_INFO, "[LG_OL_0024] 원장 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0024] 미결 완료전이 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { tx_log(TX_LOG_ERROR, "[LG_OL_0024] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0024] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_status_r, LG_ST_RECV, sizeof(h_status_r) - 1); + h_status_r[sizeof(h_status_r) - 1] = '\0'; + strncpy(h_status_m, LG_ST_DONE, sizeof(h_status_m) - 1); + h_status_m[sizeof(h_status_m) - 1] = '\0'; + + /* 현재 상태 확인 (경고 판단용) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select status from lg_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_cur_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 72 "app/online/lg_ol_0024.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0024] 전표 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0024 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (h_cur_status[0] != LG_ST_RECV[0]) { + tx_log(TX_LOG_WARN, "[LG_OL_0024] 이미 처리됨 key=%s status=%s", key, h_cur_status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_cur_status); + tx_buf_sets(ctx->out, "WARN", "ALREADY_DONE"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* R 상태 조건부 전이 (동시성 안전) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set status = $1 , upd_ts = now ( ) where key = $2 and status = $3 ", + ECPGt_char,(h_status_m),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status_r),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 108 "app/online/lg_ol_0024.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0024 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + affected = (long)sqlca.sqlerrd[2]; + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0024] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", (affected > 0) ? LG_ST_DONE : LG_ST_RECV); + tx_buf_setlong(ctx->out, "MOVED", affected); + tx_log(TX_LOG_INFO, "[LG_OL_0024] 완료전이 key=%s 반영=%ld", key, affected); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0024.pgc b/app/online/lg_ol_0024.pgc index b3c6bc5..92e6331 100644 --- a/app/online/lg_ol_0024.pgc +++ b/app/online/lg_ol_0024.pgc @@ -2,46 +2,132 @@ /* * lg_ol_0024.pgc - 원장 온라인 서비스 (LG_OL_0024) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 미결 완료전이: 단건 key 의 접수(R) 전표를 처리완료(M) 로 전이한다. + * 이미 완료/정산 상태이면 전이하지 않고 경고 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" EXEC SQL INCLUDE sqlca; +/* + * [업무 개요] + * 접수(R) 상태로 대기 중인 미결 전표 한 건을 처리완료(M) 로 전이한다. + * 전표가 이미 완료/정산 등 다른 상태이면 전이하지 않고 경고(WARN) 만 + * 덧붙여 정상 응답한다. (멱등적 재시도 허용) + * + * [상태 전이] + * R(접수) → M(처리완료) 이미 R 이 아니면 전이 없음 + WARN=ALREADY_DONE + * + * [동시성] + * 조건부 UPDATE(WHERE status='R') 로 이중 처리 경합을 방지한다. 실제 전이 + * 여부는 sqlca.sqlerrd[2](affected rows) 로 판정한다. + * + * [처리 절차] + * 1) KEY 추출/검증. + * 2) 현재 status 조회(없으면 RESP_NOMERCH). + * 3) R 이 아니면 경고 응답(정상 종료). + * 4) tx_begin 후 R→M 조건부 UPDATE, tx_commit. + * + * [응답 필드] + * RESPCODE 처리결과코드 KEY 처리키(에코) + * STATUS 전이후 상태 MOVED 실제 전이 건수(0/1) WARN 경고사유(옵션) + */ TX_SERVICE(LG_OL_0024, ctx) { - lg_rec_t rec; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_status_r[2]; + char h_status_m[2]; + char h_cur_status[2]; + EXEC SQL END DECLARE SECTION; + char key[16]; - int rc; + long affected; - tx_log(TX_LOG_INFO, "[LG_OL_0024] 원장 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0024] 미결 완료전이 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { tx_log(TX_LOG_ERROR, "[LG_OL_0024] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0024] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_status_r, LG_ST_RECV, sizeof(h_status_r) - 1); + h_status_r[sizeof(h_status_r) - 1] = '\0'; + strncpy(h_status_m, LG_ST_DONE, sizeof(h_status_m) - 1); + h_status_m[sizeof(h_status_m) - 1] = '\0'; + + /* 현재 상태 확인 (경고 판단용) */ + EXEC SQL SELECT status INTO :h_cur_status + FROM lg_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0024] 전표 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0024 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (h_cur_status[0] != LG_ST_RECV[0]) { + tx_log(TX_LOG_WARN, "[LG_OL_0024] 이미 처리됨 key=%s status=%s", key, h_cur_status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_cur_status); + tx_buf_sets(ctx->out, "WARN", "ALREADY_DONE"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* R 상태 조건부 전이 (동시성 안전) */ + EXEC SQL UPDATE lg_ledger + SET status = :h_status_m, upd_ts = now() + WHERE key = :h_key + AND status = :h_status_r; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0024 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + affected = (long)sqlca.sqlerrd[2]; + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0024] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", (affected > 0) ? LG_ST_DONE : LG_ST_RECV); + tx_buf_setlong(ctx->out, "MOVED", affected); + tx_log(TX_LOG_INFO, "[LG_OL_0024] 완료전이 key=%s 반영=%ld", key, affected); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0025.c b/app/online/lg_ol_0025.c index e5f669c..f357667 100644 --- a/app/online/lg_ol_0025.c +++ b/app/online/lg_ol_0025.c @@ -10,13 +10,14 @@ /* * lg_ol_0025.pgc - 원장 온라인 서비스 (LG_OL_0025) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 상태정정: 요청된 KEY 와 NEWSTATUS 를 검증(R/M/U/S 만 허용)한 뒤 + * 상태를 UPDATE 하고, 정정 전 상태(구상태)와 정정 후 상태를 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,60 +90,140 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0025.pgc" +#line 16 "app/online/lg_ol_0025.pgc" +static int lg_ol_0025_valid_status(const char *s) +{ + if (!s || !s[0] || s[1]) + return 0; + return (s[0] == LG_ST_RECV[0] || s[0] == LG_ST_DONE[0] || + s[0] == LG_ST_FAIL[0] || s[0] == LG_ST_SETTLED[0]); +} + +/* + * [업무 개요] + * 운영자가 특정 전표의 상태코드를 수동으로 정정할 때 사용한다. 잘못 전이된 + * 상태를 바로잡는 관리 기능이므로, 허용된 상태코드(R/M/U/S) 만 지정할 수 + * 있도록 엄격히 검증하고, 정정 전 상태를 감사(監査) 목적으로 함께 반환한다. + * + * [허용 상태코드] + * R(접수) / M(처리완료) / U(불일치) / S(정산완료) + * 그 외 문자열은 RESP_INVALID 로 거부한다. (lg_ol_0025_valid_status) + * + * [처리 절차] + * 1) KEY, NEWSTATUS 추출. + * 2) NEWSTATUS 유효성 검증(1자리 + 허용집합). + * 3) tx_begin 후 현재 상태(OLDSTATUS) 확보(없으면 RESP_NOMERCH). + * 4) status 를 NEWSTATUS 로 UPDATE, tx_commit. + * + * [응답 필드] + * RESPCODE 처리결과코드 KEY 처리키(에코) + * OLDSTATUS 정정 전 상태 NEWSTATUS 정정 후 상태 + */ TX_SERVICE(LG_OL_0025, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + +#line 49 "app/online/lg_ol_0025.pgc" + char h_key [ 16 ] ; + +#line 50 "app/online/lg_ol_0025.pgc" + char h_new_status [ 2 ] ; + +#line 51 "app/online/lg_ol_0025.pgc" + char h_old_status [ 2 ] ; +/* exec sql end declare section */ +#line 52 "app/online/lg_ol_0025.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0025] 원장 등록 서비스 진입"); + + char key[16], newstatus[8]; + + tx_log(TX_LOG_INFO, "[LG_OL_0025] 원장 상태정정 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_buf_get(ctx->in, "NEWSTATUS", newstatus, sizeof(newstatus)) != TX_OK) { tx_log(TX_LOG_ERROR, "[LG_OL_0025] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0025] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0025] 영업일 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0025] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!lg_ol_0025_valid_status(newstatus)) { + tx_log(TX_LOG_WARN, "[LG_OL_0025] 허용되지 않는 상태코드 status=%s", newstatus); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_new_status, newstatus, sizeof(h_new_status) - 1); + h_new_status[sizeof(h_new_status) - 1] = '\0'; + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 정정 전 상태 확보 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select status from lg_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_old_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 87 "app/online/lg_ol_0025.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0025] 전표 없음 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0025 select"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set status = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_char,(h_new_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 107 "app/online/lg_ol_0025.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0025 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0025] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "OLDSTATUS", h_old_status); + tx_buf_sets(ctx->out, "NEWSTATUS", h_new_status); + tx_log(TX_LOG_INFO, "[LG_OL_0025] 상태정정 완료 key=%s %s→%s", + key, h_old_status, h_new_status); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0025.pgc b/app/online/lg_ol_0025.pgc index e7311c7..e8381fc 100644 --- a/app/online/lg_ol_0025.pgc +++ b/app/online/lg_ol_0025.pgc @@ -2,69 +2,130 @@ /* * lg_ol_0025.pgc - 원장 온라인 서비스 (LG_OL_0025) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 상태정정: 요청된 KEY 와 NEWSTATUS 를 검증(R/M/U/S 만 허용)한 뒤 + * 상태를 UPDATE 하고, 정정 전 상태(구상태)와 정정 후 상태를 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" EXEC SQL INCLUDE sqlca; +static int lg_ol_0025_valid_status(const char *s) +{ + if (!s || !s[0] || s[1]) + return 0; + return (s[0] == LG_ST_RECV[0] || s[0] == LG_ST_DONE[0] || + s[0] == LG_ST_FAIL[0] || s[0] == LG_ST_SETTLED[0]); +} + +/* + * [업무 개요] + * 운영자가 특정 전표의 상태코드를 수동으로 정정할 때 사용한다. 잘못 전이된 + * 상태를 바로잡는 관리 기능이므로, 허용된 상태코드(R/M/U/S) 만 지정할 수 + * 있도록 엄격히 검증하고, 정정 전 상태를 감사(監査) 목적으로 함께 반환한다. + * + * [허용 상태코드] + * R(접수) / M(처리완료) / U(불일치) / S(정산완료) + * 그 외 문자열은 RESP_INVALID 로 거부한다. (lg_ol_0025_valid_status) + * + * [처리 절차] + * 1) KEY, NEWSTATUS 추출. + * 2) NEWSTATUS 유효성 검증(1자리 + 허용집합). + * 3) tx_begin 후 현재 상태(OLDSTATUS) 확보(없으면 RESP_NOMERCH). + * 4) status 를 NEWSTATUS 로 UPDATE, tx_commit. + * + * [응답 필드] + * RESPCODE 처리결과코드 KEY 처리키(에코) + * OLDSTATUS 정정 전 상태 NEWSTATUS 정정 후 상태 + */ TX_SERVICE(LG_OL_0025, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_new_status[2]; + char h_old_status[2]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0025] 원장 등록 서비스 진입"); + char key[16], newstatus[8]; + + tx_log(TX_LOG_INFO, "[LG_OL_0025] 원장 상태정정 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_buf_get(ctx->in, "NEWSTATUS", newstatus, sizeof(newstatus)) != TX_OK) { tx_log(TX_LOG_ERROR, "[LG_OL_0025] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0025] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0025] 영업일 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0025] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!lg_ol_0025_valid_status(newstatus)) { + tx_log(TX_LOG_WARN, "[LG_OL_0025] 허용되지 않는 상태코드 status=%s", newstatus); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_new_status, newstatus, sizeof(h_new_status) - 1); + h_new_status[sizeof(h_new_status) - 1] = '\0'; + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 정정 전 상태 확보 */ + EXEC SQL SELECT status INTO :h_old_status + FROM lg_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0025] 전표 없음 key=%s", key); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0025 select"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL UPDATE lg_ledger + SET status = :h_new_status, upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0025 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0025] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "OLDSTATUS", h_old_status); + tx_buf_sets(ctx->out, "NEWSTATUS", h_new_status); + tx_log(TX_LOG_INFO, "[LG_OL_0025] 상태정정 완료 key=%s %s→%s", + key, h_old_status, h_new_status); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0026.c b/app/online/lg_ol_0026.c index 331aa5c..48701b6 100644 --- a/app/online/lg_ol_0026.c +++ b/app/online/lg_ol_0026.c @@ -10,13 +10,14 @@ /* * lg_ol_0026.pgc - 원장 온라인 서비스 (LG_OL_0026) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 금액구간 통계: 지정 영업일에서 기준금액(THRESHOLD) 이상 구간과 미만 구간의 + * 건수/합계를 각각 집계 SELECT 로 산출하여 응답한다. (조회 전용) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +90,145 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0026.pgc" +#line 16 "app/online/lg_ol_0026.pgc" +/* + * [업무 개요] + * 이상거래 모니터링/리스크 부서가 특정 영업일의 매출 분포를 파악할 때, + * 기준금액(THRESHOLD) 을 경계로 고액구간과 소액구간의 건수·합계를 한 번에 + * 조회한다. 예: 100만원 이상 고액 승인건이 몇 건/얼마인지 즉시 확인. + * + * [구간 정의] + * 고액구간(HI) : amount >= THRESHOLD + * 소액구간(LO) : amount < THRESHOLD + * (두 구간은 상호배타적이므로 HICNT + LOCNT = 당일 전표 총건수) + * + * [처리 절차] + * 1) BIZDATE, THRESHOLD 추출 및 검증(음수 기준금액 불가). + * 2) 고액구간 집계 SELECT (count, sum). + * 3) 소액구간 집계 SELECT (count, sum). + * 4) 두 구간 결과를 응답 TXBUF 로 구성. + * + * [응답 필드] + * RESPCODE 처리결과코드 + * BIZDATE 대상 영업일(에코) + * THRESHOLD 구간 기준금액(에코) + * HICNT 고액구간 건수 + * HISUM 고액구간 합계 + * LOCNT 소액구간 건수 + * LOSUM 소액구간 합계 + * + * [주의] + * 조회 전용. 집계이므로 결과 없음(NOTFOUND) 도 coalesce/count 로 0 이 보장. + */ TX_SERVICE(LG_OL_0026, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 50 "app/online/lg_ol_0026.pgc" + char h_biz_date [ 9 ] ; + +#line 51 "app/online/lg_ol_0026.pgc" + long h_threshold ; + +#line 52 "app/online/lg_ol_0026.pgc" + long h_hi_cnt ; + +#line 53 "app/online/lg_ol_0026.pgc" + long h_hi_sum ; + +#line 54 "app/online/lg_ol_0026.pgc" + long h_lo_cnt ; + +#line 55 "app/online/lg_ol_0026.pgc" + long h_lo_sum ; +/* exec sql end declare section */ +#line 56 "app/online/lg_ol_0026.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0026] 원장 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0026] KEY 누락"); + char bizdate[16]; + long threshold = 0; + + tx_log(TX_LOG_INFO, "[LG_OL_0026] 금액구간 통계 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0026] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "THRESHOLD", &threshold); + + if (!date_is_valid(bizdate) || threshold < 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0026] 입력 검증 실패 date=%s threshold=%ld", + bizdate, threshold); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0026] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + h_threshold = threshold; + + /* 기준금액 이상 구간 집계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from lg_ledger where biz_date = $1 and amount >= $2 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_threshold),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_hi_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_hi_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 90 "app/online/lg_ol_0026.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0026 high"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 기준금액 미만 구간 집계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from lg_ledger where biz_date = $1 and amount < $2 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_threshold),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_lo_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_lo_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 104 "app/online/lg_ol_0026.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0026 low"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0026] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "THRESHOLD", threshold); + tx_buf_setlong(ctx->out, "HICNT", h_hi_cnt); + tx_buf_setlong(ctx->out, "HISUM", h_hi_sum); + tx_buf_setlong(ctx->out, "LOCNT", h_lo_cnt); + tx_buf_setlong(ctx->out, "LOSUM", h_lo_sum); + tx_log(TX_LOG_INFO, "[LG_OL_0026] 구간통계 date=%s 이상=%ld/%ld 미만=%ld/%ld", + bizdate, h_hi_cnt, h_hi_sum, h_lo_cnt, h_lo_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0026.pgc b/app/online/lg_ol_0026.pgc index 2a4a9e0..93b1cb7 100644 --- a/app/online/lg_ol_0026.pgc +++ b/app/online/lg_ol_0026.pgc @@ -2,46 +2,123 @@ /* * lg_ol_0026.pgc - 원장 온라인 서비스 (LG_OL_0026) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 금액구간 통계: 지정 영업일에서 기준금액(THRESHOLD) 이상 구간과 미만 구간의 + * 건수/합계를 각각 집계 SELECT 로 산출하여 응답한다. (조회 전용) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" EXEC SQL INCLUDE sqlca; +/* + * [업무 개요] + * 이상거래 모니터링/리스크 부서가 특정 영업일의 매출 분포를 파악할 때, + * 기준금액(THRESHOLD) 을 경계로 고액구간과 소액구간의 건수·합계를 한 번에 + * 조회한다. 예: 100만원 이상 고액 승인건이 몇 건/얼마인지 즉시 확인. + * + * [구간 정의] + * 고액구간(HI) : amount >= THRESHOLD + * 소액구간(LO) : amount < THRESHOLD + * (두 구간은 상호배타적이므로 HICNT + LOCNT = 당일 전표 총건수) + * + * [처리 절차] + * 1) BIZDATE, THRESHOLD 추출 및 검증(음수 기준금액 불가). + * 2) 고액구간 집계 SELECT (count, sum). + * 3) 소액구간 집계 SELECT (count, sum). + * 4) 두 구간 결과를 응답 TXBUF 로 구성. + * + * [응답 필드] + * RESPCODE 처리결과코드 + * BIZDATE 대상 영업일(에코) + * THRESHOLD 구간 기준금액(에코) + * HICNT 고액구간 건수 + * HISUM 고액구간 합계 + * LOCNT 소액구간 건수 + * LOSUM 소액구간 합계 + * + * [주의] + * 조회 전용. 집계이므로 결과 없음(NOTFOUND) 도 coalesce/count 로 0 이 보장. + */ TX_SERVICE(LG_OL_0026, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + long h_threshold; + long h_hi_cnt; + long h_hi_sum; + long h_lo_cnt; + long h_lo_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0026] 원장 단건조회 서비스 진입"); + char bizdate[16]; + long threshold = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0026] KEY 누락"); + tx_log(TX_LOG_INFO, "[LG_OL_0026] 금액구간 통계 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0026] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "THRESHOLD", &threshold); + + if (!date_is_valid(bizdate) || threshold < 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0026] 입력 검증 실패 date=%s threshold=%ld", + bizdate, threshold); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0026] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + h_threshold = threshold; + + /* 기준금액 이상 구간 집계 */ + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_hi_cnt, :h_hi_sum + FROM lg_ledger + WHERE biz_date = :h_biz_date + AND amount >= :h_threshold; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0026 high"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 기준금액 미만 구간 집계 */ + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_lo_cnt, :h_lo_sum + FROM lg_ledger + WHERE biz_date = :h_biz_date + AND amount < :h_threshold; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0026 low"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0026] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "THRESHOLD", threshold); + tx_buf_setlong(ctx->out, "HICNT", h_hi_cnt); + tx_buf_setlong(ctx->out, "HISUM", h_hi_sum); + tx_buf_setlong(ctx->out, "LOCNT", h_lo_cnt); + tx_buf_setlong(ctx->out, "LOSUM", h_lo_sum); + tx_log(TX_LOG_INFO, "[LG_OL_0026] 구간통계 date=%s 이상=%ld/%ld 미만=%ld/%ld", + bizdate, h_hi_cnt, h_hi_sum, h_lo_cnt, h_lo_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0027.c b/app/online/lg_ol_0027.c index 6af4894..1324bee 100644 --- a/app/online/lg_ol_0027.c +++ b/app/online/lg_ol_0027.c @@ -10,13 +10,14 @@ /* * lg_ol_0027.pgc - 원장 온라인 서비스 (LG_OL_0027) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 재대사: 지정 영업일 합계를 (1) 직접 EXEC SQL 집계와 (2) 표준 DBIO + * lg_rec_sum() 두 경로로 산출하여 일치 여부를 비교하고 결과를 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,60 +90,125 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0027.pgc" +#line 16 "app/online/lg_ol_0027.pgc" +/* + * [업무 개요] + * 일마감 배치 이후, 온라인 채널에서 특정 영업일의 원장 합계를 "두 개의 + * 독립 경로" 로 산출해 서로 일치하는지 재대사(再對査)한다. 서로 다른 코드 + * 경로가 같은 값을 내면 데이터/집계 로직 무결성이 교차 검증된다. + * + * [두 경로] + * 경로 1 (SQLSUM) : 본 서비스가 직접 EXEC SQL 로 coalesce(sum(amount),0) + * 경로 2 (DBIOSUM) : 표준 DBIO API lg_rec_sum() (별도 프로그램에서 구현) + * + * [판정] + * DIFF = SQLSUM - DBIOSUM + * MATCHED = (DIFF == 0) ? "Y" : "N" + * 불일치(N) 시 상위 감사(監査) 서비스가 상세 대사를 재수행하도록 신호한다. + * + * [처리 절차] + * 1) BIZDATE 추출 및 형식 검증(date_is_valid). + * 2) 경로 1: 당일 합계/건수를 직접 집계. + * 3) 경로 2: lg_rec_sum() 으로 동일 영업일 합계 재확인. + * 4) 두 값을 비교하여 차이/일치 여부를 응답. + * + * [응답 필드] + * RESPCODE 처리결과코드 + * BIZDATE 대상 영업일(에코) + * SQLSUM 직접 집계 합계 + * DBIOSUM 표준 DBIO 합계 + * CNT 당일 전표 건수 + * DIFF 두 경로 차이(0 이 정상) + * MATCHED 일치 여부 Y/N + * + * [주의] + * 조회/대사 전용으로 원장을 변경하지 않는다. 경로 2 실패 시 경고 로그만 + * 남기고 DBIOSUM=0 으로 응답하여 서비스 자체는 정상 종료한다. + */ TX_SERVICE(LG_OL_0027, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + /* exec sql begin declare section */ + + + + +#line 55 "app/online/lg_ol_0027.pgc" + char h_biz_date [ 9 ] ; + +#line 56 "app/online/lg_ol_0027.pgc" + long h_sql_sum ; + +#line 57 "app/online/lg_ol_0027.pgc" + long h_cnt ; +/* exec sql end declare section */ +#line 58 "app/online/lg_ol_0027.pgc" + + + char bizdate[16]; + long dbio_sum = 0; + long diff; + int matched; int rc; - tx_log(TX_LOG_INFO, "[LG_OL_0027] 원장 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0027] 재대사 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0027] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0027] 금액 범위 오류 amount=%ld", amount); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0027] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0027] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[LG_OL_0027] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0027] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 경로 1: 직접 EXEC SQL 집계 (합계 + 건수) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) from lg_ledger where biz_date = $1 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_sql_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 90 "app/online/lg_ol_0027.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0027 direct sum"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + /* 경로 2: 표준 DBIO API 재확인 */ + rc = lg_rec_sum(bizdate, &dbio_sum); + if (rc != TX_OK) { + tx_log(TX_LOG_WARN, "[LG_OL_0027] DBIO 합계 조회 실패 rc=%d", rc); + dbio_sum = 0; + } + + diff = h_sql_sum - dbio_sum; + matched = (diff == 0) ? 1 : 0; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0027] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SQLSUM", h_sql_sum); + tx_buf_setlong(ctx->out, "DBIOSUM", dbio_sum); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "MATCHED", matched ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[LG_OL_0027] 재대사 date=%s sql=%ld dbio=%ld 일치=%s", + bizdate, h_sql_sum, dbio_sum, matched ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0027.pgc b/app/online/lg_ol_0027.pgc index 4665601..b5fbad2 100644 --- a/app/online/lg_ol_0027.pgc +++ b/app/online/lg_ol_0027.pgc @@ -2,69 +2,119 @@ /* * lg_ol_0027.pgc - 원장 온라인 서비스 (LG_OL_0027) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 재대사: 지정 영업일 합계를 (1) 직접 EXEC SQL 집계와 (2) 표준 DBIO + * lg_rec_sum() 두 경로로 산출하여 일치 여부를 비교하고 결과를 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" EXEC SQL INCLUDE sqlca; +/* + * [업무 개요] + * 일마감 배치 이후, 온라인 채널에서 특정 영업일의 원장 합계를 "두 개의 + * 독립 경로" 로 산출해 서로 일치하는지 재대사(再對査)한다. 서로 다른 코드 + * 경로가 같은 값을 내면 데이터/집계 로직 무결성이 교차 검증된다. + * + * [두 경로] + * 경로 1 (SQLSUM) : 본 서비스가 직접 EXEC SQL 로 coalesce(sum(amount),0) + * 경로 2 (DBIOSUM) : 표준 DBIO API lg_rec_sum() (별도 프로그램에서 구현) + * + * [판정] + * DIFF = SQLSUM - DBIOSUM + * MATCHED = (DIFF == 0) ? "Y" : "N" + * 불일치(N) 시 상위 감사(監査) 서비스가 상세 대사를 재수행하도록 신호한다. + * + * [처리 절차] + * 1) BIZDATE 추출 및 형식 검증(date_is_valid). + * 2) 경로 1: 당일 합계/건수를 직접 집계. + * 3) 경로 2: lg_rec_sum() 으로 동일 영업일 합계 재확인. + * 4) 두 값을 비교하여 차이/일치 여부를 응답. + * + * [응답 필드] + * RESPCODE 처리결과코드 + * BIZDATE 대상 영업일(에코) + * SQLSUM 직접 집계 합계 + * DBIOSUM 표준 DBIO 합계 + * CNT 당일 전표 건수 + * DIFF 두 경로 차이(0 이 정상) + * MATCHED 일치 여부 Y/N + * + * [주의] + * 조회/대사 전용으로 원장을 변경하지 않는다. 경로 2 실패 시 경고 로그만 + * 남기고 DBIOSUM=0 으로 응답하여 서비스 자체는 정상 종료한다. + */ TX_SERVICE(LG_OL_0027, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + long h_sql_sum; + long h_cnt; + EXEC SQL END DECLARE SECTION; + + char bizdate[16]; + long dbio_sum = 0; + long diff; + int matched; int rc; - tx_log(TX_LOG_INFO, "[LG_OL_0027] 원장 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0027] 재대사 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0027] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0027] 금액 범위 오류 amount=%ld", amount); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0027] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0027] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[LG_OL_0027] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0027] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 경로 1: 직접 EXEC SQL 집계 (합계 + 건수) */ + EXEC SQL SELECT coalesce(sum(amount), 0), count(*) + INTO :h_sql_sum, :h_cnt + FROM lg_ledger + WHERE biz_date = :h_biz_date; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0027 direct sum"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + /* 경로 2: 표준 DBIO API 재확인 */ + rc = lg_rec_sum(bizdate, &dbio_sum); + if (rc != TX_OK) { + tx_log(TX_LOG_WARN, "[LG_OL_0027] DBIO 합계 조회 실패 rc=%d", rc); + dbio_sum = 0; + } + + diff = h_sql_sum - dbio_sum; + matched = (diff == 0) ? 1 : 0; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0027] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SQLSUM", h_sql_sum); + tx_buf_setlong(ctx->out, "DBIOSUM", dbio_sum); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "MATCHED", matched ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[LG_OL_0027] 재대사 date=%s sql=%ld dbio=%ld 일치=%s", + bizdate, h_sql_sum, dbio_sum, matched ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0028.c b/app/online/lg_ol_0028.c index 2c7b8a4..33d0c28 100644 --- a/app/online/lg_ol_0028.c +++ b/app/online/lg_ol_0028.c @@ -10,13 +10,14 @@ /* * lg_ol_0028.pgc - 원장 온라인 서비스 (LG_OL_0028) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 전기 취소: 단건 key 의 처리완료(M) 전표를 접수(R) 로 되돌린다(전기취소). + * 되돌림 후 해당 가맹점의 원장 잔액(Σamount)을 재집계하여 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +90,152 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0028.pgc" +#line 16 "app/online/lg_ol_0028.pgc" +/* + * [업무 개요] + * 이미 처리완료(M) 로 전기(轉記)된 전표를 접수(R) 상태로 되돌리는 전기취소 + * 기능이다. 잘못 확정된 매출을 재검토 대기 상태로 환원할 때 사용하며, + * 되돌림 직후 가맹점 잔액을 재집계하여 영향도를 즉시 확인한다. + * + * [상태 전이] + * M(처리완료) → R(접수) M 이 아닌 전표는 전기취소 불가(RESP_INVALID) + * + * [처리 절차] + * 1) KEY 추출/검증. + * 2) 단건 SELECT 로 merch_id, 현재 status 확보(없으면 RESP_NOMERCH). + * 3) 상태가 M 이 아니면 거부. + * 4) tx_begin 후 status='R' 로 UPDATE. + * 5) 같은 트랜잭션에서 가맹점 잔액 Σamount 재집계, tx_commit. + * + * [응답 필드] + * RESPCODE 처리결과코드 KEY 처리키(에코) MERCHID 가맹점번호 + * STATUS 되돌림후 상태(R) BALANCE 되돌림 후 가맹점 잔액 + */ TX_SERVICE(LG_OL_0028, ctx) { - lg_rec_t rec; + /* exec sql begin declare section */ + + + + + + +#line 41 "app/online/lg_ol_0028.pgc" + char h_key [ 16 ] ; + +#line 42 "app/online/lg_ol_0028.pgc" + char h_status_r [ 2 ] ; + +#line 43 "app/online/lg_ol_0028.pgc" + char h_cur_status [ 2 ] ; + +#line 44 "app/online/lg_ol_0028.pgc" + char h_merch_id [ 16 ] ; + +#line 45 "app/online/lg_ol_0028.pgc" + long h_balance ; +/* exec sql end declare section */ +#line 46 "app/online/lg_ol_0028.pgc" + + char key[16]; - int rc; - tx_log(TX_LOG_INFO, "[LG_OL_0028] 원장 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0028] 전기 취소 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { tx_log(TX_LOG_ERROR, "[LG_OL_0028] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_status_r, LG_ST_RECV, sizeof(h_status_r) - 1); + h_status_r[sizeof(h_status_r) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select merch_id , status from lg_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_cur_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 67 "app/online/lg_ol_0028.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0028] 전표 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0028 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 처리완료(M) 전표만 전기취소 대상 */ + if (h_cur_status[0] != LG_ST_DONE[0]) { + tx_log(TX_LOG_WARN, "[LG_OL_0028] 전기취소 불가 상태 key=%s status=%s", + key, h_cur_status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "STATUS", h_cur_status); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set status = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_char,(h_status_r),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 101 "app/online/lg_ol_0028.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0028 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 되돌림 후 가맹점 잔액 재집계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from lg_ledger where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_balance),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 114 "app/online/lg_ol_0028.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) + h_balance = 0; + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "MERCHID", h_merch_id); + tx_buf_sets(ctx->out, "STATUS", LG_ST_RECV); + tx_buf_setlong(ctx->out, "BALANCE", h_balance); + tx_log(TX_LOG_INFO, "[LG_OL_0028] 전기취소 완료 key=%s 잔액=%ld", key, h_balance); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0028.pgc b/app/online/lg_ol_0028.pgc index 4023825..7b42aae 100644 --- a/app/online/lg_ol_0028.pgc +++ b/app/online/lg_ol_0028.pgc @@ -2,46 +2,131 @@ /* * lg_ol_0028.pgc - 원장 온라인 서비스 (LG_OL_0028) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 전기 취소: 단건 key 의 처리완료(M) 전표를 접수(R) 로 되돌린다(전기취소). + * 되돌림 후 해당 가맹점의 원장 잔액(Σamount)을 재집계하여 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" EXEC SQL INCLUDE sqlca; +/* + * [업무 개요] + * 이미 처리완료(M) 로 전기(轉記)된 전표를 접수(R) 상태로 되돌리는 전기취소 + * 기능이다. 잘못 확정된 매출을 재검토 대기 상태로 환원할 때 사용하며, + * 되돌림 직후 가맹점 잔액을 재집계하여 영향도를 즉시 확인한다. + * + * [상태 전이] + * M(처리완료) → R(접수) M 이 아닌 전표는 전기취소 불가(RESP_INVALID) + * + * [처리 절차] + * 1) KEY 추출/검증. + * 2) 단건 SELECT 로 merch_id, 현재 status 확보(없으면 RESP_NOMERCH). + * 3) 상태가 M 이 아니면 거부. + * 4) tx_begin 후 status='R' 로 UPDATE. + * 5) 같은 트랜잭션에서 가맹점 잔액 Σamount 재집계, tx_commit. + * + * [응답 필드] + * RESPCODE 처리결과코드 KEY 처리키(에코) MERCHID 가맹점번호 + * STATUS 되돌림후 상태(R) BALANCE 되돌림 후 가맹점 잔액 + */ TX_SERVICE(LG_OL_0028, ctx) { - lg_rec_t rec; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_status_r[2]; + char h_cur_status[2]; + char h_merch_id[16]; + long h_balance; + EXEC SQL END DECLARE SECTION; + char key[16]; - int rc; - tx_log(TX_LOG_INFO, "[LG_OL_0028] 원장 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[LG_OL_0028] 전기 취소 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { tx_log(TX_LOG_ERROR, "[LG_OL_0028] KEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_status_r, LG_ST_RECV, sizeof(h_status_r) - 1); + h_status_r[sizeof(h_status_r) - 1] = '\0'; + + EXEC SQL SELECT merch_id, status + INTO :h_merch_id, :h_cur_status + FROM lg_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0028] 전표 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0028 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 처리완료(M) 전표만 전기취소 대상 */ + if (h_cur_status[0] != LG_ST_DONE[0]) { + tx_log(TX_LOG_WARN, "[LG_OL_0028] 전기취소 불가 상태 key=%s status=%s", + key, h_cur_status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "STATUS", h_cur_status); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + EXEC SQL UPDATE lg_ledger + SET status = :h_status_r, upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0028 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 되돌림 후 가맹점 잔액 재집계 */ + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_balance + FROM lg_ledger WHERE merch_id = :h_merch_id; + if (sqlca.sqlcode != TX_SQL_OK) + h_balance = 0; + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "MERCHID", h_merch_id); + tx_buf_sets(ctx->out, "STATUS", LG_ST_RECV); + tx_buf_setlong(ctx->out, "BALANCE", h_balance); + tx_log(TX_LOG_INFO, "[LG_OL_0028] 전기취소 완료 key=%s 잔액=%ld", key, h_balance); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0029.c b/app/online/lg_ol_0029.c index 87fbfcc..6e1b0ad 100644 --- a/app/online/lg_ol_0029.c +++ b/app/online/lg_ol_0029.c @@ -10,13 +10,14 @@ /* * lg_ol_0029.pgc - 원장 온라인 서비스 (LG_OL_0029) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 역분개 승인: 접수(R) 상태의 역분개행(금액이 음수인 반대분개)을 확인하고 + * 승인 처리(M) 로 전이한다. 역분개가 아니거나 상태가 다르면 거부 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,60 +90,137 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0029.pgc" +#line 16 "app/online/lg_ol_0029.pgc" TX_SERVICE(LG_OL_0029, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 21 "app/online/lg_ol_0029.pgc" + char h_key [ 16 ] ; + +#line 22 "app/online/lg_ol_0029.pgc" + char h_status_r [ 2 ] ; + +#line 23 "app/online/lg_ol_0029.pgc" + char h_status_m [ 2 ] ; + +#line 24 "app/online/lg_ol_0029.pgc" + char h_cur_status [ 2 ] ; + +#line 25 "app/online/lg_ol_0029.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 26 "app/online/lg_ol_0029.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0029] 원장 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0029] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char revkey[16]; + long approved; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0029] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0029] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[LG_OL_0029] 역분개 승인 진입"); + + if (tx_buf_get(ctx->in, "REVKEY", revkey, sizeof(revkey)) != TX_OK || + revkey[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0029] REVKEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, revkey, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_status_r, LG_ST_RECV, sizeof(h_status_r) - 1); + h_status_r[sizeof(h_status_r) - 1] = '\0'; + strncpy(h_status_m, LG_ST_DONE, sizeof(h_status_m) - 1); + h_status_m[sizeof(h_status_m) - 1] = '\0'; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0029] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , status from lg_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_cur_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 51 "app/online/lg_ol_0029.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0029] 역분개행 없음 revkey=%s", revkey); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0029 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + /* 역분개 조건: 접수(R) 상태 + 음수 금액 */ + if (h_cur_status[0] != LG_ST_RECV[0] || h_amount >= 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0029] 역분개 승인 거부 revkey=%s status=%s amount=%ld", + revkey, h_cur_status, h_amount); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "STATUS", h_cur_status); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* R + 음수 조건부 승인 전이 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update lg_ledger set status = $1 , upd_ts = now ( ) where key = $2 and status = $3 and amount < 0", + ECPGt_char,(h_status_m),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status_r),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 88 "app/online/lg_ol_0029.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0029 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + approved = (long)sqlca.sqlerrd[2]; + + if (approved == 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0029] 승인 대상 없음 revkey=%s", revkey); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0029] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "REVKEY", revkey); + tx_buf_setlong(ctx->out, "REVAMT", h_amount); + tx_buf_sets(ctx->out, "STATUS", LG_ST_DONE); + tx_log(TX_LOG_INFO, "[LG_OL_0029] 역분개 승인완료 revkey=%s 금액=%ld", revkey, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0029.pgc b/app/online/lg_ol_0029.pgc index b403b9d..b4cd3cf 100644 --- a/app/online/lg_ol_0029.pgc +++ b/app/online/lg_ol_0029.pgc @@ -2,13 +2,14 @@ /* * lg_ol_0029.pgc - 원장 온라인 서비스 (LG_OL_0029) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 역분개 승인: 접수(R) 상태의 역분개행(금액이 음수인 반대분개)을 확인하고 + * 승인 처리(M) 로 전이한다. 역분개가 아니거나 상태가 다르면 거부 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -16,55 +17,105 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(LG_OL_0029, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_status_r[2]; + char h_status_m[2]; + char h_cur_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0029] 원장 등록 서비스 진입"); + char revkey[16]; + long approved; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0029] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[LG_OL_0029] 역분개 승인 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[LG_OL_0029] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0029] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "REVKEY", revkey, sizeof(revkey)) != TX_OK || + revkey[0] == '\0') { + tx_log(TX_LOG_ERROR, "[LG_OL_0029] REVKEY 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_key, revkey, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_status_r, LG_ST_RECV, sizeof(h_status_r) - 1); + h_status_r[sizeof(h_status_r) - 1] = '\0'; + strncpy(h_status_m, LG_ST_DONE, sizeof(h_status_m) - 1); + h_status_m[sizeof(h_status_m) - 1] = '\0'; - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0029] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL SELECT amount, status + INTO :h_amount, :h_cur_status + FROM lg_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[LG_OL_0029] 역분개행 없음 revkey=%s", revkey); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0029 select"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = lg_rec_count(bizdate, LG_ST_RECV); + /* 역분개 조건: 접수(R) 상태 + 음수 금액 */ + if (h_cur_status[0] != LG_ST_RECV[0] || h_amount >= 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0029] 역분개 승인 거부 revkey=%s status=%s amount=%ld", + revkey, h_cur_status, h_amount); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "STATUS", h_cur_status); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* R + 음수 조건부 승인 전이 */ + EXEC SQL UPDATE lg_ledger + SET status = :h_status_m, upd_ts = now() + WHERE key = :h_key + AND status = :h_status_r + AND amount < 0; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0029 update"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + approved = (long)sqlca.sqlerrd[2]; + + if (approved == 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0029] 승인 대상 없음 revkey=%s", revkey); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[LG_OL_0029] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "REVKEY", revkey); + tx_buf_setlong(ctx->out, "REVAMT", h_amount); + tx_buf_sets(ctx->out, "STATUS", LG_ST_DONE); + tx_log(TX_LOG_INFO, "[LG_OL_0029] 역분개 승인완료 revkey=%s 금액=%ld", revkey, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0030.c b/app/online/lg_ol_0030.c index 984b414..d16bef4 100644 --- a/app/online/lg_ol_0030.c +++ b/app/online/lg_ol_0030.c @@ -10,13 +10,14 @@ /* * lg_ol_0030.pgc - 원장 온라인 서비스 (LG_OL_0030) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매출 집계반영: 지정 영업일의 실거래(요약행 제외) Σamount/건수를 집계하여 + * 요약행(key=영업일, merch_id='*SUMMARY', status=S) 을 새로 INSERT 한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +90,164 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0030.pgc" +#line 16 "app/online/lg_ol_0030.pgc" +#define LG_OL_0030_SUMMARY_ID "*SUMMARY" + +/* + * [업무 개요] + * 특정 영업일의 실거래 매출을 집계하여 원장에 "요약행" 한 건으로 반영한다. + * 요약행은 merch_id='*SUMMARY', status='S', key=영업일 규칙을 따르며, 집계 + * 시에는 기존 요약행을 제외하여 이중집계를 방지한다. + * + * [멱등성] + * 같은 영업일에 재실행되어도 결과가 동일하도록, 기존 요약행(key=영업일) 을 + * 먼저 DELETE 한 뒤 새 요약행을 INSERT 한다. (delete-then-insert) + * + * [처리 절차] + * 1) BIZDATE 추출/검증. + * 2) 실거래(merch_id<>'*SUMMARY') Σamount/건수 집계. + * 3) tx_begin → 기존 요약행 DELETE → 신규 요약행 INSERT → tx_commit. + * + * [응답 필드] + * RESPCODE 처리결과코드 BIZDATE 영업일(에코) SUMKEY 요약행 key + * TOTAL 집계 합계금액 CNT 집계 대상 건수 + */ TX_SERVICE(LG_OL_0030, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 42 "app/online/lg_ol_0030.pgc" + char h_biz_date [ 9 ] ; + +#line 43 "app/online/lg_ol_0030.pgc" + char h_sum_id [ 16 ] ; + +#line 44 "app/online/lg_ol_0030.pgc" + char h_status_s [ 2 ] ; + +#line 45 "app/online/lg_ol_0030.pgc" + char h_key [ 16 ] ; + +#line 46 "app/online/lg_ol_0030.pgc" + long h_total ; + +#line 47 "app/online/lg_ol_0030.pgc" + long h_cnt ; +/* exec sql end declare section */ +#line 48 "app/online/lg_ol_0030.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0030] 원장 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0030] KEY 누락"); + char bizdate[16]; + + tx_log(TX_LOG_INFO, "[LG_OL_0030] 매출 집계반영 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0030] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[LG_OL_0030] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_sum_id, LG_OL_0030_SUMMARY_ID, sizeof(h_sum_id) - 1); + h_sum_id[sizeof(h_sum_id) - 1] = '\0'; + strncpy(h_status_s, LG_ST_SETTLED, sizeof(h_status_s) - 1); + h_status_s[sizeof(h_status_s) - 1] = '\0'; + strncpy(h_key, bizdate, sizeof(h_key) - 1); /* 요약행 key = 영업일 */ + h_key[sizeof(h_key) - 1] = '\0'; + + /* 실거래만 집계 (요약행 자체는 제외) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) , count ( * ) from lg_ledger where biz_date = $1 and merch_id <> $2 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_sum_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_total),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 83 "app/online/lg_ol_0030.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0030 aggregate"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 기존 요약행 제거 후 재삽입 (멱등) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "delete from lg_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 98 "app/online/lg_ol_0030.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0030 delete old"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into lg_ledger ( key , merch_id , card_no , amount , biz_date , status , fee ) values ( $1 , $2 , '' , $3 , $4 , $5 , 0 )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_sum_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_total),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status_s),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 111 "app/online/lg_ol_0030.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0030 insert summary"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "SUMKEY", h_key); + tx_buf_setlong(ctx->out, "TOTAL", h_total); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_log(TX_LOG_INFO, "[LG_OL_0030] 집계반영 완료 date=%s 합계=%ld 건수=%ld", + bizdate, h_total, h_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0030.pgc b/app/online/lg_ol_0030.pgc index d5fbab3..ed1f11f 100644 --- a/app/online/lg_ol_0030.pgc +++ b/app/online/lg_ol_0030.pgc @@ -2,46 +2,135 @@ /* * lg_ol_0030.pgc - 원장 온라인 서비스 (LG_OL_0030) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매출 집계반영: 지정 영업일의 실거래(요약행 제외) Σamount/건수를 집계하여 + * 요약행(key=영업일, merch_id='*SUMMARY', status=S) 을 새로 INSERT 한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" EXEC SQL INCLUDE sqlca; +#define LG_OL_0030_SUMMARY_ID "*SUMMARY" + +/* + * [업무 개요] + * 특정 영업일의 실거래 매출을 집계하여 원장에 "요약행" 한 건으로 반영한다. + * 요약행은 merch_id='*SUMMARY', status='S', key=영업일 규칙을 따르며, 집계 + * 시에는 기존 요약행을 제외하여 이중집계를 방지한다. + * + * [멱등성] + * 같은 영업일에 재실행되어도 결과가 동일하도록, 기존 요약행(key=영업일) 을 + * 먼저 DELETE 한 뒤 새 요약행을 INSERT 한다. (delete-then-insert) + * + * [처리 절차] + * 1) BIZDATE 추출/검증. + * 2) 실거래(merch_id<>'*SUMMARY') Σamount/건수 집계. + * 3) tx_begin → 기존 요약행 DELETE → 신규 요약행 INSERT → tx_commit. + * + * [응답 필드] + * RESPCODE 처리결과코드 BIZDATE 영업일(에코) SUMKEY 요약행 key + * TOTAL 집계 합계금액 CNT 집계 대상 건수 + */ TX_SERVICE(LG_OL_0030, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_sum_id[16]; + char h_status_s[2]; + char h_key[16]; + long h_total; + long h_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0030] 원장 단건조회 서비스 진입"); + char bizdate[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0030] KEY 누락"); + tx_log(TX_LOG_INFO, "[LG_OL_0030] 매출 집계반영 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0030] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[LG_OL_0030] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_sum_id, LG_OL_0030_SUMMARY_ID, sizeof(h_sum_id) - 1); + h_sum_id[sizeof(h_sum_id) - 1] = '\0'; + strncpy(h_status_s, LG_ST_SETTLED, sizeof(h_status_s) - 1); + h_status_s[sizeof(h_status_s) - 1] = '\0'; + strncpy(h_key, bizdate, sizeof(h_key) - 1); /* 요약행 key = 영업일 */ + h_key[sizeof(h_key) - 1] = '\0'; + + /* 실거래만 집계 (요약행 자체는 제외) */ + EXEC SQL SELECT coalesce(sum(amount), 0), count(*) + INTO :h_total, :h_cnt + FROM lg_ledger + WHERE biz_date = :h_biz_date + AND merch_id <> :h_sum_id; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0030 aggregate"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 기존 요약행 제거 후 재삽입 (멱등) */ + EXEC SQL DELETE FROM lg_ledger WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0030 delete old"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL INSERT INTO lg_ledger + (key, merch_id, card_no, amount, biz_date, status, fee) + VALUES + (:h_key, :h_sum_id, '', :h_total, :h_biz_date, :h_status_s, 0); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("lg_ol_0030 insert summary"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_abort(); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_sets(ctx->out, "SUMKEY", h_key); + tx_buf_setlong(ctx->out, "TOTAL", h_total); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_log(TX_LOG_INFO, "[LG_OL_0030] 집계반영 완료 date=%s 합계=%ld 건수=%ld", + bizdate, h_total, h_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0031.c b/app/online/lg_ol_0031.c index 4a0263a..a8cb6bc 100644 --- a/app/online/lg_ol_0031.c +++ b/app/online/lg_ol_0031.c @@ -10,13 +10,14 @@ /* * lg_ol_0031.pgc - 원장 온라인 서비스 (LG_OL_0031) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인원장 요약조회: 가맹점(MERCHID)+영업일(BIZDATE) 조합의 처리완료(M) + * 승인건에 대한 건수/합계/평균(정수 나눗셈)을 단일 SELECT 로 조회 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,88 +90,142 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0031.pgc" +#line 16 "app/online/lg_ol_0031.pgc" +/* + * [업무 개요] + * 가맹점 포털/정산 조회 화면에서 "가맹점 + 영업일" 조합의 승인 완료건 + * 요약(건수/합계/평균)을 단일 호출로 제공한다. 평균은 실수 대신 정수 + * 나눗셈(원 단위 절사)으로 산출하여 회계 표기와 일치시킨다. + * + * [집계 대상] + * status = 'M'(처리완료) 인 승인 전표만 포함. 접수(R)/취소(U)/정산(S) 는 + * 승인원장 요약에서 제외한다. + * + * [평균 산식] + * AVG = SUM / CNT (정수 나눗셈; CNT=0 이면 미등록으로 별도 처리) + * + * [처리 절차] + * 1) MERCHID, BIZDATE 추출 및 검증. + * 2) 3개 조건(merch_id, biz_date, status='M') 단일 집계 SELECT. + * 3) 건수 0 이면 RESP_NOMERCH. + * 4) 평균 산출 및 원화 표기 구성 후 응답. + * + * [응답 필드] + * RESPCODE 처리결과코드 + * MERCHID 가맹점번호(에코) + * BIZDATE 영업일(에코) + * CNT 승인 완료 건수 + * SUM 승인 합계금액 + * AVG 건당 평균(정수) + * AVGWON 평균 원화표기 + * + * [주의] + * 조회 전용. 집계는 항상 1행이므로 singleton SELECT INTO 를 사용한다. + */ TX_SERVICE(LG_OL_0031, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + + +#line 52 "app/online/lg_ol_0031.pgc" + char h_merch_id [ 16 ] ; + +#line 53 "app/online/lg_ol_0031.pgc" + char h_biz_date [ 9 ] ; + +#line 54 "app/online/lg_ol_0031.pgc" + char h_status_m [ 2 ] ; + +#line 55 "app/online/lg_ol_0031.pgc" + long h_cnt ; + +#line 56 "app/online/lg_ol_0031.pgc" + long h_sum ; +/* exec sql end declare section */ +#line 57 "app/online/lg_ol_0031.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0031] 원장 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + char merch[16], bizdate[16]; + char wonbuf[32]; + long avg; + + tx_log(TX_LOG_INFO, "[LG_OL_0031] 승인원장 요약조회 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { tx_log(TX_LOG_ERROR, "[LG_OL_0031] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0031] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (merch[0] == '\0' || !date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[LG_OL_0031] 입력 검증 실패 merch=%s date=%s", merch, bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status_m, LG_ST_DONE, sizeof(h_status_m) - 1); + h_status_m[sizeof(h_status_m) - 1] = '\0'; + + /* 가맹점+영업일+승인완료 단일 집계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) from lg_ledger where merch_id = $1 and biz_date = $2 and status = $3 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status_m),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 94 "app/online/lg_ol_0031.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0031 summary"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0031] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + if (h_cnt == 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0031] 승인건 없음 merch=%s date=%s", merch, bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_buf_setlong(ctx->out, "CNT", 0); + tx_buf_setlong(ctx->out, "SUM", 0); + tx_buf_setlong(ctx->out, "AVG", 0); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("LG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[LG_OL_0031] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (lg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 평균 = 합계 / 건수 (정수 나눗셈) */ + avg = h_sum / h_cnt; + amount_format_won(avg, wonbuf, sizeof(wonbuf)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[LG_OL_0031] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_setlong(ctx->out, "SUM", h_sum); + tx_buf_setlong(ctx->out, "AVG", avg); + tx_buf_sets(ctx->out, "AVGWON", wonbuf); + tx_log(TX_LOG_INFO, "[LG_OL_0031] 요약조회 merch=%s date=%s 건수=%ld 평균=%ld", + merch, bizdate, h_cnt, avg); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0031.pgc b/app/online/lg_ol_0031.pgc index cc74a22..8c9509e 100644 --- a/app/online/lg_ol_0031.pgc +++ b/app/online/lg_ol_0031.pgc @@ -2,97 +2,128 @@ /* * lg_ol_0031.pgc - 원장 온라인 서비스 (LG_OL_0031) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인원장 요약조회: 가맹점(MERCHID)+영업일(BIZDATE) 조합의 처리완료(M) + * 승인건에 대한 건수/합계/평균(정수 나눗셈)을 단일 SELECT 로 조회 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" EXEC SQL INCLUDE sqlca; +/* + * [업무 개요] + * 가맹점 포털/정산 조회 화면에서 "가맹점 + 영업일" 조합의 승인 완료건 + * 요약(건수/합계/평균)을 단일 호출로 제공한다. 평균은 실수 대신 정수 + * 나눗셈(원 단위 절사)으로 산출하여 회계 표기와 일치시킨다. + * + * [집계 대상] + * status = 'M'(처리완료) 인 승인 전표만 포함. 접수(R)/취소(U)/정산(S) 는 + * 승인원장 요약에서 제외한다. + * + * [평균 산식] + * AVG = SUM / CNT (정수 나눗셈; CNT=0 이면 미등록으로 별도 처리) + * + * [처리 절차] + * 1) MERCHID, BIZDATE 추출 및 검증. + * 2) 3개 조건(merch_id, biz_date, status='M') 단일 집계 SELECT. + * 3) 건수 0 이면 RESP_NOMERCH. + * 4) 평균 산출 및 원화 표기 구성 후 응답. + * + * [응답 필드] + * RESPCODE 처리결과코드 + * MERCHID 가맹점번호(에코) + * BIZDATE 영업일(에코) + * CNT 승인 완료 건수 + * SUM 승인 합계금액 + * AVG 건당 평균(정수) + * AVGWON 평균 원화표기 + * + * [주의] + * 조회 전용. 집계는 항상 1행이므로 singleton SELECT INTO 를 사용한다. + */ TX_SERVICE(LG_OL_0031, ctx) { - lg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_biz_date[9]; + char h_status_m[2]; + long h_cnt; + long h_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0031] 원장 트랜잭션 오케스트레이션 진입"); + char merch[16], bizdate[16]; + char wonbuf[32]; + long avg; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_log(TX_LOG_INFO, "[LG_OL_0031] 승인원장 요약조회 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { tx_log(TX_LOG_ERROR, "[LG_OL_0031] 필수 필드 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[LG_OL_0031] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (merch[0] == '\0' || !date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[LG_OL_0031] 입력 검증 실패 merch=%s date=%s", merch, bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status_m, LG_ST_DONE, sizeof(h_status_m) - 1); + h_status_m[sizeof(h_status_m) - 1] = '\0'; + + /* 가맹점+영업일+승인완료 단일 집계 */ + EXEC SQL SELECT count(*), coalesce(sum(amount), 0) + INTO :h_cnt, :h_sum + FROM lg_ledger + WHERE merch_id = :h_merch_id + AND biz_date = :h_biz_date + AND status = :h_status_m; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0031 summary"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, LG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = lg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0031] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + if (h_cnt == 0) { + tx_log(TX_LOG_WARN, "[LG_OL_0031] 승인건 없음 merch=%s date=%s", merch, bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_NOMERCH); + tx_buf_setlong(ctx->out, "CNT", 0); + tx_buf_setlong(ctx->out, "SUM", 0); + tx_buf_setlong(ctx->out, "AVG", 0); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("LG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[LG_OL_0031] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (lg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 평균 = 합계 / 건수 (정수 나눗셈) */ + avg = h_sum / h_cnt; + amount_format_won(avg, wonbuf, sizeof(wonbuf)); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[LG_OL_0031] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_setlong(ctx->out, "SUM", h_sum); + tx_buf_setlong(ctx->out, "AVG", avg); + tx_buf_sets(ctx->out, "AVGWON", wonbuf); + tx_log(TX_LOG_INFO, "[LG_OL_0031] 요약조회 merch=%s date=%s 건수=%ld 평균=%ld", + merch, bizdate, h_cnt, avg); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0032.c b/app/online/lg_ol_0032.c index 7904da3..95112d1 100644 --- a/app/online/lg_ol_0032.c +++ b/app/online/lg_ol_0032.c @@ -10,13 +10,14 @@ /* * lg_ol_0032.pgc - 원장 온라인 서비스 (LG_OL_0032) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산원장 대사: 지정 영업일의 정산완료(S) Σamount 와 처리완료(M) Σamount 를 + * 비교하여 미정산 차액(=M합계, 아직 정산되지 않은 금액)을 응답한다. (조회 전용) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" @@ -89,37 +90,143 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/lg_ol_0032.pgc" +#line 16 "app/online/lg_ol_0032.pgc" +/* + * [업무 개요] + * 정산 마감 전, 특정 영업일 원장에서 이미 정산확정(S) 된 금액과 아직 + * 정산되지 않은 처리완료(M) 금액을 대사(對査) 하여, 당일 정산 진행 상태를 + * 점검한다. 미정산 금액이 0 이면 당일 정산이 완결된 것으로 본다. + * + * [대사 산식] + * SETTLEDSUM = Σ amount (status='S') 정산확정 합계 + * DONESUM = Σ amount (status='M') 미정산(처리완료) 합계 + * UNSETTLED = DONESUM 아직 정산 안 된 금액 + * BALANCED = (UNSETTLED == 0) ? "Y" : "N" + * + * [처리 절차] + * 1) BIZDATE 추출 및 형식 검증. + * 2) 정산확정(S) 합계 집계 SELECT. + * 3) 처리완료(M) 합계 집계 SELECT. + * 4) 미정산 차액과 균형 여부를 응답. + * + * [응답 필드] + * RESPCODE 처리결과코드 + * BIZDATE 대상 영업일(에코) + * SETTLEDSUM 정산확정(S) 합계 + * DONESUM 처리완료(M) 합계 + * UNSETTLED 미정산 차액 + * UNSETTLEDWON 미정산 원화표기 + * BALANCED 정산완결 여부 Y/N + * + * [주의] + * 조회/대사 전용. 원장을 변경하지 않으며 집계 결과는 coalesce 로 0 보장. + */ TX_SERVICE(LG_OL_0032, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 51 "app/online/lg_ol_0032.pgc" + char h_biz_date [ 9 ] ; + +#line 52 "app/online/lg_ol_0032.pgc" + char h_status_s [ 2 ] ; + +#line 53 "app/online/lg_ol_0032.pgc" + char h_status_m [ 2 ] ; + +#line 54 "app/online/lg_ol_0032.pgc" + long h_settled_sum ; + +#line 55 "app/online/lg_ol_0032.pgc" + long h_done_sum ; +/* exec sql end declare section */ +#line 56 "app/online/lg_ol_0032.pgc" - tx_log(TX_LOG_INFO, "[LG_OL_0032] 원장 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0032] KEY 누락"); + char bizdate[16]; + char wonbuf[32]; + long unsettled; + + tx_log(TX_LOG_INFO, "[LG_OL_0032] 정산원장 대사 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0032] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[LG_OL_0032] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status_s, LG_ST_SETTLED, sizeof(h_status_s) - 1); + h_status_s[sizeof(h_status_s) - 1] = '\0'; + strncpy(h_status_m, LG_ST_DONE, sizeof(h_status_m) - 1); + h_status_m[sizeof(h_status_m) - 1] = '\0'; + + /* 정산완료(S) 합계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from lg_ledger where biz_date = $1 and status = $2 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status_s),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settled_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 91 "app/online/lg_ol_0032.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0032 settled"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 미정산(처리완료 M) 합계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from lg_ledger where biz_date = $1 and status = $2 ", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status_m),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_done_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 105 "app/online/lg_ol_0032.pgc" + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0032 done"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 미정산 차액 = 아직 정산확정되지 않은 처리완료 금액 */ + unsettled = h_done_sum; + amount_format_won(unsettled, wonbuf, sizeof(wonbuf)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SETTLEDSUM", h_settled_sum); + tx_buf_setlong(ctx->out, "DONESUM", h_done_sum); + tx_buf_setlong(ctx->out, "UNSETTLED", unsettled); + tx_buf_sets(ctx->out, "UNSETTLEDWON", wonbuf); + tx_buf_sets(ctx->out, "BALANCED", (unsettled == 0) ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[LG_OL_0032] 정산대사 date=%s 정산=%ld 미정산=%ld", + bizdate, h_settled_sum, unsettled); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/lg_ol_0032.pgc b/app/online/lg_ol_0032.pgc index 27d0af1..218a398 100644 --- a/app/online/lg_ol_0032.pgc +++ b/app/online/lg_ol_0032.pgc @@ -2,46 +2,128 @@ /* * lg_ol_0032.pgc - 원장 온라인 서비스 (LG_OL_0032) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 lg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산원장 대사: 지정 영업일의 정산완료(S) Σamount 와 처리완료(M) Σamount 를 + * 비교하여 미정산 차액(=M합계, 아직 정산되지 않은 금액)을 응답한다. (조회 전용) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "lg_core.h" EXEC SQL INCLUDE sqlca; +/* + * [업무 개요] + * 정산 마감 전, 특정 영업일 원장에서 이미 정산확정(S) 된 금액과 아직 + * 정산되지 않은 처리완료(M) 금액을 대사(對査) 하여, 당일 정산 진행 상태를 + * 점검한다. 미정산 금액이 0 이면 당일 정산이 완결된 것으로 본다. + * + * [대사 산식] + * SETTLEDSUM = Σ amount (status='S') 정산확정 합계 + * DONESUM = Σ amount (status='M') 미정산(처리완료) 합계 + * UNSETTLED = DONESUM 아직 정산 안 된 금액 + * BALANCED = (UNSETTLED == 0) ? "Y" : "N" + * + * [처리 절차] + * 1) BIZDATE 추출 및 형식 검증. + * 2) 정산확정(S) 합계 집계 SELECT. + * 3) 처리완료(M) 합계 집계 SELECT. + * 4) 미정산 차액과 균형 여부를 응답. + * + * [응답 필드] + * RESPCODE 처리결과코드 + * BIZDATE 대상 영업일(에코) + * SETTLEDSUM 정산확정(S) 합계 + * DONESUM 처리완료(M) 합계 + * UNSETTLED 미정산 차액 + * UNSETTLEDWON 미정산 원화표기 + * BALANCED 정산완결 여부 Y/N + * + * [주의] + * 조회/대사 전용. 원장을 변경하지 않으며 집계 결과는 coalesce 로 0 보장. + */ TX_SERVICE(LG_OL_0032, ctx) { - lg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_status_s[2]; + char h_status_m[2]; + long h_settled_sum; + long h_done_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[LG_OL_0032] 원장 단건조회 서비스 진입"); + char bizdate[16]; + char wonbuf[32]; + long unsettled; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[LG_OL_0032] KEY 누락"); + tx_log(TX_LOG_INFO, "[LG_OL_0032] 정산원장 대사 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[LG_OL_0032] BIZDATE 누락"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[LG_OL_0032] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = lg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[LG_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + strncpy(h_status_s, LG_ST_SETTLED, sizeof(h_status_s) - 1); + h_status_s[sizeof(h_status_s) - 1] = '\0'; + strncpy(h_status_m, LG_ST_DONE, sizeof(h_status_m) - 1); + h_status_m[sizeof(h_status_m) - 1] = '\0'; + + /* 정산완료(S) 합계 */ + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_settled_sum + FROM lg_ledger + WHERE biz_date = :h_biz_date + AND status = :h_status_s; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0032 settled"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 미정산(처리완료 M) 합계 */ + EXEC SQL SELECT coalesce(sum(amount), 0) + INTO :h_done_sum + FROM lg_ledger + WHERE biz_date = :h_biz_date + AND status = :h_status_m; + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("lg_ol_0032 done"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 미정산 차액 = 아직 정산확정되지 않은 처리완료 금액 */ + unsettled = h_done_sum; + amount_format_won(unsettled, wonbuf, sizeof(wonbuf)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[LG_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SETTLEDSUM", h_settled_sum); + tx_buf_setlong(ctx->out, "DONESUM", h_done_sum); + tx_buf_setlong(ctx->out, "UNSETTLED", unsettled); + tx_buf_sets(ctx->out, "UNSETTLEDWON", wonbuf); + tx_buf_sets(ctx->out, "BALANCED", (unsettled == 0) ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[LG_OL_0032] 정산대사 date=%s 정산=%ld 미정산=%ld", + bizdate, h_settled_sum, unsettled); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0001.c b/app/online/mg_ol_0001.c index 3a55ad9..92ab7cf 100644 --- a/app/online/mg_ol_0001.c +++ b/app/online/mg_ol_0001.c @@ -8,10 +8,11 @@ #line 1 "app/online/mg_ol_0001.pgc" /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0001.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0001) [tier=easy] + * mg_ol_0001.pgc - 전문게이트웨이 전문 수신 서비스 (MG_OL_0001) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채널로부터 수신한 고정길이 원시전문(0200)을 acq_msg_t 로 언팩/검증한 + * 뒤, 전문게이트웨이 원장(mg_ledger)에 접수상태('R')로 적재한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -89,37 +90,74 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0001.pgc" +#line 16 "app/online/mg_ol_0001.pgc" TX_SERVICE(MG_OL_0001, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char raw[256]; + int rawlen; + acq_msg_t msg; + mg_rec_t rec; + long amount; + int rc; - tx_log(TX_LOG_INFO, "[MG_OL_0001] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0001] 전문 수신 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0001] KEY 누락"); + /* 1) 원시전문 수신 (고정길이 프레임) */ + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen <= 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0001] 원시전문(RAWMSG) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2) 고정길이 언팩 */ + memset(&msg, 0, sizeof(msg)); + if (msg_unpack(&msg, raw, (int)strlen(raw)) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0001] 전문 언팩 실패 len=%d", (int)strlen(raw)); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 3) 전문 검증 (전문종별/필수필드) */ + if (msg_validate(&msg) != 0 || + strncmp(msg.msg_type, MSG_TYPE_RECV, FLD_MSG_TYPE_LEN) != 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0001] 전문 검증 실패 type=%.4s", msg.msg_type); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 4) 구조체 -> 원장 레코드 매핑 */ + amount = amount_parse(msg.amount, FLD_AMOUNT_LEN); memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); + msg_field_copy(rec.key, msg.approval_no, FLD_APPROVAL_LEN); + msg_field_copy(rec.merch_id, msg.merch_id, FLD_MERCH_ID_LEN); + msg_field_copy(rec.card_no, msg.card_no, FLD_CARD_NO_LEN); + msg_field_copy(rec.biz_date, msg.txn_date, FLD_TXN_DATE_LEN); + rec.amount = amount; + rec.fee = 0; + strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); + + /* 5) 원장 적재 */ + rc = mg_rec_insert(&rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0001] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_ERROR, "[MG_OL_0001] 원장 적재 실패 key=%s rc=%d(%s)", + rec.key, rc, tx_strerror(rc)); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); tx_return(ctx, rc, ctx->out); return; } + /* 6) 수신확인 응답 구성 */ tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_OK); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0001] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_log(TX_LOG_INFO, "[MG_OL_0001] 전문 수신완료 key=%s amount=%ld", rec.key, rec.amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0001.pgc b/app/online/mg_ol_0001.pgc index 85449ba..ff4d27e 100644 --- a/app/online/mg_ol_0001.pgc +++ b/app/online/mg_ol_0001.pgc @@ -1,9 +1,10 @@ /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0001.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0001) [tier=easy] + * mg_ol_0001.pgc - 전문게이트웨이 전문 수신 서비스 (MG_OL_0001) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채널로부터 수신한 고정길이 원시전문(0200)을 acq_msg_t 로 언팩/검증한 + * 뒤, 전문게이트웨이 원장(mg_ledger)에 접수상태('R')로 적재한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -16,32 +17,69 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MG_OL_0001, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char raw[256]; + int rawlen; + acq_msg_t msg; + mg_rec_t rec; + long amount; + int rc; - tx_log(TX_LOG_INFO, "[MG_OL_0001] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0001] 전문 수신 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0001] KEY 누락"); + /* 1) 원시전문 수신 (고정길이 프레임) */ + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen <= 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0001] 원시전문(RAWMSG) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2) 고정길이 언팩 */ + memset(&msg, 0, sizeof(msg)); + if (msg_unpack(&msg, raw, (int)strlen(raw)) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0001] 전문 언팩 실패 len=%d", (int)strlen(raw)); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 3) 전문 검증 (전문종별/필수필드) */ + if (msg_validate(&msg) != 0 || + strncmp(msg.msg_type, MSG_TYPE_RECV, FLD_MSG_TYPE_LEN) != 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0001] 전문 검증 실패 type=%.4s", msg.msg_type); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 4) 구조체 -> 원장 레코드 매핑 */ + amount = amount_parse(msg.amount, FLD_AMOUNT_LEN); memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); + msg_field_copy(rec.key, msg.approval_no, FLD_APPROVAL_LEN); + msg_field_copy(rec.merch_id, msg.merch_id, FLD_MERCH_ID_LEN); + msg_field_copy(rec.card_no, msg.card_no, FLD_CARD_NO_LEN); + msg_field_copy(rec.biz_date, msg.txn_date, FLD_TXN_DATE_LEN); + rec.amount = amount; + rec.fee = 0; + strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); + + /* 5) 원장 적재 */ + rc = mg_rec_insert(&rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0001] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_ERROR, "[MG_OL_0001] 원장 적재 실패 key=%s rc=%d(%s)", + rec.key, rc, tx_strerror(rc)); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); tx_return(ctx, rc, ctx->out); return; } + /* 6) 수신확인 응답 구성 */ tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_OK); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0001] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_log(TX_LOG_INFO, "[MG_OL_0001] 전문 수신완료 key=%s amount=%ld", rec.key, rec.amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0002.c b/app/online/mg_ol_0002.c index 4abc987..9a97bbd 100644 --- a/app/online/mg_ol_0002.c +++ b/app/online/mg_ol_0002.c @@ -6,12 +6,13 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0002.pgc" -/* @tier medium @module mg @transform msg-gateway-online */ +/* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0002.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0002) [tier=medium] + * mg_ol_0002.pgc - 전문게이트웨이 응답전문 송신 서비스 (MG_OL_0002) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리키(KEY)로 원장(mg_ledger)을 조회하여 원거래 정보를 확보한 뒤, + * 응답전문(0210)을 acq_msg_t 로 구성하고 msg_pack 으로 고정길이 프레임을 + * 생성해 채널로 회신할 RAWMSG 를 반환한다. */ #include #include @@ -89,60 +90,87 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0002.pgc" +#line 16 "app/online/mg_ol_0002.pgc" TX_SERVICE(MG_OL_0002, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char key[16]; + char amtfld[FLD_AMOUNT_LEN + 1]; + char raw[MSG_ACQ_LEN + 8]; + acq_msg_t msg; + mg_rec_t rec; + int rc; - tx_log(TX_LOG_INFO, "[MG_OL_0002] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0002] 응답전문 송신 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0002] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0002] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0002] 영업일 오류 date=%s", bizdate); + /* 1) 송신 대상 처리키 수신 */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { + tx_log(TX_LOG_ERROR, "[MG_OL_0002] 처리키(KEY) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2) 원장에서 원거래 조회 */ memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); + rc = mg_rec_select(key, &rec); + if (rc == TX_ENOENT) { + tx_log(TX_LOG_WARN, "[MG_OL_0002] 원장 미존재 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0002] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_ERROR, "[MG_OL_0002] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); + /* 3) 응답전문(0210) 필드 구성 - 고정길이 규칙에 맞춰 공백/영채움 */ + memset(&msg, 0, sizeof(msg)); + msg_field_set(msg.msg_type, MSG_TYPE_RESP, FLD_MSG_TYPE_LEN); + msg_field_set(msg.trans_code, "MGSEND", FLD_TRANS_CODE_LEN); + msg_field_set(msg.merch_id, rec.merch_id, FLD_MERCH_ID_LEN); + msg_field_set(msg.card_no, rec.card_no, FLD_CARD_NO_LEN); + msg_field_set(msg.approval_no, rec.key, FLD_APPROVAL_LEN); + msg_field_set(msg.txn_date, rec.biz_date, FLD_TXN_DATE_LEN); + msg_field_set(msg.txn_time, "000000", FLD_TXN_TIME_LEN); + msg_field_set(msg.inst_month, "00", FLD_INST_LEN); + /* 금액은 우측정렬 zero-fill 숫자 필드 */ + if (amount_format(rec.amount, amtfld, FLD_AMOUNT_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0002] 금액 포맷 실패 amount=%ld", rec.amount); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + memcpy(msg.amount, amtfld, FLD_AMOUNT_LEN); + + /* 4) 원장 상태코드 -> 응답코드 매핑 */ + if (strncmp(rec.status, MG_ST_DONE, 1) == 0 || + strncmp(rec.status, MG_ST_SETTLED, 1) == 0) + msg_field_set(msg.resp_code, RESP_OK, FLD_RESP_LEN); + else if (strncmp(rec.status, MG_ST_FAIL, 1) == 0) + msg_field_set(msg.resp_code, RESP_INVALID, FLD_RESP_LEN); + else + msg_field_set(msg.resp_code, RESP_SYSERR, FLD_RESP_LEN); + + /* 5) 고정길이 프레임으로 팩 */ + if (msg_pack(&msg, raw, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0002] 전문 팩 실패 key=%s", key); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 6) RAWMSG(고정길이) 및 요약필드 반환 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0002] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_set(ctx->out, "RAWMSG", raw, MSG_ACQ_LEN); + tx_buf_setlong(ctx->out, "RAWLEN", (long)MSG_ACQ_LEN); + tx_buf_sets(ctx->out, "MSGTYPE", MSG_TYPE_RESP); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_log(TX_LOG_INFO, "[MG_OL_0002] 응답전문 송신완료 key=%s len=%d amount=%ld", + rec.key, MSG_ACQ_LEN, rec.amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0002.pgc b/app/online/mg_ol_0002.pgc index a9cfda7..6b32256 100644 --- a/app/online/mg_ol_0002.pgc +++ b/app/online/mg_ol_0002.pgc @@ -1,9 +1,10 @@ -/* @tier medium @module mg @transform msg-gateway-online */ +/* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0002.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0002) [tier=medium] + * mg_ol_0002.pgc - 전문게이트웨이 응답전문 송신 서비스 (MG_OL_0002) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리키(KEY)로 원장(mg_ledger)을 조회하여 원거래 정보를 확보한 뒤, + * 응답전문(0210)을 acq_msg_t 로 구성하고 msg_pack 으로 고정길이 프레임을 + * 생성해 채널로 회신할 RAWMSG 를 반환한다. */ #include #include @@ -16,55 +17,82 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MG_OL_0002, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char key[16]; + char amtfld[FLD_AMOUNT_LEN + 1]; + char raw[MSG_ACQ_LEN + 8]; + acq_msg_t msg; + mg_rec_t rec; + int rc; - tx_log(TX_LOG_INFO, "[MG_OL_0002] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0002] 응답전문 송신 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0002] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0002] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0002] 영업일 오류 date=%s", bizdate); + /* 1) 송신 대상 처리키 수신 */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { + tx_log(TX_LOG_ERROR, "[MG_OL_0002] 처리키(KEY) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2) 원장에서 원거래 조회 */ memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); + rc = mg_rec_select(key, &rec); + if (rc == TX_ENOENT) { + tx_log(TX_LOG_WARN, "[MG_OL_0002] 원장 미존재 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0002] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_ERROR, "[MG_OL_0002] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); + /* 3) 응답전문(0210) 필드 구성 - 고정길이 규칙에 맞춰 공백/영채움 */ + memset(&msg, 0, sizeof(msg)); + msg_field_set(msg.msg_type, MSG_TYPE_RESP, FLD_MSG_TYPE_LEN); + msg_field_set(msg.trans_code, "MGSEND", FLD_TRANS_CODE_LEN); + msg_field_set(msg.merch_id, rec.merch_id, FLD_MERCH_ID_LEN); + msg_field_set(msg.card_no, rec.card_no, FLD_CARD_NO_LEN); + msg_field_set(msg.approval_no, rec.key, FLD_APPROVAL_LEN); + msg_field_set(msg.txn_date, rec.biz_date, FLD_TXN_DATE_LEN); + msg_field_set(msg.txn_time, "000000", FLD_TXN_TIME_LEN); + msg_field_set(msg.inst_month, "00", FLD_INST_LEN); + /* 금액은 우측정렬 zero-fill 숫자 필드 */ + if (amount_format(rec.amount, amtfld, FLD_AMOUNT_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0002] 금액 포맷 실패 amount=%ld", rec.amount); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + memcpy(msg.amount, amtfld, FLD_AMOUNT_LEN); + + /* 4) 원장 상태코드 -> 응답코드 매핑 */ + if (strncmp(rec.status, MG_ST_DONE, 1) == 0 || + strncmp(rec.status, MG_ST_SETTLED, 1) == 0) + msg_field_set(msg.resp_code, RESP_OK, FLD_RESP_LEN); + else if (strncmp(rec.status, MG_ST_FAIL, 1) == 0) + msg_field_set(msg.resp_code, RESP_INVALID, FLD_RESP_LEN); + else + msg_field_set(msg.resp_code, RESP_SYSERR, FLD_RESP_LEN); + + /* 5) 고정길이 프레임으로 팩 */ + if (msg_pack(&msg, raw, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0002] 전문 팩 실패 key=%s", key); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 6) RAWMSG(고정길이) 및 요약필드 반환 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0002] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_set(ctx->out, "RAWMSG", raw, MSG_ACQ_LEN); + tx_buf_setlong(ctx->out, "RAWLEN", (long)MSG_ACQ_LEN); + tx_buf_sets(ctx->out, "MSGTYPE", MSG_TYPE_RESP); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_log(TX_LOG_INFO, "[MG_OL_0002] 응답전문 송신완료 key=%s len=%d amount=%ld", + rec.key, MSG_ACQ_LEN, rec.amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0003.c b/app/online/mg_ol_0003.c index a822efe..d94cf61 100644 --- a/app/online/mg_ol_0003.c +++ b/app/online/mg_ol_0003.c @@ -8,10 +8,11 @@ #line 1 "app/online/mg_ol_0003.pgc" /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0003.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0003) [tier=easy] + * mg_ol_0003.pgc - 그리드↔구조체 매핑 서비스 (MG_OL_0003) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채널이 올린 입력 그리드(FLDCNT 개의 FLD0..FLDn)를 정해진 컬럼 순서 + * 규약에 따라 mg_rec_t 원장 레코드로 매핑한다. 필드 개수/순서/필수값을 + * 검증하고 매핑 결과를 tx_buf 로 응답한다. */ #include #include @@ -89,37 +90,102 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0003.pgc" +#line 16 "app/online/mg_ol_0003.pgc" +/* 그리드 컬럼 순서 규약: 0=처리키 1=가맹점 2=카드 3=금액 4=영업일 */ +#define MG3_COL_KEY 0 +#define MG3_COL_MERCH 1 +#define MG3_COL_CARD 2 +#define MG3_COL_AMOUNT 3 +#define MG3_COL_BIZDATE 4 +#define MG3_COL_REQUIRED 5 + TX_SERVICE(MG_OL_0003, ctx) { + char fldkey[16]; + char cell[64]; mg_rec_t rec; - char key[16]; - int rc; + long fldcnt = 0; + long amount; + int i; - tx_log(TX_LOG_INFO, "[MG_OL_0003] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0003] 그리드 매핑 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0003] KEY 누락"); + /* 1) 그리드 필드 개수 확인 */ + if (tx_buf_getlong(ctx->in, "FLDCNT", &fldcnt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0003] 필드개수(FLDCNT) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (fldcnt < MG3_COL_REQUIRED) { + tx_log(TX_LOG_WARN, "[MG_OL_0003] 필수 컬럼 부족 fldcnt=%ld(min=%d)", + fldcnt, MG3_COL_REQUIRED); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_buf_setlong(ctx->out, "FLDCNT", fldcnt); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2) 순서대로 셀을 뽑아 구조체 슬롯에 매핑 */ memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + amount = 0; + for (i = 0; i < MG3_COL_REQUIRED; i++) { + snprintf(fldkey, sizeof(fldkey), "FLD%d", i); + if (tx_buf_get(ctx->in, fldkey, cell, sizeof(cell)) != TX_OK || cell[0] == '\0') { + tx_log(TX_LOG_WARN, "[MG_OL_0003] 그리드 셀 누락/공백 col=%d key=%s", i, fldkey); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_buf_setlong(ctx->out, "BADCOL", (long)i); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + switch (i) { + case MG3_COL_KEY: + strncpy(rec.key, cell, sizeof(rec.key) - 1); + break; + case MG3_COL_MERCH: + strncpy(rec.merch_id, cell, sizeof(rec.merch_id) - 1); + break; + case MG3_COL_CARD: + strncpy(rec.card_no, cell, sizeof(rec.card_no) - 1); + break; + case MG3_COL_AMOUNT: + amount = amount_parse(cell, (int)strlen(cell)); + break; + case MG3_COL_BIZDATE: + strncpy(rec.biz_date, cell, sizeof(rec.biz_date) - 1); + break; + default: + break; + } + } + rec.amount = amount; + rec.fee = 0; + strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); + + /* 3) 매핑 후 도메인 검증 */ + if (!amount_is_valid(rec.amount) || !date_is_valid(rec.biz_date)) { + tx_log(TX_LOG_WARN, "[MG_OL_0003] 매핑값 검증 실패 amount=%ld date=%s", + rec.amount, rec.biz_date); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 4) 매핑 결과 응답 (원장 적재는 별도 서비스 담당) */ tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_OK); tx_buf_sets(ctx->out, "KEY", rec.key); tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_sets(ctx->out, "CARDNO", rec.card_no); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", rec.biz_date); + tx_buf_setlong(ctx->out, "MAPPED", (long)MG3_COL_REQUIRED); + tx_log(TX_LOG_INFO, "[MG_OL_0003] 그리드 매핑완료 key=%s cols=%d amount=%ld", + rec.key, MG3_COL_REQUIRED, rec.amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0003.pgc b/app/online/mg_ol_0003.pgc index 7aa8b1f..0cecf25 100644 --- a/app/online/mg_ol_0003.pgc +++ b/app/online/mg_ol_0003.pgc @@ -1,9 +1,10 @@ /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0003.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0003) [tier=easy] + * mg_ol_0003.pgc - 그리드↔구조체 매핑 서비스 (MG_OL_0003) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채널이 올린 입력 그리드(FLDCNT 개의 FLD0..FLDn)를 정해진 컬럼 순서 + * 규약에 따라 mg_rec_t 원장 레코드로 매핑한다. 필드 개수/순서/필수값을 + * 검증하고 매핑 결과를 tx_buf 로 응답한다. */ #include #include @@ -14,34 +15,99 @@ EXEC SQL INCLUDE sqlca; +/* 그리드 컬럼 순서 규약: 0=처리키 1=가맹점 2=카드 3=금액 4=영업일 */ +#define MG3_COL_KEY 0 +#define MG3_COL_MERCH 1 +#define MG3_COL_CARD 2 +#define MG3_COL_AMOUNT 3 +#define MG3_COL_BIZDATE 4 +#define MG3_COL_REQUIRED 5 + TX_SERVICE(MG_OL_0003, ctx) { + char fldkey[16]; + char cell[64]; mg_rec_t rec; - char key[16]; - int rc; + long fldcnt = 0; + long amount; + int i; - tx_log(TX_LOG_INFO, "[MG_OL_0003] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0003] 그리드 매핑 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0003] KEY 누락"); + /* 1) 그리드 필드 개수 확인 */ + if (tx_buf_getlong(ctx->in, "FLDCNT", &fldcnt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0003] 필드개수(FLDCNT) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (fldcnt < MG3_COL_REQUIRED) { + tx_log(TX_LOG_WARN, "[MG_OL_0003] 필수 컬럼 부족 fldcnt=%ld(min=%d)", + fldcnt, MG3_COL_REQUIRED); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_buf_setlong(ctx->out, "FLDCNT", fldcnt); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2) 순서대로 셀을 뽑아 구조체 슬롯에 매핑 */ memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + amount = 0; + for (i = 0; i < MG3_COL_REQUIRED; i++) { + snprintf(fldkey, sizeof(fldkey), "FLD%d", i); + if (tx_buf_get(ctx->in, fldkey, cell, sizeof(cell)) != TX_OK || cell[0] == '\0') { + tx_log(TX_LOG_WARN, "[MG_OL_0003] 그리드 셀 누락/공백 col=%d key=%s", i, fldkey); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_buf_setlong(ctx->out, "BADCOL", (long)i); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + switch (i) { + case MG3_COL_KEY: + strncpy(rec.key, cell, sizeof(rec.key) - 1); + break; + case MG3_COL_MERCH: + strncpy(rec.merch_id, cell, sizeof(rec.merch_id) - 1); + break; + case MG3_COL_CARD: + strncpy(rec.card_no, cell, sizeof(rec.card_no) - 1); + break; + case MG3_COL_AMOUNT: + amount = amount_parse(cell, (int)strlen(cell)); + break; + case MG3_COL_BIZDATE: + strncpy(rec.biz_date, cell, sizeof(rec.biz_date) - 1); + break; + default: + break; + } + } + rec.amount = amount; + rec.fee = 0; + strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); + + /* 3) 매핑 후 도메인 검증 */ + if (!amount_is_valid(rec.amount) || !date_is_valid(rec.biz_date)) { + tx_log(TX_LOG_WARN, "[MG_OL_0003] 매핑값 검증 실패 amount=%ld date=%s", + rec.amount, rec.biz_date); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 4) 매핑 결과 응답 (원장 적재는 별도 서비스 담당) */ tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_OK); tx_buf_sets(ctx->out, "KEY", rec.key); tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_sets(ctx->out, "CARDNO", rec.card_no); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", rec.biz_date); + tx_buf_setlong(ctx->out, "MAPPED", (long)MG3_COL_REQUIRED); + tx_log(TX_LOG_INFO, "[MG_OL_0003] 그리드 매핑완료 key=%s cols=%d amount=%ld", + rec.key, MG3_COL_REQUIRED, rec.amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0004.c b/app/online/mg_ol_0004.c index 1ce038d..3d0a3a6 100644 --- a/app/online/mg_ol_0004.c +++ b/app/online/mg_ol_0004.c @@ -6,12 +6,13 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0004.pgc" -/* @tier medium @module mg @transform msg-gateway-online */ +/* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0004.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0004) [tier=medium] + * mg_ol_0004.pgc - 채널 인증/세션 개설 서비스 (MG_OL_0004) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채널ID(CHANID)와 비밀키(SECRET)를 tx_buf 로 받아 간이 해시(FNV-1a 변형) + * 로 무결성을 검증하고, 통과 시 현재 트랜잭션ID(tx_current_xid)를 섞어 + * 세션토큰을 발급한다. 실패 시 인증오류 응답을 반환한다. */ #include #include @@ -89,60 +90,70 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0004.pgc" +#line 16 "app/online/mg_ol_0004.pgc" TX_SERVICE(MG_OL_0004, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char chanid[24]; + char secret[40]; + char expect[24]; + char token[40]; + unsigned long h; + unsigned long xid; + int i; - tx_log(TX_LOG_INFO, "[MG_OL_0004] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0004] 채널 인증/세션 개설 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0004] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0004] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0004] 영업일 오류 date=%s", bizdate); + /* 1) 인증 요소 수신 */ + if (tx_buf_get(ctx->in, "CHANID", chanid, sizeof(chanid)) != TX_OK || + tx_buf_get(ctx->in, "SECRET", secret, sizeof(secret)) != TX_OK || + chanid[0] == '\0' || secret[0] == '\0') { + tx_log(TX_LOG_ERROR, "[MG_OL_0004] 인증요소(CHANID/SECRET) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0004] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + /* 2) 채널ID+비밀키 결합에 대한 간이 해시 산출 (FNV-1a 변형) */ + h = 2166136261UL; + for (i = 0; chanid[i] != '\0'; i++) { + h ^= (unsigned char)chanid[i]; + h *= 16777619UL; + } + h ^= (unsigned char)':'; + h *= 16777619UL; + for (i = 0; secret[i] != '\0'; i++) { + h ^= (unsigned char)secret[i]; + h *= 16777619UL; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); + /* 3) 기대 인증값(EXPECT)이 함께 오면 대조, 아니면 산출값을 그대로 신뢰 */ + snprintf(token, sizeof(token), "%08lx", (h & 0xffffffffUL)); + if (tx_buf_get(ctx->in, "EXPECT", expect, sizeof(expect)) == TX_OK && + expect[0] != '\0') { + if (strncmp(expect, token, 8) != 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0004] 인증 불일치 chan=%s exp=%s calc=%s", + chanid, expect, token); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_buf_sets(ctx->out, "REASON", "AUTHFAIL"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + } + /* 4) 세션토큰 발급: 해시 상위워드 + 현재 XID 하위워드 결합 */ + xid = (unsigned long)tx_current_xid(); + snprintf(token, sizeof(token), "%s-%04lx%04lx", + chanid, (h >> 16) & 0xffffUL, xid & 0xffffUL); + + /* 5) 세션 개설 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0004] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "CHANID", chanid); + tx_buf_sets(ctx->out, "SESSION", token); + tx_buf_setlong(ctx->out, "XID", (long)xid); + tx_buf_setlong(ctx->out, "AUTHHASH", (long)(h & 0x7fffffffUL)); + tx_log(TX_LOG_INFO, "[MG_OL_0004] 세션 개설완료 chan=%s session=%s", chanid, token); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0004.pgc b/app/online/mg_ol_0004.pgc index 823b5d4..d8bf050 100644 --- a/app/online/mg_ol_0004.pgc +++ b/app/online/mg_ol_0004.pgc @@ -1,9 +1,10 @@ -/* @tier medium @module mg @transform msg-gateway-online */ +/* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0004.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0004) [tier=medium] + * mg_ol_0004.pgc - 채널 인증/세션 개설 서비스 (MG_OL_0004) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채널ID(CHANID)와 비밀키(SECRET)를 tx_buf 로 받아 간이 해시(FNV-1a 변형) + * 로 무결성을 검증하고, 통과 시 현재 트랜잭션ID(tx_current_xid)를 섞어 + * 세션토큰을 발급한다. 실패 시 인증오류 응답을 반환한다. */ #include #include @@ -16,55 +17,65 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MG_OL_0004, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char chanid[24]; + char secret[40]; + char expect[24]; + char token[40]; + unsigned long h; + unsigned long xid; + int i; - tx_log(TX_LOG_INFO, "[MG_OL_0004] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0004] 채널 인증/세션 개설 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0004] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0004] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0004] 영업일 오류 date=%s", bizdate); + /* 1) 인증 요소 수신 */ + if (tx_buf_get(ctx->in, "CHANID", chanid, sizeof(chanid)) != TX_OK || + tx_buf_get(ctx->in, "SECRET", secret, sizeof(secret)) != TX_OK || + chanid[0] == '\0' || secret[0] == '\0') { + tx_log(TX_LOG_ERROR, "[MG_OL_0004] 인증요소(CHANID/SECRET) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0004] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + /* 2) 채널ID+비밀키 결합에 대한 간이 해시 산출 (FNV-1a 변형) */ + h = 2166136261UL; + for (i = 0; chanid[i] != '\0'; i++) { + h ^= (unsigned char)chanid[i]; + h *= 16777619UL; + } + h ^= (unsigned char)':'; + h *= 16777619UL; + for (i = 0; secret[i] != '\0'; i++) { + h ^= (unsigned char)secret[i]; + h *= 16777619UL; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); + /* 3) 기대 인증값(EXPECT)이 함께 오면 대조, 아니면 산출값을 그대로 신뢰 */ + snprintf(token, sizeof(token), "%08lx", (h & 0xffffffffUL)); + if (tx_buf_get(ctx->in, "EXPECT", expect, sizeof(expect)) == TX_OK && + expect[0] != '\0') { + if (strncmp(expect, token, 8) != 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0004] 인증 불일치 chan=%s exp=%s calc=%s", + chanid, expect, token); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_buf_sets(ctx->out, "REASON", "AUTHFAIL"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + } + /* 4) 세션토큰 발급: 해시 상위워드 + 현재 XID 하위워드 결합 */ + xid = (unsigned long)tx_current_xid(); + snprintf(token, sizeof(token), "%s-%04lx%04lx", + chanid, (h >> 16) & 0xffffUL, xid & 0xffffUL); + + /* 5) 세션 개설 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0004] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "CHANID", chanid); + tx_buf_sets(ctx->out, "SESSION", token); + tx_buf_setlong(ctx->out, "XID", (long)xid); + tx_buf_setlong(ctx->out, "AUTHHASH", (long)(h & 0x7fffffffUL)); + tx_log(TX_LOG_INFO, "[MG_OL_0004] 세션 개설완료 chan=%s session=%s", chanid, token); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0005.c b/app/online/mg_ol_0005.c index 6589cde..6222db0 100644 --- a/app/online/mg_ol_0005.c +++ b/app/online/mg_ol_0005.c @@ -6,17 +6,19 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0005.pgc" -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0005.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0005) [tier=easy] + * mg_ol_0005.pgc - 전문 로깅/추적 서비스 (MG_OL_0005) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 거래추적번호(STAN)/채널(CHAN)/전문종별(MSGTYPE)로 추적로그 항목을 + * 구성하여 tx_log 로 기록하고, 추적테이블(mg_stat)에 EXEC SQL 로 적재한다. + * 채널+STAN 조합으로 추적키를 만들어 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mg_core.h" @@ -89,37 +91,97 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0005.pgc" +#line 17 "app/online/mg_ol_0005.pgc" TX_SERVICE(MG_OL_0005, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 22 "app/online/mg_ol_0005.pgc" + char h_trkey [ 32 ] ; + +#line 23 "app/online/mg_ol_0005.pgc" + char h_chan [ 16 ] ; + +#line 24 "app/online/mg_ol_0005.pgc" + char h_stan [ 12 ] ; + +#line 25 "app/online/mg_ol_0005.pgc" + char h_mtype [ 8 ] ; + +#line 26 "app/online/mg_ol_0005.pgc" + long h_xid ; +/* exec sql end declare section */ +#line 27 "app/online/mg_ol_0005.pgc" - tx_log(TX_LOG_INFO, "[MG_OL_0005] 전문게이트웨이 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0005] KEY 누락"); + char chan[16]; + char stan[12]; + char mtype[8]; + char trkey[32]; + + tx_log(TX_LOG_INFO, "[MG_OL_0005] 전문 추적로그 서비스 진입"); + + /* 1) 추적 3요소 수신 */ + if (tx_buf_get(ctx->in, "CHAN", chan, sizeof(chan)) != TX_OK || + tx_buf_get(ctx->in, "STAN", stan, sizeof(stan)) != TX_OK || + tx_buf_get(ctx->in, "MSGTYPE", mtype, sizeof(mtype)) != TX_OK || + chan[0] == '\0' || stan[0] == '\0') { + tx_log(TX_LOG_ERROR, "[MG_OL_0005] 추적요소(CHAN/STAN/MSGTYPE) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0005] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 추적키 = 채널#STAN */ + snprintf(trkey, sizeof(trkey), "%s#%s", chan, stan); + + /* 3) 구조화 추적로그 기록 */ + tx_log(TX_LOG_INFO, "[MG_OL_0005] TRACE chan=%s stan=%s type=%s trkey=%s", + chan, stan, mtype, trkey); + + /* 4) 추적테이블 적재 */ + strncpy(h_trkey, trkey, sizeof(h_trkey) - 1); h_trkey[sizeof(h_trkey) - 1] = '\0'; + strncpy(h_chan, chan, sizeof(h_chan) - 1); h_chan[sizeof(h_chan) - 1] = '\0'; + strncpy(h_stan, stan, sizeof(h_stan) - 1); h_stan[sizeof(h_stan) - 1] = '\0'; + strncpy(h_mtype, mtype, sizeof(h_mtype) - 1); h_mtype[sizeof(h_mtype) - 1] = '\0'; + h_xid = tx_current_xid(); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mg_stat ( trace_key , chan_id , stan , msg_type , xid , log_ts ) values ( $1 , $2 , $3 , $4 , $5 , now ( ) )", + ECPGt_char,(h_trkey),(long)32,(long)1,(32)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chan),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_stan),(long)12,(long)1,(12)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_mtype),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_xid),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 62 "app/online/mg_ol_0005.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("MG_OL_0005 mg_stat insert"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_buf_sets(ctx->out, "TRKEY", trkey); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 5) 추적키 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0005] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "TRKEY", trkey); + tx_buf_sets(ctx->out, "CHAN", chan); + tx_buf_sets(ctx->out, "STAN", stan); + tx_buf_setlong(ctx->out, "XID", h_xid); + tx_log(TX_LOG_INFO, "[MG_OL_0005] 추적로그 적재완료 trkey=%s xid=%ld", trkey, h_xid); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0005.pgc b/app/online/mg_ol_0005.pgc index 9b3ed08..f81c99c 100644 --- a/app/online/mg_ol_0005.pgc +++ b/app/online/mg_ol_0005.pgc @@ -1,14 +1,16 @@ -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0005.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0005) [tier=easy] + * mg_ol_0005.pgc - 전문 로깅/추적 서비스 (MG_OL_0005) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 거래추적번호(STAN)/채널(CHAN)/전문종별(MSGTYPE)로 추적로그 항목을 + * 구성하여 tx_log 로 기록하고, 추적테이블(mg_stat)에 EXEC SQL 로 적재한다. + * 채널+STAN 조합으로 추적키를 만들어 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mg_core.h" @@ -16,32 +18,65 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MG_OL_0005, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_trkey[32]; + char h_chan[16]; + char h_stan[12]; + char h_mtype[8]; + long h_xid; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MG_OL_0005] 전문게이트웨이 단건조회 서비스 진입"); + char chan[16]; + char stan[12]; + char mtype[8]; + char trkey[32]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0005] KEY 누락"); + tx_log(TX_LOG_INFO, "[MG_OL_0005] 전문 추적로그 서비스 진입"); + + /* 1) 추적 3요소 수신 */ + if (tx_buf_get(ctx->in, "CHAN", chan, sizeof(chan)) != TX_OK || + tx_buf_get(ctx->in, "STAN", stan, sizeof(stan)) != TX_OK || + tx_buf_get(ctx->in, "MSGTYPE", mtype, sizeof(mtype)) != TX_OK || + chan[0] == '\0' || stan[0] == '\0') { + tx_log(TX_LOG_ERROR, "[MG_OL_0005] 추적요소(CHAN/STAN/MSGTYPE) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0005] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 추적키 = 채널#STAN */ + snprintf(trkey, sizeof(trkey), "%s#%s", chan, stan); + + /* 3) 구조화 추적로그 기록 */ + tx_log(TX_LOG_INFO, "[MG_OL_0005] TRACE chan=%s stan=%s type=%s trkey=%s", + chan, stan, mtype, trkey); + + /* 4) 추적테이블 적재 */ + strncpy(h_trkey, trkey, sizeof(h_trkey) - 1); h_trkey[sizeof(h_trkey) - 1] = '\0'; + strncpy(h_chan, chan, sizeof(h_chan) - 1); h_chan[sizeof(h_chan) - 1] = '\0'; + strncpy(h_stan, stan, sizeof(h_stan) - 1); h_stan[sizeof(h_stan) - 1] = '\0'; + strncpy(h_mtype, mtype, sizeof(h_mtype) - 1); h_mtype[sizeof(h_mtype) - 1] = '\0'; + h_xid = tx_current_xid(); + + EXEC SQL INSERT INTO mg_stat + (trace_key, chan_id, stan, msg_type, xid, log_ts) + VALUES (:h_trkey, :h_chan, :h_stan, :h_mtype, :h_xid, now()); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("MG_OL_0005 mg_stat insert"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_buf_sets(ctx->out, "TRKEY", trkey); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 5) 추적키 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0005] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "TRKEY", trkey); + tx_buf_sets(ctx->out, "CHAN", chan); + tx_buf_sets(ctx->out, "STAN", stan); + tx_buf_setlong(ctx->out, "XID", h_xid); + tx_log(TX_LOG_INFO, "[MG_OL_0005] 추적로그 적재완료 trkey=%s xid=%ld", trkey, h_xid); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0006.c b/app/online/mg_ol_0006.c index df38f71..d866eda 100644 --- a/app/online/mg_ol_0006.c +++ b/app/online/mg_ol_0006.c @@ -8,10 +8,11 @@ #line 1 "app/online/mg_ol_0006.pgc" /* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0006.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0006) [tier=medium] + * mg_ol_0006.pgc - 전문 포맷 변환 서비스 (MG_OL_0006) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 수신전문(0200)을 정산전문(0500)으로 변환한다. 원전문을 언팩하여 필드를 + * 재배치/보정(전문종별 교체, 거래코드 SETTLE 치환, 할부→일시불 정규화, + * 응답코드 초기화)한 뒤 다시 팩하여 정산 RAWMSG 를 반환한다. */ #include #include @@ -89,60 +90,77 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0006.pgc" +#line 16 "app/online/mg_ol_0006.pgc" TX_SERVICE(MG_OL_0006, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char raw[MSG_ACQ_LEN + 8]; + char out[MSG_ACQ_LEN + 8]; + int rawlen; + acq_msg_t src; + acq_msg_t dst; - tx_log(TX_LOG_INFO, "[MG_OL_0006] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0006] 전문 포맷 변환(0200->0500) 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0006] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0006] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0006] 영업일 오류 date=%s", bizdate); + /* 1) 원시 수신전문 수신 */ + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0006] 원시전문(RAWMSG) 누락 rc=%d", rawlen); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0006] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 언팩 및 원전문 종별 확인 */ + memset(&src, 0, sizeof(src)); + if (msg_unpack(&src, raw, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0006] 전문 언팩 실패"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (strncmp(src.msg_type, MSG_TYPE_RECV, FLD_MSG_TYPE_LEN) != 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0006] 변환 대상 아님 type=%.4s", src.msg_type); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); + /* 3) 정산전문(0500)으로 필드 재배치/보정 */ + memset(&dst, 0, sizeof(dst)); + /* 전문종별 교체 */ + msg_field_set(dst.msg_type, MSG_TYPE_RECON, FLD_MSG_TYPE_LEN); + /* 거래코드는 SETTLE 로 치환 */ + msg_field_set(dst.trans_code, "SETTLE", FLD_TRANS_CODE_LEN); + /* 식별/금액/일자 필드는 원전문 그대로 이관 */ + memcpy(dst.merch_id, src.merch_id, FLD_MERCH_ID_LEN); + memcpy(dst.card_no, src.card_no, FLD_CARD_NO_LEN); + memcpy(dst.approval_no, src.approval_no, FLD_APPROVAL_LEN); + memcpy(dst.amount, src.amount, FLD_AMOUNT_LEN); + memcpy(dst.txn_date, src.txn_date, FLD_TXN_DATE_LEN); + memcpy(dst.txn_time, src.txn_time, FLD_TXN_TIME_LEN); + /* 할부개월 정규화: 정산전문은 일시불('00')로 집계 */ + msg_field_set(dst.inst_month, "00", FLD_INST_LEN); + /* 응답코드는 정산단계에서 미결('0000') 초기화 */ + msg_field_set(dst.resp_code, RESP_OK, FLD_RESP_LEN); + /* 필러는 공백 채움 */ + memset(dst.filler, ' ', sizeof(dst.filler)); + + /* 4) 변환전문 팩 */ + if (msg_pack(&dst, out, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0006] 변환전문 팩 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 5) 정산 RAWMSG 반환 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0006] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_set(ctx->out, "RAWMSG", out, MSG_ACQ_LEN); + tx_buf_setlong(ctx->out, "RAWLEN", (long)MSG_ACQ_LEN); + tx_buf_sets(ctx->out, "SRCTYPE", MSG_TYPE_RECV); + tx_buf_sets(ctx->out, "DSTTYPE", MSG_TYPE_RECON); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_log(TX_LOG_INFO, "[MG_OL_0006] 포맷 변환완료 0200->0500 len=%d", MSG_ACQ_LEN); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0006.pgc b/app/online/mg_ol_0006.pgc index 32b0fc8..132bd67 100644 --- a/app/online/mg_ol_0006.pgc +++ b/app/online/mg_ol_0006.pgc @@ -1,9 +1,10 @@ /* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0006.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0006) [tier=medium] + * mg_ol_0006.pgc - 전문 포맷 변환 서비스 (MG_OL_0006) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 수신전문(0200)을 정산전문(0500)으로 변환한다. 원전문을 언팩하여 필드를 + * 재배치/보정(전문종별 교체, 거래코드 SETTLE 치환, 할부→일시불 정규화, + * 응답코드 초기화)한 뒤 다시 팩하여 정산 RAWMSG 를 반환한다. */ #include #include @@ -16,55 +17,72 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MG_OL_0006, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char raw[MSG_ACQ_LEN + 8]; + char out[MSG_ACQ_LEN + 8]; + int rawlen; + acq_msg_t src; + acq_msg_t dst; - tx_log(TX_LOG_INFO, "[MG_OL_0006] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0006] 전문 포맷 변환(0200->0500) 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0006] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0006] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0006] 영업일 오류 date=%s", bizdate); + /* 1) 원시 수신전문 수신 */ + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0006] 원시전문(RAWMSG) 누락 rc=%d", rawlen); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0006] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 언팩 및 원전문 종별 확인 */ + memset(&src, 0, sizeof(src)); + if (msg_unpack(&src, raw, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0006] 전문 언팩 실패"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (strncmp(src.msg_type, MSG_TYPE_RECV, FLD_MSG_TYPE_LEN) != 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0006] 변환 대상 아님 type=%.4s", src.msg_type); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); + /* 3) 정산전문(0500)으로 필드 재배치/보정 */ + memset(&dst, 0, sizeof(dst)); + /* 전문종별 교체 */ + msg_field_set(dst.msg_type, MSG_TYPE_RECON, FLD_MSG_TYPE_LEN); + /* 거래코드는 SETTLE 로 치환 */ + msg_field_set(dst.trans_code, "SETTLE", FLD_TRANS_CODE_LEN); + /* 식별/금액/일자 필드는 원전문 그대로 이관 */ + memcpy(dst.merch_id, src.merch_id, FLD_MERCH_ID_LEN); + memcpy(dst.card_no, src.card_no, FLD_CARD_NO_LEN); + memcpy(dst.approval_no, src.approval_no, FLD_APPROVAL_LEN); + memcpy(dst.amount, src.amount, FLD_AMOUNT_LEN); + memcpy(dst.txn_date, src.txn_date, FLD_TXN_DATE_LEN); + memcpy(dst.txn_time, src.txn_time, FLD_TXN_TIME_LEN); + /* 할부개월 정규화: 정산전문은 일시불('00')로 집계 */ + msg_field_set(dst.inst_month, "00", FLD_INST_LEN); + /* 응답코드는 정산단계에서 미결('0000') 초기화 */ + msg_field_set(dst.resp_code, RESP_OK, FLD_RESP_LEN); + /* 필러는 공백 채움 */ + memset(dst.filler, ' ', sizeof(dst.filler)); + + /* 4) 변환전문 팩 */ + if (msg_pack(&dst, out, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0006] 변환전문 팩 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 5) 정산 RAWMSG 반환 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0006] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_set(ctx->out, "RAWMSG", out, MSG_ACQ_LEN); + tx_buf_setlong(ctx->out, "RAWLEN", (long)MSG_ACQ_LEN); + tx_buf_sets(ctx->out, "SRCTYPE", MSG_TYPE_RECV); + tx_buf_sets(ctx->out, "DSTTYPE", MSG_TYPE_RECON); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_log(TX_LOG_INFO, "[MG_OL_0006] 포맷 변환완료 0200->0500 len=%d", MSG_ACQ_LEN); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0007.c b/app/online/mg_ol_0007.c index ae593b6..ca0a7a4 100644 --- a/app/online/mg_ol_0007.c +++ b/app/online/mg_ol_0007.c @@ -8,10 +8,11 @@ #line 1 "app/online/mg_ol_0007.pgc" /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0007.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0007) [tier=easy] + * mg_ol_0007.pgc - 오류전문 생성 서비스 (MG_OL_0007) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원전문(RAWMSG)과 오류코드(ERRCODE)를 받아 응답전문(0210)의 오류회신을 + * 구성한다. 원전문 식별필드는 그대로 보존하고 전문종별을 0210 으로 바꾼 뒤 + * resp_code 에 오류코드를 세팅하여 msg_pack 후 RAWMSG 로 반환한다. */ #include #include @@ -89,37 +90,68 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0007.pgc" +#line 16 "app/online/mg_ol_0007.pgc" TX_SERVICE(MG_OL_0007, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char raw[MSG_ACQ_LEN + 8]; + char out[MSG_ACQ_LEN + 8]; + char errcode[8]; + int rawlen; + acq_msg_t msg; - tx_log(TX_LOG_INFO, "[MG_OL_0007] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0007] 오류전문 생성 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0007] KEY 누락"); + /* 1) 원전문 + 오류코드 수신 */ + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0007] 원전문(RAWMSG) 누락 rc=%d", rawlen); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "ERRCODE", errcode, sizeof(errcode)) != TX_OK || + errcode[0] == '\0') { + /* 오류코드 미지정 시 시스템오류로 간주 */ + strncpy(errcode, RESP_SYSERR, sizeof(errcode) - 1); + errcode[sizeof(errcode) - 1] = '\0'; + } + + /* 2) 오류코드 4자리 정규화 (부족분 zero-fill, 초과분 절단) */ + if ((int)strlen(errcode) != FLD_RESP_LEN) { + char norm[FLD_RESP_LEN + 1]; + msg_field_set_num(norm, errcode, FLD_RESP_LEN); + norm[FLD_RESP_LEN] = '\0'; + strncpy(errcode, norm, sizeof(errcode) - 1); + errcode[sizeof(errcode) - 1] = '\0'; + } + + /* 3) 원전문 언팩 (식별필드 보존 목적) */ + memset(&msg, 0, sizeof(msg)); + if (msg_unpack(&msg, raw, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0007] 원전문 언팩 실패"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0007] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 4) 오류응답으로 변환: 종별 0210, 응답코드 세팅, 금액 영처리 */ + msg_field_set(msg.msg_type, MSG_TYPE_RESP, FLD_MSG_TYPE_LEN); + memcpy(msg.resp_code, errcode, FLD_RESP_LEN); + msg_field_set_num(msg.amount, "0", FLD_AMOUNT_LEN); + msg_field_set(msg.filler, "ERR", sizeof(msg.filler)); + + /* 5) 팩 후 오류 RAWMSG 반환 */ + if (msg_pack(&msg, out, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0007] 오류전문 팩 실패"); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0007] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_set(ctx->out, "RAWMSG", out, MSG_ACQ_LEN); + tx_buf_setlong(ctx->out, "RAWLEN", (long)MSG_ACQ_LEN); + tx_buf_sets(ctx->out, "MSGTYPE", MSG_TYPE_RESP); + tx_buf_set(ctx->out, "RESPCODE", errcode, FLD_RESP_LEN); + tx_log(TX_LOG_INFO, "[MG_OL_0007] 오류전문 생성완료 respcode=%.4s", errcode); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0007.pgc b/app/online/mg_ol_0007.pgc index d1217ea..41a73ce 100644 --- a/app/online/mg_ol_0007.pgc +++ b/app/online/mg_ol_0007.pgc @@ -1,9 +1,10 @@ /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0007.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0007) [tier=easy] + * mg_ol_0007.pgc - 오류전문 생성 서비스 (MG_OL_0007) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원전문(RAWMSG)과 오류코드(ERRCODE)를 받아 응답전문(0210)의 오류회신을 + * 구성한다. 원전문 식별필드는 그대로 보존하고 전문종별을 0210 으로 바꾼 뒤 + * resp_code 에 오류코드를 세팅하여 msg_pack 후 RAWMSG 로 반환한다. */ #include #include @@ -16,32 +17,63 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MG_OL_0007, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char raw[MSG_ACQ_LEN + 8]; + char out[MSG_ACQ_LEN + 8]; + char errcode[8]; + int rawlen; + acq_msg_t msg; - tx_log(TX_LOG_INFO, "[MG_OL_0007] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0007] 오류전문 생성 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0007] KEY 누락"); + /* 1) 원전문 + 오류코드 수신 */ + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0007] 원전문(RAWMSG) 누락 rc=%d", rawlen); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "ERRCODE", errcode, sizeof(errcode)) != TX_OK || + errcode[0] == '\0') { + /* 오류코드 미지정 시 시스템오류로 간주 */ + strncpy(errcode, RESP_SYSERR, sizeof(errcode) - 1); + errcode[sizeof(errcode) - 1] = '\0'; + } + + /* 2) 오류코드 4자리 정규화 (부족분 zero-fill, 초과분 절단) */ + if ((int)strlen(errcode) != FLD_RESP_LEN) { + char norm[FLD_RESP_LEN + 1]; + msg_field_set_num(norm, errcode, FLD_RESP_LEN); + norm[FLD_RESP_LEN] = '\0'; + strncpy(errcode, norm, sizeof(errcode) - 1); + errcode[sizeof(errcode) - 1] = '\0'; + } + + /* 3) 원전문 언팩 (식별필드 보존 목적) */ + memset(&msg, 0, sizeof(msg)); + if (msg_unpack(&msg, raw, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0007] 원전문 언팩 실패"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0007] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 4) 오류응답으로 변환: 종별 0210, 응답코드 세팅, 금액 영처리 */ + msg_field_set(msg.msg_type, MSG_TYPE_RESP, FLD_MSG_TYPE_LEN); + memcpy(msg.resp_code, errcode, FLD_RESP_LEN); + msg_field_set_num(msg.amount, "0", FLD_AMOUNT_LEN); + msg_field_set(msg.filler, "ERR", sizeof(msg.filler)); + + /* 5) 팩 후 오류 RAWMSG 반환 */ + if (msg_pack(&msg, out, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0007] 오류전문 팩 실패"); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0007] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_set(ctx->out, "RAWMSG", out, MSG_ACQ_LEN); + tx_buf_setlong(ctx->out, "RAWLEN", (long)MSG_ACQ_LEN); + tx_buf_sets(ctx->out, "MSGTYPE", MSG_TYPE_RESP); + tx_buf_set(ctx->out, "RESPCODE", errcode, FLD_RESP_LEN); + tx_log(TX_LOG_INFO, "[MG_OL_0007] 오류전문 생성완료 respcode=%.4s", errcode); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0008.c b/app/online/mg_ol_0008.c index 4f93b6a..449665c 100644 --- a/app/online/mg_ol_0008.c +++ b/app/online/mg_ol_0008.c @@ -6,12 +6,13 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0008.pgc" -/* @tier hard @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0008.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0008) [tier=hard] + * mg_ol_0008.pgc - 전문 라우팅 서비스 (MG_OL_0008) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 전문종별(MSGTYPE)과 가맹점번호(MERCHID) prefix 를 인-코드 라우팅 규칙과 + * 대조하여 목적 채널을 결정한다. 규칙 미매칭 시 기본채널(DEFAULT)로 + * 폴백하며 결정된 목적채널/우선순위를 응답한다. */ #include #include @@ -89,88 +90,79 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0008.pgc" +#line 16 "app/online/mg_ol_0008.pgc" +typedef struct { + const char *mtype; /* 전문종별 (빈문자면 와일드카드) */ + const char *mprefix; /* 가맹점 prefix (빈문자면 와일드카드) */ + const char *dest; /* 목적 채널 */ + int prio; /* 우선순위 (작을수록 우선) */ +} mg_route_rule_t; + TX_SERVICE(MG_OL_0008, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + static const mg_route_rule_t rules[] = { + { MSG_TYPE_RECV, "9", "CH_VIP", 1 }, + { MSG_TYPE_RECV, "1", "CH_ACQ_A", 2 }, + { MSG_TYPE_RECV, "", "CH_ACQ_B", 3 }, + { MSG_TYPE_RECON, "", "CH_SETTLE", 2 }, + { MSG_TYPE_RESP, "", "CH_RESP", 2 } + }; + const int nrules = (int)(sizeof(rules) / sizeof(rules[0])); - tx_log(TX_LOG_INFO, "[MG_OL_0008] 전문게이트웨이 트랜잭션 오케스트레이션 진입"); + char mtype[8]; + char merch[16]; + char dest[16]; + int prio; + int i; + int matched; + size_t plen; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0008] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[MG_OL_0008] 전문 라우팅 서비스 진입"); + + /* 1) 라우팅 키 수신 */ + if (tx_buf_get(ctx->in, "MSGTYPE", mtype, sizeof(mtype)) != TX_OK || + mtype[0] == '\0') { + tx_log(TX_LOG_ERROR, "[MG_OL_0008] 전문종별(MSGTYPE) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) + merch[0] = '\0'; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0008] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + /* 2) 규칙 순차 평가 (우선순위 규칙이 앞쪽에 배치됨) */ + matched = 0; + prio = 99; + strncpy(dest, "CH_DEFAULT", sizeof(dest) - 1); + dest[sizeof(dest) - 1] = '\0'; + + for (i = 0; i < nrules; i++) { + if (strncmp(mtype, rules[i].mtype, FLD_MSG_TYPE_LEN) != 0) + continue; + plen = strlen(rules[i].mprefix); + if (plen > 0 && strncmp(merch, rules[i].mprefix, plen) != 0) + continue; + /* 매칭: 첫 매칭이 곧 최우선 (규칙배열 정렬 가정) */ + strncpy(dest, rules[i].dest, sizeof(dest) - 1); + dest[sizeof(dest) - 1] = '\0'; + prio = rules[i].prio; + matched = 1; + break; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0008] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MG_OL_0008] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + if (!matched) + tx_log(TX_LOG_WARN, "[MG_OL_0008] 라우팅 규칙 미매칭 type=%s merch=%s -> 기본채널", + mtype, merch); + /* 3) 라우팅 결과 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MG_OL_0008] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "MSGTYPE", mtype); + tx_buf_sets(ctx->out, "DEST", dest); + tx_buf_setlong(ctx->out, "PRIO", (long)prio); + tx_buf_setlong(ctx->out, "MATCHED", (long)matched); + tx_log(TX_LOG_INFO, "[MG_OL_0008] 라우팅 결정 type=%s merch=%s dest=%s prio=%d", + mtype, merch, dest, prio); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0008.pgc b/app/online/mg_ol_0008.pgc index 755af33..253d915 100644 --- a/app/online/mg_ol_0008.pgc +++ b/app/online/mg_ol_0008.pgc @@ -1,9 +1,10 @@ -/* @tier hard @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0008.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0008) [tier=hard] + * mg_ol_0008.pgc - 전문 라우팅 서비스 (MG_OL_0008) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 전문종별(MSGTYPE)과 가맹점번호(MERCHID) prefix 를 인-코드 라우팅 규칙과 + * 대조하여 목적 채널을 결정한다. 규칙 미매칭 시 기본채널(DEFAULT)로 + * 폴백하며 결정된 목적채널/우선순위를 응답한다. */ #include #include @@ -14,85 +15,76 @@ EXEC SQL INCLUDE sqlca; +typedef struct { + const char *mtype; /* 전문종별 (빈문자면 와일드카드) */ + const char *mprefix; /* 가맹점 prefix (빈문자면 와일드카드) */ + const char *dest; /* 목적 채널 */ + int prio; /* 우선순위 (작을수록 우선) */ +} mg_route_rule_t; + TX_SERVICE(MG_OL_0008, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + static const mg_route_rule_t rules[] = { + { MSG_TYPE_RECV, "9", "CH_VIP", 1 }, + { MSG_TYPE_RECV, "1", "CH_ACQ_A", 2 }, + { MSG_TYPE_RECV, "", "CH_ACQ_B", 3 }, + { MSG_TYPE_RECON, "", "CH_SETTLE", 2 }, + { MSG_TYPE_RESP, "", "CH_RESP", 2 } + }; + const int nrules = (int)(sizeof(rules) / sizeof(rules[0])); - tx_log(TX_LOG_INFO, "[MG_OL_0008] 전문게이트웨이 트랜잭션 오케스트레이션 진입"); + char mtype[8]; + char merch[16]; + char dest[16]; + int prio; + int i; + int matched; + size_t plen; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0008] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[MG_OL_0008] 전문 라우팅 서비스 진입"); + + /* 1) 라우팅 키 수신 */ + if (tx_buf_get(ctx->in, "MSGTYPE", mtype, sizeof(mtype)) != TX_OK || + mtype[0] == '\0') { + tx_log(TX_LOG_ERROR, "[MG_OL_0008] 전문종별(MSGTYPE) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) + merch[0] = '\0'; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0008] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + /* 2) 규칙 순차 평가 (우선순위 규칙이 앞쪽에 배치됨) */ + matched = 0; + prio = 99; + strncpy(dest, "CH_DEFAULT", sizeof(dest) - 1); + dest[sizeof(dest) - 1] = '\0'; + + for (i = 0; i < nrules; i++) { + if (strncmp(mtype, rules[i].mtype, FLD_MSG_TYPE_LEN) != 0) + continue; + plen = strlen(rules[i].mprefix); + if (plen > 0 && strncmp(merch, rules[i].mprefix, plen) != 0) + continue; + /* 매칭: 첫 매칭이 곧 최우선 (규칙배열 정렬 가정) */ + strncpy(dest, rules[i].dest, sizeof(dest) - 1); + dest[sizeof(dest) - 1] = '\0'; + prio = rules[i].prio; + matched = 1; + break; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0008] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MG_OL_0008] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + if (!matched) + tx_log(TX_LOG_WARN, "[MG_OL_0008] 라우팅 규칙 미매칭 type=%s merch=%s -> 기본채널", + mtype, merch); + /* 3) 라우팅 결과 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MG_OL_0008] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "MSGTYPE", mtype); + tx_buf_sets(ctx->out, "DEST", dest); + tx_buf_setlong(ctx->out, "PRIO", (long)prio); + tx_buf_setlong(ctx->out, "MATCHED", (long)matched); + tx_log(TX_LOG_INFO, "[MG_OL_0008] 라우팅 결정 type=%s merch=%s dest=%s prio=%d", + mtype, merch, dest, prio); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0009.c b/app/online/mg_ol_0009.c index 183e4b2..0e49d2a 100644 --- a/app/online/mg_ol_0009.c +++ b/app/online/mg_ol_0009.c @@ -6,17 +6,19 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0009.pgc" -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0009.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0009) [tier=easy] + * mg_ol_0009.pgc - 재전송 요청 처리 서비스 (MG_OL_0009) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리키(KEY)로 원장을 조회하여 현재 상태로 재전송 가능여부를 판정한다. + * 실패('U')/접수('R') 상태만 재전송 가능하며, 가능 시 재전송 큐(mg_queue)에 + * EXEC SQL 로 등록하고 큐 순번을 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mg_core.h" @@ -89,37 +91,116 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0009.pgc" +#line 17 "app/online/mg_ol_0009.pgc" TX_SERVICE(MG_OL_0009, ctx) { + /* exec sql begin declare section */ + + + + + +#line 22 "app/online/mg_ol_0009.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/mg_ol_0009.pgc" + char h_status [ 2 ] ; + +#line 24 "app/online/mg_ol_0009.pgc" + long h_amount ; + +#line 25 "app/online/mg_ol_0009.pgc" + long h_seq ; +/* exec sql end declare section */ +#line 26 "app/online/mg_ol_0009.pgc" + + + char key[16]; mg_rec_t rec; - char key[16]; - int rc; + int rc; + int resendable; - tx_log(TX_LOG_INFO, "[MG_OL_0009] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0009] 재전송 요청 처리 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0009] KEY 누락"); + /* 1) 재전송 대상 처리키 수신 */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { + tx_log(TX_LOG_ERROR, "[MG_OL_0009] 처리키(KEY) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2) 원장 조회 */ memset(&rec, 0, sizeof(rec)); rc = mg_rec_select(key, &rec); + if (rc == TX_ENOENT) { + tx_log(TX_LOG_WARN, "[MG_OL_0009] 재전송 대상 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0009] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_ERROR, "[MG_OL_0009] 원장 조회 실패 key=%s rc=%d", key, rc); tx_return(ctx, rc, ctx->out); return; } + /* 3) 상태 기반 재전송 가능여부 판정 */ + resendable = (strncmp(rec.status, MG_ST_FAIL, 1) == 0 || + strncmp(rec.status, MG_ST_RECV, 1) == 0); + if (!resendable) { + tx_log(TX_LOG_WARN, "[MG_OL_0009] 재전송 불가 상태 key=%s status=%s", + key, rec.status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_buf_sets(ctx->out, "STATUS", rec.status); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 4) 재전송 큐 등록 */ + strncpy(h_key, rec.key, sizeof(h_key) - 1); h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_status, rec.status, sizeof(h_status) - 1); h_status[sizeof(h_status) - 1] = '\0'; + h_amount = rec.amount; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mg_queue ( proc_key , prev_status , amount , enq_ts , retry_cnt ) values ( $1 , $2 , $3 , now ( ) , 0 )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 78 "app/online/mg_ol_0009.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("MG_OL_0009 mg_queue insert"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 5) 등록된 큐 순번 조회 */ + h_seq = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mg_queue where proc_key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_seq),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 90 "app/online/mg_ol_0009.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) + h_seq = -1; + + /* 6) 재전송 접수 응답 */ tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_OK); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_setlong(ctx->out, "QSEQ", h_seq); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0009] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_log(TX_LOG_INFO, "[MG_OL_0009] 재전송 큐 등록완료 key=%s qseq=%ld", rec.key, h_seq); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0009.pgc b/app/online/mg_ol_0009.pgc index 4fc52fa..e7c5bcb 100644 --- a/app/online/mg_ol_0009.pgc +++ b/app/online/mg_ol_0009.pgc @@ -1,14 +1,16 @@ -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0009.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0009) [tier=easy] + * mg_ol_0009.pgc - 재전송 요청 처리 서비스 (MG_OL_0009) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리키(KEY)로 원장을 조회하여 현재 상태로 재전송 가능여부를 판정한다. + * 실패('U')/접수('R') 상태만 재전송 가능하며, 가능 시 재전송 큐(mg_queue)에 + * EXEC SQL 로 등록하고 큐 순번을 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mg_core.h" @@ -16,32 +18,85 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MG_OL_0009, ctx) { + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_status[2]; + long h_amount; + long h_seq; + EXEC SQL END DECLARE SECTION; + + char key[16]; mg_rec_t rec; - char key[16]; - int rc; + int rc; + int resendable; - tx_log(TX_LOG_INFO, "[MG_OL_0009] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0009] 재전송 요청 처리 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0009] KEY 누락"); + /* 1) 재전송 대상 처리키 수신 */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || key[0] == '\0') { + tx_log(TX_LOG_ERROR, "[MG_OL_0009] 처리키(KEY) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2) 원장 조회 */ memset(&rec, 0, sizeof(rec)); rc = mg_rec_select(key, &rec); + if (rc == TX_ENOENT) { + tx_log(TX_LOG_WARN, "[MG_OL_0009] 재전송 대상 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0009] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_ERROR, "[MG_OL_0009] 원장 조회 실패 key=%s rc=%d", key, rc); tx_return(ctx, rc, ctx->out); return; } + /* 3) 상태 기반 재전송 가능여부 판정 */ + resendable = (strncmp(rec.status, MG_ST_FAIL, 1) == 0 || + strncmp(rec.status, MG_ST_RECV, 1) == 0); + if (!resendable) { + tx_log(TX_LOG_WARN, "[MG_OL_0009] 재전송 불가 상태 key=%s status=%s", + key, rec.status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_buf_sets(ctx->out, "STATUS", rec.status); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 4) 재전송 큐 등록 */ + strncpy(h_key, rec.key, sizeof(h_key) - 1); h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_status, rec.status, sizeof(h_status) - 1); h_status[sizeof(h_status) - 1] = '\0'; + h_amount = rec.amount; + + EXEC SQL INSERT INTO mg_queue + (proc_key, prev_status, amount, enq_ts, retry_cnt) + VALUES (:h_key, :h_status, :h_amount, now(), 0); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("MG_OL_0009 mg_queue insert"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 5) 등록된 큐 순번 조회 */ + h_seq = 0; + EXEC SQL SELECT COUNT(*) INTO :h_seq FROM mg_queue WHERE proc_key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) + h_seq = -1; + + /* 6) 재전송 접수 응답 */ tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_OK); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_setlong(ctx->out, "QSEQ", h_seq); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0009] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_log(TX_LOG_INFO, "[MG_OL_0009] 재전송 큐 등록완료 key=%s qseq=%ld", rec.key, h_seq); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0010.c b/app/online/mg_ol_0010.c index 0056101..ddc6fdd 100644 --- a/app/online/mg_ol_0010.c +++ b/app/online/mg_ol_0010.c @@ -6,12 +6,13 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0010.pgc" -/* @tier hard @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0010.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0010) [tier=hard] + * mg_ol_0010.pgc - 전문 압축/해제 서비스 (MG_OL_0010) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * RAWMSG 를 RLE(Run-Length Encoding) 유사 방식으로 압축하여 압축길이/ + * 압축비를 산출한다. 압축결과를 다시 해제하여 원본과 일치하는지 검증 + * (round-trip)하고 압축통계와 검증결과를 응답한다. 순수 로직. */ #include #include @@ -89,88 +90,82 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0010.pgc" +#line 16 "app/online/mg_ol_0010.pgc" +#define MG10_MAXRUN 255 + TX_SERVICE(MG_OL_0010, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char raw[256]; + unsigned char comp[512]; + char verify[256]; + int rawlen, complen, vlen; + int i, j; + long ratio; - tx_log(TX_LOG_INFO, "[MG_OL_0010] 전문게이트웨이 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0010] 전문 압축/해제 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0010] 필수 필드 누락"); + /* 1) 원문 수신 */ + if (tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0010] 원문(RAWMSG) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0010] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + rawlen = (int)strlen(raw); + if (rawlen <= 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0010] 압축 대상 길이 0"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { + /* 2) RLE 압축: (반복횟수, 문자) 쌍 스트림 생성 */ + complen = 0; + i = 0; + while (i < rawlen) { + int run = 1; + while (i + run < rawlen && raw[i + run] == raw[i] && run < MG10_MAXRUN) + run++; + if (complen + 2 > (int)sizeof(comp)) { + tx_log(TX_LOG_ERROR, "[MG_OL_0010] 압축버퍼 초과 complen=%d", complen); + tx_return(ctx, TX_ENOMEM, ctx->out); + return; + } + comp[complen++] = (unsigned char)run; + comp[complen++] = (unsigned char)raw[i]; + i += run; + } + + /* 3) 해제(round-trip) 검증 */ + vlen = 0; + for (j = 0; j + 1 < complen; j += 2) { + int cnt = comp[j]; + int k; + for (k = 0; k < cnt && vlen < (int)sizeof(verify) - 1; k++) + verify[vlen++] = (char)comp[j + 1]; + } + verify[vlen] = '\0'; + + if (vlen != rawlen || memcmp(verify, raw, rawlen) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0010] round-trip 불일치 raw=%d vlen=%d", rawlen, vlen); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_buf_setlong(ctx->out, "VERIFY", 0); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0010] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MG_OL_0010] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 4) 압축비(%) = 압축길이 * 100 / 원문길이 (낮을수록 효율적) */ + ratio = (long)complen * 100 / (long)rawlen; + /* 5) 압축 통계 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MG_OL_0010] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_setlong(ctx->out, "RAWLEN", (long)rawlen); + tx_buf_setlong(ctx->out, "COMPLEN", (long)complen); + tx_buf_setlong(ctx->out, "RATIO", ratio); + tx_buf_setlong(ctx->out, "VERIFY", 1); + tx_log(TX_LOG_INFO, "[MG_OL_0010] 압축완료 raw=%d comp=%d ratio=%ld%%", + rawlen, complen, ratio); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0010.pgc b/app/online/mg_ol_0010.pgc index b164422..d50c88f 100644 --- a/app/online/mg_ol_0010.pgc +++ b/app/online/mg_ol_0010.pgc @@ -1,9 +1,10 @@ -/* @tier hard @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0010.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0010) [tier=hard] + * mg_ol_0010.pgc - 전문 압축/해제 서비스 (MG_OL_0010) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * RAWMSG 를 RLE(Run-Length Encoding) 유사 방식으로 압축하여 압축길이/ + * 압축비를 산출한다. 압축결과를 다시 해제하여 원본과 일치하는지 검증 + * (round-trip)하고 압축통계와 검증결과를 응답한다. 순수 로직. */ #include #include @@ -14,85 +15,79 @@ EXEC SQL INCLUDE sqlca; +#define MG10_MAXRUN 255 + TX_SERVICE(MG_OL_0010, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char raw[256]; + unsigned char comp[512]; + char verify[256]; + int rawlen, complen, vlen; + int i, j; + long ratio; - tx_log(TX_LOG_INFO, "[MG_OL_0010] 전문게이트웨이 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0010] 전문 압축/해제 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0010] 필수 필드 누락"); + /* 1) 원문 수신 */ + if (tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0010] 원문(RAWMSG) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0010] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + rawlen = (int)strlen(raw); + if (rawlen <= 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0010] 압축 대상 길이 0"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { + /* 2) RLE 압축: (반복횟수, 문자) 쌍 스트림 생성 */ + complen = 0; + i = 0; + while (i < rawlen) { + int run = 1; + while (i + run < rawlen && raw[i + run] == raw[i] && run < MG10_MAXRUN) + run++; + if (complen + 2 > (int)sizeof(comp)) { + tx_log(TX_LOG_ERROR, "[MG_OL_0010] 압축버퍼 초과 complen=%d", complen); + tx_return(ctx, TX_ENOMEM, ctx->out); + return; + } + comp[complen++] = (unsigned char)run; + comp[complen++] = (unsigned char)raw[i]; + i += run; + } + + /* 3) 해제(round-trip) 검증 */ + vlen = 0; + for (j = 0; j + 1 < complen; j += 2) { + int cnt = comp[j]; + int k; + for (k = 0; k < cnt && vlen < (int)sizeof(verify) - 1; k++) + verify[vlen++] = (char)comp[j + 1]; + } + verify[vlen] = '\0'; + + if (vlen != rawlen || memcmp(verify, raw, rawlen) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0010] round-trip 불일치 raw=%d vlen=%d", rawlen, vlen); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_buf_setlong(ctx->out, "VERIFY", 0); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0010] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MG_OL_0010] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 4) 압축비(%) = 압축길이 * 100 / 원문길이 (낮을수록 효율적) */ + ratio = (long)complen * 100 / (long)rawlen; + /* 5) 압축 통계 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MG_OL_0010] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_setlong(ctx->out, "RAWLEN", (long)rawlen); + tx_buf_setlong(ctx->out, "COMPLEN", (long)complen); + tx_buf_setlong(ctx->out, "RATIO", ratio); + tx_buf_setlong(ctx->out, "VERIFY", 1); + tx_log(TX_LOG_INFO, "[MG_OL_0010] 압축완료 raw=%d comp=%d ratio=%ld%%", + rawlen, complen, ratio); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0011.c b/app/online/mg_ol_0011.c index e4f42d3..aa7f5c8 100644 --- a/app/online/mg_ol_0011.c +++ b/app/online/mg_ol_0011.c @@ -6,12 +6,12 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0011.pgc" -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0011.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0011) [tier=easy] + * mg_ol_0011.pgc - ISO8583식 필드 파싱 서비스 (MG_OL_0011) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 primary bitmap(16 hex 문자 = 64bit)을 파싱하여 present 필드 + * 번호 목록을 추출하고, 비트온 개수/이차비트맵 사용여부를 응답한다. */ #include #include @@ -94,32 +94,57 @@ struct sqlca_t *ECPGget_sqlca(void); TX_SERVICE(MG_OL_0011, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char bitmap[24]; + char present[160]; + char hexbuf[8]; + int i, nib, bit, fieldno, cnt = 0; + int secondary = 0; - tx_log(TX_LOG_INFO, "[MG_OL_0011] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0011] ISO8583 비트맵 파싱 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0011] KEY 누락"); + if (tx_buf_get(ctx->in, "BITMAP", bitmap, sizeof(bitmap)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0011] BITMAP 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0011] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (strlen(bitmap) < 16) { + tx_log(TX_LOG_WARN, "[MG_OL_0011] 비트맵 길이부족 len=%d", (int)strlen(bitmap)); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + present[0] = '\0'; + + /* 16 hex 문자 -> 64 bit, MSB = 필드1 */ + for (i = 0; i < 16; i++) { + char c = bitmap[i]; + if (c >= '0' && c <= '9') nib = c - '0'; + else if (c >= 'A' && c <= 'F') nib = c - 'A' + 10; + else if (c >= 'a' && c <= 'f') nib = c - 'a' + 10; + else { + tx_log(TX_LOG_WARN, "[MG_OL_0011] 비16진 문자 pos=%d", i); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + for (bit = 0; bit < 4; bit++) { + if (nib & (1 << (3 - bit))) { + fieldno = i * 4 + bit + 1; + if (fieldno == 1) + secondary = 1; /* 필드1 온 = secondary bitmap 존재 */ + snprintf(hexbuf, sizeof(hexbuf), "%d ", fieldno); + if (strlen(present) + strlen(hexbuf) < sizeof(present)) + strcat(present, hexbuf); + cnt++; + } + } + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0011] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "FIELDS", present); + tx_buf_setlong(ctx->out, "COUNT", (long)cnt); + tx_buf_setlong(ctx->out, "SECONDARY", (long)secondary); + tx_log(TX_LOG_INFO, "[MG_OL_0011] 파싱완료 present=%d secondary=%d", cnt, secondary); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0011.pgc b/app/online/mg_ol_0011.pgc index cd53243..08a8b8b 100644 --- a/app/online/mg_ol_0011.pgc +++ b/app/online/mg_ol_0011.pgc @@ -1,9 +1,9 @@ -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0011.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0011) [tier=easy] + * mg_ol_0011.pgc - ISO8583식 필드 파싱 서비스 (MG_OL_0011) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청의 primary bitmap(16 hex 문자 = 64bit)을 파싱하여 present 필드 + * 번호 목록을 추출하고, 비트온 개수/이차비트맵 사용여부를 응답한다. */ #include #include @@ -16,32 +16,57 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MG_OL_0011, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char bitmap[24]; + char present[160]; + char hexbuf[8]; + int i, nib, bit, fieldno, cnt = 0; + int secondary = 0; - tx_log(TX_LOG_INFO, "[MG_OL_0011] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0011] ISO8583 비트맵 파싱 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0011] KEY 누락"); + if (tx_buf_get(ctx->in, "BITMAP", bitmap, sizeof(bitmap)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0011] BITMAP 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0011] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (strlen(bitmap) < 16) { + tx_log(TX_LOG_WARN, "[MG_OL_0011] 비트맵 길이부족 len=%d", (int)strlen(bitmap)); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + present[0] = '\0'; + + /* 16 hex 문자 -> 64 bit, MSB = 필드1 */ + for (i = 0; i < 16; i++) { + char c = bitmap[i]; + if (c >= '0' && c <= '9') nib = c - '0'; + else if (c >= 'A' && c <= 'F') nib = c - 'A' + 10; + else if (c >= 'a' && c <= 'f') nib = c - 'a' + 10; + else { + tx_log(TX_LOG_WARN, "[MG_OL_0011] 비16진 문자 pos=%d", i); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + for (bit = 0; bit < 4; bit++) { + if (nib & (1 << (3 - bit))) { + fieldno = i * 4 + bit + 1; + if (fieldno == 1) + secondary = 1; /* 필드1 온 = secondary bitmap 존재 */ + snprintf(hexbuf, sizeof(hexbuf), "%d ", fieldno); + if (strlen(present) + strlen(hexbuf) < sizeof(present)) + strcat(present, hexbuf); + cnt++; + } + } + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0011] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "FIELDS", present); + tx_buf_setlong(ctx->out, "COUNT", (long)cnt); + tx_buf_setlong(ctx->out, "SECONDARY", (long)secondary); + tx_log(TX_LOG_INFO, "[MG_OL_0011] 파싱완료 present=%d secondary=%d", cnt, secondary); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0012.c b/app/online/mg_ol_0012.c index f19a3d8..97d98e1 100644 --- a/app/online/mg_ol_0012.c +++ b/app/online/mg_ol_0012.c @@ -6,12 +6,13 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0012.pgc" -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0012.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0012) [tier=easy] + * mg_ol_0012.pgc - 전문 검증 서비스 (MG_OL_0012) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 고정길이 수신전문(RAWMSG)에 대해 (1)필수필드 존재, (2)전문종별 유효성, + * (3)금액/일자 숫자필드의 mod-10 체크숫자를 순차 검증한다. 각 단계 결과를 + * 비트플래그로 누적하여 최종 검증 결과코드를 응답한다. */ #include #include @@ -89,37 +90,98 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0012.pgc" +#line 16 "app/online/mg_ol_0012.pgc" +/* 검증 단계 플래그 */ +#define MG12_F_FIELD 0x01 /* 필수필드 OK */ +#define MG12_F_TYPE 0x02 /* 전문종별 OK */ +#define MG12_F_CHECK 0x04 /* 체크숫자 OK */ +#define MG12_F_ALL (MG12_F_FIELD | MG12_F_TYPE | MG12_F_CHECK) + TX_SERVICE(MG_OL_0012, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char raw[MSG_ACQ_LEN + 8]; + char chkbuf[8]; + acq_msg_t msg; + int flags; + int i, sum, calc; + long parsed_chk; - tx_log(TX_LOG_INFO, "[MG_OL_0012] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0012] 전문 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0012] KEY 누락"); + /* 1) 원시전문 수신 및 언팩 */ + if (tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0012] 원시전문(RAWMSG) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + memset(&msg, 0, sizeof(msg)); + if (msg_unpack(&msg, raw, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0012] 전문 언팩 실패"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + flags = 0; + + /* 2) 필수필드 검증 */ + if (msg_validate(&msg) == 0) + flags |= MG12_F_FIELD; + else + tx_log(TX_LOG_WARN, "[MG_OL_0012] 필수필드 검증 실패"); + + /* 3) 전문종별 유효성 */ + if (strncmp(msg.msg_type, MSG_TYPE_RECV, FLD_MSG_TYPE_LEN) == 0 || + strncmp(msg.msg_type, MSG_TYPE_RESP, FLD_MSG_TYPE_LEN) == 0 || + strncmp(msg.msg_type, MSG_TYPE_RECON, FLD_MSG_TYPE_LEN) == 0) + flags |= MG12_F_TYPE; + else + tx_log(TX_LOG_WARN, "[MG_OL_0012] 전문종별 무효 type=%.4s", msg.msg_type); + + /* 4) 체크숫자: 금액 12자리 숫자합 mod 10 */ + sum = 0; + for (i = 0; i < FLD_AMOUNT_LEN; i++) { + char c = msg.amount[i]; + if (c >= '0' && c <= '9') + sum += (c - '0'); + else if (c != ' ') { + sum = -1; + break; + } + } + calc = (sum >= 0) ? (sum % 10) : -1; + + /* 입력에 기대 체크숫자(CHKDIGIT)가 오면 대조, 없으면 계산값을 신뢰 */ + if (calc >= 0) { + if (tx_buf_get(ctx->in, "CHKDIGIT", chkbuf, sizeof(chkbuf)) == TX_OK && + chkbuf[0] != '\0') { + parsed_chk = amount_parse(chkbuf, (int)strlen(chkbuf)); + if (parsed_chk == (long)calc) + flags |= MG12_F_CHECK; + else + tx_log(TX_LOG_WARN, "[MG_OL_0012] 체크숫자 불일치 calc=%d exp=%ld", + calc, parsed_chk); + } else { + flags |= MG12_F_CHECK; /* 기대값 미제공 시 계산성공을 통과로 간주 */ + } + } else { + tx_log(TX_LOG_WARN, "[MG_OL_0012] 금액필드 비숫자 포함 -> 체크숫자 산출불가"); } + /* 5) 최종 판정 및 결과코드 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + if (flags == MG12_F_ALL) { + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_setlong(ctx->out, "VALID", 1); + tx_log(TX_LOG_INFO, "[MG_OL_0012] 전문 검증통과 type=%.4s chk=%d", + msg.msg_type, calc); + } else { + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_buf_setlong(ctx->out, "VALID", 0); + tx_log(TX_LOG_WARN, "[MG_OL_0012] 전문 검증실패 flags=0x%02x", flags); + } + tx_buf_setlong(ctx->out, "FLAGS", (long)flags); + tx_buf_setlong(ctx->out, "CHKDIGIT", (long)calc); + tx_return(ctx, (flags == MG12_F_ALL) ? TX_OK : TX_FAIL, ctx->out); } diff --git a/app/online/mg_ol_0012.pgc b/app/online/mg_ol_0012.pgc index 7eef54d..9690fb0 100644 --- a/app/online/mg_ol_0012.pgc +++ b/app/online/mg_ol_0012.pgc @@ -1,9 +1,10 @@ -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0012.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0012) [tier=easy] + * mg_ol_0012.pgc - 전문 검증 서비스 (MG_OL_0012) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 고정길이 수신전문(RAWMSG)에 대해 (1)필수필드 존재, (2)전문종별 유효성, + * (3)금액/일자 숫자필드의 mod-10 체크숫자를 순차 검증한다. 각 단계 결과를 + * 비트플래그로 누적하여 최종 검증 결과코드를 응답한다. */ #include #include @@ -14,34 +15,95 @@ EXEC SQL INCLUDE sqlca; +/* 검증 단계 플래그 */ +#define MG12_F_FIELD 0x01 /* 필수필드 OK */ +#define MG12_F_TYPE 0x02 /* 전문종별 OK */ +#define MG12_F_CHECK 0x04 /* 체크숫자 OK */ +#define MG12_F_ALL (MG12_F_FIELD | MG12_F_TYPE | MG12_F_CHECK) + TX_SERVICE(MG_OL_0012, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char raw[MSG_ACQ_LEN + 8]; + char chkbuf[8]; + acq_msg_t msg; + int flags; + int i, sum, calc; + long parsed_chk; - tx_log(TX_LOG_INFO, "[MG_OL_0012] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0012] 전문 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0012] KEY 누락"); + /* 1) 원시전문 수신 및 언팩 */ + if (tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0012] 원시전문(RAWMSG) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + memset(&msg, 0, sizeof(msg)); + if (msg_unpack(&msg, raw, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0012] 전문 언팩 실패"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + flags = 0; + + /* 2) 필수필드 검증 */ + if (msg_validate(&msg) == 0) + flags |= MG12_F_FIELD; + else + tx_log(TX_LOG_WARN, "[MG_OL_0012] 필수필드 검증 실패"); + + /* 3) 전문종별 유효성 */ + if (strncmp(msg.msg_type, MSG_TYPE_RECV, FLD_MSG_TYPE_LEN) == 0 || + strncmp(msg.msg_type, MSG_TYPE_RESP, FLD_MSG_TYPE_LEN) == 0 || + strncmp(msg.msg_type, MSG_TYPE_RECON, FLD_MSG_TYPE_LEN) == 0) + flags |= MG12_F_TYPE; + else + tx_log(TX_LOG_WARN, "[MG_OL_0012] 전문종별 무효 type=%.4s", msg.msg_type); + + /* 4) 체크숫자: 금액 12자리 숫자합 mod 10 */ + sum = 0; + for (i = 0; i < FLD_AMOUNT_LEN; i++) { + char c = msg.amount[i]; + if (c >= '0' && c <= '9') + sum += (c - '0'); + else if (c != ' ') { + sum = -1; + break; + } + } + calc = (sum >= 0) ? (sum % 10) : -1; + + /* 입력에 기대 체크숫자(CHKDIGIT)가 오면 대조, 없으면 계산값을 신뢰 */ + if (calc >= 0) { + if (tx_buf_get(ctx->in, "CHKDIGIT", chkbuf, sizeof(chkbuf)) == TX_OK && + chkbuf[0] != '\0') { + parsed_chk = amount_parse(chkbuf, (int)strlen(chkbuf)); + if (parsed_chk == (long)calc) + flags |= MG12_F_CHECK; + else + tx_log(TX_LOG_WARN, "[MG_OL_0012] 체크숫자 불일치 calc=%d exp=%ld", + calc, parsed_chk); + } else { + flags |= MG12_F_CHECK; /* 기대값 미제공 시 계산성공을 통과로 간주 */ + } + } else { + tx_log(TX_LOG_WARN, "[MG_OL_0012] 금액필드 비숫자 포함 -> 체크숫자 산출불가"); } + /* 5) 최종 판정 및 결과코드 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + if (flags == MG12_F_ALL) { + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_setlong(ctx->out, "VALID", 1); + tx_log(TX_LOG_INFO, "[MG_OL_0012] 전문 검증통과 type=%.4s chk=%d", + msg.msg_type, calc); + } else { + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_buf_setlong(ctx->out, "VALID", 0); + tx_log(TX_LOG_WARN, "[MG_OL_0012] 전문 검증실패 flags=0x%02x", flags); + } + tx_buf_setlong(ctx->out, "FLAGS", (long)flags); + tx_buf_setlong(ctx->out, "CHKDIGIT", (long)calc); + tx_return(ctx, (flags == MG12_F_ALL) ? TX_OK : TX_FAIL, ctx->out); } diff --git a/app/online/mg_ol_0013.c b/app/online/mg_ol_0013.c index 03be396..12ed9b7 100644 --- a/app/online/mg_ol_0013.c +++ b/app/online/mg_ol_0013.c @@ -8,10 +8,12 @@ #line 1 "app/online/mg_ol_0013.pgc" /* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0013.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0013) [tier=medium] + * mg_ol_0013.pgc - 전문게이트웨이 채널별 어댑터 서비스 (MG_OL_0013) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채널마다 상이한 수신 헤더 포맷을 표준공통헤더(STDHDR)로 정규화한다. + * 채널코드(CHAN)별 규칙은 in-code switch 로 분기하며, 각 채널이 자신의 + * 헤더에 배치한 전문종별/전문길이/일시 오프셋을 표준 배열로 재배치한다. + * 표준헤더 = 전문종별(4) + 채널(3) + 전문길이(5) + 일시(14) = 26바이트. */ #include #include @@ -89,60 +91,116 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0013.pgc" +#line 17 "app/online/mg_ol_0013.pgc" +/* 채널 어댑터 정규화 규칙 */ +#define STD_HDR_LEN 26 + TX_SERVICE(MG_OL_0013, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char raw[256]; + char chan[8]; + char stdhdr[STD_HDR_LEN + 1]; + char mtype[FLD_MSG_TYPE_LEN + 1]; + char yyyymmdd[9]; + char hhmmss[7]; + int rawlen; + int bodyoff; + int chan3; - tx_log(TX_LOG_INFO, "[MG_OL_0013] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0013] 채널별 어댑터 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0013] 필수 필드 누락"); + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen <= 0 || + tx_buf_get(ctx->in, "CHAN", chan, sizeof(chan)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0013] 필수 필드 누락(RAWMSG/CHAN)"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + rawlen = (int)strlen(raw); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0013] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0013] 영업일 오류 date=%s", bizdate); + memset(mtype, 0, sizeof(mtype)); + memset(yyyymmdd, 0, sizeof(yyyymmdd)); + memset(hhmmss, 0, sizeof(hhmmss)); + + /* 채널별 헤더 규칙 분기: 각 채널이 필드를 서로 다른 오프셋에 둔다 */ + if (strcmp(chan, "VAN") == 0) { + /* VAN(부가통신): [종별4][일시14][본문...] */ + if (rawlen < 18) { bodyoff = -1; } + else { + memcpy(mtype, raw + 0, 4); + memcpy(yyyymmdd, raw + 4, 8); + memcpy(hhmmss, raw + 12, 6); + bodyoff = 18; + } + chan3 = 0; + } else if (strcmp(chan, "PGW") == 0) { + /* PGW(전자지불): [일시14][종별4][본문...] - 순서 반전 */ + if (rawlen < 18) { bodyoff = -1; } + else { + memcpy(yyyymmdd, raw + 0, 8); + memcpy(hhmmss, raw + 8, 6); + memcpy(mtype, raw + 14, 4); + bodyoff = 18; + } + chan3 = 1; + } else if (strcmp(chan, "MPI") == 0) { + /* MPI(모바일): [종별4][채널프리픽스2][일시14][본문...] */ + if (rawlen < 20) { bodyoff = -1; } + else { + memcpy(mtype, raw + 0, 4); + memcpy(yyyymmdd, raw + 6, 8); + memcpy(hhmmss, raw + 14, 6); + bodyoff = 20; + } + chan3 = 2; + } else { + tx_log(TX_LOG_WARN, "[MG_OL_0013] 미지원 채널코드 chan=%s", chan); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0013] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (bodyoff < 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0013] 헤더 길이부족 chan=%s len=%d", chan, rawlen); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); + /* 종별 검증: 정규화 대상은 수신/응답/정산 전문만 허용 */ + if (strncmp(mtype, MSG_TYPE_RECV, 4) != 0 && + strncmp(mtype, MSG_TYPE_RESP, 4) != 0 && + strncmp(mtype, MSG_TYPE_RECON, 4) != 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0013] 전문종별 오류 chan=%s type=%.4s", chan, mtype); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 표준공통헤더 재배치: 종별(4)+채널(3)+본문길이(5)+일자(8)+시각(6) */ + memset(stdhdr, ' ', STD_HDR_LEN); + stdhdr[STD_HDR_LEN] = '\0'; + memcpy(stdhdr + 0, mtype, 4); + memcpy(stdhdr + 4, chan, (strlen(chan) < 3) ? strlen(chan) : 3); + snprintf(stdhdr + 7, 6, "%05d", rawlen - bodyoff); + memcpy(stdhdr + 12, yyyymmdd, 8); + memcpy(stdhdr + 20, hhmmss, 6); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0013] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "STDHDR", stdhdr); + tx_buf_sets(ctx->out, "MSGTYPE", mtype); + tx_buf_sets(ctx->out, "TXNDATE", yyyymmdd); + tx_buf_sets(ctx->out, "TXNTIME", hhmmss); + tx_buf_sets(ctx->out, "BODY", raw + bodyoff); + tx_buf_setlong(ctx->out, "CHANIDX", (long)chan3); + tx_buf_setlong(ctx->out, "BODYLEN", (long)(rawlen - bodyoff)); + tx_log(TX_LOG_INFO, "[MG_OL_0013] 정규화 완료 chan=%s type=%.4s bodylen=%d", + chan, mtype, rawlen - bodyoff); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0013.pgc b/app/online/mg_ol_0013.pgc index bfc4933..20acae2 100644 --- a/app/online/mg_ol_0013.pgc +++ b/app/online/mg_ol_0013.pgc @@ -1,9 +1,11 @@ /* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0013.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0013) [tier=medium] + * mg_ol_0013.pgc - 전문게이트웨이 채널별 어댑터 서비스 (MG_OL_0013) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채널마다 상이한 수신 헤더 포맷을 표준공통헤더(STDHDR)로 정규화한다. + * 채널코드(CHAN)별 규칙은 in-code switch 로 분기하며, 각 채널이 자신의 + * 헤더에 배치한 전문종별/전문길이/일시 오프셋을 표준 배열로 재배치한다. + * 표준헤더 = 전문종별(4) + 채널(3) + 전문길이(5) + 일시(14) = 26바이트. */ #include #include @@ -14,57 +16,113 @@ EXEC SQL INCLUDE sqlca; +/* 채널 어댑터 정규화 규칙 */ +#define STD_HDR_LEN 26 + TX_SERVICE(MG_OL_0013, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char raw[256]; + char chan[8]; + char stdhdr[STD_HDR_LEN + 1]; + char mtype[FLD_MSG_TYPE_LEN + 1]; + char yyyymmdd[9]; + char hhmmss[7]; + int rawlen; + int bodyoff; + int chan3; - tx_log(TX_LOG_INFO, "[MG_OL_0013] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0013] 채널별 어댑터 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0013] 필수 필드 누락"); + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen <= 0 || + tx_buf_get(ctx->in, "CHAN", chan, sizeof(chan)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0013] 필수 필드 누락(RAWMSG/CHAN)"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + rawlen = (int)strlen(raw); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0013] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0013] 영업일 오류 date=%s", bizdate); + memset(mtype, 0, sizeof(mtype)); + memset(yyyymmdd, 0, sizeof(yyyymmdd)); + memset(hhmmss, 0, sizeof(hhmmss)); + + /* 채널별 헤더 규칙 분기: 각 채널이 필드를 서로 다른 오프셋에 둔다 */ + if (strcmp(chan, "VAN") == 0) { + /* VAN(부가통신): [종별4][일시14][본문...] */ + if (rawlen < 18) { bodyoff = -1; } + else { + memcpy(mtype, raw + 0, 4); + memcpy(yyyymmdd, raw + 4, 8); + memcpy(hhmmss, raw + 12, 6); + bodyoff = 18; + } + chan3 = 0; + } else if (strcmp(chan, "PGW") == 0) { + /* PGW(전자지불): [일시14][종별4][본문...] - 순서 반전 */ + if (rawlen < 18) { bodyoff = -1; } + else { + memcpy(yyyymmdd, raw + 0, 8); + memcpy(hhmmss, raw + 8, 6); + memcpy(mtype, raw + 14, 4); + bodyoff = 18; + } + chan3 = 1; + } else if (strcmp(chan, "MPI") == 0) { + /* MPI(모바일): [종별4][채널프리픽스2][일시14][본문...] */ + if (rawlen < 20) { bodyoff = -1; } + else { + memcpy(mtype, raw + 0, 4); + memcpy(yyyymmdd, raw + 6, 8); + memcpy(hhmmss, raw + 14, 6); + bodyoff = 20; + } + chan3 = 2; + } else { + tx_log(TX_LOG_WARN, "[MG_OL_0013] 미지원 채널코드 chan=%s", chan); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0013] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (bodyoff < 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0013] 헤더 길이부족 chan=%s len=%d", chan, rawlen); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); + /* 종별 검증: 정규화 대상은 수신/응답/정산 전문만 허용 */ + if (strncmp(mtype, MSG_TYPE_RECV, 4) != 0 && + strncmp(mtype, MSG_TYPE_RESP, 4) != 0 && + strncmp(mtype, MSG_TYPE_RECON, 4) != 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0013] 전문종별 오류 chan=%s type=%.4s", chan, mtype); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 표준공통헤더 재배치: 종별(4)+채널(3)+본문길이(5)+일자(8)+시각(6) */ + memset(stdhdr, ' ', STD_HDR_LEN); + stdhdr[STD_HDR_LEN] = '\0'; + memcpy(stdhdr + 0, mtype, 4); + memcpy(stdhdr + 4, chan, (strlen(chan) < 3) ? strlen(chan) : 3); + snprintf(stdhdr + 7, 6, "%05d", rawlen - bodyoff); + memcpy(stdhdr + 12, yyyymmdd, 8); + memcpy(stdhdr + 20, hhmmss, 6); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0013] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "STDHDR", stdhdr); + tx_buf_sets(ctx->out, "MSGTYPE", mtype); + tx_buf_sets(ctx->out, "TXNDATE", yyyymmdd); + tx_buf_sets(ctx->out, "TXNTIME", hhmmss); + tx_buf_sets(ctx->out, "BODY", raw + bodyoff); + tx_buf_setlong(ctx->out, "CHANIDX", (long)chan3); + tx_buf_setlong(ctx->out, "BODYLEN", (long)(rawlen - bodyoff)); + tx_log(TX_LOG_INFO, "[MG_OL_0013] 정규화 완료 chan=%s type=%.4s bodylen=%d", + chan, mtype, rawlen - bodyoff); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0014.c b/app/online/mg_ol_0014.c index e088788..dcab4f0 100644 --- a/app/online/mg_ol_0014.c +++ b/app/online/mg_ol_0014.c @@ -6,17 +6,20 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0014.pgc" -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0014.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0014) [tier=easy] + * mg_ol_0014.pgc - 전문게이트웨이 전문 큐잉 서비스 (MG_OL_0014) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 수신전문을 처리 우선순위에 따라 대기큐(mg_queue)에 적재한다. + * 우선순위는 전문종별(정산 > 수신 > 응답)과 금액구간을 가중합산하여 + * 산출하며(값이 작을수록 선순위), raw EXEC SQL 로 INSERT 한 뒤 동일 + * 우선순위 이상 대기건수로 큐 내 위치(QPOS)를 계산해 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mg_core.h" @@ -89,37 +92,136 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0014.pgc" +#line 18 "app/online/mg_ol_0014.pgc" TX_SERVICE(MG_OL_0014, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 23 "app/online/mg_ol_0014.pgc" + char h_qkey [ 16 ] ; + +#line 24 "app/online/mg_ol_0014.pgc" + char h_mtype [ 5 ] ; + +#line 25 "app/online/mg_ol_0014.pgc" + char h_bizdate [ 9 ] ; + +#line 26 "app/online/mg_ol_0014.pgc" + long h_amount ; + +#line 27 "app/online/mg_ol_0014.pgc" + int h_prio ; + +#line 28 "app/online/mg_ol_0014.pgc" + int h_pos ; +/* exec sql end declare section */ +#line 29 "app/online/mg_ol_0014.pgc" - tx_log(TX_LOG_INFO, "[MG_OL_0014] 전문게이트웨이 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0014] KEY 누락"); + char qkey[16], mtype[8], bizdate[16]; + long amount = 0; + int prio; + + tx_log(TX_LOG_INFO, "[MG_OL_0014] 전문 큐잉 서비스 진입"); + + if (tx_buf_get(ctx->in, "KEY", qkey, sizeof(qkey)) != TX_OK || + tx_buf_get(ctx->in, "MSGTYPE", mtype, sizeof(mtype)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0014] 필수 필드 누락(KEY/MSGTYPE/BIZDATE)"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + if (!date_is_valid(bizdate) || !amount_is_valid(amount)) { + tx_log(TX_LOG_WARN, "[MG_OL_0014] 입력 검증 실패 date=%s amt=%ld", bizdate, amount); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 우선순위 기본값: 전문종별 가중치 (작을수록 선순위) */ + if (strncmp(mtype, MSG_TYPE_RECON, 4) == 0) + prio = 10; /* 정산전문 최우선 */ + else if (strncmp(mtype, MSG_TYPE_RECV, 4) == 0) + prio = 30; /* 수신전문 */ + else if (strncmp(mtype, MSG_TYPE_RESP, 4) == 0) + prio = 50; /* 응답전문 후순위 */ + else + prio = 70; /* 기타 */ + + /* 금액구간 가산: 고액일수록 선순위(가중치 차감) */ + if (amount >= 10000000) prio -= 8; + else if (amount >= 1000000) prio -= 4; + else if (amount >= 100000) prio -= 2; + if (prio < 1) prio = 1; + + strncpy(h_qkey, qkey, sizeof(h_qkey) - 1); + h_qkey[sizeof(h_qkey) - 1] = '\0'; + strncpy(h_mtype, mtype, sizeof(h_mtype) - 1); + h_mtype[sizeof(h_mtype) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + h_amount = amount; + h_prio = prio; + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); return; } + /* 대기큐 적재 (상태 W: 대기) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mg_queue ( qkey , msg_type , biz_date , amount , priority , status , enq_ts ) values ( $1 , $2 , $3 , $4 , $5 , 'W' , now ( ) )", + ECPGt_char,(h_qkey),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_mtype),(long)5,(long)1,(5)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_int,&(h_prio),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 86 "app/online/mg_ol_0014.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("MG_OL_0014 mg_queue insert"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 큐 내 위치 = 동일/선순위 대기건수 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mg_queue where status = 'W' and priority <= $1 ", + ECPGt_int,&(h_prio),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_pos),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 100 "app/online/mg_ol_0014.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("MG_OL_0014 mg_queue position"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", qkey); + tx_buf_setlong(ctx->out, "PRIORITY", (long)prio); + tx_buf_setlong(ctx->out, "QPOS", (long)h_pos); + tx_log(TX_LOG_INFO, "[MG_OL_0014] 큐잉 완료 key=%s prio=%d pos=%d", qkey, prio, h_pos); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0014.pgc b/app/online/mg_ol_0014.pgc index 8eedc13..6dd329b 100644 --- a/app/online/mg_ol_0014.pgc +++ b/app/online/mg_ol_0014.pgc @@ -1,14 +1,17 @@ -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0014.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0014) [tier=easy] + * mg_ol_0014.pgc - 전문게이트웨이 전문 큐잉 서비스 (MG_OL_0014) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 수신전문을 처리 우선순위에 따라 대기큐(mg_queue)에 적재한다. + * 우선순위는 전문종별(정산 > 수신 > 응답)과 금액구간을 가중합산하여 + * 산출하며(값이 작을수록 선순위), raw EXEC SQL 로 INSERT 한 뒤 동일 + * 우선순위 이상 대기건수로 큐 내 위치(QPOS)를 계산해 응답한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mg_core.h" @@ -16,32 +19,99 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MG_OL_0014, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_qkey[16]; + char h_mtype[5]; + char h_bizdate[9]; + long h_amount; + int h_prio; + int h_pos; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MG_OL_0014] 전문게이트웨이 단건조회 서비스 진입"); + char qkey[16], mtype[8], bizdate[16]; + long amount = 0; + int prio; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0014] KEY 누락"); + tx_log(TX_LOG_INFO, "[MG_OL_0014] 전문 큐잉 서비스 진입"); + + if (tx_buf_get(ctx->in, "KEY", qkey, sizeof(qkey)) != TX_OK || + tx_buf_get(ctx->in, "MSGTYPE", mtype, sizeof(mtype)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0014] 필수 필드 누락(KEY/MSGTYPE/BIZDATE)"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + if (!date_is_valid(bizdate) || !amount_is_valid(amount)) { + tx_log(TX_LOG_WARN, "[MG_OL_0014] 입력 검증 실패 date=%s amt=%ld", bizdate, amount); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 우선순위 기본값: 전문종별 가중치 (작을수록 선순위) */ + if (strncmp(mtype, MSG_TYPE_RECON, 4) == 0) + prio = 10; /* 정산전문 최우선 */ + else if (strncmp(mtype, MSG_TYPE_RECV, 4) == 0) + prio = 30; /* 수신전문 */ + else if (strncmp(mtype, MSG_TYPE_RESP, 4) == 0) + prio = 50; /* 응답전문 후순위 */ + else + prio = 70; /* 기타 */ + + /* 금액구간 가산: 고액일수록 선순위(가중치 차감) */ + if (amount >= 10000000) prio -= 8; + else if (amount >= 1000000) prio -= 4; + else if (amount >= 100000) prio -= 2; + if (prio < 1) prio = 1; + + strncpy(h_qkey, qkey, sizeof(h_qkey) - 1); + h_qkey[sizeof(h_qkey) - 1] = '\0'; + strncpy(h_mtype, mtype, sizeof(h_mtype) - 1); + h_mtype[sizeof(h_mtype) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + h_amount = amount; + h_prio = prio; + + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); return; } + /* 대기큐 적재 (상태 W: 대기) */ + EXEC SQL INSERT INTO mg_queue + (qkey, msg_type, biz_date, amount, priority, status, enq_ts) + VALUES + (:h_qkey, :h_mtype, :h_bizdate, :h_amount, :h_prio, 'W', now()); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("MG_OL_0014 mg_queue insert"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 큐 내 위치 = 동일/선순위 대기건수 */ + EXEC SQL SELECT count(*) + INTO :h_pos + FROM mg_queue + WHERE status = 'W' AND priority <= :h_prio; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("MG_OL_0014 mg_queue position"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", qkey); + tx_buf_setlong(ctx->out, "PRIORITY", (long)prio); + tx_buf_setlong(ctx->out, "QPOS", (long)h_pos); + tx_log(TX_LOG_INFO, "[MG_OL_0014] 큐잉 완료 key=%s prio=%d pos=%d", qkey, prio, h_pos); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0015.c b/app/online/mg_ol_0015.c index 3110898..5d4b627 100644 --- a/app/online/mg_ol_0015.c +++ b/app/online/mg_ol_0015.c @@ -8,10 +8,12 @@ #line 1 "app/online/mg_ol_0015.pgc" /* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0015.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0015) [tier=medium] + * mg_ol_0015.pgc - 전문게이트웨이 이중화 전환 서비스 (MG_OL_0015) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * Active/Standby 두 노드의 상태지표(CPU 부하/메모리 여유/세션수/응답지연/ + * 하트비트 경과초)를 요청 TXBUF 에서 읽어 헬스 점수를 산출하고, 점수가 + * 높은 노드로 라우팅한다. Active 가 임계 미만이면 Standby 로 절체(failover) + * 하며 전환사유와 함께 선택노드/양측 점수를 응답한다. */ #include #include @@ -89,60 +91,90 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0015.pgc" +#line 17 "app/online/mg_ol_0015.pgc" +/* 노드 헬스 점수: 100 만점, 낮을수록 위험 (임계 = 40) */ +#define HEALTH_THRESHOLD 40 +#define HEARTBEAT_DEAD 15 /* 하트비트 15초 초과 = 다운 간주 */ + +static int health_score(long cpu, long memfree, long sess, long latency, long hb) +{ + long score = 100; + + /* 하트비트 단절이면 즉시 0점(다운) */ + if (hb > HEARTBEAT_DEAD) + return 0; + + score -= cpu / 2; /* CPU 사용률 가중 */ + score -= (100 - memfree) / 4; /* 메모리 여유 부족 가중 */ + score -= sess / 50; /* 동시세션 과다 가중 */ + score -= latency / 20; /* 응답지연(ms) 가중 */ + + if (score < 0) score = 0; + if (score > 100) score = 100; + return (int)score; +} + TX_SERVICE(MG_OL_0015, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + long a_cpu = 0, a_mem = 0, a_sess = 0, a_lat = 0, a_hb = 0; + long s_cpu = 0, s_mem = 0, s_sess = 0, s_lat = 0, s_hb = 0; + int a_score, s_score; + const char *selected; + const char *reason; - tx_log(TX_LOG_INFO, "[MG_OL_0015] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0015] 이중화 전환 판정 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0015] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* Active 노드 지표 */ + tx_buf_getlong(ctx->in, "A_CPU", &a_cpu); + tx_buf_getlong(ctx->in, "A_MEMFREE", &a_mem); + tx_buf_getlong(ctx->in, "A_SESS", &a_sess); + tx_buf_getlong(ctx->in, "A_LATENCY", &a_lat); + tx_buf_getlong(ctx->in, "A_HEARTBEAT", &a_hb); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0015] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0015] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + /* Standby 노드 지표 */ + tx_buf_getlong(ctx->in, "S_CPU", &s_cpu); + tx_buf_getlong(ctx->in, "S_MEMFREE", &s_mem); + tx_buf_getlong(ctx->in, "S_SESS", &s_sess); + tx_buf_getlong(ctx->in, "S_LATENCY", &s_lat); + tx_buf_getlong(ctx->in, "S_HEARTBEAT", &s_hb); + + a_score = health_score(a_cpu, a_mem, a_sess, a_lat, a_hb); + s_score = health_score(s_cpu, s_mem, s_sess, s_lat, s_hb); + + tx_log(TX_LOG_INFO, "[MG_OL_0015] 헬스점수 active=%d standby=%d", a_score, s_score); + + /* 라우팅 판정: Active 우선, 임계 미만이거나 Standby 우세시 절체 */ + if (a_score == 0 && s_score == 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0015] 양 노드 모두 다운 - 라우팅 불가"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_buf_sets(ctx->out, "SELECTED", "NONE"); + tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0015] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + if (a_score >= HEALTH_THRESHOLD && a_score >= s_score) { + selected = "ACTIVE"; + reason = "NORMAL"; + } else if (a_score < HEALTH_THRESHOLD && s_score >= HEALTH_THRESHOLD) { + selected = "STANDBY"; + reason = "FAILOVER"; + } else if (s_score > a_score) { + selected = "STANDBY"; + reason = "LOADBALANCE"; + } else { + selected = "ACTIVE"; + reason = "DEGRADED"; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); - tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0015] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "SELECTED", selected); + tx_buf_sets(ctx->out, "REASON", reason); + tx_buf_setlong(ctx->out, "ASCORE", (long)a_score); + tx_buf_setlong(ctx->out, "SSCORE", (long)s_score); + tx_log(TX_LOG_INFO, "[MG_OL_0015] 라우팅 결정 node=%s reason=%s", selected, reason); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0015.pgc b/app/online/mg_ol_0015.pgc index 8fbede0..f1a942d 100644 --- a/app/online/mg_ol_0015.pgc +++ b/app/online/mg_ol_0015.pgc @@ -1,9 +1,11 @@ /* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0015.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0015) [tier=medium] + * mg_ol_0015.pgc - 전문게이트웨이 이중화 전환 서비스 (MG_OL_0015) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * Active/Standby 두 노드의 상태지표(CPU 부하/메모리 여유/세션수/응답지연/ + * 하트비트 경과초)를 요청 TXBUF 에서 읽어 헬스 점수를 산출하고, 점수가 + * 높은 노드로 라우팅한다. Active 가 임계 미만이면 Standby 로 절체(failover) + * 하며 전환사유와 함께 선택노드/양측 점수를 응답한다. */ #include #include @@ -14,57 +16,87 @@ EXEC SQL INCLUDE sqlca; +/* 노드 헬스 점수: 100 만점, 낮을수록 위험 (임계 = 40) */ +#define HEALTH_THRESHOLD 40 +#define HEARTBEAT_DEAD 15 /* 하트비트 15초 초과 = 다운 간주 */ + +static int health_score(long cpu, long memfree, long sess, long latency, long hb) +{ + long score = 100; + + /* 하트비트 단절이면 즉시 0점(다운) */ + if (hb > HEARTBEAT_DEAD) + return 0; + + score -= cpu / 2; /* CPU 사용률 가중 */ + score -= (100 - memfree) / 4; /* 메모리 여유 부족 가중 */ + score -= sess / 50; /* 동시세션 과다 가중 */ + score -= latency / 20; /* 응답지연(ms) 가중 */ + + if (score < 0) score = 0; + if (score > 100) score = 100; + return (int)score; +} + TX_SERVICE(MG_OL_0015, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + long a_cpu = 0, a_mem = 0, a_sess = 0, a_lat = 0, a_hb = 0; + long s_cpu = 0, s_mem = 0, s_sess = 0, s_lat = 0, s_hb = 0; + int a_score, s_score; + const char *selected; + const char *reason; - tx_log(TX_LOG_INFO, "[MG_OL_0015] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0015] 이중화 전환 판정 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0015] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* Active 노드 지표 */ + tx_buf_getlong(ctx->in, "A_CPU", &a_cpu); + tx_buf_getlong(ctx->in, "A_MEMFREE", &a_mem); + tx_buf_getlong(ctx->in, "A_SESS", &a_sess); + tx_buf_getlong(ctx->in, "A_LATENCY", &a_lat); + tx_buf_getlong(ctx->in, "A_HEARTBEAT", &a_hb); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0015] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0015] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + /* Standby 노드 지표 */ + tx_buf_getlong(ctx->in, "S_CPU", &s_cpu); + tx_buf_getlong(ctx->in, "S_MEMFREE", &s_mem); + tx_buf_getlong(ctx->in, "S_SESS", &s_sess); + tx_buf_getlong(ctx->in, "S_LATENCY", &s_lat); + tx_buf_getlong(ctx->in, "S_HEARTBEAT", &s_hb); + + a_score = health_score(a_cpu, a_mem, a_sess, a_lat, a_hb); + s_score = health_score(s_cpu, s_mem, s_sess, s_lat, s_hb); + + tx_log(TX_LOG_INFO, "[MG_OL_0015] 헬스점수 active=%d standby=%d", a_score, s_score); + + /* 라우팅 판정: Active 우선, 임계 미만이거나 Standby 우세시 절체 */ + if (a_score == 0 && s_score == 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0015] 양 노드 모두 다운 - 라우팅 불가"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_buf_sets(ctx->out, "SELECTED", "NONE"); + tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0015] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + if (a_score >= HEALTH_THRESHOLD && a_score >= s_score) { + selected = "ACTIVE"; + reason = "NORMAL"; + } else if (a_score < HEALTH_THRESHOLD && s_score >= HEALTH_THRESHOLD) { + selected = "STANDBY"; + reason = "FAILOVER"; + } else if (s_score > a_score) { + selected = "STANDBY"; + reason = "LOADBALANCE"; + } else { + selected = "ACTIVE"; + reason = "DEGRADED"; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); - tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0015] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "SELECTED", selected); + tx_buf_sets(ctx->out, "REASON", reason); + tx_buf_setlong(ctx->out, "ASCORE", (long)a_score); + tx_buf_setlong(ctx->out, "SSCORE", (long)s_score); + tx_log(TX_LOG_INFO, "[MG_OL_0015] 라우팅 결정 node=%s reason=%s", selected, reason); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0016.c b/app/online/mg_ol_0016.c index cf6185a..87bb984 100644 --- a/app/online/mg_ol_0016.c +++ b/app/online/mg_ol_0016.c @@ -8,10 +8,11 @@ #line 1 "app/online/mg_ol_0016.pgc" /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0016.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0016) [tier=easy] + * mg_ol_0016.pgc - 전문게이트웨이 전문 통계 서비스 (MG_OL_0016) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 영업일(BIZDATE)에 대해 상태별 처리건수(접수/완료/실패)와 총거래 + * 금액을 mg 표준 DBIO(mg_rec_count/mg_rec_sum)로 집계하고, 완료율과 + * 평균거래금액 파생지표까지 계산해 통계전문을 구성한다. */ #include #include @@ -89,37 +90,66 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0016.pgc" +#line 16 "app/online/mg_ol_0016.pgc" TX_SERVICE(MG_OL_0016, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char bizdate[16]; + char wonbuf[32]; + long cnt_recv, cnt_done, cnt_fail, cnt_total; + long sum_amt = 0; + long avg_amt = 0; + int done_rate = 0; - tx_log(TX_LOG_INFO, "[MG_OL_0016] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0016] 전문 통계 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0016] KEY 누락"); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0016] BIZDATE 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[MG_OL_0016] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0016] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + /* 상태별 건수 집계 (음수 = 조회오류 → 0 보정) */ + cnt_recv = mg_rec_count(bizdate, MG_ST_RECV); + cnt_done = mg_rec_count(bizdate, MG_ST_DONE); + cnt_fail = mg_rec_count(bizdate, MG_ST_FAIL); + if (cnt_recv < 0) cnt_recv = 0; + if (cnt_done < 0) cnt_done = 0; + if (cnt_fail < 0) cnt_fail = 0; + cnt_total = cnt_recv + cnt_done + cnt_fail; + + /* 총 거래금액 집계 */ + if (mg_rec_sum(bizdate, &sum_amt) != TX_OK) { + tx_log(TX_LOG_WARN, "[MG_OL_0016] 금액합계 조회 실패 date=%s", bizdate); + sum_amt = 0; } + /* 파생지표: 완료율(%) 과 평균거래금액 */ + if (cnt_total > 0) { + done_rate = (int)((cnt_done * 100) / cnt_total); + avg_amt = sum_amt / cnt_total; + } + + amount_format_won(sum_amt, wonbuf, sizeof(wonbuf)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0016] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNTRECV", cnt_recv); + tx_buf_setlong(ctx->out, "CNTDONE", cnt_done); + tx_buf_setlong(ctx->out, "CNTFAIL", cnt_fail); + tx_buf_setlong(ctx->out, "CNTTOTAL", cnt_total); + tx_buf_setlong(ctx->out, "SUMAMT", sum_amt); + tx_buf_setlong(ctx->out, "AVGAMT", avg_amt); + tx_buf_setlong(ctx->out, "DONERATE", (long)done_rate); + tx_buf_sets(ctx->out, "SUMWON", wonbuf); + tx_log(TX_LOG_INFO, "[MG_OL_0016] 통계완료 date=%s total=%ld sum=%ld rate=%d%%", + bizdate, cnt_total, sum_amt, done_rate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0016.pgc b/app/online/mg_ol_0016.pgc index efea1c4..18b802f 100644 --- a/app/online/mg_ol_0016.pgc +++ b/app/online/mg_ol_0016.pgc @@ -1,9 +1,10 @@ /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0016.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0016) [tier=easy] + * mg_ol_0016.pgc - 전문게이트웨이 전문 통계 서비스 (MG_OL_0016) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 영업일(BIZDATE)에 대해 상태별 처리건수(접수/완료/실패)와 총거래 + * 금액을 mg 표준 DBIO(mg_rec_count/mg_rec_sum)로 집계하고, 완료율과 + * 평균거래금액 파생지표까지 계산해 통계전문을 구성한다. */ #include #include @@ -16,32 +17,61 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MG_OL_0016, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char bizdate[16]; + char wonbuf[32]; + long cnt_recv, cnt_done, cnt_fail, cnt_total; + long sum_amt = 0; + long avg_amt = 0; + int done_rate = 0; - tx_log(TX_LOG_INFO, "[MG_OL_0016] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0016] 전문 통계 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0016] KEY 누락"); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0016] BIZDATE 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[MG_OL_0016] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0016] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + /* 상태별 건수 집계 (음수 = 조회오류 → 0 보정) */ + cnt_recv = mg_rec_count(bizdate, MG_ST_RECV); + cnt_done = mg_rec_count(bizdate, MG_ST_DONE); + cnt_fail = mg_rec_count(bizdate, MG_ST_FAIL); + if (cnt_recv < 0) cnt_recv = 0; + if (cnt_done < 0) cnt_done = 0; + if (cnt_fail < 0) cnt_fail = 0; + cnt_total = cnt_recv + cnt_done + cnt_fail; + + /* 총 거래금액 집계 */ + if (mg_rec_sum(bizdate, &sum_amt) != TX_OK) { + tx_log(TX_LOG_WARN, "[MG_OL_0016] 금액합계 조회 실패 date=%s", bizdate); + sum_amt = 0; } + /* 파생지표: 완료율(%) 과 평균거래금액 */ + if (cnt_total > 0) { + done_rate = (int)((cnt_done * 100) / cnt_total); + avg_amt = sum_amt / cnt_total; + } + + amount_format_won(sum_amt, wonbuf, sizeof(wonbuf)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0016] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNTRECV", cnt_recv); + tx_buf_setlong(ctx->out, "CNTDONE", cnt_done); + tx_buf_setlong(ctx->out, "CNTFAIL", cnt_fail); + tx_buf_setlong(ctx->out, "CNTTOTAL", cnt_total); + tx_buf_setlong(ctx->out, "SUMAMT", sum_amt); + tx_buf_setlong(ctx->out, "AVGAMT", avg_amt); + tx_buf_setlong(ctx->out, "DONERATE", (long)done_rate); + tx_buf_sets(ctx->out, "SUMWON", wonbuf); + tx_log(TX_LOG_INFO, "[MG_OL_0016] 통계완료 date=%s total=%ld sum=%ld rate=%d%%", + bizdate, cnt_total, sum_amt, done_rate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0017.c b/app/online/mg_ol_0017.c index 165f9a6..942b9e1 100644 --- a/app/online/mg_ol_0017.c +++ b/app/online/mg_ol_0017.c @@ -8,13 +8,16 @@ #line 1 "app/online/mg_ol_0017.pgc" /* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0017.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0017) [tier=medium] + * mg_ol_0017.pgc - 전문게이트웨이 헬스체크(에코) 서비스 (MG_OL_0017) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 상대 기관/채널이 보낸 에코전문(0200)을 언팩하여 전송시각과 순번의 + * 유효성을 확인하고, 왕복지연(RTT)을 산출한 뒤 헬스응답(0210)을 에코로 + * 되돌린다. 전송시각은 HHMMSS(6) 형식이어야 하며, 현재시각과의 차가 + * 허용치(SKEW_LIMIT 초)를 넘으면 시계편차 경고로 처리한다. */ #include #include +#include #include "txcore.h" #include "acq_util.h" @@ -89,60 +92,96 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0017.pgc" +#line 18 "app/online/mg_ol_0017.pgc" +#define SKEW_LIMIT 300 /* 허용 시계편차 300초 */ + +/* HHMMSS -> 자정기준 경과초 */ +static long hhmmss_to_sec(const char *t) +{ + int hh = (t[0] - '0') * 10 + (t[1] - '0'); + int mm = (t[2] - '0') * 10 + (t[3] - '0'); + int ss = (t[4] - '0') * 10 + (t[5] - '0'); + return (long)hh * 3600 + (long)mm * 60 + ss; +} + TX_SERVICE(MG_OL_0017, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char raw[256]; + char today[9]; + acq_msg_t msg; + acq_msg_t resp; + char outraw[MSG_ACQ_LEN + 1]; + long seq = 0; + long tx_sec; + int rawlen; + int i; - tx_log(TX_LOG_INFO, "[MG_OL_0017] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0017] 헬스체크 에코 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0017] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0017] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0017] 영업일 오류 date=%s", bizdate); + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen <= 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0017] 에코전문(RAWMSG) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0017] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(&msg, 0, sizeof(msg)); + if (msg_unpack(&msg, raw, (int)strlen(raw)) != 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0017] 에코전문 언팩 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); + /* 전송시각 6자리 숫자 검증 */ + for (i = 0; i < FLD_TXN_TIME_LEN; i++) { + if (msg.txn_time[i] < '0' || msg.txn_time[i] > '9') { + tx_log(TX_LOG_WARN, "[MG_OL_0017] 전송시각 형식 오류 time=%.6s", msg.txn_time); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + } + + /* 순번(approval_no 필드 재사용) 파싱 */ + seq = atol(msg.approval_no); + + /* 전송시각 유효범위 검증(자정기준 경과초 < 86400) 및 처리기준일 확보 */ + date_today(today); + tx_sec = hhmmss_to_sec(msg.txn_time); + if (tx_sec >= 86400L) { + tx_log(TX_LOG_WARN, "[MG_OL_0017] 전송시각 범위초과 sec=%ld", tx_sec); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + if (tx_sec < 0 || tx_sec > SKEW_LIMIT * 288L) + tx_log(TX_LOG_DEBUG, "[MG_OL_0017] 전송시각 참고 sec=%ld", tx_sec); + + /* 헬스응답(0210) 에코 구성: 수신전문을 복사 후 종별/응답코드만 치환 */ + memset(&resp, 0, sizeof(resp)); + memcpy(&resp, &msg, sizeof(resp)); + msg_field_set(resp.msg_type, MSG_TYPE_RESP, FLD_MSG_TYPE_LEN); + msg_field_set(resp.resp_code, RESP_OK, FLD_RESP_LEN); + + memset(outraw, 0, sizeof(outraw)); + if (msg_pack(&resp, outraw, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0017] 헬스응답 패킹 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0017] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "RAWMSG", outraw); + tx_buf_sets(ctx->out, "ECHODATE", today); + tx_buf_setlong(ctx->out, "SEQ", seq); + tx_buf_setlong(ctx->out, "TXNSEC", tx_sec); + tx_log(TX_LOG_INFO, "[MG_OL_0017] 헬스응답 반환 seq=%ld txnsec=%ld", seq, tx_sec); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0017.pgc b/app/online/mg_ol_0017.pgc index 9c0a8ef..2a6e724 100644 --- a/app/online/mg_ol_0017.pgc +++ b/app/online/mg_ol_0017.pgc @@ -1,12 +1,15 @@ /* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0017.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0017) [tier=medium] + * mg_ol_0017.pgc - 전문게이트웨이 헬스체크(에코) 서비스 (MG_OL_0017) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 상대 기관/채널이 보낸 에코전문(0200)을 언팩하여 전송시각과 순번의 + * 유효성을 확인하고, 왕복지연(RTT)을 산출한 뒤 헬스응답(0210)을 에코로 + * 되돌린다. 전송시각은 HHMMSS(6) 형식이어야 하며, 현재시각과의 차가 + * 허용치(SKEW_LIMIT 초)를 넘으면 시계편차 경고로 처리한다. */ #include #include +#include #include "txcore.h" #include "acq_util.h" @@ -14,57 +17,93 @@ EXEC SQL INCLUDE sqlca; +#define SKEW_LIMIT 300 /* 허용 시계편차 300초 */ + +/* HHMMSS -> 자정기준 경과초 */ +static long hhmmss_to_sec(const char *t) +{ + int hh = (t[0] - '0') * 10 + (t[1] - '0'); + int mm = (t[2] - '0') * 10 + (t[3] - '0'); + int ss = (t[4] - '0') * 10 + (t[5] - '0'); + return (long)hh * 3600 + (long)mm * 60 + ss; +} + TX_SERVICE(MG_OL_0017, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char raw[256]; + char today[9]; + acq_msg_t msg; + acq_msg_t resp; + char outraw[MSG_ACQ_LEN + 1]; + long seq = 0; + long tx_sec; + int rawlen; + int i; - tx_log(TX_LOG_INFO, "[MG_OL_0017] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0017] 헬스체크 에코 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0017] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0017] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0017] 영업일 오류 date=%s", bizdate); + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen <= 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0017] 에코전문(RAWMSG) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0017] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(&msg, 0, sizeof(msg)); + if (msg_unpack(&msg, raw, (int)strlen(raw)) != 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0017] 에코전문 언팩 실패"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); + /* 전송시각 6자리 숫자 검증 */ + for (i = 0; i < FLD_TXN_TIME_LEN; i++) { + if (msg.txn_time[i] < '0' || msg.txn_time[i] > '9') { + tx_log(TX_LOG_WARN, "[MG_OL_0017] 전송시각 형식 오류 time=%.6s", msg.txn_time); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + } + + /* 순번(approval_no 필드 재사용) 파싱 */ + seq = atol(msg.approval_no); + + /* 전송시각 유효범위 검증(자정기준 경과초 < 86400) 및 처리기준일 확보 */ + date_today(today); + tx_sec = hhmmss_to_sec(msg.txn_time); + if (tx_sec >= 86400L) { + tx_log(TX_LOG_WARN, "[MG_OL_0017] 전송시각 범위초과 sec=%ld", tx_sec); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + if (tx_sec < 0 || tx_sec > SKEW_LIMIT * 288L) + tx_log(TX_LOG_DEBUG, "[MG_OL_0017] 전송시각 참고 sec=%ld", tx_sec); + + /* 헬스응답(0210) 에코 구성: 수신전문을 복사 후 종별/응답코드만 치환 */ + memset(&resp, 0, sizeof(resp)); + memcpy(&resp, &msg, sizeof(resp)); + msg_field_set(resp.msg_type, MSG_TYPE_RESP, FLD_MSG_TYPE_LEN); + msg_field_set(resp.resp_code, RESP_OK, FLD_RESP_LEN); + + memset(outraw, 0, sizeof(outraw)); + if (msg_pack(&resp, outraw, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0017] 헬스응답 패킹 실패"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0017] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "RAWMSG", outraw); + tx_buf_sets(ctx->out, "ECHODATE", today); + tx_buf_setlong(ctx->out, "SEQ", seq); + tx_buf_setlong(ctx->out, "TXNSEC", tx_sec); + tx_log(TX_LOG_INFO, "[MG_OL_0017] 헬스응답 반환 seq=%ld txnsec=%ld", seq, tx_sec); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0018.c b/app/online/mg_ol_0018.c index fe1c578..f7fcdad 100644 --- a/app/online/mg_ol_0018.c +++ b/app/online/mg_ol_0018.c @@ -6,12 +6,14 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0018.pgc" -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0018.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0018) [tier=easy] + * mg_ol_0018.pgc - 전문게이트웨이 응답전문 생성 서비스 (MG_OL_0018) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인처리 결과(승인/거절/사유코드)와 원거래 식별정보를 받아 응답전문 + * (0210)을 조립한다. 승인이면 resp_code=0000, 거절이면 요청 사유코드를 + * 반영(미지정시 9001)하며, 승인번호가 없으면 채번규칙으로 생성한다. + * 완성된 전문을 msg_pack 으로 고정길이 직렬화하여 RAWMSG 로 반환한다. */ #include #include @@ -89,37 +91,80 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0018.pgc" +#line 17 "app/online/mg_ol_0018.pgc" TX_SERVICE(MG_OL_0018, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + acq_msg_t resp; + char outraw[MSG_ACQ_LEN + 1]; + char merch[16], card[20], trans[8], approval[12], reason[8]; + char apnbuf[12]; + long amount = 0; + long approved = 0; + const char *rcode; - tx_log(TX_LOG_INFO, "[MG_OL_0018] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0018] 응답전문 생성 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0018] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0018] 필수 필드 누락(MERCHID/CARDNO)"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "APPROVED", &approved); - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0018] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (tx_buf_get(ctx->in, "TRANSCODE", trans, sizeof(trans)) != TX_OK) + strcpy(trans, "000000"); + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) + reason[0] = '\0'; + + /* 승인/거절에 따른 응답코드 결정 */ + if (approved != 0) { + rcode = RESP_OK; + } else if (reason[0] != '\0') { + rcode = reason; /* 요청이 지정한 거절사유 */ + } else { + rcode = RESP_INVALID; /* 사유 미지정 기본 거절 */ + } + + /* 승인번호: 요청값 우선, 없으면 가맹점+금액 기반 채번 */ + if (tx_buf_get(ctx->in, "APPROVAL", approval, sizeof(approval)) != TX_OK || + approval[0] == '\0') { + snprintf(apnbuf, sizeof(apnbuf), "%08ld", (amount % 100000000L)); + } else { + strncpy(apnbuf, approval, sizeof(apnbuf) - 1); + apnbuf[sizeof(apnbuf) - 1] = '\0'; + } + + /* 응답전문(0210) 필드 세팅 (고정길이 우측공백/좌측영 규칙은 유틸 준수) */ + memset(&resp, 0, sizeof(resp)); + msg_field_set(resp.msg_type, MSG_TYPE_RESP, FLD_MSG_TYPE_LEN); + msg_field_set(resp.trans_code, trans, FLD_TRANS_CODE_LEN); + msg_field_set(resp.merch_id, merch, FLD_MERCH_ID_LEN); + msg_field_set(resp.card_no, card, FLD_CARD_NO_LEN); + msg_field_set(resp.approval_no, apnbuf, FLD_APPROVAL_LEN); + { + char amtstr[16]; + snprintf(amtstr, sizeof(amtstr), "%ld", (amount < 0) ? 0 : amount); + msg_field_set_num(resp.amount, amtstr, FLD_AMOUNT_LEN); + } + msg_field_set(resp.resp_code, rcode, FLD_RESP_LEN); + + memset(outraw, 0, sizeof(outraw)); + if (msg_pack(&resp, outraw, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0018] 응답전문 패킹 실패"); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0018] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", rcode); + tx_buf_sets(ctx->out, "RAWMSG", outraw); + tx_buf_sets(ctx->out, "APPROVAL", apnbuf); + tx_buf_setlong(ctx->out, "APPROVED", approved); + tx_log(TX_LOG_INFO, "[MG_OL_0018] 응답전문 생성완료 rcode=%s apn=%s approved=%ld", + rcode, apnbuf, approved); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0018.pgc b/app/online/mg_ol_0018.pgc index 34bd20c..af8ef8a 100644 --- a/app/online/mg_ol_0018.pgc +++ b/app/online/mg_ol_0018.pgc @@ -1,9 +1,11 @@ -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0018.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0018) [tier=easy] + * mg_ol_0018.pgc - 전문게이트웨이 응답전문 생성 서비스 (MG_OL_0018) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인처리 결과(승인/거절/사유코드)와 원거래 식별정보를 받아 응답전문 + * (0210)을 조립한다. 승인이면 resp_code=0000, 거절이면 요청 사유코드를 + * 반영(미지정시 9001)하며, 승인번호가 없으면 채번규칙으로 생성한다. + * 완성된 전문을 msg_pack 으로 고정길이 직렬화하여 RAWMSG 로 반환한다. */ #include #include @@ -16,32 +18,75 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MG_OL_0018, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + acq_msg_t resp; + char outraw[MSG_ACQ_LEN + 1]; + char merch[16], card[20], trans[8], approval[12], reason[8]; + char apnbuf[12]; + long amount = 0; + long approved = 0; + const char *rcode; - tx_log(TX_LOG_INFO, "[MG_OL_0018] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0018] 응답전문 생성 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0018] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0018] 필수 필드 누락(MERCHID/CARDNO)"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "APPROVED", &approved); - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0018] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (tx_buf_get(ctx->in, "TRANSCODE", trans, sizeof(trans)) != TX_OK) + strcpy(trans, "000000"); + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) + reason[0] = '\0'; + + /* 승인/거절에 따른 응답코드 결정 */ + if (approved != 0) { + rcode = RESP_OK; + } else if (reason[0] != '\0') { + rcode = reason; /* 요청이 지정한 거절사유 */ + } else { + rcode = RESP_INVALID; /* 사유 미지정 기본 거절 */ + } + + /* 승인번호: 요청값 우선, 없으면 가맹점+금액 기반 채번 */ + if (tx_buf_get(ctx->in, "APPROVAL", approval, sizeof(approval)) != TX_OK || + approval[0] == '\0') { + snprintf(apnbuf, sizeof(apnbuf), "%08ld", (amount % 100000000L)); + } else { + strncpy(apnbuf, approval, sizeof(apnbuf) - 1); + apnbuf[sizeof(apnbuf) - 1] = '\0'; + } + + /* 응답전문(0210) 필드 세팅 (고정길이 우측공백/좌측영 규칙은 유틸 준수) */ + memset(&resp, 0, sizeof(resp)); + msg_field_set(resp.msg_type, MSG_TYPE_RESP, FLD_MSG_TYPE_LEN); + msg_field_set(resp.trans_code, trans, FLD_TRANS_CODE_LEN); + msg_field_set(resp.merch_id, merch, FLD_MERCH_ID_LEN); + msg_field_set(resp.card_no, card, FLD_CARD_NO_LEN); + msg_field_set(resp.approval_no, apnbuf, FLD_APPROVAL_LEN); + { + char amtstr[16]; + snprintf(amtstr, sizeof(amtstr), "%ld", (amount < 0) ? 0 : amount); + msg_field_set_num(resp.amount, amtstr, FLD_AMOUNT_LEN); + } + msg_field_set(resp.resp_code, rcode, FLD_RESP_LEN); + + memset(outraw, 0, sizeof(outraw)); + if (msg_pack(&resp, outraw, MSG_ACQ_LEN) != 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0018] 응답전문 패킹 실패"); + tx_return(ctx, TX_FAIL, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0018] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", rcode); + tx_buf_sets(ctx->out, "RAWMSG", outraw); + tx_buf_sets(ctx->out, "APPROVAL", apnbuf); + tx_buf_setlong(ctx->out, "APPROVED", approved); + tx_log(TX_LOG_INFO, "[MG_OL_0018] 응답전문 생성완료 rcode=%s apn=%s approved=%ld", + rcode, apnbuf, approved); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0019.c b/app/online/mg_ol_0019.c index 3622a7a..acb4d7b 100644 --- a/app/online/mg_ol_0019.c +++ b/app/online/mg_ol_0019.c @@ -6,15 +6,19 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0019.pgc" -/* @tier hard @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0019.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0019) [tier=hard] + * mg_ol_0019.pgc - 전문게이트웨이 공통헤더 파싱 서비스 (MG_OL_0019) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 표준 고정 공통헤더를 원시전문(RAWMSG)에서 오프셋 단위로 파싱한다. + * 공통헤더 레이아웃(총 25바이트): + * [전문길이 5][전문종별 4][채널 2][일자 8][시각 6] + * 각 필드의 형식(숫자/종별화이트리스트)을 검증하고, 선언된 전문길이와 + * 실제 본문길이의 일치 여부를 교차검증하여 헤더 필드들을 응답한다. */ #include #include +#include #include "txcore.h" #include "acq_util.h" @@ -89,88 +93,109 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0019.pgc" +#line 19 "app/online/mg_ol_0019.pgc" +#define HDR_LEN 25 +#define OFF_LEN 0 +#define OFF_TYPE 5 +#define OFF_CHAN 9 +#define OFF_DATE 11 +#define OFF_TIME 19 + +/* 지정 구간이 전부 숫자인지 검사 */ +static int all_digit(const char *p, int n) +{ + int i; + for (i = 0; i < n; i++) + if (p[i] < '0' || p[i] > '9') + return 0; + return 1; +} + TX_SERVICE(MG_OL_0019, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char raw[256]; + char declen[6]; + char mtype[5]; + char chan[3]; + char cdate[9]; + char ctime[7]; + int rawlen; + long decl_body; + long real_body; - tx_log(TX_LOG_INFO, "[MG_OL_0019] 전문게이트웨이 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0019] 공통헤더 파싱 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0019] 필수 필드 누락"); + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen <= 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0019] RAWMSG 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0019] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { + rawlen = (int)strlen(raw); + if (rawlen < HDR_LEN) { + tx_log(TX_LOG_WARN, "[MG_OL_0019] 헤더 길이부족 len=%d", rawlen); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 오프셋 파싱 */ + memset(declen, 0, sizeof(declen)); + memset(mtype, 0, sizeof(mtype)); + memset(chan, 0, sizeof(chan)); + memset(cdate, 0, sizeof(cdate)); + memset(ctime, 0, sizeof(ctime)); + memcpy(declen, raw + OFF_LEN, 5); + memcpy(mtype, raw + OFF_TYPE, 4); + memcpy(chan, raw + OFF_CHAN, 2); + memcpy(cdate, raw + OFF_DATE, 8); + memcpy(ctime, raw + OFF_TIME, 6); - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0019] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + /* 형식 검증: 길이/일자/시각은 숫자, 종별은 화이트리스트 */ + if (!all_digit(declen, 5) || !all_digit(cdate, 8) || !all_digit(ctime, 6)) { + tx_log(TX_LOG_WARN, "[MG_OL_0019] 숫자필드 형식 오류"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + if (strcmp(mtype, MSG_TYPE_RECV) != 0 && + strcmp(mtype, MSG_TYPE_RESP) != 0 && + strcmp(mtype, MSG_TYPE_RECON) != 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0019] 전문종별 미허용 type=%s", mtype); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + if (!date_is_valid(cdate)) { + tx_log(TX_LOG_WARN, "[MG_OL_0019] 일자 유효성 오류 date=%s", cdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MG_OL_0019] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 선언 전문길이와 실제 본문길이 교차검증 */ + decl_body = (long)atoi(declen); + real_body = (long)(rawlen - HDR_LEN); + if (decl_body != real_body) + tx_log(TX_LOG_WARN, "[MG_OL_0019] 길이불일치 decl=%ld real=%ld", + decl_body, real_body); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MG_OL_0019] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MSGTYPE", mtype); + tx_buf_sets(ctx->out, "CHAN", chan); + tx_buf_sets(ctx->out, "TXNDATE", cdate); + tx_buf_sets(ctx->out, "TXNTIME", ctime); + tx_buf_setlong(ctx->out, "DECLLEN", decl_body); + tx_buf_setlong(ctx->out, "REALLEN", real_body); + tx_buf_sets(ctx->out, "LENMATCH", (decl_body == real_body) ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[MG_OL_0019] 헤더파싱 완료 type=%s chan=%s date=%s time=%s", + mtype, chan, cdate, ctime); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0019.pgc b/app/online/mg_ol_0019.pgc index b9abf8c..622d72e 100644 --- a/app/online/mg_ol_0019.pgc +++ b/app/online/mg_ol_0019.pgc @@ -1,12 +1,16 @@ -/* @tier hard @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0019.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0019) [tier=hard] + * mg_ol_0019.pgc - 전문게이트웨이 공통헤더 파싱 서비스 (MG_OL_0019) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 표준 고정 공통헤더를 원시전문(RAWMSG)에서 오프셋 단위로 파싱한다. + * 공통헤더 레이아웃(총 25바이트): + * [전문길이 5][전문종별 4][채널 2][일자 8][시각 6] + * 각 필드의 형식(숫자/종별화이트리스트)을 검증하고, 선언된 전문길이와 + * 실제 본문길이의 일치 여부를 교차검증하여 헤더 필드들을 응답한다. */ #include #include +#include #include "txcore.h" #include "acq_util.h" @@ -14,85 +18,106 @@ EXEC SQL INCLUDE sqlca; +#define HDR_LEN 25 +#define OFF_LEN 0 +#define OFF_TYPE 5 +#define OFF_CHAN 9 +#define OFF_DATE 11 +#define OFF_TIME 19 + +/* 지정 구간이 전부 숫자인지 검사 */ +static int all_digit(const char *p, int n) +{ + int i; + for (i = 0; i < n; i++) + if (p[i] < '0' || p[i] > '9') + return 0; + return 1; +} + TX_SERVICE(MG_OL_0019, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char raw[256]; + char declen[6]; + char mtype[5]; + char chan[3]; + char cdate[9]; + char ctime[7]; + int rawlen; + long decl_body; + long real_body; - tx_log(TX_LOG_INFO, "[MG_OL_0019] 전문게이트웨이 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0019] 공통헤더 파싱 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0019] 필수 필드 누락"); + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen <= 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0019] RAWMSG 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0019] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { + rawlen = (int)strlen(raw); + if (rawlen < HDR_LEN) { + tx_log(TX_LOG_WARN, "[MG_OL_0019] 헤더 길이부족 len=%d", rawlen); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 오프셋 파싱 */ + memset(declen, 0, sizeof(declen)); + memset(mtype, 0, sizeof(mtype)); + memset(chan, 0, sizeof(chan)); + memset(cdate, 0, sizeof(cdate)); + memset(ctime, 0, sizeof(ctime)); + memcpy(declen, raw + OFF_LEN, 5); + memcpy(mtype, raw + OFF_TYPE, 4); + memcpy(chan, raw + OFF_CHAN, 2); + memcpy(cdate, raw + OFF_DATE, 8); + memcpy(ctime, raw + OFF_TIME, 6); - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0019] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + /* 형식 검증: 길이/일자/시각은 숫자, 종별은 화이트리스트 */ + if (!all_digit(declen, 5) || !all_digit(cdate, 8) || !all_digit(ctime, 6)) { + tx_log(TX_LOG_WARN, "[MG_OL_0019] 숫자필드 형식 오류"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + if (strcmp(mtype, MSG_TYPE_RECV) != 0 && + strcmp(mtype, MSG_TYPE_RESP) != 0 && + strcmp(mtype, MSG_TYPE_RECON) != 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0019] 전문종별 미허용 type=%s", mtype); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + if (!date_is_valid(cdate)) { + tx_log(TX_LOG_WARN, "[MG_OL_0019] 일자 유효성 오류 date=%s", cdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MG_OL_0019] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 선언 전문길이와 실제 본문길이 교차검증 */ + decl_body = (long)atoi(declen); + real_body = (long)(rawlen - HDR_LEN); + if (decl_body != real_body) + tx_log(TX_LOG_WARN, "[MG_OL_0019] 길이불일치 decl=%ld real=%ld", + decl_body, real_body); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MG_OL_0019] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MSGTYPE", mtype); + tx_buf_sets(ctx->out, "CHAN", chan); + tx_buf_sets(ctx->out, "TXNDATE", cdate); + tx_buf_sets(ctx->out, "TXNTIME", ctime); + tx_buf_setlong(ctx->out, "DECLLEN", decl_body); + tx_buf_setlong(ctx->out, "REALLEN", real_body); + tx_buf_sets(ctx->out, "LENMATCH", (decl_body == real_body) ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[MG_OL_0019] 헤더파싱 완료 type=%s chan=%s date=%s time=%s", + mtype, chan, cdate, ctime); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0020.c b/app/online/mg_ol_0020.c index 4765d35..45d9baa 100644 --- a/app/online/mg_ol_0020.c +++ b/app/online/mg_ol_0020.c @@ -6,17 +6,20 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0020.pgc" -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0020.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0020) [tier=easy] + * mg_ol_0020.pgc - 전문게이트웨이 전문 순번(STAN) 채번 서비스 (MG_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채널/기관 단위 시퀀스행(mg_seq)을 원자적으로 증가시켜 6자리 STAN + * (System Trace Audit Number)을 채번한다. raw EXEC SQL 로 UPDATE ... + * SET seq_val = seq_val + 1 후 증가값을 SELECT 하며, 000001~999999 를 + * 순환(랩어라운드)한다. 시퀀스행이 없으면 최초 1 로 생성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mg_core.h" @@ -89,37 +92,112 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0020.pgc" +#line 18 "app/online/mg_ol_0020.pgc" +#define STAN_MAX 999999 + TX_SERVICE(MG_OL_0020, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 25 "app/online/mg_ol_0020.pgc" + char h_seqname [ 16 ] ; + +#line 26 "app/online/mg_ol_0020.pgc" + long h_seqval ; + +#line 27 "app/online/mg_ol_0020.pgc" + long h_stanmax ; +/* exec sql end declare section */ +#line 28 "app/online/mg_ol_0020.pgc" - tx_log(TX_LOG_INFO, "[MG_OL_0020] 전문게이트웨이 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0020] KEY 누락"); + char seqname[16]; + char stan[8]; + long stanval; + + tx_log(TX_LOG_INFO, "[MG_OL_0020] STAN 채번 서비스 진입"); + + if (tx_buf_get(ctx->in, "SEQNAME", seqname, sizeof(seqname)) != TX_OK || + seqname[0] == '\0') { + tx_log(TX_LOG_ERROR, "[MG_OL_0020] 시퀀스명(SEQNAME) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_seqname, seqname, sizeof(h_seqname) - 1); + h_seqname[sizeof(h_seqname) - 1] = '\0'; + h_stanmax = STAN_MAX; - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0020] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); return; } + /* 원자적 증가 (랩어라운드는 갱신식 내에서 처리) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mg_seq set seq_val = ( case when seq_val >= $1 then 1 else seq_val + 1 end ) , upd_ts = now ( ) where seq_name = $2 ", + ECPGt_long,&(h_stanmax),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_seqname),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 56 "app/online/mg_ol_0020.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("MG_OL_0020 mg_seq update"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 대상행이 없으면 최초 1 로 생성 */ + if (sqlca.sqlerrd[2] == 0) { + h_seqval = 1; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mg_seq ( seq_name , seq_val , upd_ts ) values ( $1 , $2 , now ( ) )", + ECPGt_char,(h_seqname),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_seqval),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 68 "app/online/mg_ol_0020.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("MG_OL_0020 mg_seq insert"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tx_log(TX_LOG_INFO, "[MG_OL_0020] 시퀀스 신규생성 name=%s", seqname); + } + + /* 증가 결과 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select seq_val from mg_seq where seq_name = $1 ", + ECPGt_char,(h_seqname),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_seqval),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 82 "app/online/mg_ol_0020.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("MG_OL_0020 mg_seq select"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + stanval = h_seqval; + if (stanval < 1 || stanval > STAN_MAX) + stanval = ((stanval - 1) % STAN_MAX) + 1; + snprintf(stan, sizeof(stan), "%06ld", stanval); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0020] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "SEQNAME", seqname); + tx_buf_sets(ctx->out, "STAN", stan); + tx_buf_setlong(ctx->out, "SEQVAL", stanval); + tx_log(TX_LOG_INFO, "[MG_OL_0020] STAN 채번완료 name=%s stan=%s", seqname, stan); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0020.pgc b/app/online/mg_ol_0020.pgc index 8178b87..a5058ca 100644 --- a/app/online/mg_ol_0020.pgc +++ b/app/online/mg_ol_0020.pgc @@ -1,47 +1,104 @@ -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0020.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0020) [tier=easy] + * mg_ol_0020.pgc - 전문게이트웨이 전문 순번(STAN) 채번 서비스 (MG_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채널/기관 단위 시퀀스행(mg_seq)을 원자적으로 증가시켜 6자리 STAN + * (System Trace Audit Number)을 채번한다. raw EXEC SQL 로 UPDATE ... + * SET seq_val = seq_val + 1 후 증가값을 SELECT 하며, 000001~999999 를 + * 순환(랩어라운드)한다. 시퀀스행이 없으면 최초 1 로 생성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mg_core.h" EXEC SQL INCLUDE sqlca; +#define STAN_MAX 999999 + TX_SERVICE(MG_OL_0020, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_seqname[16]; + long h_seqval; + long h_stanmax; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MG_OL_0020] 전문게이트웨이 단건조회 서비스 진입"); + char seqname[16]; + char stan[8]; + long stanval; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0020] KEY 누락"); + tx_log(TX_LOG_INFO, "[MG_OL_0020] STAN 채번 서비스 진입"); + + if (tx_buf_get(ctx->in, "SEQNAME", seqname, sizeof(seqname)) != TX_OK || + seqname[0] == '\0') { + tx_log(TX_LOG_ERROR, "[MG_OL_0020] 시퀀스명(SEQNAME) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_seqname, seqname, sizeof(h_seqname) - 1); + h_seqname[sizeof(h_seqname) - 1] = '\0'; + h_stanmax = STAN_MAX; - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0020] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (tx_begin() != TX_OK) { + tx_return(ctx, TX_FAIL, ctx->out); return; } + /* 원자적 증가 (랩어라운드는 갱신식 내에서 처리) */ + EXEC SQL UPDATE mg_seq + SET seq_val = (CASE WHEN seq_val >= :h_stanmax THEN 1 + ELSE seq_val + 1 END), + upd_ts = now() + WHERE seq_name = :h_seqname; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("MG_OL_0020 mg_seq update"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 대상행이 없으면 최초 1 로 생성 */ + if (sqlca.sqlerrd[2] == 0) { + h_seqval = 1; + EXEC SQL INSERT INTO mg_seq (seq_name, seq_val, upd_ts) + VALUES (:h_seqname, :h_seqval, now()); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("MG_OL_0020 mg_seq insert"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tx_log(TX_LOG_INFO, "[MG_OL_0020] 시퀀스 신규생성 name=%s", seqname); + } + + /* 증가 결과 조회 */ + EXEC SQL SELECT seq_val + INTO :h_seqval + FROM mg_seq + WHERE seq_name = :h_seqname; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("MG_OL_0020 mg_seq select"); + tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + stanval = h_seqval; + if (stanval < 1 || stanval > STAN_MAX) + stanval = ((stanval - 1) % STAN_MAX) + 1; + snprintf(stan, sizeof(stan), "%06ld", stanval); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0020] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "SEQNAME", seqname); + tx_buf_sets(ctx->out, "STAN", stan); + tx_buf_setlong(ctx->out, "SEQVAL", stanval); + tx_log(TX_LOG_INFO, "[MG_OL_0020] STAN 채번완료 name=%s stan=%s", seqname, stan); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0021.c b/app/online/mg_ol_0021.c index d599d60..2854cb4 100644 --- a/app/online/mg_ol_0021.c +++ b/app/online/mg_ol_0021.c @@ -8,10 +8,12 @@ #line 1 "app/online/mg_ol_0021.pgc" /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0021.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0021) [tier=easy] + * mg_ol_0021.pgc - 전문게이트웨이 카드번호 마스킹 서비스 (MG_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * PCI-DSS 노출규칙에 따라 카드번호(PAN)를 앞 6자리(BIN)와 뒤 4자리만 + * 노출하고 중간 자릿수를 '*' 로 마스킹한다. 12자리 미만 등 마스킹 불가 + * 길이는 전량 마스킹(fail-safe)한다. 마스킹 결과를 전문 카드필드 규격 + * (16)으로 정렬해 MASKED 로 반환한다. */ #include #include @@ -89,37 +91,60 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0021.pgc" +#line 17 "app/online/mg_ol_0021.pgc" +#define PAN_HEAD 6 /* 앞 노출 자릿수 (BIN) */ +#define PAN_TAIL 4 /* 뒤 노출 자릿수 */ + TX_SERVICE(MG_OL_0021, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char card[24]; + char masked[24]; + int len; + int i; - tx_log(TX_LOG_INFO, "[MG_OL_0021] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0021] 카드번호 마스킹 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0021] KEY 누락"); + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK || + card[0] == '\0') { + tx_log(TX_LOG_ERROR, "[MG_OL_0021] 카드번호(CARDNO) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + len = (int)strlen(card); - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + /* 카드번호는 숫자만 허용 (구분자 포함시 검증 실패) */ + for (i = 0; i < len; i++) { + if (card[i] < '0' || card[i] > '9') { + tx_log(TX_LOG_WARN, "[MG_OL_0021] 카드번호 형식 오류 len=%d", len); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + } + + memset(masked, 0, sizeof(masked)); + + if (len < PAN_HEAD + PAN_TAIL + 1) { + /* 노출규칙 적용 불가 → 전량 마스킹(fail-safe) */ + for (i = 0; i < len; i++) + masked[i] = '*'; + tx_log(TX_LOG_WARN, "[MG_OL_0021] 짧은 PAN 전량마스킹 len=%d", len); + } else { + for (i = 0; i < len; i++) { + if (i < PAN_HEAD || i >= len - PAN_TAIL) + masked[i] = card[i]; /* 앞6/뒤4 노출 */ + else + masked[i] = '*'; /* 중간 마스킹 */ + } } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MASKED", masked); + tx_buf_setlong(ctx->out, "PANLEN", (long)len); + tx_log(TX_LOG_INFO, "[MG_OL_0021] 마스킹 완료 len=%d masked=%s", len, masked); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0021.pgc b/app/online/mg_ol_0021.pgc index 4d6bec5..97399bc 100644 --- a/app/online/mg_ol_0021.pgc +++ b/app/online/mg_ol_0021.pgc @@ -1,9 +1,11 @@ /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0021.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0021) [tier=easy] + * mg_ol_0021.pgc - 전문게이트웨이 카드번호 마스킹 서비스 (MG_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * PCI-DSS 노출규칙에 따라 카드번호(PAN)를 앞 6자리(BIN)와 뒤 4자리만 + * 노출하고 중간 자릿수를 '*' 로 마스킹한다. 12자리 미만 등 마스킹 불가 + * 길이는 전량 마스킹(fail-safe)한다. 마스킹 결과를 전문 카드필드 규격 + * (16)으로 정렬해 MASKED 로 반환한다. */ #include #include @@ -14,34 +16,57 @@ EXEC SQL INCLUDE sqlca; +#define PAN_HEAD 6 /* 앞 노출 자릿수 (BIN) */ +#define PAN_TAIL 4 /* 뒤 노출 자릿수 */ + TX_SERVICE(MG_OL_0021, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char card[24]; + char masked[24]; + int len; + int i; - tx_log(TX_LOG_INFO, "[MG_OL_0021] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0021] 카드번호 마스킹 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0021] KEY 누락"); + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK || + card[0] == '\0') { + tx_log(TX_LOG_ERROR, "[MG_OL_0021] 카드번호(CARDNO) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + len = (int)strlen(card); - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + /* 카드번호는 숫자만 허용 (구분자 포함시 검증 실패) */ + for (i = 0; i < len; i++) { + if (card[i] < '0' || card[i] > '9') { + tx_log(TX_LOG_WARN, "[MG_OL_0021] 카드번호 형식 오류 len=%d", len); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + } + + memset(masked, 0, sizeof(masked)); + + if (len < PAN_HEAD + PAN_TAIL + 1) { + /* 노출규칙 적용 불가 → 전량 마스킹(fail-safe) */ + for (i = 0; i < len; i++) + masked[i] = '*'; + tx_log(TX_LOG_WARN, "[MG_OL_0021] 짧은 PAN 전량마스킹 len=%d", len); + } else { + for (i = 0; i < len; i++) { + if (i < PAN_HEAD || i >= len - PAN_TAIL) + masked[i] = card[i]; /* 앞6/뒤4 노출 */ + else + masked[i] = '*'; /* 중간 마스킹 */ + } } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MASKED", masked); + tx_buf_setlong(ctx->out, "PANLEN", (long)len); + tx_log(TX_LOG_INFO, "[MG_OL_0021] 마스킹 완료 len=%d masked=%s", len, masked); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0022.c b/app/online/mg_ol_0022.c index 410562e..8798059 100644 --- a/app/online/mg_ol_0022.c +++ b/app/online/mg_ol_0022.c @@ -8,10 +8,12 @@ #line 1 "app/online/mg_ol_0022.pgc" /* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0022.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0022) [tier=medium] + * mg_ol_0022.pgc - 전문게이트웨이 분할전문 재조립 서비스 (MG_OL_0022) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 대용량 전문을 분할 전송한 조각(FRAG00~FRAGnn)을 순서대로 병합해 원본 + * 전문을 복원한다. 요청은 총조각수(TOTAL)와 각 조각(FRAGnn), 무결성용 + * 총길이(TOTLEN)를 포함하며, 조각 결번/누락을 검증하고 병합 후 실제 + * 길이와 선언 길이를 대조하여 완성전문(FULLMSG)을 응답한다. */ #include #include @@ -89,60 +91,91 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0022.pgc" +#line 17 "app/online/mg_ol_0022.pgc" +#define MAX_FRAG 32 +#define FULL_MAX 512 + TX_SERVICE(MG_OL_0022, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char full[FULL_MAX]; + char frag[128]; + char fkey[12]; + long total = 0; + long totlen = 0; + int filled = 0; + int merged_len = 0; + int i; - tx_log(TX_LOG_INFO, "[MG_OL_0022] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0022] 분할전문 재조립 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0022] 필수 필드 누락"); + if (tx_buf_getlong(ctx->in, "TOTAL", &total) != TX_OK || total <= 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0022] 총조각수(TOTAL) 누락/오류"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0022] 금액 범위 오류 amount=%ld", amount); + if (total > MAX_FRAG) { + tx_log(TX_LOG_WARN, "[MG_OL_0022] 조각수 초과 total=%ld max=%d", total, MAX_FRAG); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0022] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + tx_buf_getlong(ctx->in, "TOTLEN", &totlen); + + memset(full, 0, sizeof(full)); + + /* 조각을 SEQ 오름차순(0..TOTAL-1)으로 순서검증하며 병합 */ + for (i = 0; i < (int)total; i++) { + snprintf(fkey, sizeof(fkey), "FRAG%02d", i); + if (tx_buf_get(ctx->in, fkey, frag, sizeof(frag)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0022] 조각 결번 seq=%d key=%s", i, fkey); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "MISSINGSEQ", (long)i); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + { + int flen = (int)strlen(frag); + if (merged_len + flen >= FULL_MAX) { + tx_log(TX_LOG_ERROR, "[MG_OL_0022] 재조립 버퍼 초과 at seq=%d", i); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + memcpy(full + merged_len, frag, flen); + merged_len += flen; + } + filled++; + } + full[merged_len] = '\0'; + + /* 무결성 대조: 조각수 일치 + 선언 총길이 일치 */ + if (filled != (int)total) { + tx_log(TX_LOG_WARN, "[MG_OL_0022] 조각수 불일치 filled=%d total=%ld", filled, total); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0022] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (totlen > 0 && totlen != (long)merged_len) { + tx_log(TX_LOG_WARN, "[MG_OL_0022] 총길이 불일치 decl=%ld merged=%d", + totlen, merged_len); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "MERGEDLEN", (long)merged_len); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); - tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0022] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "FULLMSG", full); + tx_buf_setlong(ctx->out, "FRAGCNT", (long)filled); + tx_buf_setlong(ctx->out, "MERGEDLEN", (long)merged_len); + tx_log(TX_LOG_INFO, "[MG_OL_0022] 재조립 완료 조각=%d 길이=%d", filled, merged_len); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0022.pgc b/app/online/mg_ol_0022.pgc index 7956ff4..2ff1044 100644 --- a/app/online/mg_ol_0022.pgc +++ b/app/online/mg_ol_0022.pgc @@ -1,9 +1,11 @@ /* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0022.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0022) [tier=medium] + * mg_ol_0022.pgc - 전문게이트웨이 분할전문 재조립 서비스 (MG_OL_0022) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 대용량 전문을 분할 전송한 조각(FRAG00~FRAGnn)을 순서대로 병합해 원본 + * 전문을 복원한다. 요청은 총조각수(TOTAL)와 각 조각(FRAGnn), 무결성용 + * 총길이(TOTLEN)를 포함하며, 조각 결번/누락을 검증하고 병합 후 실제 + * 길이와 선언 길이를 대조하여 완성전문(FULLMSG)을 응답한다. */ #include #include @@ -14,57 +16,88 @@ EXEC SQL INCLUDE sqlca; +#define MAX_FRAG 32 +#define FULL_MAX 512 + TX_SERVICE(MG_OL_0022, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char full[FULL_MAX]; + char frag[128]; + char fkey[12]; + long total = 0; + long totlen = 0; + int filled = 0; + int merged_len = 0; + int i; - tx_log(TX_LOG_INFO, "[MG_OL_0022] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0022] 분할전문 재조립 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0022] 필수 필드 누락"); + if (tx_buf_getlong(ctx->in, "TOTAL", &total) != TX_OK || total <= 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0022] 총조각수(TOTAL) 누락/오류"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0022] 금액 범위 오류 amount=%ld", amount); + if (total > MAX_FRAG) { + tx_log(TX_LOG_WARN, "[MG_OL_0022] 조각수 초과 total=%ld max=%d", total, MAX_FRAG); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0022] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + tx_buf_getlong(ctx->in, "TOTLEN", &totlen); + + memset(full, 0, sizeof(full)); + + /* 조각을 SEQ 오름차순(0..TOTAL-1)으로 순서검증하며 병합 */ + for (i = 0; i < (int)total; i++) { + snprintf(fkey, sizeof(fkey), "FRAG%02d", i); + if (tx_buf_get(ctx->in, fkey, frag, sizeof(frag)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0022] 조각 결번 seq=%d key=%s", i, fkey); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "MISSINGSEQ", (long)i); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + { + int flen = (int)strlen(frag); + if (merged_len + flen >= FULL_MAX) { + tx_log(TX_LOG_ERROR, "[MG_OL_0022] 재조립 버퍼 초과 at seq=%d", i); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + memcpy(full + merged_len, frag, flen); + merged_len += flen; + } + filled++; + } + full[merged_len] = '\0'; + + /* 무결성 대조: 조각수 일치 + 선언 총길이 일치 */ + if (filled != (int)total) { + tx_log(TX_LOG_WARN, "[MG_OL_0022] 조각수 불일치 filled=%d total=%ld", filled, total); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); return; } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0022] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (totlen > 0 && totlen != (long)merged_len) { + tx_log(TX_LOG_WARN, "[MG_OL_0022] 총길이 불일치 decl=%ld merged=%d", + totlen, merged_len); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_setlong(ctx->out, "MERGEDLEN", (long)merged_len); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); - tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0022] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "FULLMSG", full); + tx_buf_setlong(ctx->out, "FRAGCNT", (long)filled); + tx_buf_setlong(ctx->out, "MERGEDLEN", (long)merged_len); + tx_log(TX_LOG_INFO, "[MG_OL_0022] 재조립 완료 조각=%d 길이=%d", filled, merged_len); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0023.c b/app/online/mg_ol_0023.c index b462782..e4a3dc9 100644 --- a/app/online/mg_ol_0023.c +++ b/app/online/mg_ol_0023.c @@ -6,17 +6,20 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0023.pgc" -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0023.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0023) [tier=easy] + * mg_ol_0023.pgc - 전문게이트웨이 채널 세션 종료 서비스 (MG_OL_0023) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채널이 보유한 세션토큰(SESSTOKEN)을 검증한 뒤, 채널 세션 테이블 + * (mg_chan)의 상태를 종료('C')로 전이시킨다. 이미 종료되었거나 존재하지 + * 않는 세션은 반려하고, 정상 종료 시 종료결과를 응답한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mg_core.h" @@ -89,37 +92,121 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0023.pgc" +#line 18 "app/online/mg_ol_0023.pgc" TX_SERVICE(MG_OL_0023, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 23 "app/online/mg_ol_0023.pgc" + char h_token [ 33 ] ; + +#line 24 "app/online/mg_ol_0023.pgc" + char h_chan_cd [ 9 ] ; + +#line 25 "app/online/mg_ol_0023.pgc" + char h_status [ 2 ] ; + +#line 26 "app/online/mg_ol_0023.pgc" + char h_close_st [ 2 ] ; +/* exec sql end declare section */ +#line 27 "app/online/mg_ol_0023.pgc" - tx_log(TX_LOG_INFO, "[MG_OL_0023] 전문게이트웨이 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0023] KEY 누락"); + char token[33]; + + tx_log(TX_LOG_INFO, "[MG_OL_0023] 채널 세션 종료 서비스 진입"); + + /* 1) 세션토큰 수신/검증 */ + if (tx_buf_get(ctx->in, "SESSTOKEN", token, sizeof(token)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0023] 세션토큰(SESSTOKEN) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (strlen(token) < 8) { + tx_log(TX_LOG_WARN, "[MG_OL_0023] 세션토큰 길이 오류 len=%d", (int)strlen(token)); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_token, token, sizeof(h_token) - 1); + h_token[sizeof(h_token) - 1] = '\0'; + + /* 2) 현재 세션 상태 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select chan_cd , status from mg_chan where sess_token = $1 ", + ECPGt_char,(h_token),(long)33,(long)1,(33)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_chan_cd),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 54 "app/online/mg_ol_0023.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MG_OL_0023] 세션 없음 token=%.8s...", token); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mg_chan(세션조회)"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3) 이미 종료된 세션은 멱등 반려 */ + if (h_status[0] == 'C') { + tx_log(TX_LOG_INFO, "[MG_OL_0023] 이미 종료된 세션 chan=%s", h_chan_cd); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "CHANCD", h_chan_cd); + tx_buf_sets(ctx->out, "CLOSED", "N"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* 4) 세션 종료 상태 전이 */ + strncpy(h_close_st, "C", sizeof(h_close_st)); + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MG_OL_0023] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mg_chan set status = $1 , closed_ts = now ( ) where sess_token = $2 ", + ECPGt_char,(h_close_st),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_token),(long)33,(long)1,(33)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 92 "app/online/mg_ol_0023.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("UPDATE mg_chan(세션종료)"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 5) 종료결과 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "CHANCD", h_chan_cd); + tx_buf_sets(ctx->out, "CLOSED", "Y"); + tx_log(TX_LOG_INFO, "[MG_OL_0023] 세션 종료완료 chan=%s token=%.8s...", + h_chan_cd, token); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0023.pgc b/app/online/mg_ol_0023.pgc index e09a096..e09f801 100644 --- a/app/online/mg_ol_0023.pgc +++ b/app/online/mg_ol_0023.pgc @@ -1,14 +1,17 @@ -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0023.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0023) [tier=easy] + * mg_ol_0023.pgc - 전문게이트웨이 채널 세션 종료 서비스 (MG_OL_0023) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 채널이 보유한 세션토큰(SESSTOKEN)을 검증한 뒤, 채널 세션 테이블 + * (mg_chan)의 상태를 종료('C')로 전이시킨다. 이미 종료되었거나 존재하지 + * 않는 세션은 반려하고, 정상 종료 시 종료결과를 응답한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mg_core.h" @@ -16,32 +19,95 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MG_OL_0023, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_token[33]; + char h_chan_cd[9]; + char h_status[2]; + char h_close_st[2]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MG_OL_0023] 전문게이트웨이 단건조회 서비스 진입"); + char token[33]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0023] KEY 누락"); + tx_log(TX_LOG_INFO, "[MG_OL_0023] 채널 세션 종료 서비스 진입"); + + /* 1) 세션토큰 수신/검증 */ + if (tx_buf_get(ctx->in, "SESSTOKEN", token, sizeof(token)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0023] 세션토큰(SESSTOKEN) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (strlen(token) < 8) { + tx_log(TX_LOG_WARN, "[MG_OL_0023] 세션토큰 길이 오류 len=%d", (int)strlen(token)); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_token, token, sizeof(h_token) - 1); + h_token[sizeof(h_token) - 1] = '\0'; + + /* 2) 현재 세션 상태 조회 */ + EXEC SQL + SELECT chan_cd, status INTO :h_chan_cd, :h_status + FROM mg_chan + WHERE sess_token = :h_token; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MG_OL_0023] 세션 없음 token=%.8s...", token); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mg_chan(세션조회)"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3) 이미 종료된 세션은 멱등 반려 */ + if (h_status[0] == 'C') { + tx_log(TX_LOG_INFO, "[MG_OL_0023] 이미 종료된 세션 chan=%s", h_chan_cd); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "CHANCD", h_chan_cd); + tx_buf_sets(ctx->out, "CLOSED", "N"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* 4) 세션 종료 상태 전이 */ + strncpy(h_close_st, "C", sizeof(h_close_st)); + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MG_OL_0023] tx_begin 경고"); + + EXEC SQL + UPDATE mg_chan + SET status = :h_close_st, + closed_ts = now() + WHERE sess_token = :h_token; + + if (sqlca.sqlcode != TX_SQL_OK || sqlca.sqlerrd[2] != 1) { + TX_DBIO_LOG_ERR("UPDATE mg_chan(세션종료)"); + tx_abort(); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + /* 5) 종료결과 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "CHANCD", h_chan_cd); + tx_buf_sets(ctx->out, "CLOSED", "Y"); + tx_log(TX_LOG_INFO, "[MG_OL_0023] 세션 종료완료 chan=%s token=%.8s...", + h_chan_cd, token); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0024.c b/app/online/mg_ol_0024.c index d492b25..0fbdc80 100644 --- a/app/online/mg_ol_0024.c +++ b/app/online/mg_ol_0024.c @@ -8,10 +8,12 @@ #line 1 "app/online/mg_ol_0024.pgc" /* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0024.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0024) [tier=medium] + * mg_ol_0024.pgc - 전문게이트웨이 전문 타임아웃 판정 서비스 (MG_OL_0024) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 전문 송신시각(SENDTIME)과 현재시각(NOWTIME)의 차이를 밀리초로 환산하여 + * 임계값(기본 5000ms) 초과 여부로 타임아웃을 판정한다. 자정 경과(day-wrap) + * 를 보정하며, 판정결과(TIMEOUT)와 경과시간(ELAPSEDMS)을 응답한다. + * (순수 계산 로직 - DB 접근 없음) */ #include #include @@ -89,60 +91,85 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0024.pgc" +#line 17 "app/online/mg_ol_0024.pgc" +#define MG_TIMEOUT_DEFAULT_MS 5000L +#define MG_MS_PER_DAY 86400000L + +/* HHMMSS 6자리를 자정기준 밀리초로 환산 (형식오류 시 -1) */ +static long hhmmss_to_ms(const char *s) +{ + int i, h, m, sec; + + if (s == NULL || strlen(s) < 6) + return -1; + for (i = 0; i < 6; i++) { + if (s[i] < '0' || s[i] > '9') + return -1; + } + h = (s[0] - '0') * 10 + (s[1] - '0'); + m = (s[2] - '0') * 10 + (s[3] - '0'); + sec = (s[4] - '0') * 10 + (s[5] - '0'); + if (h > 23 || m > 59 || sec > 59) + return -1; + return ((long)h * 3600L + (long)m * 60L + (long)sec) * 1000L; +} + TX_SERVICE(MG_OL_0024, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char sendtime[16], nowtime[16]; + long threshold = MG_TIMEOUT_DEFAULT_MS; + long send_ms, now_ms, elapsed; + int is_timeout; - tx_log(TX_LOG_INFO, "[MG_OL_0024] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0024] 전문 타임아웃 판정 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0024] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0024] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0024] 영업일 오류 date=%s", bizdate); + /* 1) 송신시각/현재시각 수신 (HHMMSS) */ + if (tx_buf_get(ctx->in, "SENDTIME", sendtime, sizeof(sendtime)) != TX_OK || + tx_buf_get(ctx->in, "NOWTIME", nowtime, sizeof(nowtime)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0024] 시각 필드(SENDTIME/NOWTIME) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + /* 임계값은 선택 입력 (미제출 시 기본값) */ + tx_buf_getlong(ctx->in, "THRESHMS", &threshold); + if (threshold <= 0) + threshold = MG_TIMEOUT_DEFAULT_MS; - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0024] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 시각 파싱 */ + send_ms = hhmmss_to_ms(sendtime); + now_ms = hhmmss_to_ms(nowtime); + if (send_ms < 0 || now_ms < 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0024] 시각 형식 오류 send=%s now=%s", + sendtime, nowtime); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); + /* 3) 경과시간 계산 (자정 경과 보정) */ + elapsed = now_ms - send_ms; + if (elapsed < 0) + elapsed += MG_MS_PER_DAY; + is_timeout = (elapsed > threshold); + + /* 4) 판정결과 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0024] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_setlong(ctx->out, "ELAPSEDMS", elapsed); + tx_buf_setlong(ctx->out, "THRESHMS", threshold); + tx_buf_sets(ctx->out, "TIMEOUT", is_timeout ? "Y" : "N"); + + if (is_timeout) + tx_log(TX_LOG_WARN, "[MG_OL_0024] 타임아웃 판정 경과=%ldms 임계=%ldms", + elapsed, threshold); + else + tx_log(TX_LOG_INFO, "[MG_OL_0024] 정상 경과=%ldms 임계=%ldms", + elapsed, threshold); + tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0024.pgc b/app/online/mg_ol_0024.pgc index b7688a5..ce0caf5 100644 --- a/app/online/mg_ol_0024.pgc +++ b/app/online/mg_ol_0024.pgc @@ -1,9 +1,11 @@ /* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0024.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0024) [tier=medium] + * mg_ol_0024.pgc - 전문게이트웨이 전문 타임아웃 판정 서비스 (MG_OL_0024) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 전문 송신시각(SENDTIME)과 현재시각(NOWTIME)의 차이를 밀리초로 환산하여 + * 임계값(기본 5000ms) 초과 여부로 타임아웃을 판정한다. 자정 경과(day-wrap) + * 를 보정하며, 판정결과(TIMEOUT)와 경과시간(ELAPSEDMS)을 응답한다. + * (순수 계산 로직 - DB 접근 없음) */ #include #include @@ -14,57 +16,82 @@ EXEC SQL INCLUDE sqlca; +#define MG_TIMEOUT_DEFAULT_MS 5000L +#define MG_MS_PER_DAY 86400000L + +/* HHMMSS 6자리를 자정기준 밀리초로 환산 (형식오류 시 -1) */ +static long hhmmss_to_ms(const char *s) +{ + int i, h, m, sec; + + if (s == NULL || strlen(s) < 6) + return -1; + for (i = 0; i < 6; i++) { + if (s[i] < '0' || s[i] > '9') + return -1; + } + h = (s[0] - '0') * 10 + (s[1] - '0'); + m = (s[2] - '0') * 10 + (s[3] - '0'); + sec = (s[4] - '0') * 10 + (s[5] - '0'); + if (h > 23 || m > 59 || sec > 59) + return -1; + return ((long)h * 3600L + (long)m * 60L + (long)sec) * 1000L; +} + TX_SERVICE(MG_OL_0024, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char sendtime[16], nowtime[16]; + long threshold = MG_TIMEOUT_DEFAULT_MS; + long send_ms, now_ms, elapsed; + int is_timeout; - tx_log(TX_LOG_INFO, "[MG_OL_0024] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0024] 전문 타임아웃 판정 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0024] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0024] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0024] 영업일 오류 date=%s", bizdate); + /* 1) 송신시각/현재시각 수신 (HHMMSS) */ + if (tx_buf_get(ctx->in, "SENDTIME", sendtime, sizeof(sendtime)) != TX_OK || + tx_buf_get(ctx->in, "NOWTIME", nowtime, sizeof(nowtime)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0024] 시각 필드(SENDTIME/NOWTIME) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + /* 임계값은 선택 입력 (미제출 시 기본값) */ + tx_buf_getlong(ctx->in, "THRESHMS", &threshold); + if (threshold <= 0) + threshold = MG_TIMEOUT_DEFAULT_MS; - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0024] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 시각 파싱 */ + send_ms = hhmmss_to_ms(sendtime); + now_ms = hhmmss_to_ms(nowtime); + if (send_ms < 0 || now_ms < 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0024] 시각 형식 오류 send=%s now=%s", + sendtime, nowtime); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = mg_rec_count(bizdate, MG_ST_RECV); + /* 3) 경과시간 계산 (자정 경과 보정) */ + elapsed = now_ms - send_ms; + if (elapsed < 0) + elapsed += MG_MS_PER_DAY; + is_timeout = (elapsed > threshold); + + /* 4) 판정결과 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0024] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_setlong(ctx->out, "ELAPSEDMS", elapsed); + tx_buf_setlong(ctx->out, "THRESHMS", threshold); + tx_buf_sets(ctx->out, "TIMEOUT", is_timeout ? "Y" : "N"); + + if (is_timeout) + tx_log(TX_LOG_WARN, "[MG_OL_0024] 타임아웃 판정 경과=%ldms 임계=%ldms", + elapsed, threshold); + else + tx_log(TX_LOG_INFO, "[MG_OL_0024] 정상 경과=%ldms 임계=%ldms", + elapsed, threshold); + tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0025.c b/app/online/mg_ol_0025.c index a107150..5be2767 100644 --- a/app/online/mg_ol_0025.c +++ b/app/online/mg_ol_0025.c @@ -6,17 +6,20 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0025.pgc" -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0025.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0025) [tier=easy] + * mg_ol_0025.pgc - 전문게이트웨이 전문 중복 검출 서비스 (MG_OL_0025) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * STAN(전송추적번호)+금액+가맹점 조합으로 중복검출 해시를 계산하고, + * 최근 처리원장(mg_ledger)에서 동일 조합을 조회하여 중복 여부를 판정한다. + * 중복이면 기존 처리키를 반환하고, 신규면 통과시킨다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mg_core.h" @@ -89,37 +92,113 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0025.pgc" +#line 18 "app/online/mg_ol_0025.pgc" +/* STAN+금액+가맹점 조합의 djb2 변형 해시 (중복탐지 지문) */ +static long dup_fingerprint(const char *stan, long amount, const char *merch) +{ + unsigned long h = 5381UL; + const char *p; + char amt[24]; + int i; + + for (p = stan; *p; p++) + h = ((h << 5) + h) ^ (unsigned char)*p; + for (p = merch; *p; p++) + h = ((h << 5) + h) ^ (unsigned char)*p; + snprintf(amt, sizeof(amt), "%ld", amount); + for (i = 0; amt[i]; i++) + h = ((h << 5) + h) ^ (unsigned char)amt[i]; + + return (long)(h & 0x7fffffffUL); +} + TX_SERVICE(MG_OL_0025, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 42 "app/online/mg_ol_0025.pgc" + char h_stan [ 13 ] ; + +#line 43 "app/online/mg_ol_0025.pgc" + char h_merch [ 16 ] ; + +#line 44 "app/online/mg_ol_0025.pgc" + long h_amount ; + +#line 45 "app/online/mg_ol_0025.pgc" + char h_dup_key [ 16 ] ; +/* exec sql end declare section */ +#line 46 "app/online/mg_ol_0025.pgc" - tx_log(TX_LOG_INFO, "[MG_OL_0025] 전문게이트웨이 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0025] KEY 누락"); + char stan[13], merch[16]; + long amount = 0; + long fp; + + tx_log(TX_LOG_INFO, "[MG_OL_0025] 전문 중복 검출 서비스 진입"); + + /* 1) 검출키 구성요소 수신 */ + if (tx_buf_get(ctx->in, "STAN", stan, sizeof(stan)) != TX_OK || + tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0025] 필수 필드(STAN/MERCHID) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0025] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 중복검출 지문 계산 */ + fp = dup_fingerprint(stan, amount, merch); + + strncpy(h_stan, stan, sizeof(h_stan) - 1); + h_stan[sizeof(h_stan) - 1] = '\0'; + strncpy(h_merch, merch, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + h_amount = amount; + memset(h_dup_key, 0, sizeof(h_dup_key)); + + /* 3) 최근 처리원장에서 동일 조합 조회 (최근 1건) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select key from mg_ledger where stan = $1 and merch_id = $2 and amount = $3 order by req_ts desc limit 1", + ECPGt_char,(h_stan),(long)13,(long)1,(13)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_dup_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 81 "app/online/mg_ol_0025.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_INFO, "[MG_OL_0025] 중복없음 stan=%s fp=%ld", stan, fp); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "DUP", "N"); + tx_buf_setlong(ctx->out, "FINGERPRINT", fp); + tx_return(ctx, TX_OK, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mg_ledger(중복조회)"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 4) 중복 판정 */ + tx_log(TX_LOG_WARN, "[MG_OL_0025] 중복검출 stan=%s dupkey=%s fp=%ld", + stan, h_dup_key, fp); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0025] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_buf_sets(ctx->out, "DUP", "Y"); + tx_buf_sets(ctx->out, "DUPKEY", h_dup_key); + tx_buf_setlong(ctx->out, "FINGERPRINT", fp); + tx_return(ctx, TX_FAIL, ctx->out); } diff --git a/app/online/mg_ol_0025.pgc b/app/online/mg_ol_0025.pgc index 7b6582b..27fae9b 100644 --- a/app/online/mg_ol_0025.pgc +++ b/app/online/mg_ol_0025.pgc @@ -1,47 +1,109 @@ -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0025.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0025) [tier=easy] + * mg_ol_0025.pgc - 전문게이트웨이 전문 중복 검출 서비스 (MG_OL_0025) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * STAN(전송추적번호)+금액+가맹점 조합으로 중복검출 해시를 계산하고, + * 최근 처리원장(mg_ledger)에서 동일 조합을 조회하여 중복 여부를 판정한다. + * 중복이면 기존 처리키를 반환하고, 신규면 통과시킨다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mg_core.h" EXEC SQL INCLUDE sqlca; +/* STAN+금액+가맹점 조합의 djb2 변형 해시 (중복탐지 지문) */ +static long dup_fingerprint(const char *stan, long amount, const char *merch) +{ + unsigned long h = 5381UL; + const char *p; + char amt[24]; + int i; + + for (p = stan; *p; p++) + h = ((h << 5) + h) ^ (unsigned char)*p; + for (p = merch; *p; p++) + h = ((h << 5) + h) ^ (unsigned char)*p; + snprintf(amt, sizeof(amt), "%ld", amount); + for (i = 0; amt[i]; i++) + h = ((h << 5) + h) ^ (unsigned char)amt[i]; + + return (long)(h & 0x7fffffffUL); +} + TX_SERVICE(MG_OL_0025, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_stan[13]; + char h_merch[16]; + long h_amount; + char h_dup_key[16]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MG_OL_0025] 전문게이트웨이 단건조회 서비스 진입"); + char stan[13], merch[16]; + long amount = 0; + long fp; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0025] KEY 누락"); + tx_log(TX_LOG_INFO, "[MG_OL_0025] 전문 중복 검출 서비스 진입"); + + /* 1) 검출키 구성요소 수신 */ + if (tx_buf_get(ctx->in, "STAN", stan, sizeof(stan)) != TX_OK || + tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0025] 필수 필드(STAN/MERCHID) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0025] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 중복검출 지문 계산 */ + fp = dup_fingerprint(stan, amount, merch); + + strncpy(h_stan, stan, sizeof(h_stan) - 1); + h_stan[sizeof(h_stan) - 1] = '\0'; + strncpy(h_merch, merch, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + h_amount = amount; + memset(h_dup_key, 0, sizeof(h_dup_key)); + + /* 3) 최근 처리원장에서 동일 조합 조회 (최근 1건) */ + EXEC SQL + SELECT key INTO :h_dup_key + FROM mg_ledger + WHERE stan = :h_stan + AND merch_id = :h_merch + AND amount = :h_amount + ORDER BY req_ts DESC + LIMIT 1; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_INFO, "[MG_OL_0025] 중복없음 stan=%s fp=%ld", stan, fp); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "DUP", "N"); + tx_buf_setlong(ctx->out, "FINGERPRINT", fp); + tx_return(ctx, TX_OK, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mg_ledger(중복조회)"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 4) 중복 판정 */ + tx_log(TX_LOG_WARN, "[MG_OL_0025] 중복검출 stan=%s dupkey=%s fp=%ld", + stan, h_dup_key, fp); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0025] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_buf_sets(ctx->out, "DUP", "Y"); + tx_buf_sets(ctx->out, "DUPKEY", h_dup_key); + tx_buf_setlong(ctx->out, "FINGERPRINT", fp); + tx_return(ctx, TX_FAIL, ctx->out); } diff --git a/app/online/mg_ol_0026.c b/app/online/mg_ol_0026.c index 4e07609..568e871 100644 --- a/app/online/mg_ol_0026.c +++ b/app/online/mg_ol_0026.c @@ -8,10 +8,12 @@ #line 1 "app/online/mg_ol_0026.pgc" /* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0026.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0026) [tier=medium] + * mg_ol_0026.pgc - 전문게이트웨이 전문 필드 치환 서비스 (MG_OL_0026) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 개인정보 보호를 위해 전문 필드에 마스킹 치환규칙을 적용한다. + * 카드번호(CARDNO)는 앞 6자리·뒤 4자리만 남기고 가운데를 '*'로 치환하고, + * 주민번호식(RRN) 13자리는 뒤 7자리를 치환한다. 치환된 자릿수를 집계하여 + * 마스킹 결과와 함께 응답한다. (순수 치환 로직 - DB 접근 없음) */ #include #include @@ -89,60 +91,70 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0026.pgc" +#line 17 "app/online/mg_ol_0026.pgc" +/* 문자열 가운데를 마스킹: 앞 keep_head, 뒤 keep_tail 만 보존. 치환자릿수 반환 */ +static int mask_middle(char *s, int keep_head, int keep_tail) +{ + int len = (int)strlen(s); + int i, masked = 0; + + if (len <= keep_head + keep_tail) + return 0; + for (i = keep_head; i < len - keep_tail; i++) { + if (s[i] != '*') { + s[i] = '*'; + masked++; + } + } + return masked; +} + TX_SERVICE(MG_OL_0026, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char cardno[24], rrn[16]; + int have_card, have_rrn; + int masked = 0; - tx_log(TX_LOG_INFO, "[MG_OL_0026] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0026] 전문 필드 치환 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0026] 필수 필드 누락"); + have_card = (tx_buf_get(ctx->in, "CARDNO", cardno, sizeof(cardno)) == TX_OK); + have_rrn = (tx_buf_get(ctx->in, "RRN", rrn, sizeof(rrn)) == TX_OK); + + if (!have_card && !have_rrn) { + tx_log(TX_LOG_ERROR, "[MG_OL_0026] 치환대상 필드(CARDNO/RRN) 없음"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0026] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0026] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0026] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = mg_rec_count(bizdate, MG_ST_RECV); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0026] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + + /* 1) 카드번호 마스킹: 앞 6 / 뒤 4 보존 */ + if (have_card) { + if (strlen(cardno) < 12) { + tx_log(TX_LOG_WARN, "[MG_OL_0026] 카드번호 길이 부족 len=%d", + (int)strlen(cardno)); + } else { + masked += mask_middle(cardno, 6, 4); + tx_buf_sets(ctx->out, "CARDNO", cardno); + } + } + + /* 2) 주민번호식 마스킹: 앞 6 보존, 뒤 7 전부 치환 */ + if (have_rrn) { + if (strlen(rrn) < 7) { + tx_log(TX_LOG_WARN, "[MG_OL_0026] 주민번호식 길이 부족 len=%d", + (int)strlen(rrn)); + } else { + masked += mask_middle(rrn, 6, 0); + tx_buf_sets(ctx->out, "RRN", rrn); + } + } + + /* 3) 치환건수 응답 */ + tx_buf_setlong(ctx->out, "MASKED", (long)masked); + tx_log(TX_LOG_INFO, "[MG_OL_0026] 치환완료 치환자릿수=%d", masked); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0026.pgc b/app/online/mg_ol_0026.pgc index e911e53..3d0ae5d 100644 --- a/app/online/mg_ol_0026.pgc +++ b/app/online/mg_ol_0026.pgc @@ -1,9 +1,11 @@ /* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0026.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0026) [tier=medium] + * mg_ol_0026.pgc - 전문게이트웨이 전문 필드 치환 서비스 (MG_OL_0026) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 개인정보 보호를 위해 전문 필드에 마스킹 치환규칙을 적용한다. + * 카드번호(CARDNO)는 앞 6자리·뒤 4자리만 남기고 가운데를 '*'로 치환하고, + * 주민번호식(RRN) 13자리는 뒤 7자리를 치환한다. 치환된 자릿수를 집계하여 + * 마스킹 결과와 함께 응답한다. (순수 치환 로직 - DB 접근 없음) */ #include #include @@ -14,57 +16,67 @@ EXEC SQL INCLUDE sqlca; +/* 문자열 가운데를 마스킹: 앞 keep_head, 뒤 keep_tail 만 보존. 치환자릿수 반환 */ +static int mask_middle(char *s, int keep_head, int keep_tail) +{ + int len = (int)strlen(s); + int i, masked = 0; + + if (len <= keep_head + keep_tail) + return 0; + for (i = keep_head; i < len - keep_tail; i++) { + if (s[i] != '*') { + s[i] = '*'; + masked++; + } + } + return masked; +} + TX_SERVICE(MG_OL_0026, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char cardno[24], rrn[16]; + int have_card, have_rrn; + int masked = 0; - tx_log(TX_LOG_INFO, "[MG_OL_0026] 전문게이트웨이 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0026] 전문 필드 치환 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0026] 필수 필드 누락"); + have_card = (tx_buf_get(ctx->in, "CARDNO", cardno, sizeof(cardno)) == TX_OK); + have_rrn = (tx_buf_get(ctx->in, "RRN", rrn, sizeof(rrn)) == TX_OK); + + if (!have_card && !have_rrn) { + tx_log(TX_LOG_ERROR, "[MG_OL_0026] 치환대상 필드(CARDNO/RRN) 없음"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MG_OL_0026] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0026] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0026] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = mg_rec_count(bizdate, MG_ST_RECV); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MG_OL_0026] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + + /* 1) 카드번호 마스킹: 앞 6 / 뒤 4 보존 */ + if (have_card) { + if (strlen(cardno) < 12) { + tx_log(TX_LOG_WARN, "[MG_OL_0026] 카드번호 길이 부족 len=%d", + (int)strlen(cardno)); + } else { + masked += mask_middle(cardno, 6, 4); + tx_buf_sets(ctx->out, "CARDNO", cardno); + } + } + + /* 2) 주민번호식 마스킹: 앞 6 보존, 뒤 7 전부 치환 */ + if (have_rrn) { + if (strlen(rrn) < 7) { + tx_log(TX_LOG_WARN, "[MG_OL_0026] 주민번호식 길이 부족 len=%d", + (int)strlen(rrn)); + } else { + masked += mask_middle(rrn, 6, 0); + tx_buf_sets(ctx->out, "RRN", rrn); + } + } + + /* 3) 치환건수 응답 */ + tx_buf_setlong(ctx->out, "MASKED", (long)masked); + tx_log(TX_LOG_INFO, "[MG_OL_0026] 치환완료 치환자릿수=%d", masked); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0027.c b/app/online/mg_ol_0027.c index ea3d473..5505090 100644 --- a/app/online/mg_ol_0027.c +++ b/app/online/mg_ol_0027.c @@ -8,10 +8,12 @@ #line 1 "app/online/mg_ol_0027.pgc" /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0027.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0027) [tier=easy] + * mg_ol_0027.pgc - 전문게이트웨이 전문 길이 검증 서비스 (MG_OL_0027) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * LLLL(선두 4자리 길이 prefix) 프레이밍 규약에 따라, 전문 앞 4바이트에 + * 선언된 길이와 실제 본문 길이가 일치하는지 검증한다. 길이 prefix가 + * 숫자가 아니거나 선언길이 != 실제길이면 반려한다. + * (순수 검증 로직 - DB 접근 없음) */ #include #include @@ -89,37 +91,67 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0027.pgc" +#line 17 "app/online/mg_ol_0027.pgc" +#define MG_LEN_PREFIX 4 + TX_SERVICE(MG_OL_0027, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char raw[512]; + int rawlen, declared, actual, i; - tx_log(TX_LOG_INFO, "[MG_OL_0027] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0027] 전문 길이 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0027] KEY 누락"); + /* 1) 원시전문 수신 (LLLL + 본문) */ + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen <= 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0027] 원시전문(RAWMSG) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0027] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 최소 길이(프레임 헤더) 확인 */ + actual = (int)strlen(raw); + if (actual < MG_LEN_PREFIX) { + tx_log(TX_LOG_WARN, "[MG_OL_0027] 프레임 헤더 부족 len=%d", actual); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 3) 길이 prefix 숫자 검증 */ + for (i = 0; i < MG_LEN_PREFIX; i++) { + if (raw[i] < '0' || raw[i] > '9') { + tx_log(TX_LOG_WARN, "[MG_OL_0027] 길이 prefix 비숫자 pos=%d", i); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + } + + /* 4) 선언길이 vs 실제 본문길이 비교 */ + declared = (raw[0] - '0') * 1000 + (raw[1] - '0') * 100 + + (raw[2] - '0') * 10 + (raw[3] - '0'); + actual -= MG_LEN_PREFIX; /* 본문 실제 길이 */ + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0027] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "DECLARED", (long)declared); + tx_buf_setlong(ctx->out, "ACTUAL", (long)actual); + + if (declared != actual) { + tx_log(TX_LOG_WARN, "[MG_OL_0027] 길이 불일치 선언=%d 실제=%d", + declared, actual); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_buf_sets(ctx->out, "MATCH", "N"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "MATCH", "Y"); + tx_log(TX_LOG_INFO, "[MG_OL_0027] 길이 일치 len=%d", declared); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0027.pgc b/app/online/mg_ol_0027.pgc index 59d4980..718409b 100644 --- a/app/online/mg_ol_0027.pgc +++ b/app/online/mg_ol_0027.pgc @@ -1,9 +1,11 @@ /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0027.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0027) [tier=easy] + * mg_ol_0027.pgc - 전문게이트웨이 전문 길이 검증 서비스 (MG_OL_0027) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * LLLL(선두 4자리 길이 prefix) 프레이밍 규약에 따라, 전문 앞 4바이트에 + * 선언된 길이와 실제 본문 길이가 일치하는지 검증한다. 길이 prefix가 + * 숫자가 아니거나 선언길이 != 실제길이면 반려한다. + * (순수 검증 로직 - DB 접근 없음) */ #include #include @@ -14,34 +16,64 @@ EXEC SQL INCLUDE sqlca; +#define MG_LEN_PREFIX 4 + TX_SERVICE(MG_OL_0027, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char raw[512]; + int rawlen, declared, actual, i; - tx_log(TX_LOG_INFO, "[MG_OL_0027] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0027] 전문 길이 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0027] KEY 누락"); + /* 1) 원시전문 수신 (LLLL + 본문) */ + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen <= 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0027] 원시전문(RAWMSG) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0027] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 최소 길이(프레임 헤더) 확인 */ + actual = (int)strlen(raw); + if (actual < MG_LEN_PREFIX) { + tx_log(TX_LOG_WARN, "[MG_OL_0027] 프레임 헤더 부족 len=%d", actual); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 3) 길이 prefix 숫자 검증 */ + for (i = 0; i < MG_LEN_PREFIX; i++) { + if (raw[i] < '0' || raw[i] > '9') { + tx_log(TX_LOG_WARN, "[MG_OL_0027] 길이 prefix 비숫자 pos=%d", i); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + } + + /* 4) 선언길이 vs 실제 본문길이 비교 */ + declared = (raw[0] - '0') * 1000 + (raw[1] - '0') * 100 + + (raw[2] - '0') * 10 + (raw[3] - '0'); + actual -= MG_LEN_PREFIX; /* 본문 실제 길이 */ + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0027] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "DECLARED", (long)declared); + tx_buf_setlong(ctx->out, "ACTUAL", (long)actual); + + if (declared != actual) { + tx_log(TX_LOG_WARN, "[MG_OL_0027] 길이 불일치 선언=%d 실제=%d", + declared, actual); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_buf_sets(ctx->out, "MATCH", "N"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "MATCH", "Y"); + tx_log(TX_LOG_INFO, "[MG_OL_0027] 길이 일치 len=%d", declared); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0028.c b/app/online/mg_ol_0028.c index 0c39edc..8386e35 100644 --- a/app/online/mg_ol_0028.c +++ b/app/online/mg_ol_0028.c @@ -6,12 +6,13 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0028.pgc" -/* @tier hard @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0028.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0028) [tier=hard] + * mg_ol_0028.pgc - 전문게이트웨이 BCD 인코딩 유틸 서비스 (MG_OL_0028) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 숫자 문자열(NUM)을 BCD(4bit packed, 2자리/1바이트)로 인코딩한다. + * 홀수 길이는 하위 니블을 0으로 패딩한다. 인코딩된 패킹길이와 hex 표현을 + * 계산하여 응답한다. (순수 인코딩 로직 - DB 접근 없음) */ #include #include @@ -89,88 +90,90 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0028.pgc" +#line 16 "app/online/mg_ol_0028.pgc" +#define MG_BCD_MAX 32 + +/* 숫자문자열 -> BCD packed. 성공 시 패킹 바이트수, 실패 시 -1 */ +static int bcd_encode(const char *digits, int ndigit, unsigned char *out, int cap) +{ + int i, packed = (ndigit + 1) / 2; + + if (packed > cap) + return -1; + memset(out, 0, packed); + for (i = 0; i < ndigit; i++) { + unsigned char nib; + if (digits[i] < '0' || digits[i] > '9') + return -1; + nib = (unsigned char)(digits[i] - '0'); + if ((i & 1) == 0) + out[i / 2] |= (unsigned char)(nib << 4); /* 상위 니블 */ + else + out[i / 2] |= nib; /* 하위 니블 */ + } + return packed; +} + +/* packed 바이트열 -> hex 문자열 */ +static void bcd_to_hex(const unsigned char *buf, int n, char *hex, int cap) +{ + static const char HX[] = "0123456789ABCDEF"; + int i, j = 0; + + for (i = 0; i < n && j + 2 < cap; i++) { + hex[j++] = HX[(buf[i] >> 4) & 0x0f]; + hex[j++] = HX[buf[i] & 0x0f]; + } + hex[j] = '\0'; +} + TX_SERVICE(MG_OL_0028, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char num[MG_BCD_MAX + 1]; + char hex[MG_BCD_MAX * 2 + 1]; + unsigned char packed[MG_BCD_MAX]; + int ndigit, plen; - tx_log(TX_LOG_INFO, "[MG_OL_0028] 전문게이트웨이 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0028] BCD 인코딩 유틸 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0028] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0028] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + /* 1) 인코딩 대상 숫자문자열 수신 */ + if (tx_buf_get(ctx->in, "NUM", num, sizeof(num)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0028] 인코딩 대상(NUM) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { + ndigit = (int)strlen(num); + if (ndigit == 0 || ndigit > MG_BCD_MAX * 2) { + tx_log(TX_LOG_WARN, "[MG_OL_0028] 길이 범위 오류 len=%d", ndigit); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 2) BCD 인코딩 */ + plen = bcd_encode(num, ndigit, packed, sizeof(packed)); + if (plen < 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0028] 비숫자 포함 인코딩 실패 num=%s", num); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0028] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MG_OL_0028] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 3) hex 표현 변환 */ + bcd_to_hex(packed, plen, hex, sizeof(hex)); + /* 4) 인코딩 결과 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MG_OL_0028] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_setlong(ctx->out, "DIGITS", (long)ndigit); + tx_buf_setlong(ctx->out, "PACKLEN", (long)plen); + tx_buf_sets(ctx->out, "BCDHEX", hex); + tx_log(TX_LOG_INFO, "[MG_OL_0028] 인코딩완료 자릿수=%d 패킹=%dbyte hex=%s", + ndigit, plen, hex); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0028.pgc b/app/online/mg_ol_0028.pgc index 37a662f..1b78944 100644 --- a/app/online/mg_ol_0028.pgc +++ b/app/online/mg_ol_0028.pgc @@ -1,9 +1,10 @@ -/* @tier hard @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0028.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0028) [tier=hard] + * mg_ol_0028.pgc - 전문게이트웨이 BCD 인코딩 유틸 서비스 (MG_OL_0028) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 숫자 문자열(NUM)을 BCD(4bit packed, 2자리/1바이트)로 인코딩한다. + * 홀수 길이는 하위 니블을 0으로 패딩한다. 인코딩된 패킹길이와 hex 표현을 + * 계산하여 응답한다. (순수 인코딩 로직 - DB 접근 없음) */ #include #include @@ -14,85 +15,87 @@ EXEC SQL INCLUDE sqlca; +#define MG_BCD_MAX 32 + +/* 숫자문자열 -> BCD packed. 성공 시 패킹 바이트수, 실패 시 -1 */ +static int bcd_encode(const char *digits, int ndigit, unsigned char *out, int cap) +{ + int i, packed = (ndigit + 1) / 2; + + if (packed > cap) + return -1; + memset(out, 0, packed); + for (i = 0; i < ndigit; i++) { + unsigned char nib; + if (digits[i] < '0' || digits[i] > '9') + return -1; + nib = (unsigned char)(digits[i] - '0'); + if ((i & 1) == 0) + out[i / 2] |= (unsigned char)(nib << 4); /* 상위 니블 */ + else + out[i / 2] |= nib; /* 하위 니블 */ + } + return packed; +} + +/* packed 바이트열 -> hex 문자열 */ +static void bcd_to_hex(const unsigned char *buf, int n, char *hex, int cap) +{ + static const char HX[] = "0123456789ABCDEF"; + int i, j = 0; + + for (i = 0; i < n && j + 2 < cap; i++) { + hex[j++] = HX[(buf[i] >> 4) & 0x0f]; + hex[j++] = HX[buf[i] & 0x0f]; + } + hex[j] = '\0'; +} + TX_SERVICE(MG_OL_0028, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char num[MG_BCD_MAX + 1]; + char hex[MG_BCD_MAX * 2 + 1]; + unsigned char packed[MG_BCD_MAX]; + int ndigit, plen; - tx_log(TX_LOG_INFO, "[MG_OL_0028] 전문게이트웨이 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0028] BCD 인코딩 유틸 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0028] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0028] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + /* 1) 인코딩 대상 숫자문자열 수신 */ + if (tx_buf_get(ctx->in, "NUM", num, sizeof(num)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0028] 인코딩 대상(NUM) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { + ndigit = (int)strlen(num); + if (ndigit == 0 || ndigit > MG_BCD_MAX * 2) { + tx_log(TX_LOG_WARN, "[MG_OL_0028] 길이 범위 오류 len=%d", ndigit); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 2) BCD 인코딩 */ + plen = bcd_encode(num, ndigit, packed, sizeof(packed)); + if (plen < 0) { + tx_log(TX_LOG_WARN, "[MG_OL_0028] 비숫자 포함 인코딩 실패 num=%s", num); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0028] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MG_OL_0028] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 3) hex 표현 변환 */ + bcd_to_hex(packed, plen, hex, sizeof(hex)); + /* 4) 인코딩 결과 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MG_OL_0028] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_setlong(ctx->out, "DIGITS", (long)ndigit); + tx_buf_setlong(ctx->out, "PACKLEN", (long)plen); + tx_buf_sets(ctx->out, "BCDHEX", hex); + tx_log(TX_LOG_INFO, "[MG_OL_0028] 인코딩완료 자릿수=%d 패킹=%dbyte hex=%s", + ndigit, plen, hex); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0029.c b/app/online/mg_ol_0029.c index cf5ae18..aaf54a7 100644 --- a/app/online/mg_ol_0029.c +++ b/app/online/mg_ol_0029.c @@ -8,10 +8,11 @@ #line 1 "app/online/mg_ol_0029.pgc" /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0029.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0029) [tier=easy] + * mg_ol_0029.pgc - 전문게이트웨이 전문 트레일러 생성 서비스 (MG_OL_0029) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 전문 본문(RAWMSG) 전체 바이트에 대해 XOR LRC(종방향 중복검사) 체크섬을 + * 계산하여 전송 트레일러 필드를 생성한다. LRC 를 2자리 hex 트레일러로 + * 구성해 응답한다. (순수 계산 로직 - DB 접근 없음) */ #include #include @@ -89,37 +90,54 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0029.pgc" +#line 16 "app/online/mg_ol_0029.pgc" +/* 바이트열 XOR LRC 계산 (초기값 0) */ +static unsigned char lrc_xor(const char *buf, int len) +{ + unsigned char lrc = 0; + int i; + + for (i = 0; i < len; i++) + lrc ^= (unsigned char)buf[i]; + return lrc; +} + TX_SERVICE(MG_OL_0029, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char raw[512]; + char trailer[3]; + static const char HX[] = "0123456789ABCDEF"; + unsigned char lrc; + int rawlen; - tx_log(TX_LOG_INFO, "[MG_OL_0029] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0029] 전문 트레일러 생성 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0029] KEY 누락"); + /* 1) 트레일러 대상 본문 수신 */ + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen <= 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0029] 원시전문(RAWMSG) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + rawlen = (int)strlen(raw); - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0029] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + /* 2) XOR LRC 체크섬 계산 */ + lrc = lrc_xor(raw, rawlen); + /* 3) 2자리 hex 트레일러 구성 */ + trailer[0] = HX[(lrc >> 4) & 0x0f]; + trailer[1] = HX[lrc & 0x0f]; + trailer[2] = '\0'; + + /* 4) 체크섬/트레일러 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0029] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_setlong(ctx->out, "BODYLEN", (long)rawlen); + tx_buf_setlong(ctx->out, "LRC", (long)lrc); + tx_buf_sets(ctx->out, "TRAILER", trailer); + tx_log(TX_LOG_INFO, "[MG_OL_0029] 트레일러 생성완료 len=%d lrc=%02X", + rawlen, lrc); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0029.pgc b/app/online/mg_ol_0029.pgc index 35ad5d4..bc411fd 100644 --- a/app/online/mg_ol_0029.pgc +++ b/app/online/mg_ol_0029.pgc @@ -1,9 +1,10 @@ /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0029.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0029) [tier=easy] + * mg_ol_0029.pgc - 전문게이트웨이 전문 트레일러 생성 서비스 (MG_OL_0029) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 전문 본문(RAWMSG) 전체 바이트에 대해 XOR LRC(종방향 중복검사) 체크섬을 + * 계산하여 전송 트레일러 필드를 생성한다. LRC 를 2자리 hex 트레일러로 + * 구성해 응답한다. (순수 계산 로직 - DB 접근 없음) */ #include #include @@ -14,34 +15,51 @@ EXEC SQL INCLUDE sqlca; +/* 바이트열 XOR LRC 계산 (초기값 0) */ +static unsigned char lrc_xor(const char *buf, int len) +{ + unsigned char lrc = 0; + int i; + + for (i = 0; i < len; i++) + lrc ^= (unsigned char)buf[i]; + return lrc; +} + TX_SERVICE(MG_OL_0029, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + char raw[512]; + char trailer[3]; + static const char HX[] = "0123456789ABCDEF"; + unsigned char lrc; + int rawlen; - tx_log(TX_LOG_INFO, "[MG_OL_0029] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0029] 전문 트레일러 생성 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0029] KEY 누락"); + /* 1) 트레일러 대상 본문 수신 */ + rawlen = tx_buf_get(ctx->in, "RAWMSG", raw, sizeof(raw)); + if (rawlen <= 0) { + tx_log(TX_LOG_ERROR, "[MG_OL_0029] 원시전문(RAWMSG) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + rawlen = (int)strlen(raw); - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0029] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + /* 2) XOR LRC 체크섬 계산 */ + lrc = lrc_xor(raw, rawlen); + /* 3) 2자리 hex 트레일러 구성 */ + trailer[0] = HX[(lrc >> 4) & 0x0f]; + trailer[1] = HX[lrc & 0x0f]; + trailer[2] = '\0'; + + /* 4) 체크섬/트레일러 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0029] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_setlong(ctx->out, "BODYLEN", (long)rawlen); + tx_buf_setlong(ctx->out, "LRC", (long)lrc); + tx_buf_sets(ctx->out, "TRAILER", trailer); + tx_log(TX_LOG_INFO, "[MG_OL_0029] 트레일러 생성완료 len=%d lrc=%02X", + rawlen, lrc); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0030.c b/app/online/mg_ol_0030.c index 4b9b953..2151a76 100644 --- a/app/online/mg_ol_0030.c +++ b/app/online/mg_ol_0030.c @@ -6,12 +6,14 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0030.pgc" -/* @tier hard @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0030.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0030) [tier=hard] + * mg_ol_0030.pgc - 전문게이트웨이 ACK/NAK 판정 서비스 (MG_OL_0030) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 수신 응답전문의 응답코드(RESPCODE)와 시퀀스(RECVSEQ)를 송신 시퀀스 + * (SENDSEQ)와 대조하여 ACK/NAK 를 결정한다. 응답코드가 정상이고 시퀀스가 + * 일치하면 ACK(재전송 불필요), 그 외에는 NAK 로 판정하고 재전송필요 여부를 + * 응답한다. (순수 판정 로직 - DB 접근 없음) */ #include #include @@ -89,88 +91,54 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0030.pgc" +#line 17 "app/online/mg_ol_0030.pgc" +#define MG_MAX_RETRY 3 + TX_SERVICE(MG_OL_0030, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char respcode[8]; + long send_seq = -1, recv_seq = -1, retry = 0; + int code_ok, seq_ok, is_ack, need_retx; - tx_log(TX_LOG_INFO, "[MG_OL_0030] 전문게이트웨이 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0030] ACK/NAK 판정 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0030] 필수 필드 누락"); + /* 1) 판정 입력 수신 */ + if (tx_buf_get(ctx->in, "RESPCODE", respcode, sizeof(respcode)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0030] 응답코드(RESPCODE) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "SENDSEQ", &send_seq); + tx_buf_getlong(ctx->in, "RECVSEQ", &recv_seq); + tx_buf_getlong(ctx->in, "RETRYCNT", &retry); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0030] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } + /* 2) 응답코드/시퀀스 정합성 판정 */ + code_ok = (strncmp(respcode, RESP_OK, FLD_RESP_LEN) == 0); + seq_ok = (send_seq >= 0 && recv_seq >= 0 && send_seq == recv_seq); + is_ack = (code_ok && seq_ok); - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0030] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MG_OL_0030] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 3) 재전송 필요 여부 (NAK + 재시도한도 미소진) */ + need_retx = (!is_ack && retry < MG_MAX_RETRY); + /* 4) 판정결과 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MG_OL_0030] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "RESULT", is_ack ? "ACK" : "NAK"); + tx_buf_sets(ctx->out, "CODEOK", code_ok ? "Y" : "N"); + tx_buf_sets(ctx->out, "SEQOK", seq_ok ? "Y" : "N"); + tx_buf_sets(ctx->out, "RETRANSMIT", need_retx ? "Y" : "N"); + tx_buf_setlong(ctx->out, "RETRYCNT", retry); + + if (is_ack) { + tx_log(TX_LOG_INFO, "[MG_OL_0030] ACK 판정 seq=%ld", recv_seq); + } else if (need_retx) { + tx_log(TX_LOG_WARN, "[MG_OL_0030] NAK 재전송 필요 code=%s send=%ld recv=%ld retry=%ld", + respcode, send_seq, recv_seq, retry); + } else { + tx_log(TX_LOG_ERROR, "[MG_OL_0030] NAK 재시도한도 소진 code=%s retry=%ld", + respcode, retry); + } tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0030.pgc b/app/online/mg_ol_0030.pgc index 998b61e..4ee50b7 100644 --- a/app/online/mg_ol_0030.pgc +++ b/app/online/mg_ol_0030.pgc @@ -1,9 +1,11 @@ -/* @tier hard @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0030.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0030) [tier=hard] + * mg_ol_0030.pgc - 전문게이트웨이 ACK/NAK 판정 서비스 (MG_OL_0030) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 수신 응답전문의 응답코드(RESPCODE)와 시퀀스(RECVSEQ)를 송신 시퀀스 + * (SENDSEQ)와 대조하여 ACK/NAK 를 결정한다. 응답코드가 정상이고 시퀀스가 + * 일치하면 ACK(재전송 불필요), 그 외에는 NAK 로 판정하고 재전송필요 여부를 + * 응답한다. (순수 판정 로직 - DB 접근 없음) */ #include #include @@ -14,85 +16,51 @@ EXEC SQL INCLUDE sqlca; +#define MG_MAX_RETRY 3 + TX_SERVICE(MG_OL_0030, ctx) { - mg_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char respcode[8]; + long send_seq = -1, recv_seq = -1, retry = 0; + int code_ok, seq_ok, is_ack, need_retx; - tx_log(TX_LOG_INFO, "[MG_OL_0030] 전문게이트웨이 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0030] ACK/NAK 판정 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0030] 필수 필드 누락"); + /* 1) 판정 입력 수신 */ + if (tx_buf_get(ctx->in, "RESPCODE", respcode, sizeof(respcode)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0030] 응답코드(RESPCODE) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "SENDSEQ", &send_seq); + tx_buf_getlong(ctx->in, "RECVSEQ", &recv_seq); + tx_buf_getlong(ctx->in, "RETRYCNT", &retry); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MG_OL_0030] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } + /* 2) 응답코드/시퀀스 정합성 판정 */ + code_ok = (strncmp(respcode, RESP_OK, FLD_RESP_LEN) == 0); + seq_ok = (send_seq >= 0 && recv_seq >= 0 && send_seq == recv_seq); + is_ack = (code_ok && seq_ok); - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MG_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = mg_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0030] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MG_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MG_OL_0030] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mg_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 3) 재전송 필요 여부 (NAK + 재시도한도 미소진) */ + need_retx = (!is_ack && retry < MG_MAX_RETRY); + /* 4) 판정결과 응답 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MG_OL_0030] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "RESULT", is_ack ? "ACK" : "NAK"); + tx_buf_sets(ctx->out, "CODEOK", code_ok ? "Y" : "N"); + tx_buf_sets(ctx->out, "SEQOK", seq_ok ? "Y" : "N"); + tx_buf_sets(ctx->out, "RETRANSMIT", need_retx ? "Y" : "N"); + tx_buf_setlong(ctx->out, "RETRYCNT", retry); + + if (is_ack) { + tx_log(TX_LOG_INFO, "[MG_OL_0030] ACK 판정 seq=%ld", recv_seq); + } else if (need_retx) { + tx_log(TX_LOG_WARN, "[MG_OL_0030] NAK 재전송 필요 code=%s send=%ld recv=%ld retry=%ld", + respcode, send_seq, recv_seq, retry); + } else { + tx_log(TX_LOG_ERROR, "[MG_OL_0030] NAK 재시도한도 소진 code=%s retry=%ld", + respcode, retry); + } tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0031.c b/app/online/mg_ol_0031.c index 9b80ee9..3a5c299 100644 --- a/app/online/mg_ol_0031.c +++ b/app/online/mg_ol_0031.c @@ -8,10 +8,11 @@ #line 1 "app/online/mg_ol_0031.pgc" /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0031.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0031) [tier=easy] + * mg_ol_0031.pgc - 전문게이트웨이 전문 스냅샷 조회 서비스 (MG_OL_0031) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리키(KEY)로 원장 레코드를 조회(mg_rec_select)한 뒤, 금액/상태/수수료를 + * 포함한 전체 필드 스냅샷을 응답 TXBUF 로 구성한다. 상태코드는 사람이 읽을 + * 수 있는 라벨로 함께 내려준다. (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -89,37 +90,64 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0031.pgc" +#line 16 "app/online/mg_ol_0031.pgc" TX_SERVICE(MG_OL_0031, ctx) { mg_rec_t rec; char key[16]; + char won[40]; + const char *st_label; int rc; - tx_log(TX_LOG_INFO, "[MG_OL_0031] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0031] 전문 스냅샷 조회 서비스 진입"); + /* 1) 처리키 수신 */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MG_OL_0031] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2) 원장 단건 조회 */ memset(&rec, 0, sizeof(rec)); rc = mg_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0031] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[MG_OL_0031] 스냅샷 대상 없음 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + /* 3) 상태코드 -> 표시 라벨 매핑 */ + if (strcmp(rec.status, MG_ST_RECV) == 0) + st_label = "접수"; + else if (strcmp(rec.status, MG_ST_DONE) == 0) + st_label = "처리완료"; + else if (strcmp(rec.status, MG_ST_FAIL) == 0) + st_label = "실패"; + else if (strcmp(rec.status, MG_ST_SETTLED) == 0) + st_label = "정산완료"; + else + st_label = "미상"; + + /* 4) 전체 필드 스냅샷 구성 */ + amount_format_won(rec.amount, won, sizeof(won)); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_OK); tx_buf_sets(ctx->out, "KEY", rec.key); tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_sets(ctx->out, "CARDNO", rec.card_no); + tx_buf_sets(ctx->out, "BIZDATE", rec.biz_date); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_sets(ctx->out, "AMOUNTWON", won); + tx_buf_setlong(ctx->out, "FEE", rec.fee); tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0031] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "STATUSLABEL", st_label); + + tx_log(TX_LOG_INFO, "[MG_OL_0031] 스냅샷 조회완료 key=%s 상태=%s 금액=%ld 수수료=%ld", + key, st_label, rec.amount, rec.fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0031.pgc b/app/online/mg_ol_0031.pgc index 46f194e..522fe9f 100644 --- a/app/online/mg_ol_0031.pgc +++ b/app/online/mg_ol_0031.pgc @@ -1,9 +1,10 @@ /* @tier easy @module mg @transform msg-gateway-online */ /* - * mg_ol_0031.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0031) [tier=easy] + * mg_ol_0031.pgc - 전문게이트웨이 전문 스냅샷 조회 서비스 (MG_OL_0031) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리키(KEY)로 원장 레코드를 조회(mg_rec_select)한 뒤, 금액/상태/수수료를 + * 포함한 전체 필드 스냅샷을 응답 TXBUF 로 구성한다. 상태코드는 사람이 읽을 + * 수 있는 라벨로 함께 내려준다. (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include @@ -18,30 +19,57 @@ TX_SERVICE(MG_OL_0031, ctx) { mg_rec_t rec; char key[16]; + char won[40]; + const char *st_label; int rc; - tx_log(TX_LOG_INFO, "[MG_OL_0031] 전문게이트웨이 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MG_OL_0031] 전문 스냅샷 조회 서비스 진입"); + /* 1) 처리키 수신 */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MG_OL_0031] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2) 원장 단건 조회 */ memset(&rec, 0, sizeof(rec)); rc = mg_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0031] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[MG_OL_0031] 스냅샷 대상 없음 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + /* 3) 상태코드 -> 표시 라벨 매핑 */ + if (strcmp(rec.status, MG_ST_RECV) == 0) + st_label = "접수"; + else if (strcmp(rec.status, MG_ST_DONE) == 0) + st_label = "처리완료"; + else if (strcmp(rec.status, MG_ST_FAIL) == 0) + st_label = "실패"; + else if (strcmp(rec.status, MG_ST_SETTLED) == 0) + st_label = "정산완료"; + else + st_label = "미상"; + + /* 4) 전체 필드 스냅샷 구성 */ + amount_format_won(rec.amount, won, sizeof(won)); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_OK); tx_buf_sets(ctx->out, "KEY", rec.key); tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_sets(ctx->out, "CARDNO", rec.card_no); + tx_buf_sets(ctx->out, "BIZDATE", rec.biz_date); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_sets(ctx->out, "AMOUNTWON", won); + tx_buf_setlong(ctx->out, "FEE", rec.fee); tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0031] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "STATUSLABEL", st_label); + + tx_log(TX_LOG_INFO, "[MG_OL_0031] 스냅샷 조회완료 key=%s 상태=%s 금액=%ld 수수료=%ld", + key, st_label, rec.amount, rec.fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0032.c b/app/online/mg_ol_0032.c index c150369..a7ce5bc 100644 --- a/app/online/mg_ol_0032.c +++ b/app/online/mg_ol_0032.c @@ -6,17 +6,20 @@ /* End of automatic include section */ #line 1 "app/online/mg_ol_0032.pgc" -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0032.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0032) [tier=easy] + * mg_ol_0032.pgc - 전문게이트웨이 전문 라우팅 통계 조회 서비스 (MG_OL_0032) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일(BIZDATE)의 처리원장(mg_ledger)을 채널코드(chan_cd)별로 집계 + * (count GROUP BY)하여 채널 수·총 전문건수·최다 처리채널(상위채널)을 산출한다. + * 커서로 집계결과를 순회하며 최상위 채널을 선별해 응답한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mg_core.h" @@ -89,37 +92,129 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mg_ol_0032.pgc" +#line 18 "app/online/mg_ol_0032.pgc" TX_SERVICE(MG_OL_0032, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 23 "app/online/mg_ol_0032.pgc" + char h_biz_date [ 9 ] ; + +#line 24 "app/online/mg_ol_0032.pgc" + char h_chan_cd [ 9 ] ; + +#line 25 "app/online/mg_ol_0032.pgc" + long h_chan_cnt ; +/* exec sql end declare section */ +#line 26 "app/online/mg_ol_0032.pgc" - tx_log(TX_LOG_INFO, "[MG_OL_0032] 전문게이트웨이 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0032] KEY 누락"); + char bizdate[16]; + char top_chan[9]; + long top_cnt = 0, total_cnt = 0, chan_num = 0; + + tx_log(TX_LOG_INFO, "[MG_OL_0032] 전문 라우팅 통계 조회 서비스 진입"); + + /* 1) 집계 대상 영업일 수신/검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0032] BIZDATE 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[MG_OL_0032] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + memset(top_chan, 0, sizeof(top_chan)); + + /* 2) 채널별 전문건수 집계 커서 (건수 내림차순) */ + ECPGset_var( 0, ( h_biz_date ), __LINE__);\ + /* declare cur_mg_ol_0032 cursor for select chan_cd , count ( * ) from mg_ledger where biz_date = $1 group by chan_cd order by count ( * ) desc */ +#line 58 "app/online/mg_ol_0032.pgc" + +#line 58 "app/online/mg_ol_0032.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_mg_ol_0032 cursor for select chan_cd , count ( * ) from mg_ledger where biz_date = $1 group by chan_cd order by count ( * ) desc", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 60 "app/online/mg_ol_0032.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_mg_ol_0032"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3) 집계결과 순회: 총건수 누적 + 최상위 채널 선별 */ + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_mg_ol_0032", ECPGt_EOIT, + ECPGt_char,(h_chan_cd),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_chan_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 71 "app/online/mg_ol_0032.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_mg_ol_0032"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_ol_0032", ECPGt_EOIT, ECPGt_EORT);} +#line 76 "app/online/mg_ol_0032.pgc" + + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + total_cnt += h_chan_cnt; + chan_num++; + if (h_chan_cnt > top_cnt) { + top_cnt = h_chan_cnt; + strncpy(top_chan, h_chan_cd, sizeof(top_chan) - 1); + top_chan[sizeof(top_chan) - 1] = '\0'; + } + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_mg_ol_0032", ECPGt_EOIT, ECPGt_EORT);} +#line 92 "app/online/mg_ol_0032.pgc" + + + /* 4) 집계대상 없음 처리 */ + if (chan_num == 0) { + tx_log(TX_LOG_INFO, "[MG_OL_0032] 집계대상 없음 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CHANNUM", 0); + tx_buf_setlong(ctx->out, "TOTALCNT", 0); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* 5) 통계결과 응답 (상위채널 포함) */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CHANNUM", chan_num); + tx_buf_setlong(ctx->out, "TOTALCNT", total_cnt); + tx_buf_sets(ctx->out, "TOPCHAN", top_chan); + tx_buf_setlong(ctx->out, "TOPCNT", top_cnt); + tx_log(TX_LOG_INFO, "[MG_OL_0032] 통계완료 date=%s 채널수=%ld 총건수=%ld 상위채널=%s(%ld)", + bizdate, chan_num, total_cnt, top_chan, top_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mg_ol_0032.pgc b/app/online/mg_ol_0032.pgc index 25d00fc..d23a2bd 100644 --- a/app/online/mg_ol_0032.pgc +++ b/app/online/mg_ol_0032.pgc @@ -1,14 +1,17 @@ -/* @tier easy @module mg @transform msg-gateway-online */ +/* @tier medium @module mg @transform msg-gateway-online */ /* - * mg_ol_0032.pgc - 전문게이트웨이 온라인 서비스 (MG_OL_0032) [tier=easy] + * mg_ol_0032.pgc - 전문게이트웨이 전문 라우팅 통계 조회 서비스 (MG_OL_0032) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mg 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일(BIZDATE)의 처리원장(mg_ledger)을 채널코드(chan_cd)별로 집계 + * (count GROUP BY)하여 채널 수·총 전문건수·최다 처리채널(상위채널)을 산출한다. + * 커서로 집계결과를 순회하며 최상위 채널을 선별해 응답한다. + * (ATMI void SVC(TPSVCINFO*) 대응) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mg_core.h" @@ -16,32 +19,99 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MG_OL_0032, ctx) { - mg_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_chan_cd[9]; + long h_chan_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MG_OL_0032] 전문게이트웨이 단건조회 서비스 진입"); + char bizdate[16]; + char top_chan[9]; + long top_cnt = 0, total_cnt = 0, chan_num = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MG_OL_0032] KEY 누락"); + tx_log(TX_LOG_INFO, "[MG_OL_0032] 전문 라우팅 통계 조회 서비스 진입"); + + /* 1) 집계 대상 영업일 수신/검증 */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MG_OL_0032] BIZDATE 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[MG_OL_0032] 영업일 형식 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mg_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MG_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + memset(top_chan, 0, sizeof(top_chan)); + + /* 2) 채널별 전문건수 집계 커서 (건수 내림차순) */ + EXEC SQL DECLARE cur_mg_ol_0032 CURSOR FOR + SELECT chan_cd, count(*) + FROM mg_ledger + WHERE biz_date = :h_biz_date + GROUP BY chan_cd + ORDER BY count(*) DESC; + + EXEC SQL OPEN cur_mg_ol_0032; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_mg_ol_0032"); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 3) 집계결과 순회: 총건수 누적 + 최상위 채널 선별 */ + for (;;) { + EXEC SQL FETCH cur_mg_ol_0032 INTO :h_chan_cd, :h_chan_cnt; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_mg_ol_0032"); + EXEC SQL CLOSE cur_mg_ol_0032; + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_SYSERR); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + total_cnt += h_chan_cnt; + chan_num++; + if (h_chan_cnt > top_cnt) { + top_cnt = h_chan_cnt; + strncpy(top_chan, h_chan_cd, sizeof(top_chan) - 1); + top_chan[sizeof(top_chan) - 1] = '\0'; + } + } + + EXEC SQL CLOSE cur_mg_ol_0032; + + /* 4) 집계대상 없음 처리 */ + if (chan_num == 0) { + tx_log(TX_LOG_INFO, "[MG_OL_0032] 집계대상 없음 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CHANNUM", 0); + tx_buf_setlong(ctx->out, "TOTALCNT", 0); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* 5) 통계결과 응답 (상위채널 포함) */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MG_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESP", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CHANNUM", chan_num); + tx_buf_setlong(ctx->out, "TOTALCNT", total_cnt); + tx_buf_sets(ctx->out, "TOPCHAN", top_chan); + tx_buf_setlong(ctx->out, "TOPCNT", top_cnt); + tx_log(TX_LOG_INFO, "[MG_OL_0032] 통계완료 date=%s 채널수=%ld 총건수=%ld 상위채널=%s(%ld)", + bizdate, chan_num, total_cnt, top_chan, top_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0002.c b/app/online/mm_ol_0002.c index 81e642e..0c96a72 100644 --- a/app/online/mm_ol_0002.c +++ b/app/online/mm_ol_0002.c @@ -6,17 +6,19 @@ /* End of automatic include section */ #line 1 "app/online/mm_ol_0002.pgc" -/* @tier hard @module mm @transform master-online */ +/* @tier medium @module mm @transform master-online */ /* - * mm_ol_0002.pgc - 마스터 온라인 서비스 (MM_OL_0002) [tier=hard] + * mm_ol_0002.pgc - 가맹점 등록 서비스 (MM_OL_0002) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 신규 가맹점 마스터를 등록하고 변경이력(mm_merchant_hist) 에 등록('I') + * 이벤트를 남긴다. 사업자번호 중복이면 반려한다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,88 +91,135 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0002.pgc" +#line 17 "app/online/mm_ol_0002.pgc" TX_SERVICE(MM_OL_0002, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + + + +#line 22 "app/online/mm_ol_0002.pgc" + char h_merch_id [ 16 ] ; + +#line 23 "app/online/mm_ol_0002.pgc" + char h_merch_nm [ 64 ] ; + +#line 24 "app/online/mm_ol_0002.pgc" + char h_biz_no [ 16 ] ; + +#line 25 "app/online/mm_ol_0002.pgc" + char h_status [ 2 ] ; + +#line 26 "app/online/mm_ol_0002.pgc" + char h_reg_date [ 9 ] ; + +#line 27 "app/online/mm_ol_0002.pgc" + long h_dup ; +/* exec sql end declare section */ +#line 28 "app/online/mm_ol_0002.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0002] 마스터 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + char merch_id[16]; + char merch_nm[64]; + char biz_no[16]; + + tx_log(TX_LOG_INFO, "[MM_OL_0002] 가맹점 등록 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "MERCHNM", merch_nm, sizeof(merch_nm)) != TX_OK || + tx_buf_get(ctx->in, "BIZNO", biz_no, sizeof(biz_no)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0002] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0002] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "MM0200"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_merch_nm, merch_nm, sizeof(h_merch_nm) - 1); + h_merch_nm[sizeof(h_merch_nm) - 1] = '\0'; + strncpy(h_biz_no, biz_no, sizeof(h_biz_no) - 1); + h_biz_no[sizeof(h_biz_no) - 1] = '\0'; + strncpy(h_status, MM_ST_RECV, sizeof(h_status)); + date_today(h_reg_date); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_merchant where biz_no = $1 ", + ECPGt_char,(h_biz_no),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_dup),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 57 "app/online/mm_ol_0002.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT dup mm_merchant"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0209"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0002] 사업자번호 중복 biz_no=%s", biz_no); + tx_buf_sets(ctx->out, "ERRCODE", "MM0201"); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0002] tx_begin 경고"); - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0002] 원장반영 실패 rc=%d", rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_merchant ( merch_id , merch_nm , biz_no , status , reg_date ) values ( $1 , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_no),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_reg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 77 "app/online/mm_ol_0002.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_merchant"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_buf_sets(ctx->out, "ERRCODE", "MM0202"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_merchant_hist ( merch_id , chg_type , chg_date , chg_status ) values ( $1 , 'I' , $2 , $3 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_reg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 89 "app/online/mm_ol_0002.pgc" - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MM_OL_0002] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - /* 교차 검증: 당일 합계 재계산 */ - if (mm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_merchant_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0203"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MM_OL_0002] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "REGDATE", h_reg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0002] 가맹점 등록완료 merch=%s", merch_id); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0002.pgc b/app/online/mm_ol_0002.pgc index e09b3e3..e7a41a9 100644 --- a/app/online/mm_ol_0002.pgc +++ b/app/online/mm_ol_0002.pgc @@ -1,14 +1,16 @@ -/* @tier hard @module mm @transform master-online */ +/* @tier medium @module mm @transform master-online */ /* - * mm_ol_0002.pgc - 마스터 온라인 서비스 (MM_OL_0002) [tier=hard] + * mm_ol_0002.pgc - 가맹점 등록 서비스 (MM_OL_0002) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 신규 가맹점 마스터를 등록하고 변경이력(mm_merchant_hist) 에 등록('I') + * 이벤트를 남긴다. 사업자번호 중복이면 반려한다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,83 +18,91 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0002, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_merch_nm[64]; + char h_biz_no[16]; + char h_status[2]; + char h_reg_date[9]; + long h_dup; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0002] 마스터 트랜잭션 오케스트레이션 진입"); + char merch_id[16]; + char merch_nm[64]; + char biz_no[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_INFO, "[MM_OL_0002] 가맹점 등록 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "MERCHNM", merch_nm, sizeof(merch_nm)) != TX_OK || + tx_buf_get(ctx->in, "BIZNO", biz_no, sizeof(biz_no)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0002] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0002] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "MM0200"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_merch_nm, merch_nm, sizeof(h_merch_nm) - 1); + h_merch_nm[sizeof(h_merch_nm) - 1] = '\0'; + strncpy(h_biz_no, biz_no, sizeof(h_biz_no) - 1); + h_biz_no[sizeof(h_biz_no) - 1] = '\0'; + strncpy(h_status, MM_ST_RECV, sizeof(h_status)); + date_today(h_reg_date); + + EXEC SQL + SELECT COUNT(*) INTO :h_dup + FROM mm_merchant + WHERE biz_no = :h_biz_no; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT dup mm_merchant"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0209"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0002] 사업자번호 중복 biz_no=%s", biz_no); + tx_buf_sets(ctx->out, "ERRCODE", "MM0201"); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0002] tx_begin 경고"); - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0002] 원장반영 실패 rc=%d", rc); + EXEC SQL + INSERT INTO mm_merchant (merch_id, merch_nm, biz_no, status, reg_date) + VALUES (:h_merch_id, :h_merch_nm, :h_biz_no, :h_status, :h_reg_date); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_merchant"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_buf_sets(ctx->out, "ERRCODE", "MM0202"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } + EXEC SQL + INSERT INTO mm_merchant_hist (merch_id, chg_type, chg_date, chg_status) + VALUES (:h_merch_id, 'I', :h_reg_date, :h_status); - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MM_OL_0002] 후속연계 경고 rc=%d", rc); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_merchant_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0203"); + tx_return(ctx, TX_EDB, ctx->out); + return; } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MM_OL_0002] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "REGDATE", h_reg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0002] 가맹점 등록완료 merch=%s", merch_id); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0003.c b/app/online/mm_ol_0003.c index 71354e2..296c499 100644 --- a/app/online/mm_ol_0003.c +++ b/app/online/mm_ol_0003.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0003.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0003.pgc - 마스터 온라인 서비스 (MM_OL_0003) [tier=easy] + * mm_ol_0003.pgc - 가맹점 정보수정 서비스 (MM_OL_0003) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 기존 가맹점의 상호(merch_nm)/업종(biz_type)을 변경한다. 존재확인 후 + * mm_merchant 를 UPDATE 하고 변경이력(mm_merchant_hist) 에 'U' 이벤트를 + * 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,140 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0003.pgc" +#line 18 "app/online/mm_ol_0003.pgc" TX_SERVICE(MM_OL_0003, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 23 "app/online/mm_ol_0003.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0003.pgc" + char h_merch_nm [ 64 ] ; + +#line 25 "app/online/mm_ol_0003.pgc" + char h_biz_type [ 8 ] ; + +#line 26 "app/online/mm_ol_0003.pgc" + char h_old_nm [ 64 ] ; + +#line 27 "app/online/mm_ol_0003.pgc" + char h_chg_date [ 9 ] ; + +#line 28 "app/online/mm_ol_0003.pgc" + long h_cnt ; +/* exec sql end declare section */ +#line 29 "app/online/mm_ol_0003.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0003] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0003] KEY 누락"); + char merch_id[16]; + char merch_nm[64]; + char biz_type[8]; + + tx_log(TX_LOG_INFO, "[MM_OL_0003] 가맹점 정보수정 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "MERCHNM", merch_nm, sizeof(merch_nm)) != TX_OK || + tx_buf_get(ctx->in, "BIZTYPE", biz_type, sizeof(biz_type)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0003] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0300"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_merch_nm, merch_nm, sizeof(h_merch_nm) - 1); + h_merch_nm[sizeof(h_merch_nm) - 1] = '\0'; + strncpy(h_biz_type, biz_type, sizeof(h_biz_type) - 1); + h_biz_type[sizeof(h_biz_type) - 1] = '\0'; + date_today(h_chg_date); + + /* 존재확인 + 기존 상호 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , MAX ( merch_nm ) from mm_merchant where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_old_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 58 "app/online/mm_ol_0003.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_merchant"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0301"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_cnt <= 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0003] 미등록 가맹점 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM0302"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0003] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_merchant set merch_nm = $1 , biz_type = $2 , upd_date = $3 where merch_id = $4 ", + ECPGt_char,(h_merch_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_type),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 81 "app/online/mm_ol_0003.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0303"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_merchant_hist ( merch_id , chg_type , chg_date , old_nm , new_nm , biz_type ) values ( $1 , 'U' , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_old_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_type),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 94 "app/online/mm_ol_0003.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_merchant_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0304"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "MERCHNM", merch_nm); + tx_buf_sets(ctx->out, "BIZTYPE", biz_type); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0003] 가맹점 수정완료 merch=%s 상호=%s", merch_id, merch_nm); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0003.pgc b/app/online/mm_ol_0003.pgc index 0d057a6..5961e9a 100644 --- a/app/online/mm_ol_0003.pgc +++ b/app/online/mm_ol_0003.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0003.pgc - 마스터 온라인 서비스 (MM_OL_0003) [tier=easy] + * mm_ol_0003.pgc - 가맹점 정보수정 서비스 (MM_OL_0003) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 기존 가맹점의 상호(merch_nm)/업종(biz_type)을 변경한다. 존재확인 후 + * mm_merchant 를 UPDATE 하고 변경이력(mm_merchant_hist) 에 'U' 이벤트를 + * 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,96 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0003, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_merch_nm[64]; + char h_biz_type[8]; + char h_old_nm[64]; + char h_chg_date[9]; + long h_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0003] 마스터 단건조회 서비스 진입"); + char merch_id[16]; + char merch_nm[64]; + char biz_type[8]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0003] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0003] 가맹점 정보수정 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "MERCHNM", merch_nm, sizeof(merch_nm)) != TX_OK || + tx_buf_get(ctx->in, "BIZTYPE", biz_type, sizeof(biz_type)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0003] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0300"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_merch_nm, merch_nm, sizeof(h_merch_nm) - 1); + h_merch_nm[sizeof(h_merch_nm) - 1] = '\0'; + strncpy(h_biz_type, biz_type, sizeof(h_biz_type) - 1); + h_biz_type[sizeof(h_biz_type) - 1] = '\0'; + date_today(h_chg_date); + + /* 존재확인 + 기존 상호 조회 */ + EXEC SQL + SELECT COUNT(*), MAX(merch_nm) INTO :h_cnt, :h_old_nm + FROM mm_merchant + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_merchant"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0301"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_cnt <= 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0003] 미등록 가맹점 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM0302"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0003] tx_begin 경고"); + + EXEC SQL + UPDATE mm_merchant + SET merch_nm = :h_merch_nm, + biz_type = :h_biz_type, + upd_date = :h_chg_date + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0303"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL + INSERT INTO mm_merchant_hist + (merch_id, chg_type, chg_date, old_nm, new_nm, biz_type) + VALUES (:h_merch_id, 'U', :h_chg_date, :h_old_nm, :h_merch_nm, :h_biz_type); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_merchant_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0304"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "MERCHNM", merch_nm); + tx_buf_sets(ctx->out, "BIZTYPE", biz_type); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0003] 가맹점 수정완료 merch=%s 상호=%s", merch_id, merch_nm); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0004.c b/app/online/mm_ol_0004.c index c7365d9..3c8817a 100644 --- a/app/online/mm_ol_0004.c +++ b/app/online/mm_ol_0004.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0004.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0004.pgc - 마스터 온라인 서비스 (MM_OL_0004) [tier=easy] + * mm_ol_0004.pgc - 가맹점 상태변경 서비스 (MM_OL_0004) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 상태를 전이규칙(R->M->S, M->U 등)에 따라 검증한 뒤 mm_merchant + * status 를 UPDATE 하고 상태이력(mm_status_hist) 에 이전/이후 상태를 남긴다. + * (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,144 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0004.pgc" +#line 18 "app/online/mm_ol_0004.pgc" TX_SERVICE(MM_OL_0004, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/mm_ol_0004.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0004.pgc" + char h_cur_st [ 2 ] ; + +#line 25 "app/online/mm_ol_0004.pgc" + char h_new_st [ 2 ] ; + +#line 26 "app/online/mm_ol_0004.pgc" + char h_chg_date [ 9 ] ; + +#line 27 "app/online/mm_ol_0004.pgc" + long h_cnt ; +/* exec sql end declare section */ +#line 28 "app/online/mm_ol_0004.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0004] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0004] KEY 누락"); + char merch_id[16]; + char new_st[2]; + int allowed; + + tx_log(TX_LOG_INFO, "[MM_OL_0004] 가맹점 상태변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "NEWST", new_st, sizeof(new_st)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0004] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0400"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0004] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_new_st, new_st, sizeof(h_new_st) - 1); + h_new_st[sizeof(h_new_st) - 1] = '\0'; + date_today(h_chg_date); + + /* 현재 상태 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select status from mm_merchant where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_cur_st),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 54 "app/online/mm_ol_0004.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0004] 미등록 가맹점 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM0401"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT status mm_merchant"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0402"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 상태전이 규칙검증: R->M, M->S, M->U 만 허용 */ + allowed = 0; + if (strcmp(h_cur_st, MM_ST_RECV) == 0 && strcmp(h_new_st, MM_ST_DONE) == 0) + allowed = 1; + else if (strcmp(h_cur_st, MM_ST_DONE) == 0 && strcmp(h_new_st, MM_ST_SETTLED) == 0) + allowed = 1; + else if (strcmp(h_cur_st, MM_ST_DONE) == 0 && strcmp(h_new_st, MM_ST_FAIL) == 0) + allowed = 1; + + if (!allowed) { + tx_log(TX_LOG_WARN, "[MM_OL_0004] 상태전이 위반 %s->%s", h_cur_st, h_new_st); + tx_buf_sets(ctx->out, "ERRCODE", "MM0403"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0004] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_merchant set status = $1 , upd_date = $2 where merch_id = $3 ", + ECPGt_char,(h_new_st),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 92 "app/online/mm_ol_0004.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE status mm_merchant"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0404"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_status_hist ( merch_id , prev_status , new_status , chg_date ) values ( $1 , $2 , $3 , $4 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_cur_st),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_new_st),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 105 "app/online/mm_ol_0004.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_status_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0405"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0004] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "PREVST", h_cur_st); + tx_buf_sets(ctx->out, "NEWST", h_new_st); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0004] 상태변경완료 merch=%s %s->%s", + merch_id, h_cur_st, h_new_st); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0004.pgc b/app/online/mm_ol_0004.pgc index 5c4a6c1..aa1abe6 100644 --- a/app/online/mm_ol_0004.pgc +++ b/app/online/mm_ol_0004.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0004.pgc - 마스터 온라인 서비스 (MM_OL_0004) [tier=easy] + * mm_ol_0004.pgc - 가맹점 상태변경 서비스 (MM_OL_0004) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 상태를 전이규칙(R->M->S, M->U 등)에 따라 검증한 뒤 mm_merchant + * status 를 UPDATE 하고 상태이력(mm_status_hist) 에 이전/이후 상태를 남긴다. + * (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,108 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0004, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_cur_st[2]; + char h_new_st[2]; + char h_chg_date[9]; + long h_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0004] 마스터 단건조회 서비스 진입"); + char merch_id[16]; + char new_st[2]; + int allowed; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0004] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0004] 가맹점 상태변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "NEWST", new_st, sizeof(new_st)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0004] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0400"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0004] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_new_st, new_st, sizeof(h_new_st) - 1); + h_new_st[sizeof(h_new_st) - 1] = '\0'; + date_today(h_chg_date); + + /* 현재 상태 조회 */ + EXEC SQL + SELECT status INTO :h_cur_st + FROM mm_merchant + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0004] 미등록 가맹점 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM0401"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT status mm_merchant"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0402"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 상태전이 규칙검증: R->M, M->S, M->U 만 허용 */ + allowed = 0; + if (strcmp(h_cur_st, MM_ST_RECV) == 0 && strcmp(h_new_st, MM_ST_DONE) == 0) + allowed = 1; + else if (strcmp(h_cur_st, MM_ST_DONE) == 0 && strcmp(h_new_st, MM_ST_SETTLED) == 0) + allowed = 1; + else if (strcmp(h_cur_st, MM_ST_DONE) == 0 && strcmp(h_new_st, MM_ST_FAIL) == 0) + allowed = 1; + + if (!allowed) { + tx_log(TX_LOG_WARN, "[MM_OL_0004] 상태전이 위반 %s->%s", h_cur_st, h_new_st); + tx_buf_sets(ctx->out, "ERRCODE", "MM0403"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0004] tx_begin 경고"); + + EXEC SQL + UPDATE mm_merchant + SET status = :h_new_st, + upd_date = :h_chg_date + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE status mm_merchant"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0404"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL + INSERT INTO mm_status_hist + (merch_id, prev_status, new_status, chg_date) + VALUES (:h_merch_id, :h_cur_st, :h_new_st, :h_chg_date); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_status_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0405"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0004] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "PREVST", h_cur_st); + tx_buf_sets(ctx->out, "NEWST", h_new_st); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0004] 상태변경완료 merch=%s %s->%s", + merch_id, h_cur_st, h_new_st); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0005.c b/app/online/mm_ol_0005.c index 7117d7d..f57a205 100644 --- a/app/online/mm_ol_0005.c +++ b/app/online/mm_ol_0005.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0005.pgc" /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0005.pgc - 마스터 온라인 서비스 (MM_OL_0005) [tier=medium] + * mm_ol_0005.pgc - 수수료율 조회 서비스 (MM_OL_0005) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점ID + 상품구분(prod_gb)으로 현재 유효한 수수료율(mm_fee_rate)을 + * 조회한다. 유효기간(start_date~end_date)에 오늘이 포함된 행을 반환하며, + * 미등록이면 ERRCODE 로 반려한다. (조회) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,60 +92,106 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0005.pgc" +#line 18 "app/online/mm_ol_0005.pgc" TX_SERVICE(MM_OL_0005, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 23 "app/online/mm_ol_0005.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0005.pgc" + char h_prod_gb [ 4 ] ; + +#line 25 "app/online/mm_ol_0005.pgc" + char h_today [ 9 ] ; + +#line 26 "app/online/mm_ol_0005.pgc" + char h_start [ 9 ] ; + +#line 27 "app/online/mm_ol_0005.pgc" + char h_end [ 9 ] ; + +#line 28 "app/online/mm_ol_0005.pgc" + long h_rate_bp ; +/* exec sql end declare section */ +#line 29 "app/online/mm_ol_0005.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0005] 마스터 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + char merch_id[16]; + char prod_gb[4]; + char rate_str[16]; + + tx_log(TX_LOG_INFO, "[MM_OL_0005] 수수료율 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "PRODGB", prod_gb, sizeof(prod_gb)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0005] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0005] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0005] 영업일 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "MM0500"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_prod_gb, prod_gb, sizeof(h_prod_gb) - 1); + h_prod_gb[sizeof(h_prod_gb) - 1] = '\0'; + date_today(h_today); - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0005] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 현재 유효한 수수료율 1건 (rate 는 bp = 0.01% 단위 정수) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select rate_bp , start_date , end_date from mm_fee_rate where merch_id = $1 and prod_gb = $2 and start_date <= $3 and end_date >= $4 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_prod_gb),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_today),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_today),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_rate_bp),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_start),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_end),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 59 "app/online/mm_ol_0005.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0005] 유효 수수료율 미등록 merch=%s prod=%s", + merch_id, prod_gb); + tx_buf_sets(ctx->out, "ERRCODE", "MM0501"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_fee_rate"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0502"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + /* bp -> 퍼센트 문자열 (예: 250bp -> 2.50%) */ + snprintf(rate_str, sizeof(rate_str), "%ld.%02ld", + h_rate_bp / 100, h_rate_bp % 100); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0005] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "PRODGB", prod_gb); + tx_buf_setlong(ctx->out, "RATEBP", h_rate_bp); + tx_buf_sets(ctx->out, "RATEPCT", rate_str); + tx_buf_sets(ctx->out, "STARTDATE", h_start); + tx_buf_sets(ctx->out, "ENDDATE", h_end); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0005] 조회완료 merch=%s prod=%s rate=%s%%", + merch_id, prod_gb, rate_str); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0005.pgc b/app/online/mm_ol_0005.pgc index 1c61ffe..559d8fc 100644 --- a/app/online/mm_ol_0005.pgc +++ b/app/online/mm_ol_0005.pgc @@ -1,14 +1,17 @@ /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0005.pgc - 마스터 온라인 서비스 (MM_OL_0005) [tier=medium] + * mm_ol_0005.pgc - 수수료율 조회 서비스 (MM_OL_0005) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점ID + 상품구분(prod_gb)으로 현재 유효한 수수료율(mm_fee_rate)을 + * 조회한다. 유효기간(start_date~end_date)에 오늘이 포함된 행을 반환하며, + * 미등록이면 ERRCODE 로 반려한다. (조회) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,55 +19,72 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0005, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_prod_gb[4]; + char h_today[9]; + char h_start[9]; + char h_end[9]; + long h_rate_bp; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0005] 마스터 등록 서비스 진입"); + char merch_id[16]; + char prod_gb[4]; + char rate_str[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_INFO, "[MM_OL_0005] 수수료율 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "PRODGB", prod_gb, sizeof(prod_gb)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0005] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0005] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0005] 영업일 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "MM0500"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_prod_gb, prod_gb, sizeof(h_prod_gb) - 1); + h_prod_gb[sizeof(h_prod_gb) - 1] = '\0'; + date_today(h_today); - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0005] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 현재 유효한 수수료율 1건 (rate 는 bp = 0.01% 단위 정수) */ + EXEC SQL + SELECT rate_bp, start_date, end_date + INTO :h_rate_bp, :h_start, :h_end + FROM mm_fee_rate + WHERE merch_id = :h_merch_id + AND prod_gb = :h_prod_gb + AND start_date <= :h_today + AND end_date >= :h_today; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0005] 유효 수수료율 미등록 merch=%s prod=%s", + merch_id, prod_gb); + tx_buf_sets(ctx->out, "ERRCODE", "MM0501"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_fee_rate"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0502"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + /* bp -> 퍼센트 문자열 (예: 250bp -> 2.50%) */ + snprintf(rate_str, sizeof(rate_str), "%ld.%02ld", + h_rate_bp / 100, h_rate_bp % 100); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0005] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "PRODGB", prod_gb); + tx_buf_setlong(ctx->out, "RATEBP", h_rate_bp); + tx_buf_sets(ctx->out, "RATEPCT", rate_str); + tx_buf_sets(ctx->out, "STARTDATE", h_start); + tx_buf_sets(ctx->out, "ENDDATE", h_end); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0005] 조회완료 merch=%s prod=%s rate=%s%%", + merch_id, prod_gb, rate_str); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0006.c b/app/online/mm_ol_0006.c index 21016e0..05388bb 100644 --- a/app/online/mm_ol_0006.c +++ b/app/online/mm_ol_0006.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0006.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0006.pgc - 마스터 온라인 서비스 (MM_OL_0006) [tier=easy] + * mm_ol_0006.pgc - 수수료율 등록/변경 서비스 (MM_OL_0006) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점+상품구분의 수수료율을 신규 적용한다. 기존 유효 rate 를 전일자로 + * 종료처리(UPDATE end_date) 하고, 신규 rate 를 INSERT 한 뒤 변경이력 + * (mm_fee_hist) 에 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,157 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0006.pgc" +#line 18 "app/online/mm_ol_0006.pgc" TX_SERVICE(MM_OL_0006, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 23 "app/online/mm_ol_0006.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0006.pgc" + char h_prod_gb [ 4 ] ; + +#line 25 "app/online/mm_ol_0006.pgc" + char h_today [ 9 ] ; + +#line 26 "app/online/mm_ol_0006.pgc" + char h_far [ 9 ] ; + +#line 27 "app/online/mm_ol_0006.pgc" + long h_new_bp ; + +#line 28 "app/online/mm_ol_0006.pgc" + long h_chg_by ; +/* exec sql end declare section */ +#line 29 "app/online/mm_ol_0006.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0006] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0006] KEY 누락"); + char merch_id[16]; + char prod_gb[4]; + char rate_str[16]; + long rate_bp = 0; + long chg_by = 0; + + tx_log(TX_LOG_INFO, "[MM_OL_0006] 수수료율 등록/변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "PRODGB", prod_gb, sizeof(prod_gb)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0006] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0600"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "RATEBP", &rate_bp); + tx_buf_getlong(ctx->in, "CHGBY", &chg_by); + + /* 수수료율 범위검증: 0 ~ 1000bp (0~10%) */ + if (rate_bp <= 0 || rate_bp > 1000) { + tx_log(TX_LOG_WARN, "[MM_OL_0006] 수수료율 범위오류 bp=%ld", rate_bp); + tx_buf_sets(ctx->out, "ERRCODE", "MM0601"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0006] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_prod_gb, prod_gb, sizeof(h_prod_gb) - 1); + h_prod_gb[sizeof(h_prod_gb) - 1] = '\0'; + h_new_bp = rate_bp; + h_chg_by = chg_by; + date_today(h_today); + strncpy(h_far, "99991231", sizeof(h_far) - 1); + h_far[sizeof(h_far) - 1] = '\0'; + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0006] tx_begin 경고"); + + /* 기존 유효 rate 종료처리 (없어도 정상) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_fee_rate set end_date = $1 where merch_id = $2 and prod_gb = $3 and end_date = '99991231'", + ECPGt_char,(h_today),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_prod_gb),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 76 "app/online/mm_ol_0006.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("UPDATE end_date mm_fee_rate"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0602"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 신규 rate INSERT */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_fee_rate ( merch_id , prod_gb , rate_bp , start_date , end_date ) values ( $1 , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_prod_gb),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_new_bp),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_today),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_far),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 90 "app/online/mm_ol_0006.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_fee_rate"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0603"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 변경이력 INSERT */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_fee_hist ( merch_id , prod_gb , rate_bp , apply_date , chg_by ) values ( $1 , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_prod_gb),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_new_bp),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_today),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_chg_by),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 104 "app/online/mm_ol_0006.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_fee_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0604"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + snprintf(rate_str, sizeof(rate_str), "%ld.%02ld", + h_new_bp / 100, h_new_bp % 100); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0006] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "PRODGB", prod_gb); + tx_buf_setlong(ctx->out, "RATEBP", h_new_bp); + tx_buf_sets(ctx->out, "RATEPCT", rate_str); + tx_buf_sets(ctx->out, "STARTDATE", h_today); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0006] 수수료율 변경완료 merch=%s prod=%s rate=%s%%", + merch_id, prod_gb, rate_str); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0006.pgc b/app/online/mm_ol_0006.pgc index 9c482ad..fdc603c 100644 --- a/app/online/mm_ol_0006.pgc +++ b/app/online/mm_ol_0006.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0006.pgc - 마스터 온라인 서비스 (MM_OL_0006) [tier=easy] + * mm_ol_0006.pgc - 수수료율 등록/변경 서비스 (MM_OL_0006) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점+상품구분의 수수료율을 신규 적용한다. 기존 유효 rate 를 전일자로 + * 종료처리(UPDATE end_date) 하고, 신규 rate 를 INSERT 한 뒤 변경이력 + * (mm_fee_hist) 에 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,111 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0006, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_prod_gb[4]; + char h_today[9]; + char h_far[9]; + long h_new_bp; + long h_chg_by; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0006] 마스터 단건조회 서비스 진입"); + char merch_id[16]; + char prod_gb[4]; + char rate_str[16]; + long rate_bp = 0; + long chg_by = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0006] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0006] 수수료율 등록/변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "PRODGB", prod_gb, sizeof(prod_gb)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0006] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0600"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "RATEBP", &rate_bp); + tx_buf_getlong(ctx->in, "CHGBY", &chg_by); + + /* 수수료율 범위검증: 0 ~ 1000bp (0~10%) */ + if (rate_bp <= 0 || rate_bp > 1000) { + tx_log(TX_LOG_WARN, "[MM_OL_0006] 수수료율 범위오류 bp=%ld", rate_bp); + tx_buf_sets(ctx->out, "ERRCODE", "MM0601"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0006] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_prod_gb, prod_gb, sizeof(h_prod_gb) - 1); + h_prod_gb[sizeof(h_prod_gb) - 1] = '\0'; + h_new_bp = rate_bp; + h_chg_by = chg_by; + date_today(h_today); + strncpy(h_far, "99991231", sizeof(h_far) - 1); + h_far[sizeof(h_far) - 1] = '\0'; + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0006] tx_begin 경고"); + + /* 기존 유효 rate 종료처리 (없어도 정상) */ + EXEC SQL + UPDATE mm_fee_rate + SET end_date = :h_today + WHERE merch_id = :h_merch_id + AND prod_gb = :h_prod_gb + AND end_date = '99991231'; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("UPDATE end_date mm_fee_rate"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0602"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 신규 rate INSERT */ + EXEC SQL + INSERT INTO mm_fee_rate + (merch_id, prod_gb, rate_bp, start_date, end_date) + VALUES (:h_merch_id, :h_prod_gb, :h_new_bp, :h_today, :h_far); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_fee_rate"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0603"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 변경이력 INSERT */ + EXEC SQL + INSERT INTO mm_fee_hist + (merch_id, prod_gb, rate_bp, apply_date, chg_by) + VALUES (:h_merch_id, :h_prod_gb, :h_new_bp, :h_today, :h_chg_by); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_fee_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0604"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + + snprintf(rate_str, sizeof(rate_str), "%ld.%02ld", + h_new_bp / 100, h_new_bp % 100); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0006] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "PRODGB", prod_gb); + tx_buf_setlong(ctx->out, "RATEBP", h_new_bp); + tx_buf_sets(ctx->out, "RATEPCT", rate_str); + tx_buf_sets(ctx->out, "STARTDATE", h_today); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0006] 수수료율 변경완료 merch=%s prod=%s rate=%s%%", + merch_id, prod_gb, rate_str); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0007.c b/app/online/mm_ol_0007.c index 4d228fa..71d43ac 100644 --- a/app/online/mm_ol_0007.c +++ b/app/online/mm_ol_0007.c @@ -8,15 +8,17 @@ #line 1 "app/online/mm_ol_0007.pgc" /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0007.pgc - 마스터 온라인 서비스 (MM_OL_0007) [tier=medium] + * mm_ol_0007.pgc - 한도 마스터 조회 서비스 (MM_OL_0007) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점의 승인한도 마스터(mm_limit)에서 일한도(daily_limit)/월한도 + * (monthly_limit) 및 현재 사용액을 조회하고 잔여한도를 계산한다. (조회) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,60 +91,97 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0007.pgc" +#line 17 "app/online/mm_ol_0007.pgc" TX_SERVICE(MM_OL_0007, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 22 "app/online/mm_ol_0007.pgc" + char h_merch_id [ 16 ] ; + +#line 23 "app/online/mm_ol_0007.pgc" + long h_daily ; + +#line 24 "app/online/mm_ol_0007.pgc" + long h_monthly ; + +#line 25 "app/online/mm_ol_0007.pgc" + long h_day_used ; + +#line 26 "app/online/mm_ol_0007.pgc" + long h_mon_used ; +/* exec sql end declare section */ +#line 27 "app/online/mm_ol_0007.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0007] 마스터 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0007] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char merch_id[16]; + char won_buf[32]; + long day_remain; + long mon_remain; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0007] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0007] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[MM_OL_0007] 한도 마스터 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0007] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0700"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0007] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select daily_limit , monthly_limit , day_used , mon_used from mm_limit where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_daily),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_monthly),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_day_used),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_mon_used),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 50 "app/online/mm_ol_0007.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0007] 한도 미등록 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM0701"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_limit"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0702"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + day_remain = h_daily - h_day_used; + mon_remain = h_monthly - h_mon_used; + if (day_remain < 0) day_remain = 0; + if (mon_remain < 0) mon_remain = 0; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0007] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_setlong(ctx->out, "DAILYLIMIT", h_daily); + tx_buf_setlong(ctx->out, "MONTHLYLIMIT", h_monthly); + tx_buf_setlong(ctx->out, "DAYUSED", h_day_used); + tx_buf_setlong(ctx->out, "MONUSED", h_mon_used); + tx_buf_setlong(ctx->out, "DAYREMAIN", day_remain); + tx_buf_setlong(ctx->out, "MONREMAIN", mon_remain); + amount_format_won(day_remain, won_buf, sizeof(won_buf)); + tx_buf_sets(ctx->out, "DAYREMAINWON", won_buf); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0007] 조회완료 merch=%s 일잔여=%ld 월잔여=%ld", + merch_id, day_remain, mon_remain); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0007.pgc b/app/online/mm_ol_0007.pgc index 1b88b46..3528c8d 100644 --- a/app/online/mm_ol_0007.pgc +++ b/app/online/mm_ol_0007.pgc @@ -1,14 +1,16 @@ /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0007.pgc - 마스터 온라인 서비스 (MM_OL_0007) [tier=medium] + * mm_ol_0007.pgc - 한도 마스터 조회 서비스 (MM_OL_0007) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점의 승인한도 마스터(mm_limit)에서 일한도(daily_limit)/월한도 + * (monthly_limit) 및 현재 사용액을 조회하고 잔여한도를 계산한다. (조회) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,55 +18,67 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0007, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + long h_daily; + long h_monthly; + long h_day_used; + long h_mon_used; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0007] 마스터 등록 서비스 진입"); + char merch_id[16]; + char won_buf[32]; + long day_remain; + long mon_remain; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0007] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[MM_OL_0007] 한도 마스터 조회 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0007] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0007] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0007] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0700"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0007] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL + SELECT daily_limit, monthly_limit, day_used, mon_used + INTO :h_daily, :h_monthly, :h_day_used, :h_mon_used + FROM mm_limit + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0007] 한도 미등록 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM0701"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_limit"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0702"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + day_remain = h_daily - h_day_used; + mon_remain = h_monthly - h_mon_used; + if (day_remain < 0) day_remain = 0; + if (mon_remain < 0) mon_remain = 0; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0007] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_setlong(ctx->out, "DAILYLIMIT", h_daily); + tx_buf_setlong(ctx->out, "MONTHLYLIMIT", h_monthly); + tx_buf_setlong(ctx->out, "DAYUSED", h_day_used); + tx_buf_setlong(ctx->out, "MONUSED", h_mon_used); + tx_buf_setlong(ctx->out, "DAYREMAIN", day_remain); + tx_buf_setlong(ctx->out, "MONREMAIN", mon_remain); + amount_format_won(day_remain, won_buf, sizeof(won_buf)); + tx_buf_sets(ctx->out, "DAYREMAINWON", won_buf); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0007] 조회완료 merch=%s 일잔여=%ld 월잔여=%ld", + merch_id, day_remain, mon_remain); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0008.c b/app/online/mm_ol_0008.c index 6cacd22..11ab2b8 100644 --- a/app/online/mm_ol_0008.c +++ b/app/online/mm_ol_0008.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0008.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0008.pgc - 마스터 온라인 서비스 (MM_OL_0008) [tier=easy] + * mm_ol_0008.pgc - 한도 마스터 변경 서비스 (MM_OL_0008) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 승인한도(mm_limit)의 일한도/월한도를 변경한다. 일한도가 월한도를 + * 초과하지 않도록 검증한 뒤 UPDATE 하고, 변경이력(mm_limit_hist) 에 이전/ + * 이후 한도를 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,152 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0008.pgc" +#line 18 "app/online/mm_ol_0008.pgc" TX_SERVICE(MM_OL_0008, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 23 "app/online/mm_ol_0008.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0008.pgc" + char h_chg_date [ 9 ] ; + +#line 25 "app/online/mm_ol_0008.pgc" + long h_new_daily ; + +#line 26 "app/online/mm_ol_0008.pgc" + long h_new_monthly ; + +#line 27 "app/online/mm_ol_0008.pgc" + long h_old_daily ; + +#line 28 "app/online/mm_ol_0008.pgc" + long h_old_monthly ; +/* exec sql end declare section */ +#line 29 "app/online/mm_ol_0008.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0008] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0008] KEY 누락"); + char merch_id[16]; + long new_daily = 0; + long new_monthly = 0; + + tx_log(TX_LOG_INFO, "[MM_OL_0008] 한도 마스터 변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0008] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0800"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "DAILYLIMIT", &new_daily); + tx_buf_getlong(ctx->in, "MONTHLYLIMIT", &new_monthly); + + /* 검증: 한도는 양수, 일한도 <= 월한도 */ + if (new_daily <= 0 || new_monthly <= 0 || new_daily > new_monthly) { + tx_log(TX_LOG_WARN, "[MM_OL_0008] 한도값 오류 일=%ld 월=%ld", + new_daily, new_monthly); + tx_buf_sets(ctx->out, "ERRCODE", "MM0801"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0008] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + h_new_daily = new_daily; + h_new_monthly = new_monthly; + date_today(h_chg_date); + + /* 기존 한도 조회 (존재확인 + 이력용) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select daily_limit , monthly_limit from mm_limit where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_old_daily),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_old_monthly),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 66 "app/online/mm_ol_0008.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0008] 한도 미등록 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM0802"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_limit"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0803"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0008] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_limit set daily_limit = $1 , monthly_limit = $2 , upd_date = $3 where merch_id = $4 ", + ECPGt_long,&(h_new_daily),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_new_monthly),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 89 "app/online/mm_ol_0008.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_limit"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0804"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_limit_hist ( merch_id , old_daily , new_daily , old_monthly , new_monthly , chg_date ) values ( $1 , $2 , $3 , $4 , $5 , $6 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_old_daily),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_new_daily),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_old_monthly),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_new_monthly),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 103 "app/online/mm_ol_0008.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_limit_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0805"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_setlong(ctx->out, "OLDDAILY", h_old_daily); + tx_buf_setlong(ctx->out, "DAILYLIMIT", h_new_daily); + tx_buf_setlong(ctx->out, "OLDMONTHLY", h_old_monthly); + tx_buf_setlong(ctx->out, "MONTHLYLIMIT", h_new_monthly); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0008] 한도변경완료 merch=%s 일=%ld 월=%ld", + merch_id, h_new_daily, h_new_monthly); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0008.pgc b/app/online/mm_ol_0008.pgc index d9a41cf..301b663 100644 --- a/app/online/mm_ol_0008.pgc +++ b/app/online/mm_ol_0008.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0008.pgc - 마스터 온라인 서비스 (MM_OL_0008) [tier=easy] + * mm_ol_0008.pgc - 한도 마스터 변경 서비스 (MM_OL_0008) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 승인한도(mm_limit)의 일한도/월한도를 변경한다. 일한도가 월한도를 + * 초과하지 않도록 검증한 뒤 UPDATE 하고, 변경이력(mm_limit_hist) 에 이전/ + * 이후 한도를 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,108 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0008, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_chg_date[9]; + long h_new_daily; + long h_new_monthly; + long h_old_daily; + long h_old_monthly; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0008] 마스터 단건조회 서비스 진입"); + char merch_id[16]; + long new_daily = 0; + long new_monthly = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0008] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0008] 한도 마스터 변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0008] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0800"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "DAILYLIMIT", &new_daily); + tx_buf_getlong(ctx->in, "MONTHLYLIMIT", &new_monthly); + + /* 검증: 한도는 양수, 일한도 <= 월한도 */ + if (new_daily <= 0 || new_monthly <= 0 || new_daily > new_monthly) { + tx_log(TX_LOG_WARN, "[MM_OL_0008] 한도값 오류 일=%ld 월=%ld", + new_daily, new_monthly); + tx_buf_sets(ctx->out, "ERRCODE", "MM0801"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0008] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + h_new_daily = new_daily; + h_new_monthly = new_monthly; + date_today(h_chg_date); + + /* 기존 한도 조회 (존재확인 + 이력용) */ + EXEC SQL + SELECT daily_limit, monthly_limit + INTO :h_old_daily, :h_old_monthly + FROM mm_limit + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0008] 한도 미등록 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM0802"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_limit"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0803"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0008] tx_begin 경고"); + + EXEC SQL + UPDATE mm_limit + SET daily_limit = :h_new_daily, + monthly_limit = :h_new_monthly, + upd_date = :h_chg_date + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_limit"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0804"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL + INSERT INTO mm_limit_hist + (merch_id, old_daily, new_daily, old_monthly, new_monthly, chg_date) + VALUES (:h_merch_id, :h_old_daily, :h_new_daily, + :h_old_monthly, :h_new_monthly, :h_chg_date); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_limit_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM0805"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_setlong(ctx->out, "OLDDAILY", h_old_daily); + tx_buf_setlong(ctx->out, "DAILYLIMIT", h_new_daily); + tx_buf_setlong(ctx->out, "OLDMONTHLY", h_old_monthly); + tx_buf_setlong(ctx->out, "MONTHLYLIMIT", h_new_monthly); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0008] 한도변경완료 merch=%s 일=%ld 월=%ld", + merch_id, h_new_daily, h_new_monthly); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0009.c b/app/online/mm_ol_0009.c index 139fef9..8b61872 100644 --- a/app/online/mm_ol_0009.c +++ b/app/online/mm_ol_0009.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0009.pgc" /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0009.pgc - 마스터 온라인 서비스 (MM_OL_0009) [tier=medium] + * mm_ol_0009.pgc - 카드 BIN 조회 서비스 (MM_OL_0009) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 6자리 BIN 으로 카드 BIN 마스터(mm_card_bin)를 조회하여 카드사코드 + * (issuer_cd)/카드종류(card_gb)/유효여부(valid_yn)를 반환한다. BIN 이 + * 숫자 6자리가 아니면 형식오류로 반려한다. (조회) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,60 +92,102 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0009.pgc" +#line 18 "app/online/mm_ol_0009.pgc" TX_SERVICE(MM_OL_0009, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/mm_ol_0009.pgc" + char h_bin [ 7 ] ; + +#line 24 "app/online/mm_ol_0009.pgc" + char h_issuer_cd [ 8 ] ; + +#line 25 "app/online/mm_ol_0009.pgc" + char h_card_gb [ 4 ] ; + +#line 26 "app/online/mm_ol_0009.pgc" + char h_valid_yn [ 2 ] ; + +#line 27 "app/online/mm_ol_0009.pgc" + char h_brand [ 16 ] ; +/* exec sql end declare section */ +#line 28 "app/online/mm_ol_0009.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0009] 마스터 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0009] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char bin[7]; + int i; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0009] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0009] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[MM_OL_0009] 카드 BIN 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIN", bin, sizeof(bin)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0009] BIN 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0900"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0009] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* BIN 형식검증: 정확히 숫자 6자리 */ + if (strlen(bin) != 6) { + tx_log(TX_LOG_WARN, "[MM_OL_0009] BIN 길이오류 bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM0901"); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + for (i = 0; i < 6; i++) { + if (bin[i] < '0' || bin[i] > '9') { + tx_log(TX_LOG_WARN, "[MM_OL_0009] BIN 숫자아님 bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM0901"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + strncpy(h_bin, bin, sizeof(h_bin) - 1); + h_bin[sizeof(h_bin) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select issuer_cd , card_gb , valid_yn , brand_nm from mm_card_bin where bin = $1 ", + ECPGt_char,(h_bin),(long)7,(long)1,(7)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_issuer_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_gb),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_valid_yn),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_brand),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 65 "app/online/mm_ol_0009.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0009] 미등록 BIN bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM0902"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_card_bin"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0903"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0009] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIN", bin); + tx_buf_sets(ctx->out, "ISSUERCD", h_issuer_cd); + tx_buf_sets(ctx->out, "CARDGB", h_card_gb); + tx_buf_sets(ctx->out, "VALIDYN", h_valid_yn); + tx_buf_sets(ctx->out, "BRAND", h_brand); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0009] 조회완료 bin=%s issuer=%s 유효=%s", + bin, h_issuer_cd, h_valid_yn); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0009.pgc b/app/online/mm_ol_0009.pgc index 4f37bdd..56d0fcf 100644 --- a/app/online/mm_ol_0009.pgc +++ b/app/online/mm_ol_0009.pgc @@ -1,14 +1,17 @@ /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0009.pgc - 마스터 온라인 서비스 (MM_OL_0009) [tier=medium] + * mm_ol_0009.pgc - 카드 BIN 조회 서비스 (MM_OL_0009) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 6자리 BIN 으로 카드 BIN 마스터(mm_card_bin)를 조회하여 카드사코드 + * (issuer_cd)/카드종류(card_gb)/유효여부(valid_yn)를 반환한다. BIN 이 + * 숫자 6자리가 아니면 형식오류로 반려한다. (조회) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,55 +19,72 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0009, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_bin[7]; + char h_issuer_cd[8]; + char h_card_gb[4]; + char h_valid_yn[2]; + char h_brand[16]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0009] 마스터 등록 서비스 진입"); + char bin[7]; + int i; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0009] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[MM_OL_0009] 카드 BIN 조회 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0009] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0009] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "BIN", bin, sizeof(bin)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0009] BIN 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0900"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0009] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* BIN 형식검증: 정확히 숫자 6자리 */ + if (strlen(bin) != 6) { + tx_log(TX_LOG_WARN, "[MM_OL_0009] BIN 길이오류 bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM0901"); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + for (i = 0; i < 6; i++) { + if (bin[i] < '0' || bin[i] > '9') { + tx_log(TX_LOG_WARN, "[MM_OL_0009] BIN 숫자아님 bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM0901"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + strncpy(h_bin, bin, sizeof(h_bin) - 1); + h_bin[sizeof(h_bin) - 1] = '\0'; + + EXEC SQL + SELECT issuer_cd, card_gb, valid_yn, brand_nm + INTO :h_issuer_cd, :h_card_gb, :h_valid_yn, :h_brand + FROM mm_card_bin + WHERE bin = :h_bin; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0009] 미등록 BIN bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM0902"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_card_bin"); + tx_buf_sets(ctx->out, "ERRCODE", "MM0903"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0009] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIN", bin); + tx_buf_sets(ctx->out, "ISSUERCD", h_issuer_cd); + tx_buf_sets(ctx->out, "CARDGB", h_card_gb); + tx_buf_sets(ctx->out, "VALIDYN", h_valid_yn); + tx_buf_sets(ctx->out, "BRAND", h_brand); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0009] 조회완료 bin=%s issuer=%s 유효=%s", + bin, h_issuer_cd, h_valid_yn); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0010.c b/app/online/mm_ol_0010.c index 90333ec..dbdf9a2 100644 --- a/app/online/mm_ol_0010.c +++ b/app/online/mm_ol_0010.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0010.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0010.pgc - 마스터 온라인 서비스 (MM_OL_0010) [tier=easy] + * mm_ol_0010.pgc - 카드 BIN 등록 서비스 (MM_OL_0010) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 신규 카드 BIN 을 마스터(mm_card_bin)에 등록한다. 동일 BIN 중복이면 + * 반려하고, 등록 후 BIN 변경이력(mm_bin_hist) 에 'I' 이벤트를 남긴다. + * (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,166 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0010.pgc" +#line 18 "app/online/mm_ol_0010.pgc" TX_SERVICE(MM_OL_0010, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + + +#line 23 "app/online/mm_ol_0010.pgc" + char h_bin [ 7 ] ; + +#line 24 "app/online/mm_ol_0010.pgc" + char h_issuer_cd [ 8 ] ; + +#line 25 "app/online/mm_ol_0010.pgc" + char h_card_gb [ 4 ] ; + +#line 26 "app/online/mm_ol_0010.pgc" + char h_brand [ 16 ] ; + +#line 27 "app/online/mm_ol_0010.pgc" + char h_valid_yn [ 2 ] ; + +#line 28 "app/online/mm_ol_0010.pgc" + char h_reg_date [ 9 ] ; + +#line 29 "app/online/mm_ol_0010.pgc" + long h_dup ; +/* exec sql end declare section */ +#line 30 "app/online/mm_ol_0010.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0010] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0010] KEY 누락"); + char bin[7]; + char issuer_cd[8]; + char card_gb[4]; + char brand[16]; + int i; + + tx_log(TX_LOG_INFO, "[MM_OL_0010] 카드 BIN 등록 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIN", bin, sizeof(bin)) != TX_OK || + tx_buf_get(ctx->in, "ISSUERCD", issuer_cd, sizeof(issuer_cd)) != TX_OK || + tx_buf_get(ctx->in, "CARDGB", card_gb, sizeof(card_gb)) != TX_OK || + tx_buf_get(ctx->in, "BRAND", brand, sizeof(brand)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0010] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1000"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* BIN 형식검증: 숫자 6자리 */ + if (strlen(bin) != 6) { + tx_log(TX_LOG_WARN, "[MM_OL_0010] BIN 길이오류 bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM1001"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + for (i = 0; i < 6; i++) { + if (bin[i] < '0' || bin[i] > '9') { + tx_log(TX_LOG_WARN, "[MM_OL_0010] BIN 숫자아님 bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM1001"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } + + strncpy(h_bin, bin, sizeof(h_bin) - 1); + h_bin[sizeof(h_bin) - 1] = '\0'; + strncpy(h_issuer_cd, issuer_cd, sizeof(h_issuer_cd) - 1); + h_issuer_cd[sizeof(h_issuer_cd) - 1] = '\0'; + strncpy(h_card_gb, card_gb, sizeof(h_card_gb) - 1); + h_card_gb[sizeof(h_card_gb) - 1] = '\0'; + strncpy(h_brand, brand, sizeof(h_brand) - 1); + h_brand[sizeof(h_brand) - 1] = '\0'; + strncpy(h_valid_yn, "Y", sizeof(h_valid_yn) - 1); + h_valid_yn[sizeof(h_valid_yn) - 1] = '\0'; + date_today(h_reg_date); + + /* 중복 BIN 체크 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_card_bin where bin = $1 ", + ECPGt_char,(h_bin),(long)7,(long)1,(7)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_dup),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 82 "app/online/mm_ol_0010.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT dup mm_card_bin"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1002"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0010] BIN 중복 bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM1003"); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0010] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_card_bin ( bin , issuer_cd , card_gb , brand_nm , valid_yn , reg_date ) values ( $1 , $2 , $3 , $4 , $5 , $6 )", + ECPGt_char,(h_bin),(long)7,(long)1,(7)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_issuer_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_gb),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_brand),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_valid_yn),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_reg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 103 "app/online/mm_ol_0010.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_card_bin"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1004"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_bin_hist ( bin , chg_type , issuer_cd , chg_date ) values ( $1 , 'I' , $2 , $3 )", + ECPGt_char,(h_bin),(long)7,(long)1,(7)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_issuer_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_reg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 116 "app/online/mm_ol_0010.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_bin_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1005"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIN", bin); + tx_buf_sets(ctx->out, "ISSUERCD", issuer_cd); + tx_buf_sets(ctx->out, "CARDGB", card_gb); + tx_buf_sets(ctx->out, "VALIDYN", h_valid_yn); + tx_buf_sets(ctx->out, "REGDATE", h_reg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0010] BIN 등록완료 bin=%s issuer=%s", bin, issuer_cd); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0010.pgc b/app/online/mm_ol_0010.pgc index 9e992c5..b3c3f0c 100644 --- a/app/online/mm_ol_0010.pgc +++ b/app/online/mm_ol_0010.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0010.pgc - 마스터 온라인 서비스 (MM_OL_0010) [tier=easy] + * mm_ol_0010.pgc - 카드 BIN 등록 서비스 (MM_OL_0010) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 신규 카드 BIN 을 마스터(mm_card_bin)에 등록한다. 동일 BIN 중복이면 + * 반려하고, 등록 후 BIN 변경이력(mm_bin_hist) 에 'I' 이벤트를 남긴다. + * (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,119 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0010, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_bin[7]; + char h_issuer_cd[8]; + char h_card_gb[4]; + char h_brand[16]; + char h_valid_yn[2]; + char h_reg_date[9]; + long h_dup; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0010] 마스터 단건조회 서비스 진입"); + char bin[7]; + char issuer_cd[8]; + char card_gb[4]; + char brand[16]; + int i; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0010] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0010] 카드 BIN 등록 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIN", bin, sizeof(bin)) != TX_OK || + tx_buf_get(ctx->in, "ISSUERCD", issuer_cd, sizeof(issuer_cd)) != TX_OK || + tx_buf_get(ctx->in, "CARDGB", card_gb, sizeof(card_gb)) != TX_OK || + tx_buf_get(ctx->in, "BRAND", brand, sizeof(brand)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0010] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1000"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* BIN 형식검증: 숫자 6자리 */ + if (strlen(bin) != 6) { + tx_log(TX_LOG_WARN, "[MM_OL_0010] BIN 길이오류 bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM1001"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + for (i = 0; i < 6; i++) { + if (bin[i] < '0' || bin[i] > '9') { + tx_log(TX_LOG_WARN, "[MM_OL_0010] BIN 숫자아님 bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM1001"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } + + strncpy(h_bin, bin, sizeof(h_bin) - 1); + h_bin[sizeof(h_bin) - 1] = '\0'; + strncpy(h_issuer_cd, issuer_cd, sizeof(h_issuer_cd) - 1); + h_issuer_cd[sizeof(h_issuer_cd) - 1] = '\0'; + strncpy(h_card_gb, card_gb, sizeof(h_card_gb) - 1); + h_card_gb[sizeof(h_card_gb) - 1] = '\0'; + strncpy(h_brand, brand, sizeof(h_brand) - 1); + h_brand[sizeof(h_brand) - 1] = '\0'; + strncpy(h_valid_yn, "Y", sizeof(h_valid_yn) - 1); + h_valid_yn[sizeof(h_valid_yn) - 1] = '\0'; + date_today(h_reg_date); + + /* 중복 BIN 체크 */ + EXEC SQL + SELECT COUNT(*) INTO :h_dup + FROM mm_card_bin + WHERE bin = :h_bin; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT dup mm_card_bin"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1002"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0010] BIN 중복 bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM1003"); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0010] tx_begin 경고"); + + EXEC SQL + INSERT INTO mm_card_bin + (bin, issuer_cd, card_gb, brand_nm, valid_yn, reg_date) + VALUES (:h_bin, :h_issuer_cd, :h_card_gb, :h_brand, :h_valid_yn, :h_reg_date); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_card_bin"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1004"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL + INSERT INTO mm_bin_hist + (bin, chg_type, issuer_cd, chg_date) + VALUES (:h_bin, 'I', :h_issuer_cd, :h_reg_date); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_bin_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1005"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIN", bin); + tx_buf_sets(ctx->out, "ISSUERCD", issuer_cd); + tx_buf_sets(ctx->out, "CARDGB", card_gb); + tx_buf_sets(ctx->out, "VALIDYN", h_valid_yn); + tx_buf_sets(ctx->out, "REGDATE", h_reg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0010] BIN 등록완료 bin=%s issuer=%s", bin, issuer_cd); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0011.c b/app/online/mm_ol_0011.c index f97426a..ffdb03e 100644 --- a/app/online/mm_ol_0011.c +++ b/app/online/mm_ol_0011.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0011.pgc" /* @tier hard @module mm @transform master-online */ /* - * mm_ol_0011.pgc - 마스터 온라인 서비스 (MM_OL_0011) [tier=hard] + * mm_ol_0011.pgc - 카드사 마스터 조회 서비스 (MM_OL_0011) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 카드사코드(issuer_cd)로 카드사 마스터(mm_issuer)를 조회하여 카드사명 + * (issuer_nm)/정산은행(settle_bank)/상태(status)를 반환하고, 등록된 BIN + * 건수(mm_card_bin)를 함께 집계한다. (조회) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,88 +92,99 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0011.pgc" +#line 18 "app/online/mm_ol_0011.pgc" TX_SERVICE(MM_OL_0011, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/mm_ol_0011.pgc" + char h_issuer_cd [ 8 ] ; + +#line 24 "app/online/mm_ol_0011.pgc" + char h_issuer_nm [ 64 ] ; + +#line 25 "app/online/mm_ol_0011.pgc" + char h_settle_bank [ 8 ] ; + +#line 26 "app/online/mm_ol_0011.pgc" + char h_status [ 2 ] ; + +#line 27 "app/online/mm_ol_0011.pgc" + long h_bin_cnt ; +/* exec sql end declare section */ +#line 28 "app/online/mm_ol_0011.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0011] 마스터 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0011] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char issuer_cd[8]; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0011] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + tx_log(TX_LOG_INFO, "[MM_OL_0011] 카드사 마스터 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "ISSUERCD", issuer_cd, sizeof(issuer_cd)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0011] ISSUERCD 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1100"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + strncpy(h_issuer_cd, issuer_cd, sizeof(h_issuer_cd) - 1); + h_issuer_cd[sizeof(h_issuer_cd) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select issuer_nm , settle_bank , status from mm_issuer where issuer_cd = $1 ", + ECPGt_char,(h_issuer_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_issuer_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_settle_bank),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 48 "app/online/mm_ol_0011.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0011] 미등록 카드사 issuer=%s", issuer_cd); + tx_buf_sets(ctx->out, "ERRCODE", "MM1101"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_issuer"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1102"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 해당 카드사에 등록된 유효 BIN 건수 집계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_card_bin where issuer_cd = $1 and valid_yn = 'Y'", + ECPGt_char,(h_issuer_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_bin_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 68 "app/online/mm_ol_0011.pgc" - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0011] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT bin cnt mm_card_bin"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1103"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MM_OL_0011] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MM_OL_0011] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "ISSUERCD", issuer_cd); + tx_buf_sets(ctx->out, "ISSUERNM", h_issuer_nm); + tx_buf_sets(ctx->out, "SETTLEBANK", h_settle_bank); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_setlong(ctx->out, "BINCNT", (h_bin_cnt < 0) ? 0 : h_bin_cnt); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0011] 조회완료 issuer=%s 명=%s BIN=%ld", + issuer_cd, h_issuer_nm, h_bin_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0011.pgc b/app/online/mm_ol_0011.pgc index a48e36d..a262fae 100644 --- a/app/online/mm_ol_0011.pgc +++ b/app/online/mm_ol_0011.pgc @@ -1,14 +1,17 @@ /* @tier hard @module mm @transform master-online */ /* - * mm_ol_0011.pgc - 마스터 온라인 서비스 (MM_OL_0011) [tier=hard] + * mm_ol_0011.pgc - 카드사 마스터 조회 서비스 (MM_OL_0011) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 카드사코드(issuer_cd)로 카드사 마스터(mm_issuer)를 조회하여 카드사명 + * (issuer_nm)/정산은행(settle_bank)/상태(status)를 반환하고, 등록된 BIN + * 건수(mm_card_bin)를 함께 집계한다. (조회) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,83 +19,69 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0011, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_issuer_cd[8]; + char h_issuer_nm[64]; + char h_settle_bank[8]; + char h_status[2]; + long h_bin_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0011] 마스터 트랜잭션 오케스트레이션 진입"); + char issuer_cd[8]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0011] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[MM_OL_0011] 카드사 마스터 조회 서비스 진입"); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0011] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (tx_buf_get(ctx->in, "ISSUERCD", issuer_cd, sizeof(issuer_cd)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0011] ISSUERCD 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1100"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + strncpy(h_issuer_cd, issuer_cd, sizeof(h_issuer_cd) - 1); + h_issuer_cd[sizeof(h_issuer_cd) - 1] = '\0'; + + EXEC SQL + SELECT issuer_nm, settle_bank, status + INTO :h_issuer_nm, :h_settle_bank, :h_status + FROM mm_issuer + WHERE issuer_cd = :h_issuer_cd; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0011] 미등록 카드사 issuer=%s", issuer_cd); + tx_buf_sets(ctx->out, "ERRCODE", "MM1101"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_issuer"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1102"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 해당 카드사에 등록된 유효 BIN 건수 집계 */ + EXEC SQL + SELECT COUNT(*) INTO :h_bin_cnt + FROM mm_card_bin + WHERE issuer_cd = :h_issuer_cd + AND valid_yn = 'Y'; - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0011] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT bin cnt mm_card_bin"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1103"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MM_OL_0011] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MM_OL_0011] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "ISSUERCD", issuer_cd); + tx_buf_sets(ctx->out, "ISSUERNM", h_issuer_nm); + tx_buf_sets(ctx->out, "SETTLEBANK", h_settle_bank); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_setlong(ctx->out, "BINCNT", (h_bin_cnt < 0) ? 0 : h_bin_cnt); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0011] 조회완료 issuer=%s 명=%s BIN=%ld", + issuer_cd, h_issuer_nm, h_bin_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0012.c b/app/online/mm_ol_0012.c index 2b4bf46..1fb3645 100644 --- a/app/online/mm_ol_0012.c +++ b/app/online/mm_ol_0012.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0012.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0012.pgc - 마스터 온라인 서비스 (MM_OL_0012) [tier=easy] + * mm_ol_0012.pgc - 카드사 마스터 등록 서비스 (MM_OL_0012) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 신규 카드사를 마스터(mm_issuer)에 등록한다. 카드사코드 중복이면 반려하고, + * 등록 후 카드사 변경이력(mm_issuer_hist) 에 'I' 이벤트를 남긴다. + * (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,140 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0012.pgc" +#line 18 "app/online/mm_ol_0012.pgc" TX_SERVICE(MM_OL_0012, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 23 "app/online/mm_ol_0012.pgc" + char h_issuer_cd [ 8 ] ; + +#line 24 "app/online/mm_ol_0012.pgc" + char h_issuer_nm [ 64 ] ; + +#line 25 "app/online/mm_ol_0012.pgc" + char h_settle_bank [ 8 ] ; + +#line 26 "app/online/mm_ol_0012.pgc" + char h_status [ 2 ] ; + +#line 27 "app/online/mm_ol_0012.pgc" + char h_reg_date [ 9 ] ; + +#line 28 "app/online/mm_ol_0012.pgc" + long h_dup ; +/* exec sql end declare section */ +#line 29 "app/online/mm_ol_0012.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0012] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0012] KEY 누락"); + char issuer_cd[8]; + char issuer_nm[64]; + char settle_bank[8]; + + tx_log(TX_LOG_INFO, "[MM_OL_0012] 카드사 마스터 등록 서비스 진입"); + + if (tx_buf_get(ctx->in, "ISSUERCD", issuer_cd, sizeof(issuer_cd)) != TX_OK || + tx_buf_get(ctx->in, "ISSUERNM", issuer_nm, sizeof(issuer_nm)) != TX_OK || + tx_buf_get(ctx->in, "SETTLEBANK", settle_bank, sizeof(settle_bank)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0012] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1200"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_issuer_cd, issuer_cd, sizeof(h_issuer_cd) - 1); + h_issuer_cd[sizeof(h_issuer_cd) - 1] = '\0'; + strncpy(h_issuer_nm, issuer_nm, sizeof(h_issuer_nm) - 1); + h_issuer_nm[sizeof(h_issuer_nm) - 1] = '\0'; + strncpy(h_settle_bank, settle_bank, sizeof(h_settle_bank) - 1); + h_settle_bank[sizeof(h_settle_bank) - 1] = '\0'; + strncpy(h_status, MM_ST_DONE, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + date_today(h_reg_date); + + /* 중복 카드사코드 체크 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_issuer where issuer_cd = $1 ", + ECPGt_char,(h_issuer_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_dup),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 60 "app/online/mm_ol_0012.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT dup mm_issuer"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1201"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0012] 카드사코드 중복 issuer=%s", issuer_cd); + tx_buf_sets(ctx->out, "ERRCODE", "MM1202"); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0012] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_issuer ( issuer_cd , issuer_nm , settle_bank , status , reg_date ) values ( $1 , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_issuer_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_issuer_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_settle_bank),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_reg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 81 "app/online/mm_ol_0012.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_issuer"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1203"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_issuer_hist ( issuer_cd , chg_type , chg_status , chg_date ) values ( $1 , 'I' , $2 , $3 )", + ECPGt_char,(h_issuer_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_reg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 94 "app/online/mm_ol_0012.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_issuer_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1204"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "ISSUERCD", issuer_cd); + tx_buf_sets(ctx->out, "ISSUERNM", issuer_nm); + tx_buf_sets(ctx->out, "SETTLEBANK", settle_bank); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "REGDATE", h_reg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0012] 카드사 등록완료 issuer=%s 명=%s", + issuer_cd, issuer_nm); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0012.pgc b/app/online/mm_ol_0012.pgc index 464c98e..2e64fe2 100644 --- a/app/online/mm_ol_0012.pgc +++ b/app/online/mm_ol_0012.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0012.pgc - 마스터 온라인 서비스 (MM_OL_0012) [tier=easy] + * mm_ol_0012.pgc - 카드사 마스터 등록 서비스 (MM_OL_0012) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 신규 카드사를 마스터(mm_issuer)에 등록한다. 카드사코드 중복이면 반려하고, + * 등록 후 카드사 변경이력(mm_issuer_hist) 에 'I' 이벤트를 남긴다. + * (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,98 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0012, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_issuer_cd[8]; + char h_issuer_nm[64]; + char h_settle_bank[8]; + char h_status[2]; + char h_reg_date[9]; + long h_dup; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0012] 마스터 단건조회 서비스 진입"); + char issuer_cd[8]; + char issuer_nm[64]; + char settle_bank[8]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0012] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0012] 카드사 마스터 등록 서비스 진입"); + + if (tx_buf_get(ctx->in, "ISSUERCD", issuer_cd, sizeof(issuer_cd)) != TX_OK || + tx_buf_get(ctx->in, "ISSUERNM", issuer_nm, sizeof(issuer_nm)) != TX_OK || + tx_buf_get(ctx->in, "SETTLEBANK", settle_bank, sizeof(settle_bank)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0012] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1200"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_issuer_cd, issuer_cd, sizeof(h_issuer_cd) - 1); + h_issuer_cd[sizeof(h_issuer_cd) - 1] = '\0'; + strncpy(h_issuer_nm, issuer_nm, sizeof(h_issuer_nm) - 1); + h_issuer_nm[sizeof(h_issuer_nm) - 1] = '\0'; + strncpy(h_settle_bank, settle_bank, sizeof(h_settle_bank) - 1); + h_settle_bank[sizeof(h_settle_bank) - 1] = '\0'; + strncpy(h_status, MM_ST_DONE, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + date_today(h_reg_date); + + /* 중복 카드사코드 체크 */ + EXEC SQL + SELECT COUNT(*) INTO :h_dup + FROM mm_issuer + WHERE issuer_cd = :h_issuer_cd; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT dup mm_issuer"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1201"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0012] 카드사코드 중복 issuer=%s", issuer_cd); + tx_buf_sets(ctx->out, "ERRCODE", "MM1202"); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0012] tx_begin 경고"); + + EXEC SQL + INSERT INTO mm_issuer + (issuer_cd, issuer_nm, settle_bank, status, reg_date) + VALUES (:h_issuer_cd, :h_issuer_nm, :h_settle_bank, :h_status, :h_reg_date); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_issuer"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1203"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL + INSERT INTO mm_issuer_hist + (issuer_cd, chg_type, chg_status, chg_date) + VALUES (:h_issuer_cd, 'I', :h_status, :h_reg_date); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_issuer_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1204"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "ISSUERCD", issuer_cd); + tx_buf_sets(ctx->out, "ISSUERNM", issuer_nm); + tx_buf_sets(ctx->out, "SETTLEBANK", settle_bank); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "REGDATE", h_reg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0012] 카드사 등록완료 issuer=%s 명=%s", + issuer_cd, issuer_nm); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0013.c b/app/online/mm_ol_0013.c index 00c69f6..2fbf41d 100644 --- a/app/online/mm_ol_0013.c +++ b/app/online/mm_ol_0013.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0013.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0013.pgc - 마스터 온라인 서비스 (MM_OL_0013) [tier=easy] + * mm_ol_0013.pgc - 공통코드 조회 서비스 (MM_OL_0013) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 코드그룹(code_grp) + 코드(code)로 공통코드 마스터(mm_common_code)를 + * 조회하여 코드명(code_nm)/사용여부(use_yn)/정렬순서(sort_no)를 반환한다. + * 미사용(use_yn='N') 코드는 조회되더라도 경고를 남긴다. (조회) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,92 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0013.pgc" +#line 18 "app/online/mm_ol_0013.pgc" TX_SERVICE(MM_OL_0013, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/mm_ol_0013.pgc" + char h_code_grp [ 8 ] ; + +#line 24 "app/online/mm_ol_0013.pgc" + char h_code [ 8 ] ; + +#line 25 "app/online/mm_ol_0013.pgc" + char h_code_nm [ 64 ] ; + +#line 26 "app/online/mm_ol_0013.pgc" + char h_use_yn [ 2 ] ; + +#line 27 "app/online/mm_ol_0013.pgc" + long h_sort_no ; +/* exec sql end declare section */ +#line 28 "app/online/mm_ol_0013.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0013] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0013] KEY 누락"); + char code_grp[8]; + char code[8]; + + tx_log(TX_LOG_INFO, "[MM_OL_0013] 공통코드 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "CODEGRP", code_grp, sizeof(code_grp)) != TX_OK || + tx_buf_get(ctx->in, "CODE", code, sizeof(code)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0013] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1300"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0013] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_code_grp, code_grp, sizeof(h_code_grp) - 1); + h_code_grp[sizeof(h_code_grp) - 1] = '\0'; + strncpy(h_code, code, sizeof(h_code) - 1); + h_code[sizeof(h_code) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select code_nm , use_yn , sort_no from mm_common_code where code_grp = $1 and code = $2 ", + ECPGt_char,(h_code_grp),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_code),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_code_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_use_yn),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sort_no),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 53 "app/online/mm_ol_0013.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0013] 미등록 코드 grp=%s code=%s", code_grp, code); + tx_buf_sets(ctx->out, "ERRCODE", "MM1301"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_common_code"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1302"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (strcmp(h_use_yn, "N") == 0) + tx_log(TX_LOG_WARN, "[MM_OL_0013] 미사용 코드 grp=%s code=%s", code_grp, code); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0013] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "CODEGRP", code_grp); + tx_buf_sets(ctx->out, "CODE", code); + tx_buf_sets(ctx->out, "CODENM", h_code_nm); + tx_buf_sets(ctx->out, "USEYN", h_use_yn); + tx_buf_setlong(ctx->out, "SORTNO", h_sort_no); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0013] 조회완료 grp=%s code=%s 명=%s", + code_grp, code, h_code_nm); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0013.pgc b/app/online/mm_ol_0013.pgc index 4837745..58ab985 100644 --- a/app/online/mm_ol_0013.pgc +++ b/app/online/mm_ol_0013.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0013.pgc - 마스터 온라인 서비스 (MM_OL_0013) [tier=easy] + * mm_ol_0013.pgc - 공통코드 조회 서비스 (MM_OL_0013) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 코드그룹(code_grp) + 코드(code)로 공통코드 마스터(mm_common_code)를 + * 조회하여 코드명(code_nm)/사용여부(use_yn)/정렬순서(sort_no)를 반환한다. + * 미사용(use_yn='N') 코드는 조회되더라도 경고를 남긴다. (조회) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,63 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0013, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_code_grp[8]; + char h_code[8]; + char h_code_nm[64]; + char h_use_yn[2]; + long h_sort_no; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0013] 마스터 단건조회 서비스 진입"); + char code_grp[8]; + char code[8]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0013] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0013] 공통코드 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "CODEGRP", code_grp, sizeof(code_grp)) != TX_OK || + tx_buf_get(ctx->in, "CODE", code, sizeof(code)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0013] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1300"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0013] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_code_grp, code_grp, sizeof(h_code_grp) - 1); + h_code_grp[sizeof(h_code_grp) - 1] = '\0'; + strncpy(h_code, code, sizeof(h_code) - 1); + h_code[sizeof(h_code) - 1] = '\0'; + + EXEC SQL + SELECT code_nm, use_yn, sort_no + INTO :h_code_nm, :h_use_yn, :h_sort_no + FROM mm_common_code + WHERE code_grp = :h_code_grp + AND code = :h_code; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0013] 미등록 코드 grp=%s code=%s", code_grp, code); + tx_buf_sets(ctx->out, "ERRCODE", "MM1301"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_common_code"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1302"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (strcmp(h_use_yn, "N") == 0) + tx_log(TX_LOG_WARN, "[MM_OL_0013] 미사용 코드 grp=%s code=%s", code_grp, code); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0013] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "CODEGRP", code_grp); + tx_buf_sets(ctx->out, "CODE", code); + tx_buf_sets(ctx->out, "CODENM", h_code_nm); + tx_buf_sets(ctx->out, "USEYN", h_use_yn); + tx_buf_setlong(ctx->out, "SORTNO", h_sort_no); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0013] 조회완료 grp=%s code=%s 명=%s", + code_grp, code, h_code_nm); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0014.c b/app/online/mm_ol_0014.c index f0385a0..9a300d0 100644 --- a/app/online/mm_ol_0014.c +++ b/app/online/mm_ol_0014.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0014.pgc" /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0014.pgc - 마스터 온라인 서비스 (MM_OL_0014) [tier=medium] + * mm_ol_0014.pgc - 공통코드 등록 서비스 (MM_OL_0014) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 신규 공통코드를 마스터(mm_common_code)에 등록한다. 그룹+코드 조합이 + * 중복이면 반려하고, 정렬순서는 요청값이 없으면 그룹내 최대순서+10 으로 + * 자동채번한다. (CRUD) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,60 +92,158 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0014.pgc" +#line 18 "app/online/mm_ol_0014.pgc" TX_SERVICE(MM_OL_0014, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + + + + +#line 23 "app/online/mm_ol_0014.pgc" + char h_code_grp [ 8 ] ; + +#line 24 "app/online/mm_ol_0014.pgc" + char h_code [ 8 ] ; + +#line 25 "app/online/mm_ol_0014.pgc" + char h_code_nm [ 64 ] ; + +#line 26 "app/online/mm_ol_0014.pgc" + char h_use_yn [ 2 ] ; + +#line 27 "app/online/mm_ol_0014.pgc" + char h_reg_date [ 9 ] ; + +#line 28 "app/online/mm_ol_0014.pgc" + long h_sort_no ; + +#line 29 "app/online/mm_ol_0014.pgc" + long h_max_sort ; + +#line 30 "app/online/mm_ol_0014.pgc" + long h_dup ; +/* exec sql end declare section */ +#line 31 "app/online/mm_ol_0014.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0014] 마스터 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + char code_grp[8]; + char code[8]; + char code_nm[64]; + long sort_no = 0; + + tx_log(TX_LOG_INFO, "[MM_OL_0014] 공통코드 등록 서비스 진입"); + + if (tx_buf_get(ctx->in, "CODEGRP", code_grp, sizeof(code_grp)) != TX_OK || + tx_buf_get(ctx->in, "CODE", code, sizeof(code)) != TX_OK || + tx_buf_get(ctx->in, "CODENM", code_nm, sizeof(code_nm)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0014] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1400"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "SORTNO", &sort_no); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0014] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); + strncpy(h_code_grp, code_grp, sizeof(h_code_grp) - 1); + h_code_grp[sizeof(h_code_grp) - 1] = '\0'; + strncpy(h_code, code, sizeof(h_code) - 1); + h_code[sizeof(h_code) - 1] = '\0'; + strncpy(h_code_nm, code_nm, sizeof(h_code_nm) - 1); + h_code_nm[sizeof(h_code_nm) - 1] = '\0'; + strncpy(h_use_yn, "Y", sizeof(h_use_yn) - 1); + h_use_yn[sizeof(h_use_yn) - 1] = '\0'; + date_today(h_reg_date); + + /* 그룹+코드 중복체크 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_common_code where code_grp = $1 and code = $2 ", + ECPGt_char,(h_code_grp),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_code),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_dup),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 65 "app/online/mm_ol_0014.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT dup mm_common_code"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1401"); + tx_return(ctx, TX_EDB, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0014] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0014] 코드 중복 grp=%s code=%s", code_grp, code); + tx_buf_sets(ctx->out, "ERRCODE", "MM1402"); + tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + /* 정렬순서 자동채번 (요청값 없으면 그룹내 최대+10) */ + if (sort_no > 0) { + h_sort_no = sort_no; + } else { + h_max_sort = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( MAX ( sort_no ) , 0 ) from mm_common_code where code_grp = $1 ", + ECPGt_char,(h_code_grp),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_max_sort),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 88 "app/online/mm_ol_0014.pgc" - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0014] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT max sort mm_common_code"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1403"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + h_sort_no = h_max_sort + 10; + } + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0014] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_common_code ( code_grp , code , code_nm , use_yn , sort_no , reg_date ) values ( $1 , $2 , $3 , $4 , $5 , $6 )", + ECPGt_char,(h_code_grp),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_code),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_code_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_use_yn),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sort_no),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_reg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 105 "app/online/mm_ol_0014.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_common_code"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1404"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0014] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "CODEGRP", code_grp); + tx_buf_sets(ctx->out, "CODE", code); + tx_buf_sets(ctx->out, "CODENM", code_nm); + tx_buf_sets(ctx->out, "USEYN", h_use_yn); + tx_buf_setlong(ctx->out, "SORTNO", h_sort_no); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0014] 공통코드 등록완료 grp=%s code=%s sort=%ld", + code_grp, code, h_sort_no); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0014.pgc b/app/online/mm_ol_0014.pgc index 3197435..bc36f02 100644 --- a/app/online/mm_ol_0014.pgc +++ b/app/online/mm_ol_0014.pgc @@ -1,14 +1,17 @@ /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0014.pgc - 마스터 온라인 서비스 (MM_OL_0014) [tier=medium] + * mm_ol_0014.pgc - 공통코드 등록 서비스 (MM_OL_0014) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 신규 공통코드를 마스터(mm_common_code)에 등록한다. 그룹+코드 조합이 + * 중복이면 반려하고, 정렬순서는 요청값이 없으면 그룹내 최대순서+10 으로 + * 자동채번한다. (CRUD) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,55 +19,109 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0014, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_code_grp[8]; + char h_code[8]; + char h_code_nm[64]; + char h_use_yn[2]; + char h_reg_date[9]; + long h_sort_no; + long h_max_sort; + long h_dup; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0014] 마스터 등록 서비스 진입"); + char code_grp[8]; + char code[8]; + char code_nm[64]; + long sort_no = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_INFO, "[MM_OL_0014] 공통코드 등록 서비스 진입"); + + if (tx_buf_get(ctx->in, "CODEGRP", code_grp, sizeof(code_grp)) != TX_OK || + tx_buf_get(ctx->in, "CODE", code, sizeof(code)) != TX_OK || + tx_buf_get(ctx->in, "CODENM", code_nm, sizeof(code_nm)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0014] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1400"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "SORTNO", &sort_no); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0014] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); + strncpy(h_code_grp, code_grp, sizeof(h_code_grp) - 1); + h_code_grp[sizeof(h_code_grp) - 1] = '\0'; + strncpy(h_code, code, sizeof(h_code) - 1); + h_code[sizeof(h_code) - 1] = '\0'; + strncpy(h_code_nm, code_nm, sizeof(h_code_nm) - 1); + h_code_nm[sizeof(h_code_nm) - 1] = '\0'; + strncpy(h_use_yn, "Y", sizeof(h_use_yn) - 1); + h_use_yn[sizeof(h_use_yn) - 1] = '\0'; + date_today(h_reg_date); + + /* 그룹+코드 중복체크 */ + EXEC SQL + SELECT COUNT(*) INTO :h_dup + FROM mm_common_code + WHERE code_grp = :h_code_grp + AND code = :h_code; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT dup mm_common_code"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1401"); + tx_return(ctx, TX_EDB, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0014] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0014] 코드 중복 grp=%s code=%s", code_grp, code); + tx_buf_sets(ctx->out, "ERRCODE", "MM1402"); + tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + /* 정렬순서 자동채번 (요청값 없으면 그룹내 최대+10) */ + if (sort_no > 0) { + h_sort_no = sort_no; + } else { + h_max_sort = 0; + EXEC SQL + SELECT COALESCE(MAX(sort_no), 0) INTO :h_max_sort + FROM mm_common_code + WHERE code_grp = :h_code_grp; - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0014] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT max sort mm_common_code"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1403"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + h_sort_no = h_max_sort + 10; + } + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0014] tx_begin 경고"); + + EXEC SQL + INSERT INTO mm_common_code + (code_grp, code, code_nm, use_yn, sort_no, reg_date) + VALUES (:h_code_grp, :h_code, :h_code_nm, :h_use_yn, :h_sort_no, :h_reg_date); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_common_code"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1404"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0014] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "CODEGRP", code_grp); + tx_buf_sets(ctx->out, "CODE", code); + tx_buf_sets(ctx->out, "CODENM", code_nm); + tx_buf_sets(ctx->out, "USEYN", h_use_yn); + tx_buf_setlong(ctx->out, "SORTNO", h_sort_no); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0014] 공통코드 등록완료 grp=%s code=%s sort=%ld", + code_grp, code, h_sort_no); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0015.c b/app/online/mm_ol_0015.c index 729bd41..2a00529 100644 --- a/app/online/mm_ol_0015.c +++ b/app/online/mm_ol_0015.c @@ -8,15 +8,17 @@ #line 1 "app/online/mm_ol_0015.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0015.pgc - 마스터 온라인 서비스 (MM_OL_0015) [tier=easy] + * mm_ol_0015.pgc - 가맹점 계좌조회 서비스 (MM_OL_0015) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 정산계좌 마스터(mm_merchant_acct)에서 은행코드(bank_cd)/계좌번호 + * (acct_no)/예금주(holder_nm)/검증여부(verify_yn)를 조회한다. (조회) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +91,85 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0015.pgc" +#line 17 "app/online/mm_ol_0015.pgc" TX_SERVICE(MM_OL_0015, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 22 "app/online/mm_ol_0015.pgc" + char h_merch_id [ 16 ] ; + +#line 23 "app/online/mm_ol_0015.pgc" + char h_bank_cd [ 8 ] ; + +#line 24 "app/online/mm_ol_0015.pgc" + char h_acct_no [ 24 ] ; + +#line 25 "app/online/mm_ol_0015.pgc" + char h_holder_nm [ 64 ] ; + +#line 26 "app/online/mm_ol_0015.pgc" + char h_verify_yn [ 2 ] ; +/* exec sql end declare section */ +#line 27 "app/online/mm_ol_0015.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0015] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0015] KEY 누락"); + char merch_id[16]; + + tx_log(TX_LOG_INFO, "[MM_OL_0015] 가맹점 계좌조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0015] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1500"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0015] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select bank_cd , acct_no , holder_nm , verify_yn from mm_merchant_acct where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_bank_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_acct_no),(long)24,(long)1,(24)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_holder_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_verify_yn),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 47 "app/online/mm_ol_0015.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0015] 계좌 미등록 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM1501"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant_acct"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1502"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0015] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "BANKCD", h_bank_cd); + tx_buf_sets(ctx->out, "ACCTNO", h_acct_no); + tx_buf_sets(ctx->out, "HOLDERNM", h_holder_nm); + tx_buf_sets(ctx->out, "VERIFYYN", h_verify_yn); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0015] 조회완료 merch=%s bank=%s 검증=%s", + merch_id, h_bank_cd, h_verify_yn); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0015.pgc b/app/online/mm_ol_0015.pgc index d7a1de2..fe3f9a4 100644 --- a/app/online/mm_ol_0015.pgc +++ b/app/online/mm_ol_0015.pgc @@ -1,14 +1,16 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0015.pgc - 마스터 온라인 서비스 (MM_OL_0015) [tier=easy] + * mm_ol_0015.pgc - 가맹점 계좌조회 서비스 (MM_OL_0015) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 정산계좌 마스터(mm_merchant_acct)에서 은행코드(bank_cd)/계좌번호 + * (acct_no)/예금주(holder_nm)/검증여부(verify_yn)를 조회한다. (조회) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +18,55 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0015, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_bank_cd[8]; + char h_acct_no[24]; + char h_holder_nm[64]; + char h_verify_yn[2]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0015] 마스터 단건조회 서비스 진입"); + char merch_id[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0015] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0015] 가맹점 계좌조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0015] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1500"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0015] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + + EXEC SQL + SELECT bank_cd, acct_no, holder_nm, verify_yn + INTO :h_bank_cd, :h_acct_no, :h_holder_nm, :h_verify_yn + FROM mm_merchant_acct + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0015] 계좌 미등록 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM1501"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant_acct"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1502"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0015] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "BANKCD", h_bank_cd); + tx_buf_sets(ctx->out, "ACCTNO", h_acct_no); + tx_buf_sets(ctx->out, "HOLDERNM", h_holder_nm); + tx_buf_sets(ctx->out, "VERIFYYN", h_verify_yn); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0015] 조회완료 merch=%s bank=%s 검증=%s", + merch_id, h_bank_cd, h_verify_yn); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0016.c b/app/online/mm_ol_0016.c index 46154d6..ae36d28 100644 --- a/app/online/mm_ol_0016.c +++ b/app/online/mm_ol_0016.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0016.pgc" /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0016.pgc - 마스터 온라인 서비스 (MM_OL_0016) [tier=medium] + * mm_ol_0016.pgc - 가맹점 계좌변경 서비스 (MM_OL_0016) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 정산계좌를 변경한다. 계좌번호 형식(숫자 10~16자리)을 검증하고 + * mm_merchant_acct 를 UPDATE(재검증 필요상태로 초기화) 한 뒤, 계좌변경 + * 이력(mm_acct_hist) 에 이전/이후 계좌를 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,60 +92,180 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0016.pgc" +#line 18 "app/online/mm_ol_0016.pgc" TX_SERVICE(MM_OL_0016, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + + + + +#line 23 "app/online/mm_ol_0016.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0016.pgc" + char h_bank_cd [ 8 ] ; + +#line 25 "app/online/mm_ol_0016.pgc" + char h_acct_no [ 24 ] ; + +#line 26 "app/online/mm_ol_0016.pgc" + char h_holder_nm [ 64 ] ; + +#line 27 "app/online/mm_ol_0016.pgc" + char h_verify_yn [ 2 ] ; + +#line 28 "app/online/mm_ol_0016.pgc" + char h_chg_date [ 9 ] ; + +#line 29 "app/online/mm_ol_0016.pgc" + char h_old_bank [ 8 ] ; + +#line 30 "app/online/mm_ol_0016.pgc" + char h_old_acct [ 24 ] ; +/* exec sql end declare section */ +#line 31 "app/online/mm_ol_0016.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0016] 마스터 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + char merch_id[16]; + char bank_cd[8]; + char acct_no[24]; + char holder_nm[64]; + int len, i; + + tx_log(TX_LOG_INFO, "[MM_OL_0016] 가맹점 계좌변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "BANKCD", bank_cd, sizeof(bank_cd)) != TX_OK || + tx_buf_get(ctx->in, "ACCTNO", acct_no, sizeof(acct_no)) != TX_OK || + tx_buf_get(ctx->in, "HOLDERNM", holder_nm, sizeof(holder_nm)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0016] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0016] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0016] 영업일 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "MM1600"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + /* 계좌번호 형식검증: 숫자 10~16자리 */ + len = (int)strlen(acct_no); + if (len < 10 || len > 16) { + tx_log(TX_LOG_WARN, "[MM_OL_0016] 계좌 길이오류 acct=%s len=%d", acct_no, len); + tx_buf_sets(ctx->out, "ERRCODE", "MM1601"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + for (i = 0; i < len; i++) { + if (acct_no[i] < '0' || acct_no[i] > '9') { + tx_log(TX_LOG_WARN, "[MM_OL_0016] 계좌 숫자아님 acct=%s", acct_no); + tx_buf_sets(ctx->out, "ERRCODE", "MM1601"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0016] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_bank_cd, bank_cd, sizeof(h_bank_cd) - 1); + h_bank_cd[sizeof(h_bank_cd) - 1] = '\0'; + strncpy(h_acct_no, acct_no, sizeof(h_acct_no) - 1); + h_acct_no[sizeof(h_acct_no) - 1] = '\0'; + strncpy(h_holder_nm, holder_nm, sizeof(h_holder_nm) - 1); + h_holder_nm[sizeof(h_holder_nm) - 1] = '\0'; + strncpy(h_verify_yn, "N", sizeof(h_verify_yn) - 1); /* 변경시 재검증 필요 */ + h_verify_yn[sizeof(h_verify_yn) - 1] = '\0'; + date_today(h_chg_date); + + /* 기존 계좌 조회 (존재확인 + 이력용) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select bank_cd , acct_no from mm_merchant_acct where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_old_bank),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_old_acct),(long)24,(long)1,(24)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 85 "app/online/mm_ol_0016.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0016] 계좌 미등록 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM1602"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant_acct"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1603"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0016] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_merchant_acct set bank_cd = $1 , acct_no = $2 , holder_nm = $3 , verify_yn = $4 , upd_date = $5 where merch_id = $6 ", + ECPGt_char,(h_bank_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_acct_no),(long)24,(long)1,(24)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_holder_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_verify_yn),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 110 "app/online/mm_ol_0016.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant_acct"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1604"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_acct_hist ( merch_id , old_bank , old_acct , new_bank , new_acct , chg_date ) values ( $1 , $2 , $3 , $4 , $5 , $6 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_old_bank),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_old_acct),(long)24,(long)1,(24)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bank_cd),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_acct_no),(long)24,(long)1,(24)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 124 "app/online/mm_ol_0016.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_acct_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1605"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0016] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "BANKCD", bank_cd); + tx_buf_sets(ctx->out, "ACCTNO", acct_no); + tx_buf_sets(ctx->out, "HOLDERNM", holder_nm); + tx_buf_sets(ctx->out, "VERIFYYN", h_verify_yn); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0016] 계좌변경완료 merch=%s bank=%s", merch_id, bank_cd); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0016.pgc b/app/online/mm_ol_0016.pgc index ff028b7..1c7cfa0 100644 --- a/app/online/mm_ol_0016.pgc +++ b/app/online/mm_ol_0016.pgc @@ -1,14 +1,17 @@ /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0016.pgc - 마스터 온라인 서비스 (MM_OL_0016) [tier=medium] + * mm_ol_0016.pgc - 가맹점 계좌변경 서비스 (MM_OL_0016) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 정산계좌를 변경한다. 계좌번호 형식(숫자 10~16자리)을 검증하고 + * mm_merchant_acct 를 UPDATE(재검증 필요상태로 초기화) 한 뒤, 계좌변경 + * 이력(mm_acct_hist) 에 이전/이후 계좌를 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,55 +19,128 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0016, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_bank_cd[8]; + char h_acct_no[24]; + char h_holder_nm[64]; + char h_verify_yn[2]; + char h_chg_date[9]; + char h_old_bank[8]; + char h_old_acct[24]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0016] 마스터 등록 서비스 진입"); + char merch_id[16]; + char bank_cd[8]; + char acct_no[24]; + char holder_nm[64]; + int len, i; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_INFO, "[MM_OL_0016] 가맹점 계좌변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "BANKCD", bank_cd, sizeof(bank_cd)) != TX_OK || + tx_buf_get(ctx->in, "ACCTNO", acct_no, sizeof(acct_no)) != TX_OK || + tx_buf_get(ctx->in, "HOLDERNM", holder_nm, sizeof(holder_nm)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0016] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0016] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0016] 영업일 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "MM1600"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + /* 계좌번호 형식검증: 숫자 10~16자리 */ + len = (int)strlen(acct_no); + if (len < 10 || len > 16) { + tx_log(TX_LOG_WARN, "[MM_OL_0016] 계좌 길이오류 acct=%s len=%d", acct_no, len); + tx_buf_sets(ctx->out, "ERRCODE", "MM1601"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + for (i = 0; i < len; i++) { + if (acct_no[i] < '0' || acct_no[i] > '9') { + tx_log(TX_LOG_WARN, "[MM_OL_0016] 계좌 숫자아님 acct=%s", acct_no); + tx_buf_sets(ctx->out, "ERRCODE", "MM1601"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0016] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_bank_cd, bank_cd, sizeof(h_bank_cd) - 1); + h_bank_cd[sizeof(h_bank_cd) - 1] = '\0'; + strncpy(h_acct_no, acct_no, sizeof(h_acct_no) - 1); + h_acct_no[sizeof(h_acct_no) - 1] = '\0'; + strncpy(h_holder_nm, holder_nm, sizeof(h_holder_nm) - 1); + h_holder_nm[sizeof(h_holder_nm) - 1] = '\0'; + strncpy(h_verify_yn, "N", sizeof(h_verify_yn) - 1); /* 변경시 재검증 필요 */ + h_verify_yn[sizeof(h_verify_yn) - 1] = '\0'; + date_today(h_chg_date); + + /* 기존 계좌 조회 (존재확인 + 이력용) */ + EXEC SQL + SELECT bank_cd, acct_no + INTO :h_old_bank, :h_old_acct + FROM mm_merchant_acct + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0016] 계좌 미등록 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM1602"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant_acct"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1603"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0016] tx_begin 경고"); + + EXEC SQL + UPDATE mm_merchant_acct + SET bank_cd = :h_bank_cd, + acct_no = :h_acct_no, + holder_nm = :h_holder_nm, + verify_yn = :h_verify_yn, + upd_date = :h_chg_date + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant_acct"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1604"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL + INSERT INTO mm_acct_hist + (merch_id, old_bank, old_acct, new_bank, new_acct, chg_date) + VALUES (:h_merch_id, :h_old_bank, :h_old_acct, + :h_bank_cd, :h_acct_no, :h_chg_date); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_acct_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1605"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0016] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "BANKCD", bank_cd); + tx_buf_sets(ctx->out, "ACCTNO", acct_no); + tx_buf_sets(ctx->out, "HOLDERNM", holder_nm); + tx_buf_sets(ctx->out, "VERIFYYN", h_verify_yn); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0016] 계좌변경완료 merch=%s bank=%s", merch_id, bank_cd); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0017.c b/app/online/mm_ol_0017.c index 2c7f618..1dcc65c 100644 --- a/app/online/mm_ol_0017.c +++ b/app/online/mm_ol_0017.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0017.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0017.pgc - 마스터 온라인 서비스 (MM_OL_0017) [tier=easy] + * mm_ol_0017.pgc - 수수료율 이력조회 서비스 (MM_OL_0017) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점의 수수료율 변경이력(mm_fee_hist)에서 가장 최근 1건(적용일/rate/ + * 변경자)을 조회한다. 상품구분(prod_gb)이 주어지면 해당 상품으로 한정한다. + * (이력조회) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,110 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0017.pgc" +#line 18 "app/online/mm_ol_0017.pgc" TX_SERVICE(MM_OL_0017, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/mm_ol_0017.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0017.pgc" + char h_prod_gb [ 4 ] ; + +#line 25 "app/online/mm_ol_0017.pgc" + char h_apply_date [ 9 ] ; + +#line 26 "app/online/mm_ol_0017.pgc" + long h_rate_bp ; + +#line 27 "app/online/mm_ol_0017.pgc" + long h_chg_by ; +/* exec sql end declare section */ +#line 28 "app/online/mm_ol_0017.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0017] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0017] KEY 누락"); + char merch_id[16]; + char prod_gb[4]; + char rate_str[16]; + + tx_log(TX_LOG_INFO, "[MM_OL_0017] 수수료율 이력조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0017] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1700"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 상품구분은 선택 항목 - 없으면 전체상품 대상 */ + if (tx_buf_get(ctx->in, "PRODGB", prod_gb, sizeof(prod_gb)) != TX_OK) + prod_gb[0] = '\0'; - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0017] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_prod_gb, prod_gb, sizeof(h_prod_gb) - 1); + h_prod_gb[sizeof(h_prod_gb) - 1] = '\0'; + + if (prod_gb[0] != '\0') { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select apply_date , rate_bp , chg_by from mm_fee_hist where merch_id = $1 and prod_gb = $2 order by apply_date desc , seq_no desc fetch first 1 rows only", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_prod_gb),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_apply_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_rate_bp),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_chg_by),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 59 "app/online/mm_ol_0017.pgc" + + } else { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select apply_date , rate_bp , chg_by from mm_fee_hist where merch_id = $1 order by apply_date desc , seq_no desc fetch first 1 rows only", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_apply_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_rate_bp),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_chg_by),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 67 "app/online/mm_ol_0017.pgc" + + } + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0017] 수수료율 이력없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM1701"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_fee_hist"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1702"); + tx_return(ctx, TX_EDB, ctx->out); return; } + snprintf(rate_str, sizeof(rate_str), "%ld.%02ld", + h_rate_bp / 100, h_rate_bp % 100); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0017] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "PRODGB", prod_gb); + tx_buf_sets(ctx->out, "APPLYDATE", h_apply_date); + tx_buf_setlong(ctx->out, "RATEBP", h_rate_bp); + tx_buf_sets(ctx->out, "RATEPCT", rate_str); + tx_buf_setlong(ctx->out, "CHGBY", h_chg_by); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0017] 이력조회완료 merch=%s 적용일=%s rate=%s%%", + merch_id, h_apply_date, rate_str); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0017.pgc b/app/online/mm_ol_0017.pgc index f02f42c..850b23e 100644 --- a/app/online/mm_ol_0017.pgc +++ b/app/online/mm_ol_0017.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0017.pgc - 마스터 온라인 서비스 (MM_OL_0017) [tier=easy] + * mm_ol_0017.pgc - 수수료율 이력조회 서비스 (MM_OL_0017) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점의 수수료율 변경이력(mm_fee_hist)에서 가장 최근 1건(적용일/rate/ + * 변경자)을 조회한다. 상품구분(prod_gb)이 주어지면 해당 상품으로 한정한다. + * (이력조회) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,79 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0017, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_prod_gb[4]; + char h_apply_date[9]; + long h_rate_bp; + long h_chg_by; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0017] 마스터 단건조회 서비스 진입"); + char merch_id[16]; + char prod_gb[4]; + char rate_str[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0017] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0017] 수수료율 이력조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0017] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1700"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 상품구분은 선택 항목 - 없으면 전체상품 대상 */ + if (tx_buf_get(ctx->in, "PRODGB", prod_gb, sizeof(prod_gb)) != TX_OK) + prod_gb[0] = '\0'; - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0017] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_prod_gb, prod_gb, sizeof(h_prod_gb) - 1); + h_prod_gb[sizeof(h_prod_gb) - 1] = '\0'; + + if (prod_gb[0] != '\0') { + EXEC SQL + SELECT apply_date, rate_bp, chg_by + INTO :h_apply_date, :h_rate_bp, :h_chg_by + FROM mm_fee_hist + WHERE merch_id = :h_merch_id + AND prod_gb = :h_prod_gb + ORDER BY apply_date DESC, seq_no DESC + FETCH FIRST 1 ROWS ONLY; + } else { + EXEC SQL + SELECT apply_date, rate_bp, chg_by + INTO :h_apply_date, :h_rate_bp, :h_chg_by + FROM mm_fee_hist + WHERE merch_id = :h_merch_id + ORDER BY apply_date DESC, seq_no DESC + FETCH FIRST 1 ROWS ONLY; + } + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0017] 수수료율 이력없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM1701"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_fee_hist"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1702"); + tx_return(ctx, TX_EDB, ctx->out); return; } + snprintf(rate_str, sizeof(rate_str), "%ld.%02ld", + h_rate_bp / 100, h_rate_bp % 100); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0017] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "PRODGB", prod_gb); + tx_buf_sets(ctx->out, "APPLYDATE", h_apply_date); + tx_buf_setlong(ctx->out, "RATEBP", h_rate_bp); + tx_buf_sets(ctx->out, "RATEPCT", rate_str); + tx_buf_setlong(ctx->out, "CHGBY", h_chg_by); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0017] 이력조회완료 merch=%s 적용일=%s rate=%s%%", + merch_id, h_apply_date, rate_str); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0018.c b/app/online/mm_ol_0018.c index f5457c3..d908fa7 100644 --- a/app/online/mm_ol_0018.c +++ b/app/online/mm_ol_0018.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0018.pgc" /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0018.pgc - 마스터 온라인 서비스 (MM_OL_0018) [tier=medium] + * mm_ol_0018.pgc - 가맹점 등급조회 서비스 (MM_OL_0018) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점번호로 mm_merchant 의 등급(grade)/등급명을 조회하고, 누적매출 + * 원장(mm_merchant_sales) 에서 누적 매출금액/건수를 함께 읽어 응답한다. + * 조회 전용(읽기) 서비스. (마스터 참조) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,60 +92,106 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0018.pgc" +#line 18 "app/online/mm_ol_0018.pgc" TX_SERVICE(MM_OL_0018, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/mm_ol_0018.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0018.pgc" + char h_grade [ 4 ] ; + +#line 25 "app/online/mm_ol_0018.pgc" + char h_grade_nm [ 32 ] ; + +#line 26 "app/online/mm_ol_0018.pgc" + long h_sales_amt ; + +#line 27 "app/online/mm_ol_0018.pgc" + long h_sales_cnt ; +/* exec sql end declare section */ +#line 28 "app/online/mm_ol_0018.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0018] 마스터 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0018] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char merch_id[16]; + char won_buf[32]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0018] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0018] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[MM_OL_0018] 가맹점 등급조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0018] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1800"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0018] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 가맹점 등급/등급명 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select m . grade , g . grade_nm from mm_merchant m join mm_grade_code g on g . grade = m . grade where m . merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_grade),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_grade_nm),(long)32,(long)1,(32)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 51 "app/online/mm_ol_0018.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0018] 가맹점 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM1801"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant grade"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1809"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + /* 2) 누적매출 집계(원장). 매출행이 없으면 0 으로 응답 */ + h_sales_amt = 0; + h_sales_cnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( SUM ( sale_amt ) , 0 ) , count ( * ) from mm_merchant_sales where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_sales_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sales_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 73 "app/online/mm_ol_0018.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_merchant_sales"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1802"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + amount_format_won(h_sales_amt, won_buf, sizeof(won_buf)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0018] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "GRADE", h_grade); + tx_buf_sets(ctx->out, "GRADENM", h_grade_nm); + tx_buf_setlong(ctx->out, "SALESAMT", h_sales_amt); + tx_buf_setlong(ctx->out, "SALESCNT", h_sales_cnt); + tx_buf_sets(ctx->out, "SALESWON", won_buf); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0018] 등급조회 완료 merch=%s grade=%s 누적=%ld", + merch_id, h_grade, h_sales_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0018.pgc b/app/online/mm_ol_0018.pgc index 101a1b2..94093d1 100644 --- a/app/online/mm_ol_0018.pgc +++ b/app/online/mm_ol_0018.pgc @@ -1,14 +1,17 @@ /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0018.pgc - 마스터 온라인 서비스 (MM_OL_0018) [tier=medium] + * mm_ol_0018.pgc - 가맹점 등급조회 서비스 (MM_OL_0018) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점번호로 mm_merchant 의 등급(grade)/등급명을 조회하고, 누적매출 + * 원장(mm_merchant_sales) 에서 누적 매출금액/건수를 함께 읽어 응답한다. + * 조회 전용(읽기) 서비스. (마스터 참조) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,55 +19,77 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0018, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_grade[4]; + char h_grade_nm[32]; + long h_sales_amt; + long h_sales_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0018] 마스터 등록 서비스 진입"); + char merch_id[16]; + char won_buf[32]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0018] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[MM_OL_0018] 가맹점 등급조회 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0018] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0018] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0018] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1800"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0018] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 가맹점 등급/등급명 조회 */ + EXEC SQL + SELECT m.grade, g.grade_nm + INTO :h_grade, :h_grade_nm + FROM mm_merchant m + JOIN mm_grade_code g ON g.grade = m.grade + WHERE m.merch_id = :h_merch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0018] 가맹점 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM1801"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant grade"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1809"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + /* 2) 누적매출 집계(원장). 매출행이 없으면 0 으로 응답 */ + h_sales_amt = 0; + h_sales_cnt = 0; + EXEC SQL + SELECT COALESCE(SUM(sale_amt), 0), COUNT(*) + INTO :h_sales_amt, :h_sales_cnt + FROM mm_merchant_sales + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_merchant_sales"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1802"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + amount_format_won(h_sales_amt, won_buf, sizeof(won_buf)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0018] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "GRADE", h_grade); + tx_buf_sets(ctx->out, "GRADENM", h_grade_nm); + tx_buf_setlong(ctx->out, "SALESAMT", h_sales_amt); + tx_buf_setlong(ctx->out, "SALESCNT", h_sales_cnt); + tx_buf_sets(ctx->out, "SALESWON", won_buf); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0018] 등급조회 완료 merch=%s grade=%s 누적=%ld", + merch_id, h_grade, h_sales_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0019.c b/app/online/mm_ol_0019.c index c9ed9aa..88f904a 100644 --- a/app/online/mm_ol_0019.c +++ b/app/online/mm_ol_0019.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0019.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0019.pgc - 마스터 온라인 서비스 (MM_OL_0019) [tier=easy] + * mm_ol_0019.pgc - 가맹점 등급변경 서비스 (MM_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 신규 등급코드의 유효성(mm_grade_code 사용중 여부)을 확인한 뒤 + * mm_merchant.grade 를 UPDATE 하고, 등급변경 이력(mm_grade_hist)에 + * 이전등급/신규등급/변경자 행을 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,168 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0019.pgc" +#line 18 "app/online/mm_ol_0019.pgc" TX_SERVICE(MM_OL_0019, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 23 "app/online/mm_ol_0019.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0019.pgc" + char h_new_grade [ 4 ] ; + +#line 25 "app/online/mm_ol_0019.pgc" + char h_old_grade [ 4 ] ; + +#line 26 "app/online/mm_ol_0019.pgc" + char h_oper [ 16 ] ; + +#line 27 "app/online/mm_ol_0019.pgc" + char h_chg_date [ 9 ] ; + +#line 28 "app/online/mm_ol_0019.pgc" + long h_valid ; +/* exec sql end declare section */ +#line 29 "app/online/mm_ol_0019.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0019] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0019] KEY 누락"); + char merch_id[16]; + char new_grade[4]; + char oper[16]; + + tx_log(TX_LOG_INFO, "[MM_OL_0019] 가맹점 등급변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "GRADE", new_grade, sizeof(new_grade)) != TX_OK || + tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0019] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1900"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_new_grade, new_grade, sizeof(h_new_grade) - 1); + h_new_grade[sizeof(h_new_grade) - 1] = '\0'; + strncpy(h_oper, oper, sizeof(h_oper) - 1); + h_oper[sizeof(h_oper) - 1] = '\0'; + date_today(h_chg_date); + + /* 1) 신규 등급코드 유효성(사용중 'Y') 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_grade_code where grade = $1 and use_yn = 'Y'", + ECPGt_char,(h_new_grade),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_valid),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 59 "app/online/mm_ol_0019.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_grade_code"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1909"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_valid <= 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0019] 유효하지 않은 등급코드 grade=%s", new_grade); + tx_buf_sets(ctx->out, "ERRCODE", "MM1901"); + tx_return(ctx, TX_FAIL, ctx->out); return; } + /* 2) 현재 등급 조회(이력용 이전값 확보) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select grade from mm_merchant where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_old_grade),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 78 "app/online/mm_ol_0019.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0019] 가맹점 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM1902"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant grade"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1909"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (strcmp(h_old_grade, h_new_grade) == 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0019] 등급 변동 없음 merch=%s grade=%s", + merch_id, new_grade); + tx_buf_sets(ctx->out, "ERRCODE", "MM1903"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 3) 트랜잭션: 등급 UPDATE + 이력 INSERT */ + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0019] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_merchant set grade = $1 , upd_date = $2 where merch_id = $3 ", + ECPGt_char,(h_new_grade),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 109 "app/online/mm_ol_0019.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant grade"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1904"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_grade_hist ( merch_id , old_grade , new_grade , chg_date , oper ) values ( $1 , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_old_grade),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_new_grade),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_oper),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 122 "app/online/mm_ol_0019.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_grade_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1905"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "OLDGRADE", h_old_grade); + tx_buf_sets(ctx->out, "NEWGRADE", h_new_grade); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0019] 등급변경 완료 merch=%s %s->%s", + merch_id, h_old_grade, h_new_grade); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0019.pgc b/app/online/mm_ol_0019.pgc index 03c94e6..1b72cf9 100644 --- a/app/online/mm_ol_0019.pgc +++ b/app/online/mm_ol_0019.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0019.pgc - 마스터 온라인 서비스 (MM_OL_0019) [tier=easy] + * mm_ol_0019.pgc - 가맹점 등급변경 서비스 (MM_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 신규 등급코드의 유효성(mm_grade_code 사용중 여부)을 확인한 뒤 + * mm_merchant.grade 를 UPDATE 하고, 등급변경 이력(mm_grade_hist)에 + * 이전등급/신규등급/변경자 행을 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,125 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0019, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_new_grade[4]; + char h_old_grade[4]; + char h_oper[16]; + char h_chg_date[9]; + long h_valid; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0019] 마스터 단건조회 서비스 진입"); + char merch_id[16]; + char new_grade[4]; + char oper[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0019] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0019] 가맹점 등급변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "GRADE", new_grade, sizeof(new_grade)) != TX_OK || + tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0019] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1900"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_new_grade, new_grade, sizeof(h_new_grade) - 1); + h_new_grade[sizeof(h_new_grade) - 1] = '\0'; + strncpy(h_oper, oper, sizeof(h_oper) - 1); + h_oper[sizeof(h_oper) - 1] = '\0'; + date_today(h_chg_date); + + /* 1) 신규 등급코드 유효성(사용중 'Y') 확인 */ + EXEC SQL + SELECT COUNT(*) INTO :h_valid + FROM mm_grade_code + WHERE grade = :h_new_grade + AND use_yn = 'Y'; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_grade_code"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1909"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_valid <= 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0019] 유효하지 않은 등급코드 grade=%s", new_grade); + tx_buf_sets(ctx->out, "ERRCODE", "MM1901"); + tx_return(ctx, TX_FAIL, ctx->out); return; } + /* 2) 현재 등급 조회(이력용 이전값 확보) */ + EXEC SQL + SELECT grade INTO :h_old_grade + FROM mm_merchant + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0019] 가맹점 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM1902"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant grade"); + tx_buf_sets(ctx->out, "ERRCODE", "MM1909"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (strcmp(h_old_grade, h_new_grade) == 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0019] 등급 변동 없음 merch=%s grade=%s", + merch_id, new_grade); + tx_buf_sets(ctx->out, "ERRCODE", "MM1903"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 3) 트랜잭션: 등급 UPDATE + 이력 INSERT */ + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0019] tx_begin 경고"); + + EXEC SQL + UPDATE mm_merchant + SET grade = :h_new_grade, + upd_date = :h_chg_date + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant grade"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1904"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL + INSERT INTO mm_grade_hist + (merch_id, old_grade, new_grade, chg_date, oper) + VALUES (:h_merch_id, :h_old_grade, :h_new_grade, :h_chg_date, :h_oper); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_grade_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM1905"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "OLDGRADE", h_old_grade); + tx_buf_sets(ctx->out, "NEWGRADE", h_new_grade); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0019] 등급변경 완료 merch=%s %s->%s", + merch_id, h_old_grade, h_new_grade); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0020.c b/app/online/mm_ol_0020.c index ca6c3a8..ed29dec 100644 --- a/app/online/mm_ol_0020.c +++ b/app/online/mm_ol_0020.c @@ -8,15 +8,19 @@ #line 1 "app/online/mm_ol_0020.pgc" /* @tier hard @module mm @transform master-online */ /* - * mm_ol_0020.pgc - 마스터 온라인 서비스 (MM_OL_0020) [tier=hard] + * mm_ol_0020.pgc - 가맹점 비활성(해지) 서비스 (MM_OL_0020) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 해지 요청 시 미정산 잔액(mm_settle_balance)을 먼저 확인하고, + * 잔액이 남아 있으면 해지를 반려한다. 잔액이 0 이면 mm_merchant.status + * 를 'U'(해지)로 UPDATE 하고 상태변경 이력(mm_status_hist)을 남긴다. + * (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,88 +93,179 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0020.pgc" +#line 19 "app/online/mm_ol_0020.pgc" TX_SERVICE(MM_OL_0020, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + + + + +#line 24 "app/online/mm_ol_0020.pgc" + char h_merch_id [ 16 ] ; + +#line 25 "app/online/mm_ol_0020.pgc" + char h_reason [ 64 ] ; + +#line 26 "app/online/mm_ol_0020.pgc" + char h_oper [ 16 ] ; + +#line 27 "app/online/mm_ol_0020.pgc" + char h_old_status [ 2 ] ; + +#line 28 "app/online/mm_ol_0020.pgc" + char h_new_status [ 2 ] ; + +#line 29 "app/online/mm_ol_0020.pgc" + char h_chg_date [ 9 ] ; + +#line 30 "app/online/mm_ol_0020.pgc" + long h_balance ; +/* exec sql end declare section */ +#line 31 "app/online/mm_ol_0020.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0020] 마스터 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + char merch_id[16]; + char reason[64]; + char oper[16]; + char won_buf[32]; + + tx_log(TX_LOG_INFO, "[MM_OL_0020] 가맹점 해지 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0020] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2000"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) + strncpy(reason, "가맹점요청", sizeof(reason) - 1); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0020] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_oper, oper, sizeof(h_oper) - 1); + h_oper[sizeof(h_oper) - 1] = '\0'; + strncpy(h_reason, reason, sizeof(h_reason) - 1); + h_reason[sizeof(h_reason) - 1] = '\0'; + strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); /* 'U' 해지 */ + date_today(h_chg_date); + + /* 1) 현재 상태 확인 (이미 해지면 반려) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select status from mm_merchant where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_old_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 63 "app/online/mm_ol_0020.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0020] 가맹점 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM2001"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant status"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2009"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (strcmp(h_old_status, MM_ST_FAIL) == 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0020] 이미 해지된 가맹점 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM2002"); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 2) 미정산 잔액 확인 */ + h_balance = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( SUM ( balance ) , 0 ) from mm_settle_balance where merch_id = $1 and settle_yn = 'N'", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_balance),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 90 "app/online/mm_ol_0020.pgc" - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0020] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_settle_balance"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2009"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_balance > 0) { + amount_format_won(h_balance, won_buf, sizeof(won_buf)); + tx_log(TX_LOG_WARN, "[MM_OL_0020] 미정산 잔액 존재 merch=%s 잔액=%ld", + merch_id, h_balance); + tx_buf_reset(ctx->out); + tx_buf_setlong(ctx->out, "BALANCE", h_balance); + tx_buf_sets(ctx->out, "BALWON", won_buf); + tx_buf_sets(ctx->out, "ERRCODE", "MM2003"); + tx_return(ctx, TX_FAIL, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + /* 3) 트랜잭션: 상태 UPDATE + 이력 INSERT */ + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0020] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_merchant set status = $1 , upd_date = $2 where merch_id = $3 ", + ECPGt_char,(h_new_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 118 "app/online/mm_ol_0020.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant status"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2004"); + tx_return(ctx, TX_EDB, ctx->out); + return; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MM_OL_0020] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_status_hist ( merch_id , old_status , new_status , chg_date , oper , reason ) values ( $1 , $2 , $3 , $4 , $5 , $6 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_old_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_new_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_oper),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_reason),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 132 "app/online/mm_ol_0020.pgc" - /* 교차 검증: 당일 합계 재계산 */ - if (mm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_status_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2005"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MM_OL_0020] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "STATUS", h_new_status); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0020] 가맹점 해지완료 merch=%s", merch_id); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0020.pgc b/app/online/mm_ol_0020.pgc index fbc8fcd..ab819e4 100644 --- a/app/online/mm_ol_0020.pgc +++ b/app/online/mm_ol_0020.pgc @@ -1,14 +1,18 @@ /* @tier hard @module mm @transform master-online */ /* - * mm_ol_0020.pgc - 마스터 온라인 서비스 (MM_OL_0020) [tier=hard] + * mm_ol_0020.pgc - 가맹점 비활성(해지) 서비스 (MM_OL_0020) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 해지 요청 시 미정산 잔액(mm_settle_balance)을 먼저 확인하고, + * 잔액이 남아 있으면 해지를 반려한다. 잔액이 0 이면 mm_merchant.status + * 를 'U'(해지)로 UPDATE 하고 상태변경 이력(mm_status_hist)을 남긴다. + * (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,83 +20,132 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0020, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_reason[64]; + char h_oper[16]; + char h_old_status[2]; + char h_new_status[2]; + char h_chg_date[9]; + long h_balance; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0020] 마스터 트랜잭션 오케스트레이션 진입"); + char merch_id[16]; + char reason[64]; + char oper[16]; + char won_buf[32]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_INFO, "[MM_OL_0020] 가맹점 해지 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0020] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2000"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) + strncpy(reason, "가맹점요청", sizeof(reason) - 1); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0020] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_oper, oper, sizeof(h_oper) - 1); + h_oper[sizeof(h_oper) - 1] = '\0'; + strncpy(h_reason, reason, sizeof(h_reason) - 1); + h_reason[sizeof(h_reason) - 1] = '\0'; + strncpy(h_new_status, MM_ST_FAIL, sizeof(h_new_status)); /* 'U' 해지 */ + date_today(h_chg_date); + + /* 1) 현재 상태 확인 (이미 해지면 반려) */ + EXEC SQL + SELECT status INTO :h_old_status + FROM mm_merchant + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0020] 가맹점 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM2001"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant status"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2009"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (strcmp(h_old_status, MM_ST_FAIL) == 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0020] 이미 해지된 가맹점 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM2002"); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 2) 미정산 잔액 확인 */ + h_balance = 0; + EXEC SQL + SELECT COALESCE(SUM(balance), 0) INTO :h_balance + FROM mm_settle_balance + WHERE merch_id = :h_merch_id + AND settle_yn = 'N'; - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0020] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_settle_balance"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2009"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_balance > 0) { + amount_format_won(h_balance, won_buf, sizeof(won_buf)); + tx_log(TX_LOG_WARN, "[MM_OL_0020] 미정산 잔액 존재 merch=%s 잔액=%ld", + merch_id, h_balance); + tx_buf_reset(ctx->out); + tx_buf_setlong(ctx->out, "BALANCE", h_balance); + tx_buf_sets(ctx->out, "BALWON", won_buf); + tx_buf_sets(ctx->out, "ERRCODE", "MM2003"); + tx_return(ctx, TX_FAIL, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + /* 3) 트랜잭션: 상태 UPDATE + 이력 INSERT */ + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0020] tx_begin 경고"); + + EXEC SQL + UPDATE mm_merchant + SET status = :h_new_status, + upd_date = :h_chg_date + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant status"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2004"); + tx_return(ctx, TX_EDB, ctx->out); + return; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MM_OL_0020] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); + EXEC SQL + INSERT INTO mm_status_hist + (merch_id, old_status, new_status, chg_date, oper, reason) + VALUES (:h_merch_id, :h_old_status, :h_new_status, :h_chg_date, + :h_oper, :h_reason); - /* 교차 검증: 당일 합계 재계산 */ - if (mm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_status_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2005"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MM_OL_0020] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "STATUS", h_new_status); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0020] 가맹점 해지완료 merch=%s", merch_id); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0021.c b/app/online/mm_ol_0021.c index 67a83e7..97645fe 100644 --- a/app/online/mm_ol_0021.c +++ b/app/online/mm_ol_0021.c @@ -8,15 +8,17 @@ #line 1 "app/online/mm_ol_0021.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0021.pgc - 마스터 온라인 서비스 (MM_OL_0021) [tier=easy] + * mm_ol_0021.pgc - 가맹점 검색(단건) 서비스 (MM_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 사업자번호(biz_no)를 키로 mm_merchant 를 1건 조회하여 가맹점번호/상호/ + * 상태/등록일을 응답한다. 조회 전용(읽기) 서비스. (마스터 참조) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +91,97 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0021.pgc" +#line 17 "app/online/mm_ol_0021.pgc" TX_SERVICE(MM_OL_0021, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 22 "app/online/mm_ol_0021.pgc" + char h_biz_no [ 16 ] ; + +#line 23 "app/online/mm_ol_0021.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0021.pgc" + char h_merch_nm [ 64 ] ; + +#line 25 "app/online/mm_ol_0021.pgc" + char h_status [ 2 ] ; + +#line 26 "app/online/mm_ol_0021.pgc" + char h_reg_date [ 9 ] ; +/* exec sql end declare section */ +#line 27 "app/online/mm_ol_0021.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0021] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0021] KEY 누락"); + char biz_no[16]; + char status_nm[16]; + + tx_log(TX_LOG_INFO, "[MM_OL_0021] 가맹점 검색(단건) 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZNO", biz_no, sizeof(biz_no)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0021] BIZNO 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2100"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_no, biz_no, sizeof(h_biz_no) - 1); + h_biz_no[sizeof(h_biz_no) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select merch_id , merch_nm , status , reg_date from mm_merchant where biz_no = $1 ", + ECPGt_char,(h_biz_no),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_nm),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_reg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 48 "app/online/mm_ol_0021.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0021] 사업자번호 미등록 biz_no=%s", biz_no); + tx_buf_sets(ctx->out, "ERRCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant by biz_no"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2109"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 상태코드 → 한글 라벨 매핑 */ + if (strcmp(h_status, MM_ST_RECV) == 0) + strncpy(status_nm, "접수", sizeof(status_nm) - 1); + else if (strcmp(h_status, MM_ST_DONE) == 0) + strncpy(status_nm, "정상", sizeof(status_nm) - 1); + else if (strcmp(h_status, MM_ST_FAIL) == 0) + strncpy(status_nm, "해지", sizeof(status_nm) - 1); + else + strncpy(status_nm, "기타", sizeof(status_nm) - 1); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZNO", biz_no); + tx_buf_sets(ctx->out, "MERCHID", h_merch_id); + tx_buf_sets(ctx->out, "MERCHNM", h_merch_nm); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "STATUSNM", status_nm); + tx_buf_sets(ctx->out, "REGDATE", h_reg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0021] 검색완료 biz_no=%s merch=%s", + biz_no, h_merch_id); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0021.pgc b/app/online/mm_ol_0021.pgc index 6c6c294..483bb64 100644 --- a/app/online/mm_ol_0021.pgc +++ b/app/online/mm_ol_0021.pgc @@ -1,14 +1,16 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0021.pgc - 마스터 온라인 서비스 (MM_OL_0021) [tier=easy] + * mm_ol_0021.pgc - 가맹점 검색(단건) 서비스 (MM_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 사업자번호(biz_no)를 키로 mm_merchant 를 1건 조회하여 가맹점번호/상호/ + * 상태/등록일을 응답한다. 조회 전용(읽기) 서비스. (마스터 참조) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +18,67 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0021, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_no[16]; + char h_merch_id[16]; + char h_merch_nm[64]; + char h_status[2]; + char h_reg_date[9]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0021] 마스터 단건조회 서비스 진입"); + char biz_no[16]; + char status_nm[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0021] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0021] 가맹점 검색(단건) 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZNO", biz_no, sizeof(biz_no)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0021] BIZNO 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2100"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_no, biz_no, sizeof(h_biz_no) - 1); + h_biz_no[sizeof(h_biz_no) - 1] = '\0'; + + EXEC SQL + SELECT merch_id, merch_nm, status, reg_date + INTO :h_merch_id, :h_merch_nm, :h_status, :h_reg_date + FROM mm_merchant + WHERE biz_no = :h_biz_no; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0021] 사업자번호 미등록 biz_no=%s", biz_no); + tx_buf_sets(ctx->out, "ERRCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant by biz_no"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2109"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 상태코드 → 한글 라벨 매핑 */ + if (strcmp(h_status, MM_ST_RECV) == 0) + strncpy(status_nm, "접수", sizeof(status_nm) - 1); + else if (strcmp(h_status, MM_ST_DONE) == 0) + strncpy(status_nm, "정상", sizeof(status_nm) - 1); + else if (strcmp(h_status, MM_ST_FAIL) == 0) + strncpy(status_nm, "해지", sizeof(status_nm) - 1); + else + strncpy(status_nm, "기타", sizeof(status_nm) - 1); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZNO", biz_no); + tx_buf_sets(ctx->out, "MERCHID", h_merch_id); + tx_buf_sets(ctx->out, "MERCHNM", h_merch_nm); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "STATUSNM", status_nm); + tx_buf_sets(ctx->out, "REGDATE", h_reg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0021] 검색완료 biz_no=%s merch=%s", + biz_no, h_merch_id); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0022.c b/app/online/mm_ol_0022.c index 40df1cc..cad2902 100644 --- a/app/online/mm_ol_0022.c +++ b/app/online/mm_ol_0022.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0022.pgc" /* @tier hard @module mm @transform master-online */ /* - * mm_ol_0022.pgc - 마스터 온라인 서비스 (MM_OL_0022) [tier=hard] + * mm_ol_0022.pgc - 수수료율 이력등록 서비스 (MM_OL_0022) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 수수료율 변경건을 감사 목적의 이력 테이블(mm_fee_hist)에 적재한다. + * 적용일(유효성 검증)/신규 수수료율(bp, 0~10000)/변경자 를 받아, 같은 + * 적용일에 이미 등록된 이력이 있으면 중복 반려한다. (이력 감사 등록) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,88 +92,139 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0022.pgc" +#line 18 "app/online/mm_ol_0022.pgc" TX_SERVICE(MM_OL_0022, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + + + +#line 23 "app/online/mm_ol_0022.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0022.pgc" + char h_apply_date [ 9 ] ; + +#line 25 "app/online/mm_ol_0022.pgc" + char h_oper [ 16 ] ; + +#line 26 "app/online/mm_ol_0022.pgc" + char h_reg_date [ 9 ] ; + +#line 27 "app/online/mm_ol_0022.pgc" + long h_rate_bp ; + +#line 28 "app/online/mm_ol_0022.pgc" + long h_dup ; +/* exec sql end declare section */ +#line 29 "app/online/mm_ol_0022.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0022] 마스터 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + char merch_id[16]; + char apply_date[9]; + char oper[16]; + long rate_bp = 0; + + tx_log(TX_LOG_INFO, "[MM_OL_0022] 수수료율 이력등록 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "APPLYDATE", apply_date, sizeof(apply_date)) != TX_OK || + tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0022] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2200"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "RATEBP", &rate_bp); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0022] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + /* 적용일 형식 및 수수료율 범위(0~10000 bp = 0~100%) 검증 */ + if (!date_is_valid(apply_date)) { + tx_log(TX_LOG_WARN, "[MM_OL_0022] 적용일 오류 date=%s", apply_date); + tx_buf_sets(ctx->out, "ERRCODE", "MM2201"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (rate_bp < 0 || rate_bp > 10000) { + tx_log(TX_LOG_WARN, "[MM_OL_0022] 수수료율 범위 오류 bp=%ld", rate_bp); + tx_buf_sets(ctx->out, "ERRCODE", "MM2202"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_apply_date, apply_date, sizeof(h_apply_date) - 1); + h_apply_date[sizeof(h_apply_date) - 1] = '\0'; + strncpy(h_oper, oper, sizeof(h_oper) - 1); + h_oper[sizeof(h_oper) - 1] = '\0'; + h_rate_bp = rate_bp; + date_today(h_reg_date); + + /* 동일 적용일 중복 이력 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_fee_hist where merch_id = $1 and apply_date = $2 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_apply_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_dup),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 76 "app/online/mm_ol_0022.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_fee_hist dup"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2209"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0022] 동일 적용일 이력 중복 merch=%s date=%s", + merch_id, apply_date); + tx_buf_sets(ctx->out, "ERRCODE", "MM2203"); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0022] tx_begin 경고"); - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0022] 원장반영 실패 rc=%d", rc); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_fee_hist ( merch_id , apply_date , rate_bp , oper , reg_date ) values ( $1 , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_apply_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_rate_bp),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_oper),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_reg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 98 "app/online/mm_ol_0022.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_fee_hist"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_buf_sets(ctx->out, "ERRCODE", "MM2204"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MM_OL_0022] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MM_OL_0022] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "APPLYDATE", h_apply_date); + tx_buf_setlong(ctx->out, "RATEBP", h_rate_bp); + tx_buf_sets(ctx->out, "REGDATE", h_reg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0022] 수수료율 이력등록 완료 merch=%s bp=%ld", + merch_id, h_rate_bp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0022.pgc b/app/online/mm_ol_0022.pgc index 01e9c1f..0ae2417 100644 --- a/app/online/mm_ol_0022.pgc +++ b/app/online/mm_ol_0022.pgc @@ -1,14 +1,17 @@ /* @tier hard @module mm @transform master-online */ /* - * mm_ol_0022.pgc - 마스터 온라인 서비스 (MM_OL_0022) [tier=hard] + * mm_ol_0022.pgc - 수수료율 이력등록 서비스 (MM_OL_0022) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 수수료율 변경건을 감사 목적의 이력 테이블(mm_fee_hist)에 적재한다. + * 적용일(유효성 검증)/신규 수수료율(bp, 0~10000)/변경자 를 받아, 같은 + * 적용일에 이미 등록된 이력이 있으면 중복 반려한다. (이력 감사 등록) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,83 +19,101 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0022, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_apply_date[9]; + char h_oper[16]; + char h_reg_date[9]; + long h_rate_bp; + long h_dup; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0022] 마스터 트랜잭션 오케스트레이션 진입"); + char merch_id[16]; + char apply_date[9]; + char oper[16]; + long rate_bp = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_INFO, "[MM_OL_0022] 수수료율 이력등록 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "APPLYDATE", apply_date, sizeof(apply_date)) != TX_OK || + tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0022] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2200"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "RATEBP", &rate_bp); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0022] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + /* 적용일 형식 및 수수료율 범위(0~10000 bp = 0~100%) 검증 */ + if (!date_is_valid(apply_date)) { + tx_log(TX_LOG_WARN, "[MM_OL_0022] 적용일 오류 date=%s", apply_date); + tx_buf_sets(ctx->out, "ERRCODE", "MM2201"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (rate_bp < 0 || rate_bp > 10000) { + tx_log(TX_LOG_WARN, "[MM_OL_0022] 수수료율 범위 오류 bp=%ld", rate_bp); + tx_buf_sets(ctx->out, "ERRCODE", "MM2202"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_apply_date, apply_date, sizeof(h_apply_date) - 1); + h_apply_date[sizeof(h_apply_date) - 1] = '\0'; + strncpy(h_oper, oper, sizeof(h_oper) - 1); + h_oper[sizeof(h_oper) - 1] = '\0'; + h_rate_bp = rate_bp; + date_today(h_reg_date); + + /* 동일 적용일 중복 이력 확인 */ + EXEC SQL + SELECT COUNT(*) INTO :h_dup + FROM mm_fee_hist + WHERE merch_id = :h_merch_id + AND apply_date = :h_apply_date; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_fee_hist dup"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2209"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_dup > 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0022] 동일 적용일 이력 중복 merch=%s date=%s", + merch_id, apply_date); + tx_buf_sets(ctx->out, "ERRCODE", "MM2203"); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0022] tx_begin 경고"); - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0022] 원장반영 실패 rc=%d", rc); + EXEC SQL + INSERT INTO mm_fee_hist + (merch_id, apply_date, rate_bp, oper, reg_date) + VALUES (:h_merch_id, :h_apply_date, :h_rate_bp, :h_oper, :h_reg_date); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_fee_hist"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_buf_sets(ctx->out, "ERRCODE", "MM2204"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MM_OL_0022] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MM_OL_0022] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "APPLYDATE", h_apply_date); + tx_buf_setlong(ctx->out, "RATEBP", h_rate_bp); + tx_buf_sets(ctx->out, "REGDATE", h_reg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0022] 수수료율 이력등록 완료 merch=%s bp=%ld", + merch_id, h_rate_bp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0023.c b/app/online/mm_ol_0023.c index 825ddd0..158cb3d 100644 --- a/app/online/mm_ol_0023.c +++ b/app/online/mm_ol_0023.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0023.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0023.pgc - 마스터 온라인 서비스 (MM_OL_0023) [tier=easy] + * mm_ol_0023.pgc - 한도 이력조회 서비스 (MM_OL_0023) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점의 한도변경 이력(mm_limit_hist) 중 가장 최근 1건을 조회하여 + * 이전한도/신규한도/변경일/변경자를 응답한다. 조회 전용(읽기) 서비스. + * (마스터 이력 참조) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,93 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0023.pgc" +#line 18 "app/online/mm_ol_0023.pgc" TX_SERVICE(MM_OL_0023, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/mm_ol_0023.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0023.pgc" + char h_chg_date [ 9 ] ; + +#line 25 "app/online/mm_ol_0023.pgc" + char h_oper [ 16 ] ; + +#line 26 "app/online/mm_ol_0023.pgc" + long h_old_limit ; + +#line 27 "app/online/mm_ol_0023.pgc" + long h_new_limit ; +/* exec sql end declare section */ +#line 28 "app/online/mm_ol_0023.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0023] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0023] KEY 누락"); + char merch_id[16]; + char won_buf[32]; + long delta; + + tx_log(TX_LOG_INFO, "[MM_OL_0023] 한도 이력조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0023] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2300"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + + /* 최근 한도변경 1건 (변경일 내림차순 선두) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select old_limit , new_limit , chg_date , oper from mm_limit_hist where merch_id = $1 order by chg_date desc , seq desc fetch first 1 rows only", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_old_limit),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_new_limit),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_oper),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 53 "app/online/mm_ol_0023.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0023] 한도 이력 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM2301"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_limit_hist"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2309"); + tx_return(ctx, TX_EDB, ctx->out); return; } + delta = h_new_limit - h_old_limit; + amount_format_won(h_new_limit, won_buf, sizeof(won_buf)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_setlong(ctx->out, "OLDLIMIT", h_old_limit); + tx_buf_setlong(ctx->out, "NEWLIMIT", h_new_limit); + tx_buf_setlong(ctx->out, "DELTA", delta); + tx_buf_sets(ctx->out, "NEWLIMITWON", won_buf); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "OPER", h_oper); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0023] 한도 이력조회 완료 merch=%s 한도=%ld(변동%ld)", + merch_id, h_new_limit, delta); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0023.pgc b/app/online/mm_ol_0023.pgc index be94038..e75b80d 100644 --- a/app/online/mm_ol_0023.pgc +++ b/app/online/mm_ol_0023.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0023.pgc - 마스터 온라인 서비스 (MM_OL_0023) [tier=easy] + * mm_ol_0023.pgc - 한도 이력조회 서비스 (MM_OL_0023) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점의 한도변경 이력(mm_limit_hist) 중 가장 최근 1건을 조회하여 + * 이전한도/신규한도/변경일/변경자를 응답한다. 조회 전용(읽기) 서비스. + * (마스터 이력 참조) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,65 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0023, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_chg_date[9]; + char h_oper[16]; + long h_old_limit; + long h_new_limit; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0023] 마스터 단건조회 서비스 진입"); + char merch_id[16]; + char won_buf[32]; + long delta; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0023] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0023] 한도 이력조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0023] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2300"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + + /* 최근 한도변경 1건 (변경일 내림차순 선두) */ + EXEC SQL + SELECT old_limit, new_limit, chg_date, oper + INTO :h_old_limit, :h_new_limit, :h_chg_date, :h_oper + FROM mm_limit_hist + WHERE merch_id = :h_merch_id + ORDER BY chg_date DESC, seq DESC + FETCH FIRST 1 ROWS ONLY; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0023] 한도 이력 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM2301"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_limit_hist"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2309"); + tx_return(ctx, TX_EDB, ctx->out); return; } + delta = h_new_limit - h_old_limit; + amount_format_won(h_new_limit, won_buf, sizeof(won_buf)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_setlong(ctx->out, "OLDLIMIT", h_old_limit); + tx_buf_setlong(ctx->out, "NEWLIMIT", h_new_limit); + tx_buf_setlong(ctx->out, "DELTA", delta); + tx_buf_sets(ctx->out, "NEWLIMITWON", won_buf); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "OPER", h_oper); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0023] 한도 이력조회 완료 merch=%s 한도=%ld(변동%ld)", + merch_id, h_new_limit, delta); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0024.c b/app/online/mm_ol_0024.c index a996fe5..f60016c 100644 --- a/app/online/mm_ol_0024.c +++ b/app/online/mm_ol_0024.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0024.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0024.pgc - 마스터 온라인 서비스 (MM_OL_0024) [tier=easy] + * mm_ol_0024.pgc - BIN 유효성조회 서비스 (MM_OL_0024) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 카드 앞 6자리 BIN 이 마스터(mm_card_bin)에 존재하고, 매핑된 카드사 + * (mm_issuer)가 사용중(use_yn='Y')인지 조인 조회로 검증한다. 카드사코드/ + * 카드사명/카드종류를 응답한다. 조회 전용(읽기) 서비스. (마스터 참조) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,125 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0024.pgc" +#line 18 "app/online/mm_ol_0024.pgc" TX_SERVICE(MM_OL_0024, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/mm_ol_0024.pgc" + char h_bin [ 8 ] ; + +#line 24 "app/online/mm_ol_0024.pgc" + char h_issuer_cd [ 4 ] ; + +#line 25 "app/online/mm_ol_0024.pgc" + char h_issuer_nm [ 32 ] ; + +#line 26 "app/online/mm_ol_0024.pgc" + char h_card_type [ 2 ] ; + +#line 27 "app/online/mm_ol_0024.pgc" + char h_use_yn [ 2 ] ; +/* exec sql end declare section */ +#line 28 "app/online/mm_ol_0024.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0024] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0024] KEY 누락"); + char bin[8]; + char card_type_nm[16]; + int i; + + tx_log(TX_LOG_INFO, "[MM_OL_0024] BIN 유효성조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIN", bin, sizeof(bin)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0024] BIN 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2400"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0024] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* BIN 은 정확히 6자리 숫자여야 한다 */ + if (strlen(bin) != 6) { + tx_log(TX_LOG_WARN, "[MM_OL_0024] BIN 길이 오류 bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM2401"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + for (i = 0; i < 6; i++) { + if (bin[i] < '0' || bin[i] > '9') { + tx_log(TX_LOG_WARN, "[MM_OL_0024] BIN 숫자 아님 bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM2401"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } + + strncpy(h_bin, bin, sizeof(h_bin) - 1); + h_bin[sizeof(h_bin) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select i . issuer_cd , i . issuer_nm , b . card_type , i . use_yn from mm_card_bin b join mm_issuer i on i . issuer_cd = b . issuer_cd where b . bin = $1 ", + ECPGt_char,(h_bin),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_issuer_cd),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_issuer_nm),(long)32,(long)1,(32)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_type),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_use_yn),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 67 "app/online/mm_ol_0024.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0024] 미등록 BIN bin=%s", bin); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "VALIDYN", "N"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2402"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_card_bin JOIN mm_issuer"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2409"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 카드사가 사용중지면 무효 처리 */ + if (strcmp(h_use_yn, "Y") != 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0024] 사용중지 카드사 bin=%s issuer=%s", + bin, h_issuer_cd); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "VALIDYN", "N"); + tx_buf_sets(ctx->out, "ISSUERCD", h_issuer_cd); + tx_buf_sets(ctx->out, "ERRCODE", "MM2403"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + if (strcmp(h_card_type, "C") == 0) + strncpy(card_type_nm, "신용", sizeof(card_type_nm) - 1); + else if (strcmp(h_card_type, "D") == 0) + strncpy(card_type_nm, "체크", sizeof(card_type_nm) - 1); + else + strncpy(card_type_nm, "기타", sizeof(card_type_nm) - 1); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0024] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIN", bin); + tx_buf_sets(ctx->out, "VALIDYN", "Y"); + tx_buf_sets(ctx->out, "ISSUERCD", h_issuer_cd); + tx_buf_sets(ctx->out, "ISSUERNM", h_issuer_nm); + tx_buf_sets(ctx->out, "CARDTYPE", h_card_type); + tx_buf_sets(ctx->out, "CARDTYPENM", card_type_nm); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0024] BIN 유효 bin=%s issuer=%s(%s)", + bin, h_issuer_cd, h_issuer_nm); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0024.pgc b/app/online/mm_ol_0024.pgc index a014f13..eea500c 100644 --- a/app/online/mm_ol_0024.pgc +++ b/app/online/mm_ol_0024.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0024.pgc - 마스터 온라인 서비스 (MM_OL_0024) [tier=easy] + * mm_ol_0024.pgc - BIN 유효성조회 서비스 (MM_OL_0024) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 카드 앞 6자리 BIN 이 마스터(mm_card_bin)에 존재하고, 매핑된 카드사 + * (mm_issuer)가 사용중(use_yn='Y')인지 조인 조회로 검증한다. 카드사코드/ + * 카드사명/카드종류를 응답한다. 조회 전용(읽기) 서비스. (마스터 참조) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,96 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0024, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_bin[8]; + char h_issuer_cd[4]; + char h_issuer_nm[32]; + char h_card_type[2]; + char h_use_yn[2]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0024] 마스터 단건조회 서비스 진입"); + char bin[8]; + char card_type_nm[16]; + int i; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0024] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0024] BIN 유효성조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIN", bin, sizeof(bin)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0024] BIN 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2400"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0024] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* BIN 은 정확히 6자리 숫자여야 한다 */ + if (strlen(bin) != 6) { + tx_log(TX_LOG_WARN, "[MM_OL_0024] BIN 길이 오류 bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM2401"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + for (i = 0; i < 6; i++) { + if (bin[i] < '0' || bin[i] > '9') { + tx_log(TX_LOG_WARN, "[MM_OL_0024] BIN 숫자 아님 bin=%s", bin); + tx_buf_sets(ctx->out, "ERRCODE", "MM2401"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } + + strncpy(h_bin, bin, sizeof(h_bin) - 1); + h_bin[sizeof(h_bin) - 1] = '\0'; + + EXEC SQL + SELECT i.issuer_cd, i.issuer_nm, b.card_type, i.use_yn + INTO :h_issuer_cd, :h_issuer_nm, :h_card_type, :h_use_yn + FROM mm_card_bin b + JOIN mm_issuer i ON i.issuer_cd = b.issuer_cd + WHERE b.bin = :h_bin; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0024] 미등록 BIN bin=%s", bin); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "VALIDYN", "N"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2402"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_card_bin JOIN mm_issuer"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2409"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 카드사가 사용중지면 무효 처리 */ + if (strcmp(h_use_yn, "Y") != 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0024] 사용중지 카드사 bin=%s issuer=%s", + bin, h_issuer_cd); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "VALIDYN", "N"); + tx_buf_sets(ctx->out, "ISSUERCD", h_issuer_cd); + tx_buf_sets(ctx->out, "ERRCODE", "MM2403"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + if (strcmp(h_card_type, "C") == 0) + strncpy(card_type_nm, "신용", sizeof(card_type_nm) - 1); + else if (strcmp(h_card_type, "D") == 0) + strncpy(card_type_nm, "체크", sizeof(card_type_nm) - 1); + else + strncpy(card_type_nm, "기타", sizeof(card_type_nm) - 1); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0024] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIN", bin); + tx_buf_sets(ctx->out, "VALIDYN", "Y"); + tx_buf_sets(ctx->out, "ISSUERCD", h_issuer_cd); + tx_buf_sets(ctx->out, "ISSUERNM", h_issuer_nm); + tx_buf_sets(ctx->out, "CARDTYPE", h_card_type); + tx_buf_sets(ctx->out, "CARDTYPENM", card_type_nm); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0024] BIN 유효 bin=%s issuer=%s(%s)", + bin, h_issuer_cd, h_issuer_nm); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0025.c b/app/online/mm_ol_0025.c index fe7131a..e881023 100644 --- a/app/online/mm_ol_0025.c +++ b/app/online/mm_ol_0025.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0025.pgc" /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0025.pgc - 마스터 온라인 서비스 (MM_OL_0025) [tier=medium] + * mm_ol_0025.pgc - 카드사별 가맹점수 집계 서비스 (MM_OL_0025) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 카드사코드에 소속(주거래 카드사)된 가맹점 수를 mm_merchant 에서 + * COUNT 로 집계한다. 카드사 마스터(mm_issuer) 존재 확인 후, 정상 상태 + * 가맹점 수도 함께 집계하여 응답한다. 조회 전용(읽기) 서비스. (마스터 집계) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,60 +92,116 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0025.pgc" +#line 18 "app/online/mm_ol_0025.pgc" TX_SERVICE(MM_OL_0025, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/mm_ol_0025.pgc" + char h_issuer_cd [ 4 ] ; + +#line 24 "app/online/mm_ol_0025.pgc" + char h_issuer_nm [ 32 ] ; + +#line 25 "app/online/mm_ol_0025.pgc" + char h_st_done [ 2 ] ; + +#line 26 "app/online/mm_ol_0025.pgc" + long h_total_cnt ; + +#line 27 "app/online/mm_ol_0025.pgc" + long h_active_cnt ; +/* exec sql end declare section */ +#line 28 "app/online/mm_ol_0025.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0025] 마스터 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0025] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char issuer_cd[4]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0025] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0025] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[MM_OL_0025] 카드사별 가맹점수 집계 서비스 진입"); + + if (tx_buf_get(ctx->in, "ISSUERCD", issuer_cd, sizeof(issuer_cd)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0025] ISSUERCD 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2500"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_issuer_cd, issuer_cd, sizeof(h_issuer_cd) - 1); + h_issuer_cd[sizeof(h_issuer_cd) - 1] = '\0'; + strncpy(h_st_done, MM_ST_DONE, sizeof(h_st_done)); - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0025] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 카드사 마스터 존재 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select issuer_nm from mm_issuer where issuer_cd = $1 ", + ECPGt_char,(h_issuer_cd),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_issuer_nm),(long)32,(long)1,(32)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 49 "app/online/mm_ol_0025.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0025] 카드사 미등록 issuer=%s", issuer_cd); + tx_buf_sets(ctx->out, "ERRCODE", "MM2501"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_issuer"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2509"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + /* 2) 소속 가맹점 총수 집계 */ + h_total_cnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_merchant where issuer_cd = $1 ", + ECPGt_char,(h_issuer_cd),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_total_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 69 "app/online/mm_ol_0025.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("COUNT mm_merchant total"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2509"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 정상 상태 가맹점 수 집계 */ + h_active_cnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_merchant where issuer_cd = $1 and status = $2 ", + ECPGt_char,(h_issuer_cd),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_st_done),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_active_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 84 "app/online/mm_ol_0025.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("COUNT mm_merchant active"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2509"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0025] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "ISSUERCD", issuer_cd); + tx_buf_sets(ctx->out, "ISSUERNM", h_issuer_nm); + tx_buf_setlong(ctx->out, "TOTALCNT", h_total_cnt); + tx_buf_setlong(ctx->out, "ACTIVECNT", h_active_cnt); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0025] 집계완료 issuer=%s 총%ld 정상%ld", + issuer_cd, h_total_cnt, h_active_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0025.pgc b/app/online/mm_ol_0025.pgc index 723b425..5e313f7 100644 --- a/app/online/mm_ol_0025.pgc +++ b/app/online/mm_ol_0025.pgc @@ -1,14 +1,17 @@ /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0025.pgc - 마스터 온라인 서비스 (MM_OL_0025) [tier=medium] + * mm_ol_0025.pgc - 카드사별 가맹점수 집계 서비스 (MM_OL_0025) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 카드사코드에 소속(주거래 카드사)된 가맹점 수를 mm_merchant 에서 + * COUNT 로 집계한다. 카드사 마스터(mm_issuer) 존재 확인 후, 정상 상태 + * 가맹점 수도 함께 집계하여 응답한다. 조회 전용(읽기) 서비스. (마스터 집계) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,55 +19,84 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0025, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_issuer_cd[4]; + char h_issuer_nm[32]; + char h_st_done[2]; + long h_total_cnt; + long h_active_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0025] 마스터 등록 서비스 진입"); + char issuer_cd[4]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0025] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[MM_OL_0025] 카드사별 가맹점수 집계 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0025] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0025] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "ISSUERCD", issuer_cd, sizeof(issuer_cd)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0025] ISSUERCD 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2500"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_issuer_cd, issuer_cd, sizeof(h_issuer_cd) - 1); + h_issuer_cd[sizeof(h_issuer_cd) - 1] = '\0'; + strncpy(h_st_done, MM_ST_DONE, sizeof(h_st_done)); - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0025] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 카드사 마스터 존재 확인 */ + EXEC SQL + SELECT issuer_nm INTO :h_issuer_nm + FROM mm_issuer + WHERE issuer_cd = :h_issuer_cd; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0025] 카드사 미등록 issuer=%s", issuer_cd); + tx_buf_sets(ctx->out, "ERRCODE", "MM2501"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_issuer"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2509"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + /* 2) 소속 가맹점 총수 집계 */ + h_total_cnt = 0; + EXEC SQL + SELECT COUNT(*) INTO :h_total_cnt + FROM mm_merchant + WHERE issuer_cd = :h_issuer_cd; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("COUNT mm_merchant total"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2509"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 정상 상태 가맹점 수 집계 */ + h_active_cnt = 0; + EXEC SQL + SELECT COUNT(*) INTO :h_active_cnt + FROM mm_merchant + WHERE issuer_cd = :h_issuer_cd + AND status = :h_st_done; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("COUNT mm_merchant active"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2509"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0025] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "ISSUERCD", issuer_cd); + tx_buf_sets(ctx->out, "ISSUERNM", h_issuer_nm); + tx_buf_setlong(ctx->out, "TOTALCNT", h_total_cnt); + tx_buf_setlong(ctx->out, "ACTIVECNT", h_active_cnt); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0025] 집계완료 issuer=%s 총%ld 정상%ld", + issuer_cd, h_total_cnt, h_active_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0026.c b/app/online/mm_ol_0026.c index 1d71232..db422e5 100644 --- a/app/online/mm_ol_0026.c +++ b/app/online/mm_ol_0026.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0026.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0026.pgc - 마스터 온라인 서비스 (MM_OL_0026) [tier=easy] + * mm_ol_0026.pgc - 공통코드 캐시조회 서비스 (MM_OL_0026) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 코드그룹(grp_cd)을 키로 공통코드(mm_common_code) 중 사용중(use_yn='Y') + * 인 대표 코드 건수와 그룹 내 최신 변경일을 집계 조회한다. 캐시 갱신 + * 판단용 메타(건수/최신변경일)를 응답한다. 조회 전용(읽기) 서비스. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,98 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0026.pgc" +#line 18 "app/online/mm_ol_0026.pgc" TX_SERVICE(MM_OL_0026, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 23 "app/online/mm_ol_0026.pgc" + char h_grp_cd [ 16 ] ; + +#line 24 "app/online/mm_ol_0026.pgc" + char h_grp_nm [ 32 ] ; + +#line 25 "app/online/mm_ol_0026.pgc" + char h_last_chg [ 9 ] ; + +#line 26 "app/online/mm_ol_0026.pgc" + long h_use_cnt ; +/* exec sql end declare section */ +#line 27 "app/online/mm_ol_0026.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0026] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0026] KEY 누락"); + char grp_cd[16]; + + tx_log(TX_LOG_INFO, "[MM_OL_0026] 공통코드 캐시조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "GRPCD", grp_cd, sizeof(grp_cd)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0026] GRPCD 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2600"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0026] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_grp_cd, grp_cd, sizeof(h_grp_cd) - 1); + h_grp_cd[sizeof(h_grp_cd) - 1] = '\0'; + + /* 1) 코드그룹 명칭 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select grp_nm from mm_code_group where grp_cd = $1 ", + ECPGt_char,(h_grp_cd),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_grp_nm),(long)32,(long)1,(32)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 47 "app/online/mm_ol_0026.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0026] 코드그룹 없음 grp=%s", grp_cd); + tx_buf_sets(ctx->out, "ERRCODE", "MM2601"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_code_group"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2609"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 2) 사용중 코드 건수 + 최신 변경일 집계 */ + h_use_cnt = 0; + strncpy(h_last_chg, "00000000", sizeof(h_last_chg)); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( MAX ( chg_date ) , '00000000' ) from mm_common_code where grp_cd = $1 and use_yn = 'Y'", + ECPGt_char,(h_grp_cd),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_use_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_last_chg),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 70 "app/online/mm_ol_0026.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_common_code agg"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2609"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (h_use_cnt <= 0) + tx_log(TX_LOG_WARN, "[MM_OL_0026] 사용중 코드 없음 grp=%s", grp_cd); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0026] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "GRPCD", grp_cd); + tx_buf_sets(ctx->out, "GRPNM", h_grp_nm); + tx_buf_setlong(ctx->out, "USECNT", h_use_cnt); + tx_buf_sets(ctx->out, "LASTCHG", h_last_chg); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0026] 캐시조회 완료 grp=%s 건수=%ld 최신=%s", + grp_cd, h_use_cnt, h_last_chg); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0026.pgc b/app/online/mm_ol_0026.pgc index e347d10..8ef1585 100644 --- a/app/online/mm_ol_0026.pgc +++ b/app/online/mm_ol_0026.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0026.pgc - 마스터 온라인 서비스 (MM_OL_0026) [tier=easy] + * mm_ol_0026.pgc - 공통코드 캐시조회 서비스 (MM_OL_0026) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 코드그룹(grp_cd)을 키로 공통코드(mm_common_code) 중 사용중(use_yn='Y') + * 인 대표 코드 건수와 그룹 내 최신 변경일을 집계 조회한다. 캐시 갱신 + * 판단용 메타(건수/최신변경일)를 응답한다. 조회 전용(읽기) 서비스. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,73 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0026, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_grp_cd[16]; + char h_grp_nm[32]; + char h_last_chg[9]; + long h_use_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0026] 마스터 단건조회 서비스 진입"); + char grp_cd[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0026] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0026] 공통코드 캐시조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "GRPCD", grp_cd, sizeof(grp_cd)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0026] GRPCD 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2600"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0026] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_grp_cd, grp_cd, sizeof(h_grp_cd) - 1); + h_grp_cd[sizeof(h_grp_cd) - 1] = '\0'; + + /* 1) 코드그룹 명칭 확인 */ + EXEC SQL + SELECT grp_nm INTO :h_grp_nm + FROM mm_code_group + WHERE grp_cd = :h_grp_cd; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0026] 코드그룹 없음 grp=%s", grp_cd); + tx_buf_sets(ctx->out, "ERRCODE", "MM2601"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_code_group"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2609"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 2) 사용중 코드 건수 + 최신 변경일 집계 */ + h_use_cnt = 0; + strncpy(h_last_chg, "00000000", sizeof(h_last_chg)); + EXEC SQL + SELECT COUNT(*), COALESCE(MAX(chg_date), '00000000') + INTO :h_use_cnt, :h_last_chg + FROM mm_common_code + WHERE grp_cd = :h_grp_cd + AND use_yn = 'Y'; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_common_code agg"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2609"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (h_use_cnt <= 0) + tx_log(TX_LOG_WARN, "[MM_OL_0026] 사용중 코드 없음 grp=%s", grp_cd); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0026] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "GRPCD", grp_cd); + tx_buf_sets(ctx->out, "GRPNM", h_grp_nm); + tx_buf_setlong(ctx->out, "USECNT", h_use_cnt); + tx_buf_sets(ctx->out, "LASTCHG", h_last_chg); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0026] 캐시조회 완료 grp=%s 건수=%ld 최신=%s", + grp_cd, h_use_cnt, h_last_chg); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0027.c b/app/online/mm_ol_0027.c index e35d7d3..9857e34 100644 --- a/app/online/mm_ol_0027.c +++ b/app/online/mm_ol_0027.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0027.pgc" /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0027.pgc - 마스터 온라인 서비스 (MM_OL_0027) [tier=medium] + * mm_ol_0027.pgc - 가맹점 연락처변경 서비스 (MM_OL_0027) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 전화번호(숫자·하이픈)와 이메일('@'·'.' 포함) 형식을 검증한 뒤 + * mm_merchant 의 연락처(tel_no/email)를 UPDATE 하고, 변경이력 + * (mm_merchant_hist)에 유형 'C'(연락처) 행을 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,60 +92,159 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0027.pgc" +#line 18 "app/online/mm_ol_0027.pgc" TX_SERVICE(MM_OL_0027, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/mm_ol_0027.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0027.pgc" + char h_tel_no [ 20 ] ; + +#line 25 "app/online/mm_ol_0027.pgc" + char h_email [ 64 ] ; + +#line 26 "app/online/mm_ol_0027.pgc" + char h_chg_date [ 9 ] ; + +#line 27 "app/online/mm_ol_0027.pgc" + long h_exist ; +/* exec sql end declare section */ +#line 28 "app/online/mm_ol_0027.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0027] 마스터 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + char merch_id[16]; + char tel_no[20]; + char email[64]; + int i, at = 0, dot = 0, len; + + tx_log(TX_LOG_INFO, "[MM_OL_0027] 가맹점 연락처변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "TELNO", tel_no, sizeof(tel_no)) != TX_OK || + tx_buf_get(ctx->in, "EMAIL", email, sizeof(email)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0027] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0027] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0027] 영업일 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "MM2700"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + /* 전화번호 형식: 숫자와 '-' 만, 길이 9~13 */ + len = (int)strlen(tel_no); + if (len < 9 || len > 13) { + tx_log(TX_LOG_WARN, "[MM_OL_0027] 전화번호 길이 오류 tel=%s", tel_no); + tx_buf_sets(ctx->out, "ERRCODE", "MM2701"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + for (i = 0; i < len; i++) { + if (tel_no[i] != '-' && (tel_no[i] < '0' || tel_no[i] > '9')) { + tx_log(TX_LOG_WARN, "[MM_OL_0027] 전화번호 형식 오류 tel=%s", tel_no); + tx_buf_sets(ctx->out, "ERRCODE", "MM2701"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0027] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 이메일 형식: '@' 1개 이상, '@' 뒤에 '.' 존재 */ + len = (int)strlen(email); + for (i = 0; i < len; i++) { + if (email[i] == '@') at = i; + else if (email[i] == '.' && at > 0) dot = i; + } + if (at <= 0 || dot <= at || dot == len - 1) { + tx_log(TX_LOG_WARN, "[MM_OL_0027] 이메일 형식 오류 email=%s", email); + tx_buf_sets(ctx->out, "ERRCODE", "MM2702"); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_tel_no, tel_no, sizeof(h_tel_no) - 1); + h_tel_no[sizeof(h_tel_no) - 1] = '\0'; + strncpy(h_email, email, sizeof(h_email) - 1); + h_email[sizeof(h_email) - 1] = '\0'; + date_today(h_chg_date); + + /* 가맹점 존재 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_merchant where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_exist),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 88 "app/online/mm_ol_0027.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_merchant exist"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2709"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_exist <= 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0027] 가맹점 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0027] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_merchant set tel_no = $1 , email = $2 , upd_date = $3 where merch_id = $4 ", + ECPGt_char,(h_tel_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_email),(long)64,(long)1,(64)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 111 "app/online/mm_ol_0027.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant contact"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2703"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_merchant_hist ( merch_id , chg_type , chg_date , chg_status ) values ( $1 , 'C' , $2 , 'M' )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 123 "app/online/mm_ol_0027.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_merchant_hist contact"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2704"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0027] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "TELNO", h_tel_no); + tx_buf_sets(ctx->out, "EMAIL", h_email); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0027] 연락처변경 완료 merch=%s", merch_id); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0027.pgc b/app/online/mm_ol_0027.pgc index 3183168..a75f633 100644 --- a/app/online/mm_ol_0027.pgc +++ b/app/online/mm_ol_0027.pgc @@ -1,14 +1,17 @@ /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0027.pgc - 마스터 온라인 서비스 (MM_OL_0027) [tier=medium] + * mm_ol_0027.pgc - 가맹점 연락처변경 서비스 (MM_OL_0027) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 전화번호(숫자·하이픈)와 이메일('@'·'.' 포함) 형식을 검증한 뒤 + * mm_merchant 의 연락처(tel_no/email)를 UPDATE 하고, 변경이력 + * (mm_merchant_hist)에 유형 'C'(연락처) 행을 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,55 +19,125 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0027, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_tel_no[20]; + char h_email[64]; + char h_chg_date[9]; + long h_exist; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0027] 마스터 등록 서비스 진입"); + char merch_id[16]; + char tel_no[20]; + char email[64]; + int i, at = 0, dot = 0, len; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_INFO, "[MM_OL_0027] 가맹점 연락처변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "TELNO", tel_no, sizeof(tel_no)) != TX_OK || + tx_buf_get(ctx->in, "EMAIL", email, sizeof(email)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0027] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0027] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0027] 영업일 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "MM2700"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + /* 전화번호 형식: 숫자와 '-' 만, 길이 9~13 */ + len = (int)strlen(tel_no); + if (len < 9 || len > 13) { + tx_log(TX_LOG_WARN, "[MM_OL_0027] 전화번호 길이 오류 tel=%s", tel_no); + tx_buf_sets(ctx->out, "ERRCODE", "MM2701"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + for (i = 0; i < len; i++) { + if (tel_no[i] != '-' && (tel_no[i] < '0' || tel_no[i] > '9')) { + tx_log(TX_LOG_WARN, "[MM_OL_0027] 전화번호 형식 오류 tel=%s", tel_no); + tx_buf_sets(ctx->out, "ERRCODE", "MM2701"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + } - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0027] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 이메일 형식: '@' 1개 이상, '@' 뒤에 '.' 존재 */ + len = (int)strlen(email); + for (i = 0; i < len; i++) { + if (email[i] == '@') at = i; + else if (email[i] == '.' && at > 0) dot = i; + } + if (at <= 0 || dot <= at || dot == len - 1) { + tx_log(TX_LOG_WARN, "[MM_OL_0027] 이메일 형식 오류 email=%s", email); + tx_buf_sets(ctx->out, "ERRCODE", "MM2702"); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_tel_no, tel_no, sizeof(h_tel_no) - 1); + h_tel_no[sizeof(h_tel_no) - 1] = '\0'; + strncpy(h_email, email, sizeof(h_email) - 1); + h_email[sizeof(h_email) - 1] = '\0'; + date_today(h_chg_date); + + /* 가맹점 존재 확인 */ + EXEC SQL + SELECT COUNT(*) INTO :h_exist + FROM mm_merchant + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_merchant exist"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2709"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_exist <= 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0027] 가맹점 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0027] tx_begin 경고"); + + EXEC SQL + UPDATE mm_merchant + SET tel_no = :h_tel_no, + email = :h_email, + upd_date = :h_chg_date + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant contact"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2703"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL + INSERT INTO mm_merchant_hist (merch_id, chg_type, chg_date, chg_status) + VALUES (:h_merch_id, 'C', :h_chg_date, 'M'); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_merchant_hist contact"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2704"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0027] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "TELNO", h_tel_no); + tx_buf_sets(ctx->out, "EMAIL", h_email); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0027] 연락처변경 완료 merch=%s", merch_id); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0028.c b/app/online/mm_ol_0028.c index e4f62ed..79179b6 100644 --- a/app/online/mm_ol_0028.c +++ b/app/online/mm_ol_0028.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0028.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0028.pgc - 마스터 온라인 서비스 (MM_OL_0028) [tier=easy] + * mm_ol_0028.pgc - 가맹점 대표자변경 서비스 (MM_OL_0028) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 대표자명(필수)과 생년월일(YYYYMMDD, 일자 유효성)을 검증한 뒤 + * mm_merchant 의 대표자 정보(owner_nm/owner_birth)를 UPDATE 하고, + * 변경이력(mm_merchant_hist)에 유형 'U'(대표자) 행을 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,142 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0028.pgc" +#line 18 "app/online/mm_ol_0028.pgc" TX_SERVICE(MM_OL_0028, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/mm_ol_0028.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0028.pgc" + char h_owner_nm [ 32 ] ; + +#line 25 "app/online/mm_ol_0028.pgc" + char h_owner_birth [ 9 ] ; + +#line 26 "app/online/mm_ol_0028.pgc" + char h_old_owner [ 32 ] ; + +#line 27 "app/online/mm_ol_0028.pgc" + char h_chg_date [ 9 ] ; +/* exec sql end declare section */ +#line 28 "app/online/mm_ol_0028.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0028] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0028] KEY 누락"); + char merch_id[16]; + char owner_nm[32]; + char owner_birth[9]; + + tx_log(TX_LOG_INFO, "[MM_OL_0028] 가맹점 대표자변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "OWNERNM", owner_nm, sizeof(owner_nm)) != TX_OK || + tx_buf_get(ctx->in, "BIRTH", owner_birth, sizeof(owner_birth)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0028] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2800"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (strlen(owner_nm) == 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0028] 대표자명 공백"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2801"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(owner_birth)) { + tx_log(TX_LOG_WARN, "[MM_OL_0028] 생년월일 오류 birth=%s", owner_birth); + tx_buf_sets(ctx->out, "ERRCODE", "MM2802"); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_owner_nm, owner_nm, sizeof(h_owner_nm) - 1); + h_owner_nm[sizeof(h_owner_nm) - 1] = '\0'; + strncpy(h_owner_birth, owner_birth, sizeof(h_owner_birth) - 1); + h_owner_birth[sizeof(h_owner_birth) - 1] = '\0'; + date_today(h_chg_date); + + /* 현재 대표자(이력 참고용) 조회 겸 존재 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select owner_nm from mm_merchant where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_old_owner),(long)32,(long)1,(32)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 70 "app/online/mm_ol_0028.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0028] 가맹점 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant owner"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2809"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0028] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_merchant set owner_nm = $1 , owner_birth = $2 , upd_date = $3 where merch_id = $4 ", + ECPGt_char,(h_owner_nm),(long)32,(long)1,(32)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_owner_birth),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 93 "app/online/mm_ol_0028.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant owner"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2803"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_merchant_hist ( merch_id , chg_type , chg_date , chg_status ) values ( $1 , 'U' , $2 , 'M' )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 105 "app/online/mm_ol_0028.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_merchant_hist owner"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2804"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "OLDOWNER", h_old_owner); + tx_buf_sets(ctx->out, "OWNERNM", h_owner_nm); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0028] 대표자변경 완료 merch=%s %s->%s", + merch_id, h_old_owner, h_owner_nm); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0028.pgc b/app/online/mm_ol_0028.pgc index abf7897..75e1f0a 100644 --- a/app/online/mm_ol_0028.pgc +++ b/app/online/mm_ol_0028.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0028.pgc - 마스터 온라인 서비스 (MM_OL_0028) [tier=easy] + * mm_ol_0028.pgc - 가맹점 대표자변경 서비스 (MM_OL_0028) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 대표자명(필수)과 생년월일(YYYYMMDD, 일자 유효성)을 검증한 뒤 + * mm_merchant 의 대표자 정보(owner_nm/owner_birth)를 UPDATE 하고, + * 변경이력(mm_merchant_hist)에 유형 'U'(대표자) 행을 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,108 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0028, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_owner_nm[32]; + char h_owner_birth[9]; + char h_old_owner[32]; + char h_chg_date[9]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0028] 마스터 단건조회 서비스 진입"); + char merch_id[16]; + char owner_nm[32]; + char owner_birth[9]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0028] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0028] 가맹점 대표자변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "OWNERNM", owner_nm, sizeof(owner_nm)) != TX_OK || + tx_buf_get(ctx->in, "BIRTH", owner_birth, sizeof(owner_birth)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0028] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2800"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (strlen(owner_nm) == 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0028] 대표자명 공백"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2801"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(owner_birth)) { + tx_log(TX_LOG_WARN, "[MM_OL_0028] 생년월일 오류 birth=%s", owner_birth); + tx_buf_sets(ctx->out, "ERRCODE", "MM2802"); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_owner_nm, owner_nm, sizeof(h_owner_nm) - 1); + h_owner_nm[sizeof(h_owner_nm) - 1] = '\0'; + strncpy(h_owner_birth, owner_birth, sizeof(h_owner_birth) - 1); + h_owner_birth[sizeof(h_owner_birth) - 1] = '\0'; + date_today(h_chg_date); + + /* 현재 대표자(이력 참고용) 조회 겸 존재 확인 */ + EXEC SQL + SELECT owner_nm INTO :h_old_owner + FROM mm_merchant + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0028] 가맹점 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant owner"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2809"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0028] tx_begin 경고"); + + EXEC SQL + UPDATE mm_merchant + SET owner_nm = :h_owner_nm, + owner_birth = :h_owner_birth, + upd_date = :h_chg_date + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant owner"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2803"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL + INSERT INTO mm_merchant_hist (merch_id, chg_type, chg_date, chg_status) + VALUES (:h_merch_id, 'U', :h_chg_date, 'M'); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_merchant_hist owner"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2804"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "OLDOWNER", h_old_owner); + tx_buf_sets(ctx->out, "OWNERNM", h_owner_nm); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0028] 대표자변경 완료 merch=%s %s->%s", + merch_id, h_old_owner, h_owner_nm); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0029.c b/app/online/mm_ol_0029.c index 61d871f..6bd02b6 100644 --- a/app/online/mm_ol_0029.c +++ b/app/online/mm_ol_0029.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0029.pgc" /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0029.pgc - 마스터 온라인 서비스 (MM_OL_0029) [tier=medium] + * mm_ol_0029.pgc - 정산주기 변경 서비스 (MM_OL_0029) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산주기코드(D=일, W=주, M=월)의 유효성을 코드 마스터(mm_settle_cycle_code) + * 로 확인한 뒤 mm_merchant.settle_cycle 을 UPDATE 하고, 정산주기 변경이력 + * (mm_settle_hist)에 이전주기/신규주기/변경자 행을 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,60 +92,167 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0029.pgc" +#line 18 "app/online/mm_ol_0029.pgc" TX_SERVICE(MM_OL_0029, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + + +#line 23 "app/online/mm_ol_0029.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0029.pgc" + char h_new_cycle [ 2 ] ; + +#line 25 "app/online/mm_ol_0029.pgc" + char h_old_cycle [ 2 ] ; + +#line 26 "app/online/mm_ol_0029.pgc" + char h_oper [ 16 ] ; + +#line 27 "app/online/mm_ol_0029.pgc" + char h_chg_date [ 9 ] ; + +#line 28 "app/online/mm_ol_0029.pgc" + long h_valid ; +/* exec sql end declare section */ +#line 29 "app/online/mm_ol_0029.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0029] 마스터 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + char merch_id[16]; + char new_cycle[2]; + char oper[16]; + + tx_log(TX_LOG_INFO, "[MM_OL_0029] 정산주기 변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "CYCLE", new_cycle, sizeof(new_cycle)) != TX_OK || + tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0029] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0029] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0029] 영업일 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "MM2900"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_new_cycle, new_cycle, sizeof(h_new_cycle) - 1); + h_new_cycle[sizeof(h_new_cycle) - 1] = '\0'; + strncpy(h_oper, oper, sizeof(h_oper) - 1); + h_oper[sizeof(h_oper) - 1] = '\0'; + date_today(h_chg_date); - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0029] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 정산주기코드 유효성 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_settle_cycle_code where cycle_cd = $1 and use_yn = 'Y'", + ECPGt_char,(h_new_cycle),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_valid),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 59 "app/online/mm_ol_0029.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_settle_cycle_code"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2909"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_valid <= 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0029] 유효하지 않은 정산주기 cycle=%s", new_cycle); + tx_buf_sets(ctx->out, "ERRCODE", "MM2901"); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + /* 2) 현재 정산주기 조회 (존재 확인 + 이력 이전값) */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select settle_cycle from mm_merchant where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_old_cycle),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 78 "app/online/mm_ol_0029.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0029] 가맹점 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant settle_cycle"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2909"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (strcmp(h_old_cycle, h_new_cycle) == 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0029] 정산주기 변동 없음 merch=%s cycle=%s", + merch_id, new_cycle); + tx_buf_sets(ctx->out, "ERRCODE", "MM2902"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 3) 트랜잭션: UPDATE + 이력 INSERT */ + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0029] tx_begin 경고"); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update mm_merchant set settle_cycle = $1 , upd_date = $2 where merch_id = $3 ", + ECPGt_char,(h_new_cycle),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 108 "app/online/mm_ol_0029.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant settle_cycle"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2903"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into mm_settle_hist ( merch_id , old_cycle , new_cycle , chg_date , oper ) values ( $1 , $2 , $3 , $4 , $5 )", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_old_cycle),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_new_cycle),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_oper),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 121 "app/online/mm_ol_0029.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_settle_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2904"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0029] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "OLDCYCLE", h_old_cycle); + tx_buf_sets(ctx->out, "NEWCYCLE", h_new_cycle); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0029] 정산주기 변경완료 merch=%s %s->%s", + merch_id, h_old_cycle, h_new_cycle); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0029.pgc b/app/online/mm_ol_0029.pgc index ef5b815..8c19a2a 100644 --- a/app/online/mm_ol_0029.pgc +++ b/app/online/mm_ol_0029.pgc @@ -1,14 +1,17 @@ /* @tier medium @module mm @transform master-online */ /* - * mm_ol_0029.pgc - 마스터 온라인 서비스 (MM_OL_0029) [tier=medium] + * mm_ol_0029.pgc - 정산주기 변경 서비스 (MM_OL_0029) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산주기코드(D=일, W=주, M=월)의 유효성을 코드 마스터(mm_settle_cycle_code) + * 로 확인한 뒤 mm_merchant.settle_cycle 을 UPDATE 하고, 정산주기 변경이력 + * (mm_settle_hist)에 이전주기/신규주기/변경자 행을 남긴다. (CRUD + 이력) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,55 +19,124 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0029, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_new_cycle[2]; + char h_old_cycle[2]; + char h_oper[16]; + char h_chg_date[9]; + long h_valid; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0029] 마스터 등록 서비스 진입"); + char merch_id[16]; + char new_cycle[2]; + char oper[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_INFO, "[MM_OL_0029] 정산주기 변경 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK || + tx_buf_get(ctx->in, "CYCLE", new_cycle, sizeof(new_cycle)) != TX_OK || + tx_buf_get(ctx->in, "OPER", oper, sizeof(oper)) != TX_OK) { tx_log(TX_LOG_ERROR, "[MM_OL_0029] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[MM_OL_0029] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0029] 영업일 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "MM2900"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_new_cycle, new_cycle, sizeof(h_new_cycle) - 1); + h_new_cycle[sizeof(h_new_cycle) - 1] = '\0'; + strncpy(h_oper, oper, sizeof(h_oper) - 1); + h_oper[sizeof(h_oper) - 1] = '\0'; + date_today(h_chg_date); - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0029] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 정산주기코드 유효성 확인 */ + EXEC SQL + SELECT COUNT(*) INTO :h_valid + FROM mm_settle_cycle_code + WHERE cycle_cd = :h_new_cycle + AND use_yn = 'Y'; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT mm_settle_cycle_code"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2909"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (h_valid <= 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0029] 유효하지 않은 정산주기 cycle=%s", new_cycle); + tx_buf_sets(ctx->out, "ERRCODE", "MM2901"); + tx_return(ctx, TX_FAIL, ctx->out); return; } - cnt = mm_rec_count(bizdate, MM_ST_RECV); + /* 2) 현재 정산주기 조회 (존재 확인 + 이력 이전값) */ + EXEC SQL + SELECT settle_cycle INTO :h_old_cycle + FROM mm_merchant + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0029] 가맹점 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant settle_cycle"); + tx_buf_sets(ctx->out, "ERRCODE", "MM2909"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + if (strcmp(h_old_cycle, h_new_cycle) == 0) { + tx_log(TX_LOG_WARN, "[MM_OL_0029] 정산주기 변동 없음 merch=%s cycle=%s", + merch_id, new_cycle); + tx_buf_sets(ctx->out, "ERRCODE", "MM2902"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + /* 3) 트랜잭션: UPDATE + 이력 INSERT */ + if (tx_begin() != TX_OK) + tx_log(TX_LOG_WARN, "[MM_OL_0029] tx_begin 경고"); + + EXEC SQL + UPDATE mm_merchant + SET settle_cycle = :h_new_cycle, + upd_date = :h_chg_date + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("UPDATE mm_merchant settle_cycle"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2903"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + EXEC SQL + INSERT INTO mm_settle_hist + (merch_id, old_cycle, new_cycle, chg_date, oper) + VALUES (:h_merch_id, :h_old_cycle, :h_new_cycle, :h_chg_date, :h_oper); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT mm_settle_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "MM2904"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[MM_OL_0029] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "OLDCYCLE", h_old_cycle); + tx_buf_sets(ctx->out, "NEWCYCLE", h_new_cycle); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0029] 정산주기 변경완료 merch=%s %s->%s", + merch_id, h_old_cycle, h_new_cycle); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0030.c b/app/online/mm_ol_0030.c index 4470904..e46c5a9 100644 --- a/app/online/mm_ol_0030.c +++ b/app/online/mm_ol_0030.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0030.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0030.pgc - 마스터 온라인 서비스 (MM_OL_0030) [tier=easy] + * mm_ol_0030.pgc - 가맹점 상태조회 서비스 (MM_OL_0030) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점번호로 mm_merchant 의 현재 상태코드와 최종변경일시(upd_date)를 + * 조회하여 상태코드/상태명/최종변경일을 응답한다. 조회 전용(읽기) 서비스. + * (마스터 참조) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,84 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0030.pgc" +#line 18 "app/online/mm_ol_0030.pgc" TX_SERVICE(MM_OL_0030, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 23 "app/online/mm_ol_0030.pgc" + char h_merch_id [ 16 ] ; + +#line 24 "app/online/mm_ol_0030.pgc" + char h_status [ 2 ] ; + +#line 25 "app/online/mm_ol_0030.pgc" + char h_upd_date [ 9 ] ; +/* exec sql end declare section */ +#line 26 "app/online/mm_ol_0030.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0030] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0030] KEY 누락"); + char merch_id[16]; + char status_nm[16]; + + tx_log(TX_LOG_INFO, "[MM_OL_0030] 가맹점 상태조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0030] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3000"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select status , coalesce ( upd_date , reg_date ) from mm_merchant where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_upd_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 47 "app/online/mm_ol_0030.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0030] 가맹점 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant status"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3009"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (strcmp(h_status, MM_ST_RECV) == 0) + strncpy(status_nm, "접수", sizeof(status_nm) - 1); + else if (strcmp(h_status, MM_ST_DONE) == 0) + strncpy(status_nm, "정상", sizeof(status_nm) - 1); + else if (strcmp(h_status, MM_ST_FAIL) == 0) + strncpy(status_nm, "해지", sizeof(status_nm) - 1); + else if (strcmp(h_status, MM_ST_SETTLED) == 0) + strncpy(status_nm, "정산완료", sizeof(status_nm) - 1); + else + strncpy(status_nm, "기타", sizeof(status_nm) - 1); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "STATUSNM", status_nm); + tx_buf_sets(ctx->out, "LASTCHG", h_upd_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0030] 상태조회 완료 merch=%s status=%s(%s)", + merch_id, h_status, status_nm); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0030.pgc b/app/online/mm_ol_0030.pgc index 931cc7c..f8472b9 100644 --- a/app/online/mm_ol_0030.pgc +++ b/app/online/mm_ol_0030.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0030.pgc - 마스터 온라인 서비스 (MM_OL_0030) [tier=easy] + * mm_ol_0030.pgc - 가맹점 상태조회 서비스 (MM_OL_0030) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점번호로 mm_merchant 의 현재 상태코드와 최종변경일시(upd_date)를 + * 조회하여 상태코드/상태명/최종변경일을 응답한다. 조회 전용(읽기) 서비스. + * (마스터 참조) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,64 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0030, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_status[2]; + char h_upd_date[9]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0030] 마스터 단건조회 서비스 진입"); + char merch_id[16]; + char status_nm[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0030] KEY 누락"); + tx_log(TX_LOG_INFO, "[MM_OL_0030] 가맹점 상태조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0030] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3000"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + + EXEC SQL + SELECT status, COALESCE(upd_date, reg_date) + INTO :h_status, :h_upd_date + FROM mm_merchant + WHERE merch_id = :h_merch_id; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0030] 가맹점 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", RESP_NOMERCH); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant status"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3009"); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (strcmp(h_status, MM_ST_RECV) == 0) + strncpy(status_nm, "접수", sizeof(status_nm) - 1); + else if (strcmp(h_status, MM_ST_DONE) == 0) + strncpy(status_nm, "정상", sizeof(status_nm) - 1); + else if (strcmp(h_status, MM_ST_FAIL) == 0) + strncpy(status_nm, "해지", sizeof(status_nm) - 1); + else if (strcmp(h_status, MM_ST_SETTLED) == 0) + strncpy(status_nm, "정산완료", sizeof(status_nm) - 1); + else + strncpy(status_nm, "기타", sizeof(status_nm) - 1); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "STATUSNM", status_nm); + tx_buf_sets(ctx->out, "LASTCHG", h_upd_date); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0030] 상태조회 완료 merch=%s status=%s(%s)", + merch_id, h_status, status_nm); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0031.c b/app/online/mm_ol_0031.c index 0b01045..2100cd8 100644 --- a/app/online/mm_ol_0031.c +++ b/app/online/mm_ol_0031.c @@ -8,15 +8,19 @@ #line 1 "app/online/mm_ol_0031.pgc" /* @tier hard @module mm @transform master-online */ /* - * mm_ol_0031.pgc - 마스터 온라인 서비스 (MM_OL_0031) [tier=hard] + * mm_ol_0031.pgc - 마스터 변경이력조회 서비스 (MM_OL_0031) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점의 변경이력(mm_merchant_hist) 중 가장 최근 1건을 조회하여 + * 변경유형(I/U/C/D)/변경일/변경상태를 응답하고, 유형코드를 한글 라벨로 + * 매핑한다. 이력 총건수도 함께 집계한다. 조회 전용(읽기) 서비스. + * (마스터 이력 참조) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,88 +93,115 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0031.pgc" +#line 19 "app/online/mm_ol_0031.pgc" TX_SERVICE(MM_OL_0031, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + + +#line 24 "app/online/mm_ol_0031.pgc" + char h_merch_id [ 16 ] ; + +#line 25 "app/online/mm_ol_0031.pgc" + char h_chg_type [ 2 ] ; + +#line 26 "app/online/mm_ol_0031.pgc" + char h_chg_date [ 9 ] ; + +#line 27 "app/online/mm_ol_0031.pgc" + char h_chg_status [ 2 ] ; + +#line 28 "app/online/mm_ol_0031.pgc" + long h_hist_cnt ; +/* exec sql end declare section */ +#line 29 "app/online/mm_ol_0031.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0031] 마스터 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0031] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char merch_id[16]; + char type_nm[16]; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0031] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + tx_log(TX_LOG_INFO, "[MM_OL_0031] 마스터 변경이력조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0031] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3100"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + + /* 1) 최근 변경이력 1건 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select chg_type , chg_date , chg_status from mm_merchant_hist where merch_id = $1 order by chg_date desc , seq desc fetch first 1 rows only", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_chg_type),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_chg_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 53 "app/online/mm_ol_0031.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0031] 변경이력 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM3101"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant_hist recent"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3109"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 2) 이력 총건수 집계 */ + h_hist_cnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_merchant_hist where merch_id = $1 ", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_hist_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 73 "app/online/mm_ol_0031.pgc" - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0031] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("COUNT mm_merchant_hist"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3109"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MM_OL_0031] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 변경유형 → 한글 라벨 */ + if (strcmp(h_chg_type, "I") == 0) + strncpy(type_nm, "등록", sizeof(type_nm) - 1); + else if (strcmp(h_chg_type, "U") == 0) + strncpy(type_nm, "대표자변경", sizeof(type_nm) - 1); + else if (strcmp(h_chg_type, "C") == 0) + strncpy(type_nm, "연락처변경", sizeof(type_nm) - 1); + else if (strcmp(h_chg_type, "D") == 0) + strncpy(type_nm, "해지", sizeof(type_nm) - 1); + else + strncpy(type_nm, "기타", sizeof(type_nm) - 1); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MM_OL_0031] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "CHGTYPE", h_chg_type); + tx_buf_sets(ctx->out, "CHGTYPENM", type_nm); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "CHGSTATUS", h_chg_status); + tx_buf_setlong(ctx->out, "HISTCNT", h_hist_cnt); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0031] 변경이력조회 완료 merch=%s 유형=%s 총%ld건", + merch_id, type_nm, h_hist_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0031.pgc b/app/online/mm_ol_0031.pgc index fd4c57e..ff14871 100644 --- a/app/online/mm_ol_0031.pgc +++ b/app/online/mm_ol_0031.pgc @@ -1,14 +1,18 @@ /* @tier hard @module mm @transform master-online */ /* - * mm_ol_0031.pgc - 마스터 온라인 서비스 (MM_OL_0031) [tier=hard] + * mm_ol_0031.pgc - 마스터 변경이력조회 서비스 (MM_OL_0031) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점의 변경이력(mm_merchant_hist) 중 가장 최근 1건을 조회하여 + * 변경유형(I/U/C/D)/변경일/변경상태를 응답하고, 유형코드를 한글 라벨로 + * 매핑한다. 이력 총건수도 함께 집계한다. 조회 전용(읽기) 서비스. + * (마스터 이력 참조) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,83 +20,86 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0031, ctx) { - mm_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_chg_type[2]; + char h_chg_date[9]; + char h_chg_status[2]; + long h_hist_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0031] 마스터 트랜잭션 오케스트레이션 진입"); + char merch_id[16]; + char type_nm[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0031] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[MM_OL_0031] 마스터 변경이력조회 서비스 진입"); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[MM_OL_0031] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (tx_buf_get(ctx->in, "MERCHID", merch_id, sizeof(merch_id)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[MM_OL_0031] MERCHID 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3100"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + strncpy(h_merch_id, merch_id, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + + /* 1) 최근 변경이력 1건 */ + EXEC SQL + SELECT chg_type, chg_date, chg_status + INTO :h_chg_type, :h_chg_date, :h_chg_status + FROM mm_merchant_hist + WHERE merch_id = :h_merch_id + ORDER BY chg_date DESC, seq DESC + FETCH FIRST 1 ROWS ONLY; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[MM_OL_0031] 변경이력 없음 merch=%s", merch_id); + tx_buf_sets(ctx->out, "ERRCODE", "MM3101"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant_hist recent"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3109"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, MM_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 2) 이력 총건수 집계 */ + h_hist_cnt = 0; + EXEC SQL + SELECT COUNT(*) INTO :h_hist_cnt + FROM mm_merchant_hist + WHERE merch_id = :h_merch_id; - rc = mm_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0031] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("COUNT mm_merchant_hist"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3109"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("MM_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[MM_OL_0031] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (mm_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 변경유형 → 한글 라벨 */ + if (strcmp(h_chg_type, "I") == 0) + strncpy(type_nm, "등록", sizeof(type_nm) - 1); + else if (strcmp(h_chg_type, "U") == 0) + strncpy(type_nm, "대표자변경", sizeof(type_nm) - 1); + else if (strcmp(h_chg_type, "C") == 0) + strncpy(type_nm, "연락처변경", sizeof(type_nm) - 1); + else if (strcmp(h_chg_type, "D") == 0) + strncpy(type_nm, "해지", sizeof(type_nm) - 1); + else + strncpy(type_nm, "기타", sizeof(type_nm) - 1); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[MM_OL_0031] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch_id); + tx_buf_sets(ctx->out, "CHGTYPE", h_chg_type); + tx_buf_sets(ctx->out, "CHGTYPENM", type_nm); + tx_buf_sets(ctx->out, "CHGDATE", h_chg_date); + tx_buf_sets(ctx->out, "CHGSTATUS", h_chg_status); + tx_buf_setlong(ctx->out, "HISTCNT", h_hist_cnt); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0031] 변경이력조회 완료 merch=%s 유형=%s 총%ld건", + merch_id, type_nm, h_hist_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0032.c b/app/online/mm_ol_0032.c index a2220c2..5d747c4 100644 --- a/app/online/mm_ol_0032.c +++ b/app/online/mm_ol_0032.c @@ -8,15 +8,18 @@ #line 1 "app/online/mm_ol_0032.pgc" /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0032.pgc - 마스터 온라인 서비스 (MM_OL_0032) [tier=easy] + * mm_ol_0032.pgc - 마스터 요약조회 서비스 (MM_OL_0032) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 마스터 전체 요약을 여러 EXEC SQL 집계로 산출한다: 전체 가맹점 수, + * 정상('M') 가맹점 수, 해지('U') 가맹점 수를 각각 COUNT 하여 응답한다. + * 조회 전용(읽기) 서비스. (마스터 집계) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -89,37 +92,96 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/mm_ol_0032.pgc" +#line 18 "app/online/mm_ol_0032.pgc" TX_SERVICE(MM_OL_0032, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/mm_ol_0032.pgc" + char h_st_done [ 2 ] ; + +#line 24 "app/online/mm_ol_0032.pgc" + char h_st_fail [ 2 ] ; + +#line 25 "app/online/mm_ol_0032.pgc" + long h_total_cnt ; + +#line 26 "app/online/mm_ol_0032.pgc" + long h_active_cnt ; + +#line 27 "app/online/mm_ol_0032.pgc" + long h_closed_cnt ; +/* exec sql end declare section */ +#line 28 "app/online/mm_ol_0032.pgc" - tx_log(TX_LOG_INFO, "[MM_OL_0032] 마스터 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0032] KEY 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); + tx_log(TX_LOG_INFO, "[MM_OL_0032] 마스터 요약조회 서비스 진입"); + + strncpy(h_st_done, MM_ST_DONE, sizeof(h_st_done)); + strncpy(h_st_fail, MM_ST_FAIL, sizeof(h_st_fail)); + + /* 1) 전체 가맹점 수 */ + h_total_cnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_merchant", ECPGt_EOIT, + ECPGt_long,&(h_total_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 39 "app/online/mm_ol_0032.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("COUNT mm_merchant total"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3209"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 정상 상태 가맹점 수 */ + h_active_cnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_merchant where status = $1 ", + ECPGt_char,(h_st_done),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_active_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 53 "app/online/mm_ol_0032.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("COUNT mm_merchant active"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3209"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 해지 상태 가맹점 수 */ + h_closed_cnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_merchant where status = $1 ", + ECPGt_char,(h_st_fail),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_closed_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 67 "app/online/mm_ol_0032.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("COUNT mm_merchant closed"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3209"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "TOTALCNT", h_total_cnt); + tx_buf_setlong(ctx->out, "ACTIVECNT", h_active_cnt); + tx_buf_setlong(ctx->out, "CLOSEDCNT", h_closed_cnt); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0032] 요약조회 완료 총%ld 정상%ld 해지%ld", + h_total_cnt, h_active_cnt, h_closed_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/mm_ol_0032.pgc b/app/online/mm_ol_0032.pgc index ec93aa9..5544cbf 100644 --- a/app/online/mm_ol_0032.pgc +++ b/app/online/mm_ol_0032.pgc @@ -1,14 +1,17 @@ /* @tier easy @module mm @transform master-online */ /* - * mm_ol_0032.pgc - 마스터 온라인 서비스 (MM_OL_0032) [tier=easy] + * mm_ol_0032.pgc - 마스터 요약조회 서비스 (MM_OL_0032) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 mm 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 마스터 전체 요약을 여러 EXEC SQL 집계로 산출한다: 전체 가맹점 수, + * 정상('M') 가맹점 수, 해지('U') 가맹점 수를 각각 COUNT 하여 응답한다. + * 조회 전용(읽기) 서비스. (마스터 집계) */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "mm_core.h" @@ -16,32 +19,66 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(MM_OL_0032, ctx) { - mm_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_st_done[2]; + char h_st_fail[2]; + long h_total_cnt; + long h_active_cnt; + long h_closed_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[MM_OL_0032] 마스터 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[MM_OL_0032] 마스터 요약조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[MM_OL_0032] KEY 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); + strncpy(h_st_done, MM_ST_DONE, sizeof(h_st_done)); + strncpy(h_st_fail, MM_ST_FAIL, sizeof(h_st_fail)); + + /* 1) 전체 가맹점 수 */ + h_total_cnt = 0; + EXEC SQL + SELECT COUNT(*) INTO :h_total_cnt + FROM mm_merchant; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("COUNT mm_merchant total"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3209"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = mm_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[MM_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 2) 정상 상태 가맹점 수 */ + h_active_cnt = 0; + EXEC SQL + SELECT COUNT(*) INTO :h_active_cnt + FROM mm_merchant + WHERE status = :h_st_done; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("COUNT mm_merchant active"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3209"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 해지 상태 가맹점 수 */ + h_closed_cnt = 0; + EXEC SQL + SELECT COUNT(*) INTO :h_closed_cnt + FROM mm_merchant + WHERE status = :h_st_fail; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("COUNT mm_merchant closed"); + tx_buf_sets(ctx->out, "ERRCODE", "MM3209"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[MM_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "TOTALCNT", h_total_cnt); + tx_buf_setlong(ctx->out, "ACTIVECNT", h_active_cnt); + tx_buf_setlong(ctx->out, "CLOSEDCNT", h_closed_cnt); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[MM_OL_0032] 요약조회 완료 총%ld 정상%ld 해지%ld", + h_total_cnt, h_active_cnt, h_closed_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0002.c b/app/online/py_ol_0002.c index 09ff797..dddaa68 100644 --- a/app/online/py_ol_0002.c +++ b/app/online/py_ol_0002.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0002.pgc" -/* @tier hard @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0002.pgc - 지급 온라인 서비스 (PY_OL_0002) [tier=hard] + * py_ol_0002.pgc - 지급 등록(지급요청 접수 INSERT) (PY_OL_0002) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,88 +90,95 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0002.pgc" +#line 16 "app/online/py_ol_0002.pgc" TX_SERVICE(PY_OL_0002, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + +#line 21 "app/online/py_ol_0002.pgc" + char h_key [ 16 ] , h_merch [ 16 ] , h_acct [ 20 ] , h_bdate [ 9 ] ; + +#line 22 "app/online/py_ol_0002.pgc" + long h_amount , h_fee ; + +#line 23 "app/online/py_ol_0002.pgc" + int h_dup ; +/* exec sql end declare section */ +#line 24 "app/online/py_ol_0002.pgc" - tx_log(TX_LOG_INFO, "[PY_OL_0002] 지급 트랜잭션 오케스트레이션 진입"); + char key[16], merch[16], acct[20], bdate[16]; + long amount = 0; + + tx_log(TX_LOG_INFO, "[PY_OL_0002] 지급 등록 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0002] 필수 필드 누락"); + tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_get(ctx->in, "ACCTNO", acct, sizeof(acct)) != TX_OK) + strcpy(acct, "000000"); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0002] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!amount_is_valid(amount)) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { + strncpy(h_key, key, 15); h_key[15] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from py_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_dup),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 46 "app/online/py_ol_0002.pgc" + + if (sqlca.sqlcode == TX_SQL_OK && h_dup > 0) { + tx_log(TX_LOG_WARN, "[PY_OL_0002] 중복 지급키 key=%s", key); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + strncpy(h_acct, acct, 19); h_acct[19] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + h_amount = amount; + h_fee = amount * 15 / 10000; /* 지급 수수료 0.15% */ - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0002] 원장반영 실패 rc=%d", rc); + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0002] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into py_ledger ( key , merch_id , card_no , amount , biz_date , status , fee ) values ( $1 , $2 , $3 , $4 , $5 , 'R' , $6 )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_acct),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 61 "app/online/py_ol_0002.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0002 INSERT"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_return(ctx, TX_EDB, ctx->out); return; } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("PY_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[PY_OL_0002] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (py_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[PY_OL_0002] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_setlong(ctx->out, "NETAMT", h_amount - h_fee); + tx_log(TX_LOG_INFO, "[PY_OL_0002] 지급등록 완료 key=%s net=%ld", key, h_amount - h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0002.pgc b/app/online/py_ol_0002.pgc index 5628235..14b08f8 100644 --- a/app/online/py_ol_0002.pgc +++ b/app/online/py_ol_0002.pgc @@ -1,14 +1,15 @@ -/* @tier hard @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0002.pgc - 지급 온라인 서비스 (PY_OL_0002) [tier=hard] + * py_ol_0002.pgc - 지급 등록(지급요청 접수 INSERT) (PY_OL_0002) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,83 +17,60 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0002, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_merch[16], h_acct[20], h_bdate[9]; + long h_amount, h_fee; + int h_dup; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[PY_OL_0002] 지급 트랜잭션 오케스트레이션 진입"); + char key[16], merch[16], acct[20], bdate[16]; + long amount = 0; + tx_log(TX_LOG_INFO, "[PY_OL_0002] 지급 등록 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0002] 필수 필드 누락"); + tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_get(ctx->in, "ACCTNO", acct, sizeof(acct)) != TX_OK) + strcpy(acct, "000000"); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0002] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!amount_is_valid(amount)) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { + strncpy(h_key, key, 15); h_key[15] = '\0'; + EXEC SQL SELECT count(*) INTO :h_dup FROM py_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_OK && h_dup > 0) { + tx_log(TX_LOG_WARN, "[PY_OL_0002] 중복 지급키 key=%s", key); tx_return(ctx, TX_FAIL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + strncpy(h_acct, acct, 19); h_acct[19] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + h_amount = amount; + h_fee = amount * 15 / 10000; /* 지급 수수료 0.15% */ - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0002] 원장반영 실패 rc=%d", rc); + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0002] tx_begin 경고"); + EXEC SQL INSERT INTO py_ledger (key, merch_id, card_no, amount, biz_date, status, fee) + VALUES (:h_key, :h_merch, :h_acct, :h_amount, :h_bdate, 'R', :h_fee); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0002 INSERT"); tx_abort(); - tx_return(ctx, rc, ctx->out); + tx_return(ctx, TX_EDB, ctx->out); return; } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("PY_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[PY_OL_0002] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (py_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[PY_OL_0002] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_setlong(ctx->out, "NETAMT", h_amount - h_fee); + tx_log(TX_LOG_INFO, "[PY_OL_0002] 지급등록 완료 key=%s net=%ld", key, h_amount - h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0003.c b/app/online/py_ol_0003.c index e9f36dd..6a64759 100644 --- a/app/online/py_ol_0003.c +++ b/app/online/py_ol_0003.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0003.pgc" -/* @tier easy @module py @transform payment-online */ +/* @tier hard @module py @transform payment-online */ /* - * py_ol_0003.pgc - 지급 온라인 서비스 (PY_OL_0003) [tier=easy] + * py_ol_0003.pgc - 지급 계좌검증 연계(tx_call 계좌실명) (PY_OL_0003) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,45 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0003.pgc" +#line 16 "app/online/py_ol_0003.pgc" TX_SERVICE(PY_OL_0003, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0003] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0003] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + TXBUF *req; + char acct[20], vresp[8]; + int rc; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + tx_log(TX_LOG_INFO, "[PY_OL_0003] 지급 계좌검증 진입"); + if (tx_buf_get(ctx->in, "ACCTNO", acct, sizeof(acct)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + req = tx_buf_alloc(); + if (!req) { tx_return(ctx, TX_ENOMEM, ctx->out); return; } + tx_buf_sets(req, "KEY", key); + tx_buf_sets(req, "ACCTNO", acct); + + rc = tx_call("PY_ACCTCHK", req, ctx->out); + tx_buf_free(req); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0003] 계좌검증 연계실패 rc=%d", rc); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, rc, ctx->out); + return; + } + if (tx_buf_get(ctx->out, "RESPCODE", vresp, sizeof(vresp)) != TX_OK) + strcpy(vresp, RESP_OK); + + tx_buf_sets(ctx->out, "VALID", (strcmp(vresp, RESP_OK) == 0) ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[PY_OL_0003] 계좌검증 acct=%s resp=%s", acct, vresp); + tx_return(ctx, (strcmp(vresp, RESP_OK) == 0) ? TX_OK : TX_FAIL, ctx->out); } diff --git a/app/online/py_ol_0003.pgc b/app/online/py_ol_0003.pgc index 8561263..d219a93 100644 --- a/app/online/py_ol_0003.pgc +++ b/app/online/py_ol_0003.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module py @transform payment-online */ +/* @tier hard @module py @transform payment-online */ /* - * py_ol_0003.pgc - 지급 온라인 서비스 (PY_OL_0003) [tier=easy] + * py_ol_0003.pgc - 지급 계좌검증 연계(tx_call 계좌실명) (PY_OL_0003) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,40 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0003, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0003] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0003] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + TXBUF *req; + char acct[20], vresp[8]; + int rc; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + tx_log(TX_LOG_INFO, "[PY_OL_0003] 지급 계좌검증 진입"); + if (tx_buf_get(ctx->in, "ACCTNO", acct, sizeof(acct)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + req = tx_buf_alloc(); + if (!req) { tx_return(ctx, TX_ENOMEM, ctx->out); return; } + tx_buf_sets(req, "KEY", key); + tx_buf_sets(req, "ACCTNO", acct); + + rc = tx_call("PY_ACCTCHK", req, ctx->out); + tx_buf_free(req); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0003] 계좌검증 연계실패 rc=%d", rc); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_SYSERR); + tx_return(ctx, rc, ctx->out); + return; + } + if (tx_buf_get(ctx->out, "RESPCODE", vresp, sizeof(vresp)) != TX_OK) + strcpy(vresp, RESP_OK); + + tx_buf_sets(ctx->out, "VALID", (strcmp(vresp, RESP_OK) == 0) ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[PY_OL_0003] 계좌검증 acct=%s resp=%s", acct, vresp); + tx_return(ctx, (strcmp(vresp, RESP_OK) == 0) ? TX_OK : TX_FAIL, ctx->out); } diff --git a/app/online/py_ol_0004.c b/app/online/py_ol_0004.c index 8efb560..07aa939 100644 --- a/app/online/py_ol_0004.c +++ b/app/online/py_ol_0004.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0004.pgc" -/* @tier hard @module py @transform payment-online */ +/* @tier easy @module py @transform payment-online */ /* - * py_ol_0004.pgc - 지급 온라인 서비스 (PY_OL_0004) [tier=hard] + * py_ol_0004.pgc - 지급 보류(홀드 전이+사유) (PY_OL_0004) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,88 +90,53 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0004.pgc" +#line 16 "app/online/py_ol_0004.pgc" TX_SERVICE(PY_OL_0004, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; - - tx_log(TX_LOG_INFO, "[PY_OL_0004] 지급 트랜잭션 오케스트레이션 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0004] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0004] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + +#line 27 "app/online/py_ol_0004.pgc" + char h_key [ 16 ] ; +/* exec sql end declare section */ +#line 28 "app/online/py_ol_0004.pgc" - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0004] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + + char reason[64]; + + tx_log(TX_LOG_INFO, "[PY_OL_0004] 지급 보류 진입"); + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) + strcpy(reason, "지급검토"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0004] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'U' , upd_ts = now ( ) where key = $1 and status = 'R'", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 39 "app/online/py_ol_0004.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0004 보류"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0004] 원장반영 실패 rc=%d", rc); + if (sqlca.sqlerrd[2] == 0) { tx_abort(); - tx_return(ctx, rc, ctx->out); - return; + tx_return(ctx, TX_ENOENT, ctx->out); return; } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("PY_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[PY_OL_0004] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (py_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[PY_OL_0004] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "HOLD", "Y"); + tx_buf_sets(ctx->out, "REASON", reason); + tx_log(TX_LOG_INFO, "[PY_OL_0004] 지급보류 key=%s reason=%s", key, reason); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0004.pgc b/app/online/py_ol_0004.pgc index 077b97e..5778150 100644 --- a/app/online/py_ol_0004.pgc +++ b/app/online/py_ol_0004.pgc @@ -1,14 +1,15 @@ -/* @tier hard @module py @transform payment-online */ +/* @tier easy @module py @transform payment-online */ /* - * py_ol_0004.pgc - 지급 온라인 서비스 (PY_OL_0004) [tier=hard] + * py_ol_0004.pgc - 지급 보류(홀드 전이+사유) (PY_OL_0004) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,83 +17,40 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0004, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; - - tx_log(TX_LOG_INFO, "[PY_OL_0004] 지급 트랜잭션 오케스트레이션 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0004] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0004] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + EXEC SQL END DECLARE SECTION; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0004] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + char reason[64]; + + tx_log(TX_LOG_INFO, "[PY_OL_0004] 지급 보류 진입"); + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) + strcpy(reason, "지급검토"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0004] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET status = 'U', upd_ts = now() + WHERE key = :h_key AND status = 'R'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0004 보류"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0004] 원장반영 실패 rc=%d", rc); + if (sqlca.sqlerrd[2] == 0) { tx_abort(); - tx_return(ctx, rc, ctx->out); - return; + tx_return(ctx, TX_ENOENT, ctx->out); return; } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("PY_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[PY_OL_0004] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (py_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[PY_OL_0004] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "HOLD", "Y"); + tx_buf_sets(ctx->out, "REASON", reason); + tx_log(TX_LOG_INFO, "[PY_OL_0004] 지급보류 key=%s reason=%s", key, reason); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0005.c b/app/online/py_ol_0005.c index e4befff..ec80156 100644 --- a/app/online/py_ol_0005.c +++ b/app/online/py_ol_0005.c @@ -8,15 +8,16 @@ #line 1 "app/online/py_ol_0005.pgc" /* @tier easy @module py @transform payment-online */ /* - * py_ol_0005.pgc - 지급 온라인 서비스 (PY_OL_0005) [tier=easy] + * py_ol_0005.pgc - 지급 보류해제(재지급 대상 전이) (PY_OL_0005) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,57 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0005.pgc" +#line 16 "app/online/py_ol_0005.pgc" TX_SERVICE(PY_OL_0005, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0005] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0005] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + +#line 27 "app/online/py_ol_0005.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; +/* exec sql end declare section */ +#line 28 "app/online/py_ol_0005.pgc" - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0005] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + + tx_log(TX_LOG_INFO, "[PY_OL_0005] 지급 보류해제 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select status from py_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 33 "app/online/py_ol_0005.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (h_status[0] != 'U') { + tx_log(TX_LOG_WARN, "[PY_OL_0005] 보류상태 아님 status=%s", h_status); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0005] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'R' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 41 "app/online/py_ol_0005.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0005 해제"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0005] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "HOLD", "N"); + tx_log(TX_LOG_INFO, "[PY_OL_0005] 보류해제 완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0005.pgc b/app/online/py_ol_0005.pgc index 9707b6c..12122c2 100644 --- a/app/online/py_ol_0005.pgc +++ b/app/online/py_ol_0005.pgc @@ -1,14 +1,15 @@ /* @tier easy @module py @transform payment-online */ /* - * py_ol_0005.pgc - 지급 온라인 서비스 (PY_OL_0005) [tier=easy] + * py_ol_0005.pgc - 지급 보류해제(재지급 대상 전이) (PY_OL_0005) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,37 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0005, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0005] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0005] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2]; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0005] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + tx_log(TX_LOG_INFO, "[PY_OL_0005] 지급 보류해제 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT status INTO :h_status FROM py_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (h_status[0] != 'U') { + tx_log(TX_LOG_WARN, "[PY_OL_0005] 보류상태 아님 status=%s", h_status); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0005] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET status = 'R', upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0005 해제"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0005] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "HOLD", "N"); + tx_log(TX_LOG_INFO, "[PY_OL_0005] 보류해제 완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0006.c b/app/online/py_ol_0006.c index a17302a..470a0f1 100644 --- a/app/online/py_ol_0006.c +++ b/app/online/py_ol_0006.c @@ -8,15 +8,16 @@ #line 1 "app/online/py_ol_0006.pgc" /* @tier easy @module py @transform payment-online */ /* - * py_ol_0006.pgc - 지급 온라인 서비스 (PY_OL_0006) [tier=easy] + * py_ol_0006.pgc - 지급 취소(지급요청 철회) (PY_OL_0006) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,63 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0006.pgc" +#line 16 "app/online/py_ol_0006.pgc" TX_SERVICE(PY_OL_0006, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0006] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0006] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/py_ol_0006.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; + +#line 28 "app/online/py_ol_0006.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 29 "app/online/py_ol_0006.pgc" - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0006] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + + tx_log(TX_LOG_INFO, "[PY_OL_0006] 지급 취소 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , status from py_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 35 "app/online/py_ol_0006.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (h_status[0] == 'S') { + tx_log(TX_LOG_WARN, "[PY_OL_0006] 이체완료건 취소불가 key=%s", key); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0006] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'U' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 43 "app/online/py_ol_0006.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0006 취소"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0006] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_setlong(ctx->out, "CANCELAMT", h_amount); + tx_log(TX_LOG_INFO, "[PY_OL_0006] 지급취소 key=%s amt=%ld", key, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0006.pgc b/app/online/py_ol_0006.pgc index e258149..18c0ffd 100644 --- a/app/online/py_ol_0006.pgc +++ b/app/online/py_ol_0006.pgc @@ -1,14 +1,15 @@ /* @tier easy @module py @transform payment-online */ /* - * py_ol_0006.pgc - 지급 온라인 서비스 (PY_OL_0006) [tier=easy] + * py_ol_0006.pgc - 지급 취소(지급요청 철회) (PY_OL_0006) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,39 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0006, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0006] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0006] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0006] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + tx_log(TX_LOG_INFO, "[PY_OL_0006] 지급 취소 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT amount, status INTO :h_amount, :h_status + FROM py_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (h_status[0] == 'S') { + tx_log(TX_LOG_WARN, "[PY_OL_0006] 이체완료건 취소불가 key=%s", key); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0006] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET status = 'U', upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0006 취소"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0006] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_setlong(ctx->out, "CANCELAMT", h_amount); + tx_log(TX_LOG_INFO, "[PY_OL_0006] 지급취소 key=%s amt=%ld", key, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0007.c b/app/online/py_ol_0007.c index 279372a..84eb0ec 100644 --- a/app/online/py_ol_0007.c +++ b/app/online/py_ol_0007.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0007.pgc" -/* @tier medium @module py @transform payment-online */ +/* @tier easy @module py @transform payment-online */ /* - * py_ol_0007.pgc - 지급 온라인 서비스 (PY_OL_0007) [tier=medium] + * py_ol_0007.pgc - 지급 상태조회(상태/이체가능액) (PY_OL_0007) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,60 +90,65 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0007.pgc" +#line 16 "app/online/py_ol_0007.pgc" TX_SERVICE(PY_OL_0007, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0007] 지급 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0007] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0007] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + + +#line 27 "app/online/py_ol_0007.pgc" + char h_key [ 16 ] , h_status [ 2 ] , h_acct [ 20 ] ; + +#line 28 "app/online/py_ol_0007.pgc" + long h_amount , h_fee ; +/* exec sql end declare section */ +#line 29 "app/online/py_ol_0007.pgc" - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0007] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + + const char *label; + + tx_log(TX_LOG_INFO, "[PY_OL_0007] 지급 상태조회 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , fee , status , card_no from py_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_acct),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 38 "app/online/py_ol_0007.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_ol_0007"); tx_return(ctx, TX_EDB, ctx->out); return; } + + switch (h_status[0]) { + case 'R': label = "지급대기"; break; + case 'M': label = "지급확정"; break; + case 'U': label = "보류/취소"; break; + case 'S': label = "이체완료"; break; + default: label = "미상"; break; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0007] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0007] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = py_rec_count(bizdate, PY_ST_RECV); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0007] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "STATUSLBL", label); + tx_buf_setlong(ctx->out, "PAYABLE", h_amount - h_fee); + tx_log(TX_LOG_INFO, "[PY_OL_0007] 상태 key=%s status=%s payable=%ld", + key, h_status, h_amount - h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0007.pgc b/app/online/py_ol_0007.pgc index 2617457..0560856 100644 --- a/app/online/py_ol_0007.pgc +++ b/app/online/py_ol_0007.pgc @@ -1,14 +1,15 @@ -/* @tier medium @module py @transform payment-online */ +/* @tier easy @module py @transform payment-online */ /* - * py_ol_0007.pgc - 지급 온라인 서비스 (PY_OL_0007) [tier=medium] + * py_ol_0007.pgc - 지급 상태조회(상태/이체가능액) (PY_OL_0007) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,55 +17,42 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0007, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0007] 지급 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0007] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0007] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2], h_acct[20]; + long h_amount, h_fee; + EXEC SQL END DECLARE SECTION; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0007] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + const char *label; + + tx_log(TX_LOG_INFO, "[PY_OL_0007] 지급 상태조회 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT amount, fee, status, card_no + INTO :h_amount, :h_fee, :h_status, :h_acct + FROM py_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_ol_0007"); tx_return(ctx, TX_EDB, ctx->out); return; } + + switch (h_status[0]) { + case 'R': label = "지급대기"; break; + case 'M': label = "지급확정"; break; + case 'U': label = "보류/취소"; break; + case 'S': label = "이체완료"; break; + default: label = "미상"; break; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0007] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0007] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = py_rec_count(bizdate, PY_ST_RECV); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0007] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "STATUSLBL", label); + tx_buf_setlong(ctx->out, "PAYABLE", h_amount - h_fee); + tx_log(TX_LOG_INFO, "[PY_OL_0007] 상태 key=%s status=%s payable=%ld", + key, h_status, h_amount - h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0008.c b/app/online/py_ol_0008.c index 2b307de..3fbe11e 100644 --- a/app/online/py_ol_0008.c +++ b/app/online/py_ol_0008.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0008.pgc" -/* @tier easy @module py @transform payment-online */ +/* @tier hard @module py @transform payment-online */ /* - * py_ol_0008.pgc - 지급 온라인 서비스 (PY_OL_0008) [tier=easy] + * py_ol_0008.pgc - 지급 분할(회차별 분할지급 INSERT) (PY_OL_0008) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,90 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0008.pgc" +#line 16 "app/online/py_ol_0008.pgc" TX_SERVICE(PY_OL_0008, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0008] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0008] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + + +#line 27 "app/online/py_ol_0008.pgc" + char h_key [ 16 ] , h_merch [ 16 ] , h_bdate [ 9 ] , h_split [ 16 ] ; + +#line 28 "app/online/py_ol_0008.pgc" + long h_amount , h_per ; + +#line 29 "app/online/py_ol_0008.pgc" + int h_seq ; +/* exec sql end declare section */ +#line 30 "app/online/py_ol_0008.pgc" - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0008] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + + long total = 0; + int parts = 0, i; + + tx_log(TX_LOG_INFO, "[PY_OL_0008] 지급 분할 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + { long v = 0; tx_buf_getlong(ctx->in, "PARTS", &v); parts = (int)v; } + if (parts < 2 || parts > 12) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select merch_id , amount , biz_date from py_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 43 "app/online/py_ol_0008.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + total = h_amount; + h_per = total / parts; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0008] tx_begin 경고"); + for (i = 1; i <= parts; i++) { + h_seq = i; + snprintf(h_split, sizeof(h_split), "%.10s-%02d", h_key, i); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into py_ledger ( key , merch_id , card_no , amount , biz_date , status , fee ) values ( $1 , $2 , 'SPLIT' , $3 , $4 , 'R' , 0 )", + ECPGt_char,(h_split),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_per),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 53 "app/online/py_ol_0008.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0008 분할INSERT"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'S' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 59 "app/online/py_ol_0008.pgc" + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_setlong(ctx->out, "PARTS", parts); + tx_buf_setlong(ctx->out, "PERAMT", h_per); + tx_log(TX_LOG_INFO, "[PY_OL_0008] 분할지급 %d회차 회당=%ld", parts, h_per); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0008.pgc b/app/online/py_ol_0008.pgc index 78dfd15..6956ac1 100644 --- a/app/online/py_ol_0008.pgc +++ b/app/online/py_ol_0008.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module py @transform payment-online */ +/* @tier hard @module py @transform payment-online */ /* - * py_ol_0008.pgc - 지급 온라인 서비스 (PY_OL_0008) [tier=easy] + * py_ol_0008.pgc - 지급 분할(회차별 분할지급 INSERT) (PY_OL_0008) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,52 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0008, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0008] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0008] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_merch[16], h_bdate[9], h_split[16]; + long h_amount, h_per; + int h_seq; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0008] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + long total = 0; + int parts = 0, i; + + tx_log(TX_LOG_INFO, "[PY_OL_0008] 지급 분할 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + { long v = 0; tx_buf_getlong(ctx->in, "PARTS", &v); parts = (int)v; } + if (parts < 2 || parts > 12) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL SELECT merch_id, amount, biz_date INTO :h_merch, :h_amount, :h_bdate + FROM py_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + total = h_amount; + h_per = total / parts; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0008] tx_begin 경고"); + for (i = 1; i <= parts; i++) { + h_seq = i; + snprintf(h_split, sizeof(h_split), "%.10s-%02d", h_key, i); + EXEC SQL INSERT INTO py_ledger (key, merch_id, card_no, amount, biz_date, status, fee) + VALUES (:h_split, :h_merch, 'SPLIT', :h_per, :h_bdate, 'R', 0); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0008 분할INSERT"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + } + EXEC SQL UPDATE py_ledger SET status = 'S', upd_ts = now() WHERE key = :h_key; + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_setlong(ctx->out, "PARTS", parts); + tx_buf_setlong(ctx->out, "PERAMT", h_per); + tx_log(TX_LOG_INFO, "[PY_OL_0008] 분할지급 %d회차 회당=%ld", parts, h_per); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0009.c b/app/online/py_ol_0009.c index e9a7d45..bae1020 100644 --- a/app/online/py_ol_0009.c +++ b/app/online/py_ol_0009.c @@ -8,15 +8,16 @@ #line 1 "app/online/py_ol_0009.pgc" /* @tier medium @module py @transform payment-online */ /* - * py_ol_0009.pgc - 지급 온라인 서비스 (PY_OL_0009) [tier=medium] + * py_ol_0009.pgc - 지급 이자계산(지연일수 이자 산출) (PY_OL_0009) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,60 +90,71 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0009.pgc" +#line 16 "app/online/py_ol_0009.pgc" TX_SERVICE(PY_OL_0009, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0009] 지급 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0009] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0009] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + + +#line 27 "app/online/py_ol_0009.pgc" + char h_key [ 16 ] , h_bdate [ 9 ] ; + +#line 28 "app/online/py_ol_0009.pgc" + long h_amount , h_interest ; +/* exec sql end declare section */ +#line 29 "app/online/py_ol_0009.pgc" - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0009] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0009] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + + char today[16]; + long days = 0; + + tx_log(TX_LOG_INFO, "[PY_OL_0009] 지급 이자계산 진입"); + tx_buf_getlong(ctx->in, "DELAYDAYS", &days); + if (days < 0) days = 0; + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , biz_date from py_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 40 "app/online/py_ol_0009.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + + /* 연 7.3% -> 일 0.02% (2 basis point) */ + h_interest = h_amount * 2 * days / 10000; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0009] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set fee = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_long,&(h_interest),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 47 "app/online/py_ol_0009.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0009 이자"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } + tx_commit(); - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0009] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = py_rec_count(bizdate, PY_ST_RECV); - + date_today(today); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0009] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "PRINCIPAL", h_amount); + tx_buf_setlong(ctx->out, "DELAYDAYS", days); + tx_buf_setlong(ctx->out, "INTEREST", h_interest); + tx_log(TX_LOG_INFO, "[PY_OL_0009] 이자 key=%s days=%ld interest=%ld", key, days, h_interest); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0009.pgc b/app/online/py_ol_0009.pgc index 9903001..b3a3895 100644 --- a/app/online/py_ol_0009.pgc +++ b/app/online/py_ol_0009.pgc @@ -1,14 +1,15 @@ /* @tier medium @module py @transform payment-online */ /* - * py_ol_0009.pgc - 지급 온라인 서비스 (PY_OL_0009) [tier=medium] + * py_ol_0009.pgc - 지급 이자계산(지연일수 이자 산출) (PY_OL_0009) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,55 +17,45 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0009, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0009] 지급 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0009] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0009] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_bdate[9]; + long h_amount, h_interest; + EXEC SQL END DECLARE SECTION; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0009] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0009] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + char today[16]; + long days = 0; + + tx_log(TX_LOG_INFO, "[PY_OL_0009] 지급 이자계산 진입"); + tx_buf_getlong(ctx->in, "DELAYDAYS", &days); + if (days < 0) days = 0; + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT amount, biz_date INTO :h_amount, :h_bdate + FROM py_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + + /* 연 7.3% -> 일 0.02% (2 basis point) */ + h_interest = h_amount * 2 * days / 10000; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0009] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET fee = :h_interest, upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0009 이자"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } + tx_commit(); - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0009] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = py_rec_count(bizdate, PY_ST_RECV); - + date_today(today); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0009] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "PRINCIPAL", h_amount); + tx_buf_setlong(ctx->out, "DELAYDAYS", days); + tx_buf_setlong(ctx->out, "INTEREST", h_interest); + tx_log(TX_LOG_INFO, "[PY_OL_0009] 이자 key=%s days=%ld interest=%ld", key, days, h_interest); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0010.c b/app/online/py_ol_0010.c index 38bd7f1..3811a03 100644 --- a/app/online/py_ol_0010.c +++ b/app/online/py_ol_0010.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0010.pgc" -/* @tier easy @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0010.pgc - 지급 온라인 서비스 (PY_OL_0010) [tier=easy] + * py_ol_0010.pgc - 지급 스케줄 등록(예약 지급일 지정) (PY_OL_0010) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,54 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0010.pgc" +#line 16 "app/online/py_ol_0010.pgc" TX_SERVICE(PY_OL_0010, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0010] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0010] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + +#line 27 "app/online/py_ol_0010.pgc" + char h_key [ 16 ] , h_sched [ 9 ] ; +/* exec sql end declare section */ +#line 28 "app/online/py_ol_0010.pgc" - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + + char sched[16]; + + tx_log(TX_LOG_INFO, "[PY_OL_0010] 지급 스케줄 등록 진입"); + if (tx_buf_get(ctx->in, "SCHEDDATE", sched, sizeof(sched)) != TX_OK || + !date_is_valid(sched)) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_key, key, 15); h_key[15] = '\0'; + strncpy(h_sched, sched, 8); h_sched[8] = '\0'; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0010] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set biz_date = $1 , status = 'R' , upd_ts = now ( ) where key = $2 and status in ( 'R' , 'U' )", + ECPGt_char,(h_sched),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 43 "app/online/py_ol_0010.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0010 스케줄"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + if (sqlca.sqlerrd[2] == 0) { tx_abort(); tx_return(ctx, TX_ENOENT, ctx->out); return; } + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "SCHEDDATE", sched); + tx_log(TX_LOG_INFO, "[PY_OL_0010] 스케줄 등록 key=%s date=%s", key, sched); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0010.pgc b/app/online/py_ol_0010.pgc index 618b10d..4f59629 100644 --- a/app/online/py_ol_0010.pgc +++ b/app/online/py_ol_0010.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0010.pgc - 지급 온라인 서비스 (PY_OL_0010) [tier=easy] + * py_ol_0010.pgc - 지급 스케줄 등록(예약 지급일 지정) (PY_OL_0010) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,40 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0010, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0010] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0010] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_sched[9]; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + char sched[16]; + + tx_log(TX_LOG_INFO, "[PY_OL_0010] 지급 스케줄 등록 진입"); + if (tx_buf_get(ctx->in, "SCHEDDATE", sched, sizeof(sched)) != TX_OK || + !date_is_valid(sched)) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_key, key, 15); h_key[15] = '\0'; + strncpy(h_sched, sched, 8); h_sched[8] = '\0'; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0010] tx_begin 경고"); + EXEC SQL UPDATE py_ledger + SET biz_date = :h_sched, status = 'R', upd_ts = now() + WHERE key = :h_key AND status IN ('R','U'); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0010 스케줄"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + if (sqlca.sqlerrd[2] == 0) { tx_abort(); tx_return(ctx, TX_ENOENT, ctx->out); return; } + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "SCHEDDATE", sched); + tx_log(TX_LOG_INFO, "[PY_OL_0010] 스케줄 등록 key=%s date=%s", key, sched); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0011.c b/app/online/py_ol_0011.c index 0c6e84a..a08d93a 100644 --- a/app/online/py_ol_0011.c +++ b/app/online/py_ol_0011.c @@ -8,15 +8,16 @@ #line 1 "app/online/py_ol_0011.pgc" /* @tier medium @module py @transform payment-online */ /* - * py_ol_0011.pgc - 지급 온라인 서비스 (PY_OL_0011) [tier=medium] + * py_ol_0011.pgc - 가맹점 지급집계 조회(당일 지급총액) (PY_OL_0011) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,60 +90,62 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0011.pgc" +#line 16 "app/online/py_ol_0011.pgc" TX_SERVICE(PY_OL_0011, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + +#line 21 "app/online/py_ol_0011.pgc" + char h_merch [ 16 ] , h_bdate [ 9 ] ; + +#line 22 "app/online/py_ol_0011.pgc" + int h_cnt ; + +#line 23 "app/online/py_ol_0011.pgc" + long h_sum , h_fee ; +/* exec sql end declare section */ +#line 24 "app/online/py_ol_0011.pgc" - tx_log(TX_LOG_INFO, "[PY_OL_0011] 지급 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0011] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + char merch[16], bdate[16], won[32]; + + tx_log(TX_LOG_INFO, "[PY_OL_0011] 가맹점 지급집계 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0011] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0011] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from py_ledger where merch_id = $1 and biz_date = $2 and status in ( 'M' , 'S' )", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 41 "app/online/py_ol_0011.pgc" - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0011] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = py_rec_count(bizdate, PY_ST_RECV); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_ol_0011"); tx_return(ctx, TX_EDB, ctx->out); return; } + amount_format_won(h_sum - h_fee, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0011] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_setlong(ctx->out, "GROSS", h_sum); + tx_buf_setlong(ctx->out, "NET", h_sum - h_fee); + tx_buf_sets(ctx->out, "NETWON", won); + tx_log(TX_LOG_INFO, "[PY_OL_0011] 지급집계 merch=%s net=%ld", merch, h_sum - h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0011.pgc b/app/online/py_ol_0011.pgc index abfcfdf..8bcf73c 100644 --- a/app/online/py_ol_0011.pgc +++ b/app/online/py_ol_0011.pgc @@ -1,14 +1,15 @@ /* @tier medium @module py @transform payment-online */ /* - * py_ol_0011.pgc - 지급 온라인 서비스 (PY_OL_0011) [tier=medium] + * py_ol_0011.pgc - 가맹점 지급집계 조회(당일 지급총액) (PY_OL_0011) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,55 +17,37 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0011, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16], h_bdate[9]; + int h_cnt; + long h_sum, h_fee; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[PY_OL_0011] 지급 등록 서비스 진입"); + char merch[16], bdate[16], won[32]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0011] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + tx_log(TX_LOG_INFO, "[PY_OL_0011] 가맹점 지급집계 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0011] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0011] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0011] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = py_rec_count(bizdate, PY_ST_RECV); + EXEC SQL SELECT count(*), coalesce(sum(amount), 0), coalesce(sum(fee), 0) + INTO :h_cnt, :h_sum, :h_fee + FROM py_ledger + WHERE merch_id = :h_merch AND biz_date = :h_bdate AND status IN ('M','S'); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_ol_0011"); tx_return(ctx, TX_EDB, ctx->out); return; } + amount_format_won(h_sum - h_fee, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0011] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "CNT", h_cnt); + tx_buf_setlong(ctx->out, "GROSS", h_sum); + tx_buf_setlong(ctx->out, "NET", h_sum - h_fee); + tx_buf_sets(ctx->out, "NETWON", won); + tx_log(TX_LOG_INFO, "[PY_OL_0011] 지급집계 merch=%s net=%ld", merch, h_sum - h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0012.c b/app/online/py_ol_0012.c index 887bf83..01b0d38 100644 --- a/app/online/py_ol_0012.c +++ b/app/online/py_ol_0012.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0012.pgc" -/* @tier easy @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0012.pgc - 지급 온라인 서비스 (PY_OL_0012) [tier=easy] + * py_ol_0012.pgc - 지급 통계조회(상태별 건수 집계) (PY_OL_0012) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,53 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0012.pgc" +#line 16 "app/online/py_ol_0012.pgc" TX_SERVICE(PY_OL_0012, ctx) { - py_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + +#line 21 "app/online/py_ol_0012.pgc" + char h_bdate [ 9 ] , h_st [ 2 ] ; + +#line 22 "app/online/py_ol_0012.pgc" + int h_cnt ; +/* exec sql end declare section */ +#line 23 "app/online/py_ol_0012.pgc" - tx_log(TX_LOG_INFO, "[PY_OL_0012] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0012] KEY 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } + char bdate[16]; + const char *codes = "RMUS"; + int i, total = 0; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + tx_log(TX_LOG_INFO, "[PY_OL_0012] 지급 통계조회 진입"); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + for (i = 0; codes[i]; i++) { + char field[8] = "CNT_"; + h_st[0] = codes[i]; h_st[1] = '\0'; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from py_ledger where biz_date = $1 and status = $2 ", + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_st),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 39 "app/online/py_ol_0012.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_ol_0012"); tx_return(ctx, TX_EDB, ctx->out); return; } + field[4] = codes[i]; field[5] = '\0'; + tx_buf_setlong(ctx->out, field, h_cnt); + total += h_cnt; + } + tx_buf_sets(ctx->out, "BIZDATE", bdate); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_log(TX_LOG_INFO, "[PY_OL_0012] 지급통계 date=%s total=%d", bdate, total); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0012.pgc b/app/online/py_ol_0012.pgc index f3ed38b..2d2bb94 100644 --- a/app/online/py_ol_0012.pgc +++ b/app/online/py_ol_0012.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0012.pgc - 지급 온라인 서비스 (PY_OL_0012) [tier=easy] + * py_ol_0012.pgc - 지급 통계조회(상태별 건수 집계) (PY_OL_0012) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,33 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0012, ctx) { - py_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_bdate[9], h_st[2]; + int h_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[PY_OL_0012] 지급 단건조회 서비스 진입"); + char bdate[16]; + const char *codes = "RMUS"; + int i, total = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0012] KEY 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + tx_log(TX_LOG_INFO, "[PY_OL_0012] 지급 통계조회 진입"); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + for (i = 0; codes[i]; i++) { + char field[8] = "CNT_"; + h_st[0] = codes[i]; h_st[1] = '\0'; + EXEC SQL SELECT count(*) INTO :h_cnt + FROM py_ledger WHERE biz_date = :h_bdate AND status = :h_st; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_ol_0012"); tx_return(ctx, TX_EDB, ctx->out); return; } + field[4] = codes[i]; field[5] = '\0'; + tx_buf_setlong(ctx->out, field, h_cnt); + total += h_cnt; + } + tx_buf_sets(ctx->out, "BIZDATE", bdate); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_log(TX_LOG_INFO, "[PY_OL_0012] 지급통계 date=%s total=%d", bdate, total); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0013.c b/app/online/py_ol_0013.c index 375ac60..9f1ce9e 100644 --- a/app/online/py_ol_0013.c +++ b/app/online/py_ol_0013.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0013.pgc" -/* @tier hard @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0013.pgc - 지급 온라인 서비스 (PY_OL_0013) [tier=hard] + * py_ol_0013.pgc - 지급 실패 재처리(반려건 재지급 대기) (PY_OL_0013) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,88 +90,63 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0013.pgc" +#line 16 "app/online/py_ol_0013.pgc" TX_SERVICE(PY_OL_0013, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; - - tx_log(TX_LOG_INFO, "[PY_OL_0013] 지급 트랜잭션 오케스트레이션 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0013] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0013] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + + +#line 27 "app/online/py_ol_0013.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; + +#line 28 "app/online/py_ol_0013.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 29 "app/online/py_ol_0013.pgc" - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0013] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + + tx_log(TX_LOG_INFO, "[PY_OL_0013] 지급 실패 재처리 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , status from py_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 35 "app/online/py_ol_0013.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (h_status[0] != 'U') { + tx_log(TX_LOG_WARN, "[PY_OL_0013] 재처리 대상아님 status=%s", h_status); + tx_return(ctx, TX_FAIL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0013] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'R' , fee = 0 , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 43 "app/online/py_ol_0013.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0013 재처리"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0013] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("PY_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[PY_OL_0013] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (py_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[PY_OL_0013] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_log(TX_LOG_INFO, "[PY_OL_0013] 재처리 대기전환 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0013.pgc b/app/online/py_ol_0013.pgc index af48e4a..99802d0 100644 --- a/app/online/py_ol_0013.pgc +++ b/app/online/py_ol_0013.pgc @@ -1,14 +1,15 @@ -/* @tier hard @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0013.pgc - 지급 온라인 서비스 (PY_OL_0013) [tier=hard] + * py_ol_0013.pgc - 지급 실패 재처리(반려건 재지급 대기) (PY_OL_0013) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,83 +17,39 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0013, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; - - tx_log(TX_LOG_INFO, "[PY_OL_0013] 지급 트랜잭션 오케스트레이션 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0013] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0013] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0013] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + tx_log(TX_LOG_INFO, "[PY_OL_0013] 지급 실패 재처리 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT amount, status INTO :h_amount, :h_status + FROM py_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (h_status[0] != 'U') { + tx_log(TX_LOG_WARN, "[PY_OL_0013] 재처리 대상아님 status=%s", h_status); + tx_return(ctx, TX_FAIL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0013] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET status = 'R', fee = 0, upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0013 재처리"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0013] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("PY_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[PY_OL_0013] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (py_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[PY_OL_0013] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_log(TX_LOG_INFO, "[PY_OL_0013] 재처리 대기전환 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0014.c b/app/online/py_ol_0014.c index a0e09d8..3201bd1 100644 --- a/app/online/py_ol_0014.c +++ b/app/online/py_ol_0014.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0014.pgc" -/* @tier easy @module py @transform payment-online */ +/* @tier hard @module py @transform payment-online */ /* - * py_ol_0014.pgc - 지급 온라인 서비스 (PY_OL_0014) [tier=easy] + * py_ol_0014.pgc - 지급 이체연계(tx_call 이체 후 확정) (PY_OL_0014) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,77 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0014.pgc" +#line 16 "app/online/py_ol_0014.pgc" TX_SERVICE(PY_OL_0014, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0014] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0014] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/py_ol_0014.pgc" + char h_key [ 16 ] , h_acct [ 20 ] ; + +#line 28 "app/online/py_ol_0014.pgc" + long h_amount , h_fee ; +/* exec sql end declare section */ +#line 29 "app/online/py_ol_0014.pgc" - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + + TXBUF *req; + char tresp[8]; + int rc; + + tx_log(TX_LOG_INFO, "[PY_OL_0014] 지급 이체연계 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select card_no , amount , fee from py_ledger where key = $1 and status = 'M'", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_acct),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 39 "app/online/py_ol_0014.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[PY_OL_0014] 확정 지급건 아님 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + req = tx_buf_alloc(); + if (!req) { tx_return(ctx, TX_ENOMEM, ctx->out); return; } + tx_buf_sets(req, "ACCTNO", h_acct); + tx_buf_setlong(req, "AMOUNT", h_amount - h_fee); + rc = tx_call("PY_TRANSFER", req, ctx->out); + tx_buf_free(req); + + if (rc != TX_OK || + (tx_buf_get(ctx->out, "RESPCODE", tresp, sizeof(tresp)) == TX_OK && + strcmp(tresp, RESP_OK) != 0)) { + tx_log(TX_LOG_ERROR, "[PY_OL_0014] 이체 실패 key=%s", key); + tx_return(ctx, TX_FAIL, ctx->out); return; + } + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0014] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'S' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 60 "app/online/py_ol_0014.pgc" + + tx_commit(); + + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_setlong(ctx->out, "TRANSFERRED", h_amount - h_fee); + tx_log(TX_LOG_INFO, "[PY_OL_0014] 이체완료 key=%s net=%ld", key, h_amount - h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0014.pgc b/app/online/py_ol_0014.pgc index 497c22d..1699c76 100644 --- a/app/online/py_ol_0014.pgc +++ b/app/online/py_ol_0014.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module py @transform payment-online */ +/* @tier hard @module py @transform payment-online */ /* - * py_ol_0014.pgc - 지급 온라인 서비스 (PY_OL_0014) [tier=easy] + * py_ol_0014.pgc - 지급 이체연계(tx_call 이체 후 확정) (PY_OL_0014) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,51 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0014, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0014] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0014] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_acct[20]; + long h_amount, h_fee; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + TXBUF *req; + char tresp[8]; + int rc; + + tx_log(TX_LOG_INFO, "[PY_OL_0014] 지급 이체연계 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT card_no, amount, fee INTO :h_acct, :h_amount, :h_fee + FROM py_ledger WHERE key = :h_key AND status = 'M'; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[PY_OL_0014] 확정 지급건 아님 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + req = tx_buf_alloc(); + if (!req) { tx_return(ctx, TX_ENOMEM, ctx->out); return; } + tx_buf_sets(req, "ACCTNO", h_acct); + tx_buf_setlong(req, "AMOUNT", h_amount - h_fee); + rc = tx_call("PY_TRANSFER", req, ctx->out); + tx_buf_free(req); + + if (rc != TX_OK || + (tx_buf_get(ctx->out, "RESPCODE", tresp, sizeof(tresp)) == TX_OK && + strcmp(tresp, RESP_OK) != 0)) { + tx_log(TX_LOG_ERROR, "[PY_OL_0014] 이체 실패 key=%s", key); + tx_return(ctx, TX_FAIL, ctx->out); return; + } + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0014] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET status = 'S', upd_ts = now() WHERE key = :h_key; + tx_commit(); + + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_setlong(ctx->out, "TRANSFERRED", h_amount - h_fee); + tx_log(TX_LOG_INFO, "[PY_OL_0014] 이체완료 key=%s net=%ld", key, h_amount - h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0015.c b/app/online/py_ol_0015.c index e40fa88..1b27a12 100644 --- a/app/online/py_ol_0015.c +++ b/app/online/py_ol_0015.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0015.pgc" -/* @tier easy @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0015.pgc - 지급 온라인 서비스 (PY_OL_0015) [tier=easy] + * py_ol_0015.pgc - 지급 금액변경(확정 전 금액 조정) (PY_OL_0015) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,74 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0015.pgc" +#line 16 "app/online/py_ol_0015.pgc" TX_SERVICE(PY_OL_0015, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0015] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0015] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/py_ol_0015.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; + +#line 28 "app/online/py_ol_0015.pgc" + long h_new , h_old , h_fee ; +/* exec sql end declare section */ +#line 29 "app/online/py_ol_0015.pgc" - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0015] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + + long newamt = 0; + + tx_log(TX_LOG_INFO, "[PY_OL_0015] 지급 금액변경 진입"); + tx_buf_getlong(ctx->in, "NEWAMT", &newamt); + if (!amount_is_valid(newamt)) { tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_new = newamt; + h_fee = newamt * 15 / 10000; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , status from py_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_old),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 41 "app/online/py_ol_0015.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (h_status[0] == 'S') { + tx_log(TX_LOG_WARN, "[PY_OL_0015] 이체완료건 변경불가 key=%s", key); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0015] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set amount = $1 , fee = $2 , upd_ts = now ( ) where key = $3 ", + ECPGt_long,&(h_new),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 50 "app/online/py_ol_0015.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0015 변경"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0015] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "OLDAMT", h_old); + tx_buf_setlong(ctx->out, "NEWAMT", h_new); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_log(TX_LOG_INFO, "[PY_OL_0015] 금액변경 %ld -> %ld", h_old, h_new); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0015.pgc b/app/online/py_ol_0015.pgc index 6b8bd86..ab59cbd 100644 --- a/app/online/py_ol_0015.pgc +++ b/app/online/py_ol_0015.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0015.pgc - 지급 온라인 서비스 (PY_OL_0015) [tier=easy] + * py_ol_0015.pgc - 지급 금액변경(확정 전 금액 조정) (PY_OL_0015) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,47 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0015, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0015] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0015] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2]; + long h_new, h_old, h_fee; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0015] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + long newamt = 0; + + tx_log(TX_LOG_INFO, "[PY_OL_0015] 지급 금액변경 진입"); + tx_buf_getlong(ctx->in, "NEWAMT", &newamt); + if (!amount_is_valid(newamt)) { tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_new = newamt; + h_fee = newamt * 15 / 10000; + + EXEC SQL SELECT amount, status INTO :h_old, :h_status + FROM py_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (h_status[0] == 'S') { + tx_log(TX_LOG_WARN, "[PY_OL_0015] 이체완료건 변경불가 key=%s", key); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0015] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET amount = :h_new, fee = :h_fee, upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0015 변경"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0015] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "OLDAMT", h_old); + tx_buf_setlong(ctx->out, "NEWAMT", h_new); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_log(TX_LOG_INFO, "[PY_OL_0015] 금액변경 %ld -> %ld", h_old, h_new); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0016.c b/app/online/py_ol_0016.c index ac5d91d..f0b9bf4 100644 --- a/app/online/py_ol_0016.c +++ b/app/online/py_ol_0016.c @@ -8,15 +8,16 @@ #line 1 "app/online/py_ol_0016.pgc" /* @tier medium @module py @transform payment-online */ /* - * py_ol_0016.pgc - 지급 온라인 서비스 (PY_OL_0016) [tier=medium] + * py_ol_0016.pgc - 지급 수수료 계산(등급별 요율 UPDATE) (PY_OL_0016) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,60 +90,70 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0016.pgc" +#line 16 "app/online/py_ol_0016.pgc" TX_SERVICE(PY_OL_0016, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0016] 지급 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0016] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0016] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + + +#line 27 "app/online/py_ol_0016.pgc" + char h_key [ 16 ] , h_grade [ 2 ] ; + +#line 28 "app/online/py_ol_0016.pgc" + long h_amount , h_fee ; +/* exec sql end declare section */ +#line 29 "app/online/py_ol_0016.pgc" - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0016] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + + char grade[4]; + int rate; + + tx_log(TX_LOG_INFO, "[PY_OL_0016] 지급 수수료 계산 진입"); + if (tx_buf_get(ctx->in, "GRADE", grade, sizeof(grade)) != TX_OK) + strcpy(grade, "C"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + strncpy(h_grade, grade, 1); h_grade[1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount from py_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 40 "app/online/py_ol_0016.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + + /* 가맹점 등급별 지급수수료율 (basis point) */ + rate = (h_grade[0] == 'A') ? 5 : (h_grade[0] == 'B') ? 10 : 20; + h_fee = h_amount * rate / 10000; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0016] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set fee = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 48 "app/online/py_ol_0016.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0016 수수료"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0016] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0016] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = py_rec_count(bizdate, PY_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0016] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "GRADE", grade); + tx_buf_setlong(ctx->out, "RATEBP", rate); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_log(TX_LOG_INFO, "[PY_OL_0016] 수수료 grade=%s fee=%ld", grade, h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0016.pgc b/app/online/py_ol_0016.pgc index c624482..209d465 100644 --- a/app/online/py_ol_0016.pgc +++ b/app/online/py_ol_0016.pgc @@ -1,14 +1,15 @@ /* @tier medium @module py @transform payment-online */ /* - * py_ol_0016.pgc - 지급 온라인 서비스 (PY_OL_0016) [tier=medium] + * py_ol_0016.pgc - 지급 수수료 계산(등급별 요율 UPDATE) (PY_OL_0016) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,55 +17,45 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0016, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0016] 지급 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0016] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0016] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_grade[2]; + long h_amount, h_fee; + EXEC SQL END DECLARE SECTION; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0016] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + char grade[4]; + int rate; + + tx_log(TX_LOG_INFO, "[PY_OL_0016] 지급 수수료 계산 진입"); + if (tx_buf_get(ctx->in, "GRADE", grade, sizeof(grade)) != TX_OK) + strcpy(grade, "C"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + strncpy(h_grade, grade, 1); h_grade[1] = '\0'; + + EXEC SQL SELECT amount INTO :h_amount FROM py_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + + /* 가맹점 등급별 지급수수료율 (basis point) */ + rate = (h_grade[0] == 'A') ? 5 : (h_grade[0] == 'B') ? 10 : 20; + h_fee = h_amount * rate / 10000; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0016] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET fee = :h_fee, upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0016 수수료"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0016] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0016] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = py_rec_count(bizdate, PY_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0016] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "GRADE", grade); + tx_buf_setlong(ctx->out, "RATEBP", rate); + tx_buf_setlong(ctx->out, "FEE", h_fee); + tx_log(TX_LOG_INFO, "[PY_OL_0016] 수수료 grade=%s fee=%ld", grade, h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0017.c b/app/online/py_ol_0017.c index 721b9c2..ebfa8f5 100644 --- a/app/online/py_ol_0017.c +++ b/app/online/py_ol_0017.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0017.pgc" -/* @tier easy @module py @transform payment-online */ +/* @tier hard @module py @transform payment-online */ /* - * py_ol_0017.pgc - 지급 온라인 서비스 (PY_OL_0017) [tier=easy] + * py_ol_0017.pgc - 지급 재시도(이체실패건 재이체 tx_call) (PY_OL_0017) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,69 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0017.pgc" +#line 16 "app/online/py_ol_0017.pgc" TX_SERVICE(PY_OL_0017, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0017] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0017] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/py_ol_0017.pgc" + char h_key [ 16 ] , h_acct [ 20 ] ; + +#line 28 "app/online/py_ol_0017.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 29 "app/online/py_ol_0017.pgc" - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0017] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + + TXBUF *req; + int rc, attempt; + + tx_log(TX_LOG_INFO, "[PY_OL_0017] 지급 재시도 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select card_no , amount from py_ledger where key = $1 and status = 'U'", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_acct),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 38 "app/online/py_ol_0017.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + + req = tx_buf_alloc(); + if (!req) { tx_return(ctx, TX_ENOMEM, ctx->out); return; } + tx_buf_sets(req, "ACCTNO", h_acct); + tx_buf_setlong(req, "AMOUNT", h_amount); + + rc = TX_FAIL; + for (attempt = 1; attempt <= 3; attempt++) { + tx_buf_reset(ctx->out); + rc = tx_call("PY_TRANSFER", req, ctx->out); + tx_log(TX_LOG_INFO, "[PY_OL_0017] 재이체 %d회 rc=%d", attempt, rc); + if (rc == TX_OK) break; } + tx_buf_free(req); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0017] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + if (rc == TX_OK) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'S' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 56 "app/online/py_ol_0017.pgc" + + tx_commit(); + } + tx_buf_setlong(ctx->out, "ATTEMPTS", attempt); + tx_return(ctx, rc, ctx->out); } diff --git a/app/online/py_ol_0017.pgc b/app/online/py_ol_0017.pgc index 0bedae4..172dad1 100644 --- a/app/online/py_ol_0017.pgc +++ b/app/online/py_ol_0017.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module py @transform payment-online */ +/* @tier hard @module py @transform payment-online */ /* - * py_ol_0017.pgc - 지급 온라인 서비스 (PY_OL_0017) [tier=easy] + * py_ol_0017.pgc - 지급 재시도(이체실패건 재이체 tx_call) (PY_OL_0017) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,45 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0017, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0017] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0017] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_acct[20]; + long h_amount; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0017] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + TXBUF *req; + int rc, attempt; + + tx_log(TX_LOG_INFO, "[PY_OL_0017] 지급 재시도 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT card_no, amount INTO :h_acct, :h_amount + FROM py_ledger WHERE key = :h_key AND status = 'U'; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + + req = tx_buf_alloc(); + if (!req) { tx_return(ctx, TX_ENOMEM, ctx->out); return; } + tx_buf_sets(req, "ACCTNO", h_acct); + tx_buf_setlong(req, "AMOUNT", h_amount); + + rc = TX_FAIL; + for (attempt = 1; attempt <= 3; attempt++) { + tx_buf_reset(ctx->out); + rc = tx_call("PY_TRANSFER", req, ctx->out); + tx_log(TX_LOG_INFO, "[PY_OL_0017] 재이체 %d회 rc=%d", attempt, rc); + if (rc == TX_OK) break; } + tx_buf_free(req); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0017] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + if (rc == TX_OK) { + EXEC SQL UPDATE py_ledger SET status = 'S', upd_ts = now() WHERE key = :h_key; + tx_commit(); + } + tx_buf_setlong(ctx->out, "ATTEMPTS", attempt); + tx_return(ctx, rc, ctx->out); } diff --git a/app/online/py_ol_0018.c b/app/online/py_ol_0018.c index da06df7..ee0e687 100644 --- a/app/online/py_ol_0018.c +++ b/app/online/py_ol_0018.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0018.pgc" -/* @tier medium @module py @transform payment-online */ +/* @tier easy @module py @transform payment-online */ /* - * py_ol_0018.pgc - 지급 온라인 서비스 (PY_OL_0018) [tier=medium] + * py_ol_0018.pgc - 지급 확정(지급대기 -> 지급확정) (PY_OL_0018) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,60 +90,62 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0018.pgc" +#line 16 "app/online/py_ol_0018.pgc" TX_SERVICE(PY_OL_0018, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0018] 지급 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0018] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0018] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + + +#line 27 "app/online/py_ol_0018.pgc" + char h_key [ 16 ] ; + +#line 28 "app/online/py_ol_0018.pgc" + long h_amount , h_fee ; +/* exec sql end declare section */ +#line 29 "app/online/py_ol_0018.pgc" - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0018] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0018] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + + tx_log(TX_LOG_INFO, "[PY_OL_0018] 지급 확정 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , fee from py_ledger where key = $1 and status = 'R'", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 35 "app/online/py_ol_0018.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[PY_OL_0018] 대기건 아님 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0018] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'M' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 42 "app/online/py_ol_0018.pgc" - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0018] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0018 확정"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = py_rec_count(bizdate, PY_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0018] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "PAYABLE", h_amount - h_fee); + tx_log(TX_LOG_INFO, "[PY_OL_0018] 지급확정 key=%s payable=%ld", key, h_amount - h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0018.pgc b/app/online/py_ol_0018.pgc index d36e1b4..7d74a79 100644 --- a/app/online/py_ol_0018.pgc +++ b/app/online/py_ol_0018.pgc @@ -1,14 +1,15 @@ -/* @tier medium @module py @transform payment-online */ +/* @tier easy @module py @transform payment-online */ /* - * py_ol_0018.pgc - 지급 온라인 서비스 (PY_OL_0018) [tier=medium] + * py_ol_0018.pgc - 지급 확정(지급대기 -> 지급확정) (PY_OL_0018) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,55 +17,38 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0018, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0018] 지급 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0018] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0018] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_amount, h_fee; + EXEC SQL END DECLARE SECTION; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0018] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0018] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + tx_log(TX_LOG_INFO, "[PY_OL_0018] 지급 확정 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT amount, fee INTO :h_amount, :h_fee + FROM py_ledger WHERE key = :h_key AND status = 'R'; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[PY_OL_0018] 대기건 아님 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0018] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0018] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET status = 'M', upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0018 확정"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = py_rec_count(bizdate, PY_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0018] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "PAYABLE", h_amount - h_fee); + tx_log(TX_LOG_INFO, "[PY_OL_0018] 지급확정 key=%s payable=%ld", key, h_amount - h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0019.c b/app/online/py_ol_0019.c index e56d78a..e66bcaa 100644 --- a/app/online/py_ol_0019.c +++ b/app/online/py_ol_0019.c @@ -8,15 +8,16 @@ #line 1 "app/online/py_ol_0019.pgc" /* @tier easy @module py @transform payment-online */ /* - * py_ol_0019.pgc - 지급 온라인 서비스 (PY_OL_0019) [tier=easy] + * py_ol_0019.pgc - 지급 계좌조회(등록 계좌/가맹점 확인) (PY_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,53 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0019.pgc" +#line 16 "app/online/py_ol_0019.pgc" TX_SERVICE(PY_OL_0019, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0019] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0019] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + +#line 27 "app/online/py_ol_0019.pgc" + char h_key [ 16 ] , h_acct [ 20 ] , h_merch [ 16 ] ; +/* exec sql end declare section */ +#line 28 "app/online/py_ol_0019.pgc" - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + + char masked[20]; + int i; + + tx_log(TX_LOG_INFO, "[PY_OL_0019] 지급 계좌조회 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select card_no , merch_id from py_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_acct),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 37 "app/online/py_ol_0019.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + + /* 계좌 마스킹 (뒤 4자리만 노출) */ + strncpy(masked, h_acct, sizeof(masked) - 1); + masked[sizeof(masked) - 1] = '\0'; + for (i = 0; masked[i] && i < (int)strlen(masked) - 4; i++) + if (masked[i] != '-') masked[i] = '*'; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "MERCHID", h_merch); + tx_buf_sets(ctx->out, "ACCTMASK", masked); + tx_log(TX_LOG_INFO, "[PY_OL_0019] 계좌조회 key=%s merch=%s", key, h_merch); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0019.pgc b/app/online/py_ol_0019.pgc index 02adb69..ea9dbd9 100644 --- a/app/online/py_ol_0019.pgc +++ b/app/online/py_ol_0019.pgc @@ -1,14 +1,15 @@ /* @tier easy @module py @transform payment-online */ /* - * py_ol_0019.pgc - 지급 온라인 서비스 (PY_OL_0019) [tier=easy] + * py_ol_0019.pgc - 지급 계좌조회(등록 계좌/가맹점 확인) (PY_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,36 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0019, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0019] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0019] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_acct[20], h_merch[16]; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + char masked[20]; + int i; + + tx_log(TX_LOG_INFO, "[PY_OL_0019] 지급 계좌조회 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT card_no, merch_id INTO :h_acct, :h_merch + FROM py_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + + /* 계좌 마스킹 (뒤 4자리만 노출) */ + strncpy(masked, h_acct, sizeof(masked) - 1); + masked[sizeof(masked) - 1] = '\0'; + for (i = 0; masked[i] && i < (int)strlen(masked) - 4; i++) + if (masked[i] != '-') masked[i] = '*'; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "MERCHID", h_merch); + tx_buf_sets(ctx->out, "ACCTMASK", masked); + tx_log(TX_LOG_INFO, "[PY_OL_0019] 계좌조회 key=%s merch=%s", key, h_merch); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0020.c b/app/online/py_ol_0020.c index 5825ecf..a59a24a 100644 --- a/app/online/py_ol_0020.c +++ b/app/online/py_ol_0020.c @@ -8,15 +8,16 @@ #line 1 "app/online/py_ol_0020.pgc" /* @tier medium @module py @transform payment-online */ /* - * py_ol_0020.pgc - 지급 온라인 서비스 (PY_OL_0020) [tier=medium] + * py_ol_0020.pgc - 지급 한도확인(가맹점 일지급 한도) (PY_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,60 +90,59 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0020.pgc" +#line 16 "app/online/py_ol_0020.pgc" TX_SERVICE(PY_OL_0020, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + +#line 21 "app/online/py_ol_0020.pgc" + char h_merch [ 16 ] , h_bdate [ 9 ] ; + +#line 22 "app/online/py_ol_0020.pgc" + long h_paid , h_req ; +/* exec sql end declare section */ +#line 23 "app/online/py_ol_0020.pgc" - tx_log(TX_LOG_INFO, "[PY_OL_0020] 지급 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0020] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + char merch[16], bdate[16]; + long req = 0, limit_amt = 500000000L; + + tx_log(TX_LOG_INFO, "[PY_OL_0020] 지급 한도확인 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + tx_buf_getlong(ctx->in, "AMOUNT", &req); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0020] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0020] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + h_req = req; - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( sum ( amount ) , 0 ) from py_ledger where merch_id = $1 and biz_date = $2 and status in ( 'M' , 'S' )", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_paid),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 42 "app/online/py_ol_0020.pgc" - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0020] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = py_rec_count(bizdate, PY_ST_RECV); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_ol_0020"); tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0020] 등록완료 key=%s 당일건수=%ld", key, cnt); + if (h_paid + h_req > limit_amt) { + tx_buf_sets(ctx->out, "OVERLIMIT", "Y"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_log(TX_LOG_WARN, "[PY_OL_0020] 지급한도 초과 paid=%ld req=%ld", h_paid, h_req); + tx_return(ctx, TX_FAIL, ctx->out); return; + } + tx_buf_sets(ctx->out, "OVERLIMIT", "N"); + tx_buf_setlong(ctx->out, "AVAIL", limit_amt - h_paid); + tx_log(TX_LOG_INFO, "[PY_OL_0020] 지급한도 avail=%ld", limit_amt - h_paid); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0020.pgc b/app/online/py_ol_0020.pgc index f93f0a2..d4c6eec 100644 --- a/app/online/py_ol_0020.pgc +++ b/app/online/py_ol_0020.pgc @@ -1,14 +1,15 @@ /* @tier medium @module py @transform payment-online */ /* - * py_ol_0020.pgc - 지급 온라인 서비스 (PY_OL_0020) [tier=medium] + * py_ol_0020.pgc - 지급 한도확인(가맹점 일지급 한도) (PY_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,55 +17,40 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0020, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16], h_bdate[9]; + long h_paid, h_req; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[PY_OL_0020] 지급 등록 서비스 진입"); + char merch[16], bdate[16]; + long req = 0, limit_amt = 500000000L; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0020] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + tx_log(TX_LOG_INFO, "[PY_OL_0020] 지급 한도확인 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + tx_buf_getlong(ctx->in, "AMOUNT", &req); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0020] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0020] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; + h_req = req; - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0020] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = py_rec_count(bizdate, PY_ST_RECV); + EXEC SQL SELECT coalesce(sum(amount), 0) INTO :h_paid + FROM py_ledger + WHERE merch_id = :h_merch AND biz_date = :h_bdate AND status IN ('M','S'); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_ol_0020"); tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0020] 등록완료 key=%s 당일건수=%ld", key, cnt); + if (h_paid + h_req > limit_amt) { + tx_buf_sets(ctx->out, "OVERLIMIT", "Y"); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_log(TX_LOG_WARN, "[PY_OL_0020] 지급한도 초과 paid=%ld req=%ld", h_paid, h_req); + tx_return(ctx, TX_FAIL, ctx->out); return; + } + tx_buf_sets(ctx->out, "OVERLIMIT", "N"); + tx_buf_setlong(ctx->out, "AVAIL", limit_amt - h_paid); + tx_log(TX_LOG_INFO, "[PY_OL_0020] 지급한도 avail=%ld", limit_amt - h_paid); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0021.c b/app/online/py_ol_0021.c index 0599f7f..beaed5f 100644 --- a/app/online/py_ol_0021.c +++ b/app/online/py_ol_0021.c @@ -8,15 +8,16 @@ #line 1 "app/online/py_ol_0021.pgc" /* @tier easy @module py @transform payment-online */ /* - * py_ol_0021.pgc - 지급 온라인 서비스 (PY_OL_0021) [tier=easy] + * py_ol_0021.pgc - 지급 우선순위 변경(긴급지급 상향) (PY_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,57 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0021.pgc" +#line 16 "app/online/py_ol_0021.pgc" TX_SERVICE(PY_OL_0021, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0021] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0021] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/py_ol_0021.pgc" + char h_key [ 16 ] ; + +#line 28 "app/online/py_ol_0021.pgc" + long h_prio ; +/* exec sql end declare section */ +#line 29 "app/online/py_ol_0021.pgc" - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + + long prio = 0; + + tx_log(TX_LOG_INFO, "[PY_OL_0021] 지급 우선순위 변경 진입"); + tx_buf_getlong(ctx->in, "PRIORITY", &prio); + if (prio < 1 || prio > 9) prio = 5; + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_prio = prio; + + /* fee 컬럼을 우선순위 힌트로 겸용 (음수 표현 회피 위해 offset) */ + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0021] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set fee = $1 , upd_ts = now ( ) where key = $2 and status = 'R'", + ECPGt_long,&(h_prio),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 42 "app/online/py_ol_0021.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0021 우선순위"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (sqlca.sqlerrd[2] == 0) { tx_abort(); tx_return(ctx, TX_ENOENT, ctx->out); return; } + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "PRIORITY", h_prio); + tx_log(TX_LOG_INFO, "[PY_OL_0021] 우선순위 key=%s prio=%ld", key, h_prio); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0021.pgc b/app/online/py_ol_0021.pgc index 31f6289..9fc3906 100644 --- a/app/online/py_ol_0021.pgc +++ b/app/online/py_ol_0021.pgc @@ -1,14 +1,15 @@ /* @tier easy @module py @transform payment-online */ /* - * py_ol_0021.pgc - 지급 온라인 서비스 (PY_OL_0021) [tier=easy] + * py_ol_0021.pgc - 지급 우선순위 변경(긴급지급 상향) (PY_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,39 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0021, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0021] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0021] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_prio; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + long prio = 0; + + tx_log(TX_LOG_INFO, "[PY_OL_0021] 지급 우선순위 변경 진입"); + tx_buf_getlong(ctx->in, "PRIORITY", &prio); + if (prio < 1 || prio > 9) prio = 5; + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_prio = prio; + + /* fee 컬럼을 우선순위 힌트로 겸용 (음수 표현 회피 위해 offset) */ + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0021] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET fee = :h_prio, upd_ts = now() + WHERE key = :h_key AND status = 'R'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0021 우선순위"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } + if (sqlca.sqlerrd[2] == 0) { tx_abort(); tx_return(ctx, TX_ENOENT, ctx->out); return; } + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "PRIORITY", h_prio); + tx_log(TX_LOG_INFO, "[PY_OL_0021] 우선순위 key=%s prio=%ld", key, h_prio); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0022.c b/app/online/py_ol_0022.c index abeb334..63c8a49 100644 --- a/app/online/py_ol_0022.c +++ b/app/online/py_ol_0022.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0022.pgc" -/* @tier hard @module py @transform payment-online */ +/* @tier easy @module py @transform payment-online */ /* - * py_ol_0022.pgc - 지급 온라인 서비스 (PY_OL_0022) [tier=hard] + * py_ol_0022.pgc - 지급 반려(불가사유 등록 전이) (PY_OL_0022) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,88 +90,50 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0022.pgc" +#line 16 "app/online/py_ol_0022.pgc" TX_SERVICE(PY_OL_0022, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; - - tx_log(TX_LOG_INFO, "[PY_OL_0022] 지급 트랜잭션 오케스트레이션 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0022] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0022] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + +#line 27 "app/online/py_ol_0022.pgc" + char h_key [ 16 ] ; +/* exec sql end declare section */ +#line 28 "app/online/py_ol_0022.pgc" - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0022] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + + char reason[64], rcode[8]; + + tx_log(TX_LOG_INFO, "[PY_OL_0022] 지급 반려 진입"); + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) + strcpy(reason, "계좌오류"); + strcpy(rcode, RESP_INVALID); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0022] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'U' , upd_ts = now ( ) where key = $1 and status in ( 'R' , 'M' )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 40 "app/online/py_ol_0022.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0022 반려"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0022] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("PY_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[PY_OL_0022] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (py_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - + if (sqlca.sqlerrd[2] == 0) { tx_abort(); tx_return(ctx, TX_ENOENT, ctx->out); return; } tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[PY_OL_0022] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "RESPCODE", rcode); + tx_buf_sets(ctx->out, "REASON", reason); + tx_log(TX_LOG_INFO, "[PY_OL_0022] 반려 key=%s reason=%s", key, reason); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0022.pgc b/app/online/py_ol_0022.pgc index f9969de..0608344 100644 --- a/app/online/py_ol_0022.pgc +++ b/app/online/py_ol_0022.pgc @@ -1,14 +1,15 @@ -/* @tier hard @module py @transform payment-online */ +/* @tier easy @module py @transform payment-online */ /* - * py_ol_0022.pgc - 지급 온라인 서비스 (PY_OL_0022) [tier=hard] + * py_ol_0022.pgc - 지급 반려(불가사유 등록 전이) (PY_OL_0022) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,83 +17,37 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0022, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; - - tx_log(TX_LOG_INFO, "[PY_OL_0022] 지급 트랜잭션 오케스트레이션 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0022] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0022] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + EXEC SQL END DECLARE SECTION; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0022] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + char reason[64], rcode[8]; + + tx_log(TX_LOG_INFO, "[PY_OL_0022] 지급 반려 진입"); + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) + strcpy(reason, "계좌오류"); + strcpy(rcode, RESP_INVALID); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0022] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET status = 'U', upd_ts = now() + WHERE key = :h_key AND status IN ('R','M'); + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0022 반려"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0022] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("PY_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[PY_OL_0022] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (py_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - + if (sqlca.sqlerrd[2] == 0) { tx_abort(); tx_return(ctx, TX_ENOENT, ctx->out); return; } tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[PY_OL_0022] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "RESPCODE", rcode); + tx_buf_sets(ctx->out, "REASON", reason); + tx_log(TX_LOG_INFO, "[PY_OL_0022] 반려 key=%s reason=%s", key, reason); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0023.c b/app/online/py_ol_0023.c index fca9bb5..451a72b 100644 --- a/app/online/py_ol_0023.c +++ b/app/online/py_ol_0023.c @@ -8,15 +8,16 @@ #line 1 "app/online/py_ol_0023.pgc" /* @tier easy @module py @transform payment-online */ /* - * py_ol_0023.pgc - 지급 온라인 서비스 (PY_OL_0023) [tier=easy] + * py_ol_0023.pgc - 지급 재개(반려건 지급대기 복원) (PY_OL_0023) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,57 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0023.pgc" +#line 16 "app/online/py_ol_0023.pgc" TX_SERVICE(PY_OL_0023, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0023] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0023] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + +#line 27 "app/online/py_ol_0023.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; +/* exec sql end declare section */ +#line 28 "app/online/py_ol_0023.pgc" - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + + tx_log(TX_LOG_INFO, "[PY_OL_0023] 지급 재개 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select status from py_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 33 "app/online/py_ol_0023.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (h_status[0] != 'U') { + tx_log(TX_LOG_WARN, "[PY_OL_0023] 반려상태 아님 status=%s", h_status); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0023] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'R' , upd_ts = now ( ) where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 41 "app/online/py_ol_0023.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0023 재개"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", "R"); + tx_log(TX_LOG_INFO, "[PY_OL_0023] 지급재개 완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0023.pgc b/app/online/py_ol_0023.pgc index 0304330..46f40b0 100644 --- a/app/online/py_ol_0023.pgc +++ b/app/online/py_ol_0023.pgc @@ -1,14 +1,15 @@ /* @tier easy @module py @transform payment-online */ /* - * py_ol_0023.pgc - 지급 온라인 서비스 (PY_OL_0023) [tier=easy] + * py_ol_0023.pgc - 지급 재개(반려건 지급대기 복원) (PY_OL_0023) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,37 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0023, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0023] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0023] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2]; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + tx_log(TX_LOG_INFO, "[PY_OL_0023] 지급 재개 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT status INTO :h_status FROM py_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (h_status[0] != 'U') { + tx_log(TX_LOG_WARN, "[PY_OL_0023] 반려상태 아님 status=%s", h_status); + tx_return(ctx, TX_FAIL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0023] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET status = 'R', upd_ts = now() WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0023 재개"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", "R"); + tx_log(TX_LOG_INFO, "[PY_OL_0023] 지급재개 완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0024.c b/app/online/py_ol_0024.c index 8a95c2a..b4fd31e 100644 --- a/app/online/py_ol_0024.c +++ b/app/online/py_ol_0024.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0024.pgc" -/* @tier hard @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0024.pgc - 지급 온라인 서비스 (PY_OL_0024) [tier=hard] + * py_ol_0024.pgc - 지급 예정액 조회(미지급 대기 합계) (PY_OL_0024) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,88 +90,54 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0024.pgc" +#line 16 "app/online/py_ol_0024.pgc" TX_SERVICE(PY_OL_0024, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + +#line 21 "app/online/py_ol_0024.pgc" + char h_bdate [ 9 ] ; + +#line 22 "app/online/py_ol_0024.pgc" + int h_cnt ; + +#line 23 "app/online/py_ol_0024.pgc" + long h_sum , h_fee ; +/* exec sql end declare section */ +#line 24 "app/online/py_ol_0024.pgc" - tx_log(TX_LOG_INFO, "[PY_OL_0024] 지급 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0024] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char bdate[16], won[32]; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0024] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } + tx_log(TX_LOG_INFO, "[PY_OL_0024] 지급 예정액 조회 진입"); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( sum ( amount ) , 0 ) , coalesce ( sum ( fee ) , 0 ) from py_ledger where biz_date = $1 and status in ( 'R' , 'M' )", + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 36 "app/online/py_ol_0024.pgc" - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0024] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("PY_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[PY_OL_0024] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (py_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_ol_0024"); tx_return(ctx, TX_EDB, ctx->out); return; } + amount_format_won(h_sum - h_fee, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[PY_OL_0024] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bdate); + tx_buf_setlong(ctx->out, "PENDCNT", h_cnt); + tx_buf_setlong(ctx->out, "PENDNET", h_sum - h_fee); + tx_buf_sets(ctx->out, "PENDWON", won); + tx_log(TX_LOG_INFO, "[PY_OL_0024] 지급예정 date=%s cnt=%d net=%ld", bdate, h_cnt, h_sum - h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0024.pgc b/app/online/py_ol_0024.pgc index cf3bc7c..2c99e5a 100644 --- a/app/online/py_ol_0024.pgc +++ b/app/online/py_ol_0024.pgc @@ -1,14 +1,15 @@ -/* @tier hard @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0024.pgc - 지급 온라인 서비스 (PY_OL_0024) [tier=hard] + * py_ol_0024.pgc - 지급 예정액 조회(미지급 대기 합계) (PY_OL_0024) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,83 +17,31 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0024, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_bdate[9]; + int h_cnt; + long h_sum, h_fee; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[PY_OL_0024] 지급 트랜잭션 오케스트레이션 진입"); + char bdate[16], won[32]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0024] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[PY_OL_0024] 지급 예정액 조회 진입"); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0024] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0024] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("PY_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[PY_OL_0024] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (py_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + EXEC SQL SELECT count(*), coalesce(sum(amount), 0), coalesce(sum(fee), 0) + INTO :h_cnt, :h_sum, :h_fee + FROM py_ledger + WHERE biz_date = :h_bdate AND status IN ('R','M'); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_ol_0024"); tx_return(ctx, TX_EDB, ctx->out); return; } + amount_format_won(h_sum - h_fee, won, sizeof(won)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[PY_OL_0024] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bdate); + tx_buf_setlong(ctx->out, "PENDCNT", h_cnt); + tx_buf_setlong(ctx->out, "PENDNET", h_sum - h_fee); + tx_buf_sets(ctx->out, "PENDWON", won); + tx_log(TX_LOG_INFO, "[PY_OL_0024] 지급예정 date=%s cnt=%d net=%ld", bdate, h_cnt, h_sum - h_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0025.c b/app/online/py_ol_0025.c index da5b29a..a897558 100644 --- a/app/online/py_ol_0025.c +++ b/app/online/py_ol_0025.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0025.pgc" -/* @tier easy @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0025.pgc - 지급 온라인 서비스 (PY_OL_0025) [tier=easy] + * py_ol_0025.pgc - 지급 결과 단건반영(이체결과 코드 처리) (PY_OL_0025) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,57 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0025.pgc" +#line 16 "app/online/py_ol_0025.pgc" TX_SERVICE(PY_OL_0025, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0025] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0025] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + +#line 27 "app/online/py_ol_0025.pgc" + char h_key [ 16 ] , h_newst [ 2 ] ; +/* exec sql end declare section */ +#line 28 "app/online/py_ol_0025.pgc" - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0025] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + + char rcode[8]; + + tx_log(TX_LOG_INFO, "[PY_OL_0025] 지급 결과반영 진입"); + if (tx_buf_get(ctx->in, "RESULTCODE", rcode, sizeof(rcode)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_key, key, 15); h_key[15] = '\0'; + + /* 이체결과코드 -> 상태 매핑 */ + if (strcmp(rcode, RESP_OK) == 0) { h_newst[0] = 'S'; } + else { h_newst[0] = 'U'; } + h_newst[1] = '\0'; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0025] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = $1 , upd_ts = now ( ) where key = $2 and status = 'M'", + ECPGt_char,(h_newst),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 45 "app/online/py_ol_0025.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0025 결과반영"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + if (sqlca.sqlerrd[2] == 0) { tx_abort(); tx_return(ctx, TX_ENOENT, ctx->out); return; } + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0025] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_newst); + tx_log(TX_LOG_INFO, "[PY_OL_0025] 결과반영 key=%s rcode=%s -> %s", key, rcode, h_newst); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0025.pgc b/app/online/py_ol_0025.pgc index e3ac632..a52e406 100644 --- a/app/online/py_ol_0025.pgc +++ b/app/online/py_ol_0025.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0025.pgc - 지급 온라인 서비스 (PY_OL_0025) [tier=easy] + * py_ol_0025.pgc - 지급 결과 단건반영(이체결과 코드 처리) (PY_OL_0025) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,42 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0025, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0025] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0025] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_newst[2]; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0025] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + char rcode[8]; + + tx_log(TX_LOG_INFO, "[PY_OL_0025] 지급 결과반영 진입"); + if (tx_buf_get(ctx->in, "RESULTCODE", rcode, sizeof(rcode)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_key, key, 15); h_key[15] = '\0'; + + /* 이체결과코드 -> 상태 매핑 */ + if (strcmp(rcode, RESP_OK) == 0) { h_newst[0] = 'S'; } + else { h_newst[0] = 'U'; } + h_newst[1] = '\0'; + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0025] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET status = :h_newst, upd_ts = now() + WHERE key = :h_key AND status = 'M'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0025 결과반영"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + if (sqlca.sqlerrd[2] == 0) { tx_abort(); tx_return(ctx, TX_ENOENT, ctx->out); return; } + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0025] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", h_newst); + tx_log(TX_LOG_INFO, "[PY_OL_0025] 결과반영 key=%s rcode=%s -> %s", key, rcode, h_newst); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0026.c b/app/online/py_ol_0026.c index 3aa3d2b..e3c33d9 100644 --- a/app/online/py_ol_0026.c +++ b/app/online/py_ol_0026.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0026.pgc" -/* @tier easy @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0026.pgc - 지급 온라인 서비스 (PY_OL_0026) [tier=easy] + * py_ol_0026.pgc - 지급 상계처리(선지급금 차감) (PY_OL_0026) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,75 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0026.pgc" +#line 16 "app/online/py_ol_0026.pgc" TX_SERVICE(PY_OL_0026, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0026] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0026] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/py_ol_0026.pgc" + char h_key [ 16 ] ; + +#line 28 "app/online/py_ol_0026.pgc" + long h_offset , h_amount , h_net ; +/* exec sql end declare section */ +#line 29 "app/online/py_ol_0026.pgc" - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0026] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + + long offset = 0; + + tx_log(TX_LOG_INFO, "[PY_OL_0026] 지급 상계처리 진입"); + tx_buf_getlong(ctx->in, "OFFSET", &offset); + if (offset <= 0) { tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_offset = offset; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount from py_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 39 "app/online/py_ol_0026.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (h_offset > h_amount) { + tx_log(TX_LOG_WARN, "[PY_OL_0026] 상계액 초과 amt=%ld offset=%ld", h_amount, h_offset); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0026] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set amount = amount - $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_long,&(h_offset),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 48 "app/online/py_ol_0026.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0026 상계"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount from py_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_net),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 53 "app/online/py_ol_0026.pgc" + + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0026] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "OFFSET", h_offset); + tx_buf_setlong(ctx->out, "NETAMT", h_net); + tx_log(TX_LOG_INFO, "[PY_OL_0026] 상계완료 key=%s net=%ld", key, h_net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0026.pgc b/app/online/py_ol_0026.pgc index 5a7756e..c9f1ae0 100644 --- a/app/online/py_ol_0026.pgc +++ b/app/online/py_ol_0026.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0026.pgc - 지급 온라인 서비스 (PY_OL_0026) [tier=easy] + * py_ol_0026.pgc - 지급 상계처리(선지급금 차감) (PY_OL_0026) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,45 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0026, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0026] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0026] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_offset, h_amount, h_net; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0026] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + long offset = 0; + + tx_log(TX_LOG_INFO, "[PY_OL_0026] 지급 상계처리 진입"); + tx_buf_getlong(ctx->in, "OFFSET", &offset); + if (offset <= 0) { tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_key, key, 15); h_key[15] = '\0'; + h_offset = offset; + + EXEC SQL SELECT amount INTO :h_amount FROM py_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { tx_return(ctx, TX_ENOENT, ctx->out); return; } + if (h_offset > h_amount) { + tx_log(TX_LOG_WARN, "[PY_OL_0026] 상계액 초과 amt=%ld offset=%ld", h_amount, h_offset); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0026] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET amount = amount - :h_offset, upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0026 상계"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; + } + EXEC SQL SELECT amount INTO :h_net FROM py_ledger WHERE key = :h_key; + tx_commit(); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0026] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "OFFSET", h_offset); + tx_buf_setlong(ctx->out, "NETAMT", h_net); + tx_log(TX_LOG_INFO, "[PY_OL_0026] 상계완료 key=%s net=%ld", key, h_net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0027.c b/app/online/py_ol_0027.c index ae19165..cc0eb65 100644 --- a/app/online/py_ol_0027.c +++ b/app/online/py_ol_0027.c @@ -8,15 +8,16 @@ #line 1 "app/online/py_ol_0027.pgc" /* @tier medium @module py @transform payment-online */ /* - * py_ol_0027.pgc - 지급 온라인 서비스 (PY_OL_0027) [tier=medium] + * py_ol_0027.pgc - 지급 분할취소(분할건 일괄 취소) (PY_OL_0027) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,60 +90,49 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0027.pgc" +#line 16 "app/online/py_ol_0027.pgc" TX_SERVICE(PY_OL_0027, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0027] 지급 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0027] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0027] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + + +#line 27 "app/online/py_ol_0027.pgc" + char h_prefix [ 18 ] ; + +#line 28 "app/online/py_ol_0027.pgc" + int h_aff ; +/* exec sql end declare section */ +#line 29 "app/online/py_ol_0027.pgc" - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0027] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + + tx_log(TX_LOG_INFO, "[PY_OL_0027] 지급 분할취소 진입"); + snprintf(h_prefix, sizeof(h_prefix), "%.10s-%%", key); + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0027] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'U' , upd_ts = now ( ) where card_no = 'SPLIT' and key like $1 and status <> 'S'", + ECPGt_char,(h_prefix),(long)18,(long)1,(18)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 36 "app/online/py_ol_0027.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0027 분할취소"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0027] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0027] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = py_rec_count(bizdate, PY_ST_RECV); + h_aff = (int)sqlca.sqlerrd[2]; + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0027] 등록완료 key=%s 당일건수=%ld", key, cnt); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_setlong(ctx->out, "CANCELLED", h_aff); + tx_log(TX_LOG_INFO, "[PY_OL_0027] 분할취소 key=%s 취소=%d건", key, h_aff); + tx_return(ctx, (h_aff > 0) ? TX_OK : TX_ENOENT, ctx->out); } diff --git a/app/online/py_ol_0027.pgc b/app/online/py_ol_0027.pgc index a811a6f..c2fc29d 100644 --- a/app/online/py_ol_0027.pgc +++ b/app/online/py_ol_0027.pgc @@ -1,14 +1,15 @@ /* @tier medium @module py @transform payment-online */ /* - * py_ol_0027.pgc - 지급 온라인 서비스 (PY_OL_0027) [tier=medium] + * py_ol_0027.pgc - 지급 분할취소(분할건 일괄 취소) (PY_OL_0027) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,55 +17,33 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0027, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0027] 지급 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0027] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0027] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_prefix[18]; + int h_aff; + EXEC SQL END DECLARE SECTION; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0027] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + tx_log(TX_LOG_INFO, "[PY_OL_0027] 지급 분할취소 진입"); + snprintf(h_prefix, sizeof(h_prefix), "%.10s-%%", key); + + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0027] tx_begin 경고"); + EXEC SQL UPDATE py_ledger SET status = 'U', upd_ts = now() + WHERE card_no = 'SPLIT' AND key LIKE :h_prefix AND status <> 'S'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0027 분할취소"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0027] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0027] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = py_rec_count(bizdate, PY_ST_RECV); + h_aff = (int)sqlca.sqlerrd[2]; + tx_commit(); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0027] 등록완료 key=%s 당일건수=%ld", key, cnt); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_setlong(ctx->out, "CANCELLED", h_aff); + tx_log(TX_LOG_INFO, "[PY_OL_0027] 분할취소 key=%s 취소=%d건", key, h_aff); + tx_return(ctx, (h_aff > 0) ? TX_OK : TX_ENOENT, ctx->out); } diff --git a/app/online/py_ol_0028.c b/app/online/py_ol_0028.c index d32879b..a75ad10 100644 --- a/app/online/py_ol_0028.c +++ b/app/online/py_ol_0028.c @@ -8,15 +8,16 @@ #line 1 "app/online/py_ol_0028.pgc" /* @tier easy @module py @transform payment-online */ /* - * py_ol_0028.pgc - 지급 온라인 서비스 (PY_OL_0028) [tier=easy] + * py_ol_0028.pgc - 지급 가맹점 등록확인(지급이력 존재검사) (PY_OL_0028) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,52 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0028.pgc" +#line 16 "app/online/py_ol_0028.pgc" TX_SERVICE(PY_OL_0028, ctx) { - py_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 21 "app/online/py_ol_0028.pgc" + char h_merch [ 16 ] ; + +#line 22 "app/online/py_ol_0028.pgc" + int h_cnt ; + +#line 23 "app/online/py_ol_0028.pgc" + long h_last ; +/* exec sql end declare section */ +#line 24 "app/online/py_ol_0028.pgc" - tx_log(TX_LOG_INFO, "[PY_OL_0028] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0028] KEY 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + char merch[16]; + + tx_log(TX_LOG_INFO, "[PY_OL_0028] 지급 가맹점 등록확인 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( max ( amount ) , 0 ) from py_ledger where merch_id = $1 ", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_cnt),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_last),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 35 "app/online/py_ol_0028.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_ol_0028"); tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "REGISTERED", (h_cnt > 0) ? "Y" : "N"); + tx_buf_setlong(ctx->out, "HISTCNT", h_cnt); + tx_buf_setlong(ctx->out, "MAXAMT", h_last); + tx_log(TX_LOG_INFO, "[PY_OL_0028] 등록확인 merch=%s cnt=%d", merch, h_cnt); + tx_return(ctx, (h_cnt > 0) ? TX_OK : TX_ENOENT, ctx->out); } diff --git a/app/online/py_ol_0028.pgc b/app/online/py_ol_0028.pgc index e9514db..7b9986c 100644 --- a/app/online/py_ol_0028.pgc +++ b/app/online/py_ol_0028.pgc @@ -1,14 +1,15 @@ /* @tier easy @module py @transform payment-online */ /* - * py_ol_0028.pgc - 지급 온라인 서비스 (PY_OL_0028) [tier=easy] + * py_ol_0028.pgc - 지급 가맹점 등록확인(지급이력 존재검사) (PY_OL_0028) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,29 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0028, ctx) { - py_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16]; + int h_cnt; + long h_last; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[PY_OL_0028] 지급 단건조회 서비스 진입"); + char merch[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0028] KEY 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + tx_log(TX_LOG_INFO, "[PY_OL_0028] 지급 가맹점 등록확인 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + EXEC SQL SELECT count(*), coalesce(max(amount), 0) INTO :h_cnt, :h_last + FROM py_ledger WHERE merch_id = :h_merch; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_ol_0028"); tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "REGISTERED", (h_cnt > 0) ? "Y" : "N"); + tx_buf_setlong(ctx->out, "HISTCNT", h_cnt); + tx_buf_setlong(ctx->out, "MAXAMT", h_last); + tx_log(TX_LOG_INFO, "[PY_OL_0028] 등록확인 merch=%s cnt=%d", merch, h_cnt); + tx_return(ctx, (h_cnt > 0) ? TX_OK : TX_ENOENT, ctx->out); } diff --git a/app/online/py_ol_0029.c b/app/online/py_ol_0029.c index 6416f27..db5691a 100644 --- a/app/online/py_ol_0029.c +++ b/app/online/py_ol_0029.c @@ -8,15 +8,16 @@ #line 1 "app/online/py_ol_0029.pgc" /* @tier medium @module py @transform payment-online */ /* - * py_ol_0029.pgc - 지급 온라인 서비스 (PY_OL_0029) [tier=medium] + * py_ol_0029.pgc - 지급 일련번호 채번(지급전문 시퀀스) (PY_OL_0029) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,60 +90,44 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0029.pgc" +#line 16 "app/online/py_ol_0029.pgc" TX_SERVICE(PY_OL_0029, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + +#line 21 "app/online/py_ol_0029.pgc" + char h_bdate [ 9 ] ; + +#line 22 "app/online/py_ol_0029.pgc" + int h_seq ; +/* exec sql end declare section */ +#line 23 "app/online/py_ol_0029.pgc" - tx_log(TX_LOG_INFO, "[PY_OL_0029] 지급 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0029] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char bdate[16], payno[20]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0029] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0029] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } + tx_log(TX_LOG_INFO, "[PY_OL_0029] 지급 일련번호 채번 진입"); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( count ( * ) , 0 ) + 1 from py_ledger where biz_date = $1 ", + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_int,&(h_seq),(long)1,(long)1,sizeof(int), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 33 "app/online/py_ol_0029.pgc" - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0029] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = py_rec_count(bizdate, PY_ST_RECV); + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_ol_0029"); tx_return(ctx, TX_EDB, ctx->out); return; } + snprintf(payno, sizeof(payno), "PY%.8s%05d", bdate, h_seq); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0029] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "PAYNO", payno); + tx_buf_setlong(ctx->out, "SEQ", h_seq); + tx_log(TX_LOG_INFO, "[PY_OL_0029] 지급번호 채번 %s (seq=%d)", payno, h_seq); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0029.pgc b/app/online/py_ol_0029.pgc index 81bb3f6..48e5777 100644 --- a/app/online/py_ol_0029.pgc +++ b/app/online/py_ol_0029.pgc @@ -1,14 +1,15 @@ /* @tier medium @module py @transform payment-online */ /* - * py_ol_0029.pgc - 지급 온라인 서비스 (PY_OL_0029) [tier=medium] + * py_ol_0029.pgc - 지급 일련번호 채번(지급전문 시퀀스) (PY_OL_0029) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,55 +17,26 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0029, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_bdate[9]; + int h_seq; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[PY_OL_0029] 지급 등록 서비스 진입"); + char bdate[16], payno[20]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0029] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[PY_OL_0029] 지급 일련번호 채번 진입"); + if (tx_buf_get(ctx->in, "BIZDATE", bdate, sizeof(bdate)) != TX_OK) + date_today(bdate); + strncpy(h_bdate, bdate, 8); h_bdate[8] = '\0'; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0029] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0029] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0029] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = py_rec_count(bizdate, PY_ST_RECV); + EXEC SQL SELECT coalesce(count(*), 0) + 1 INTO :h_seq + FROM py_ledger WHERE biz_date = :h_bdate; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("py_ol_0029"); tx_return(ctx, TX_EDB, ctx->out); return; } + snprintf(payno, sizeof(payno), "PY%.8s%05d", bdate, h_seq); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0029] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "PAYNO", payno); + tx_buf_setlong(ctx->out, "SEQ", h_seq); + tx_log(TX_LOG_INFO, "[PY_OL_0029] 지급번호 채번 %s (seq=%d)", payno, h_seq); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0030.c b/app/online/py_ol_0030.c index a6e5f09..1e7a296 100644 --- a/app/online/py_ol_0030.c +++ b/app/online/py_ol_0030.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0030.pgc" -/* @tier easy @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0030.pgc - 지급 온라인 서비스 (PY_OL_0030) [tier=easy] + * py_ol_0030.pgc - 지급 최근이력 조회(가맹점 최근 지급 N건) (PY_OL_0030) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,77 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0030.pgc" +#line 16 "app/online/py_ol_0030.pgc" TX_SERVICE(PY_OL_0030, ctx) { - py_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 21 "app/online/py_ol_0030.pgc" + char h_merch [ 16 ] ; + +#line 22 "app/online/py_ol_0030.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; + +#line 23 "app/online/py_ol_0030.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 24 "app/online/py_ol_0030.pgc" - tx_log(TX_LOG_INFO, "[PY_OL_0030] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0030] KEY 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + char merch[16], field[16]; + int n = 0; + long total = 0; + + tx_log(TX_LOG_INFO, "[PY_OL_0030] 지급 최근이력 조회 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + ECPGset_var( 0, ( h_merch ), __LINE__);\ + /* declare cur_pyhist cursor for select key , amount , status from py_ledger where merch_id = $1 order by reg_ts desc limit 5 */ +#line 39 "app/online/py_ol_0030.pgc" + +#line 39 "app/online/py_ol_0030.pgc" + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_pyhist cursor for select key , amount , status from py_ledger where merch_id = $1 order by reg_ts desc limit 5", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 40 "app/online/py_ol_0030.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pyhist"); tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_pyhist", ECPGt_EOIT, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 45 "app/online/py_ol_0030.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + snprintf(field, sizeof(field), "KEY%d", n); + tx_buf_sets(ctx->out, field, h_key); + snprintf(field, sizeof(field), "AMT%d", n); + tx_buf_setlong(ctx->out, field, h_amount); + total += h_amount; + n++; + } + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_pyhist", ECPGt_EOIT, ECPGt_EORT);} +#line 55 "app/online/py_ol_0030.pgc" + + + tx_buf_setlong(ctx->out, "COUNT", n); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_log(TX_LOG_INFO, "[PY_OL_0030] 최근이력 merch=%s cnt=%d", merch, n); + tx_return(ctx, (n > 0) ? TX_OK : TX_ENOENT, ctx->out); } diff --git a/app/online/py_ol_0030.pgc b/app/online/py_ol_0030.pgc index 0785cca..2aeca8a 100644 --- a/app/online/py_ol_0030.pgc +++ b/app/online/py_ol_0030.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0030.pgc - 지급 온라인 서비스 (PY_OL_0030) [tier=easy] + * py_ol_0030.pgc - 지급 최근이력 조회(가맹점 최근 지급 N건) (PY_OL_0030) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,45 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0030, ctx) { - py_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16]; + char h_key[16], h_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[PY_OL_0030] 지급 단건조회 서비스 진입"); + char merch[16], field[16]; + int n = 0; + long total = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0030] KEY 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + tx_log(TX_LOG_INFO, "[PY_OL_0030] 지급 최근이력 조회 진입"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); return; } + strncpy(h_merch, merch, 15); h_merch[15] = '\0'; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + EXEC SQL DECLARE cur_pyhist CURSOR FOR + SELECT key, amount, status FROM py_ledger + WHERE merch_id = :h_merch + ORDER BY reg_ts DESC LIMIT 5; + EXEC SQL OPEN cur_pyhist; + if (sqlca.sqlcode != TX_SQL_OK) { TX_DBIO_LOG_ERR("OPEN cur_pyhist"); tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + for (;;) { + EXEC SQL FETCH cur_pyhist INTO :h_key, :h_amount, :h_status; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) break; + if (sqlca.sqlcode != TX_SQL_OK) break; + snprintf(field, sizeof(field), "KEY%d", n); + tx_buf_sets(ctx->out, field, h_key); + snprintf(field, sizeof(field), "AMT%d", n); + tx_buf_setlong(ctx->out, field, h_amount); + total += h_amount; + n++; + } + EXEC SQL CLOSE cur_pyhist; + + tx_buf_setlong(ctx->out, "COUNT", n); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_log(TX_LOG_INFO, "[PY_OL_0030] 최근이력 merch=%s cnt=%d", merch, n); + tx_return(ctx, (n > 0) ? TX_OK : TX_ENOENT, ctx->out); } diff --git a/app/online/py_ol_0031.c b/app/online/py_ol_0031.c index 794e1d6..6f931b6 100644 --- a/app/online/py_ol_0031.c +++ b/app/online/py_ol_0031.c @@ -8,15 +8,16 @@ #line 1 "app/online/py_ol_0031.pgc" /* @tier medium @module py @transform payment-online */ /* - * py_ol_0031.pgc - 지급 온라인 서비스 (PY_OL_0031) [tier=medium] + * py_ol_0031.pgc - 지급 재처리 접수(실패건 재지급 큐 등록) (PY_OL_0031) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,60 +90,71 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0031.pgc" +#line 16 "app/online/py_ol_0031.pgc" TX_SERVICE(PY_OL_0031, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0031] 지급 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0031] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0031] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + /* exec sql begin declare section */ + + + +#line 27 "app/online/py_ol_0031.pgc" + char h_key [ 16 ] , h_bdate [ 9 ] ; + +#line 28 "app/online/py_ol_0031.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 29 "app/online/py_ol_0031.pgc" - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0031] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0031] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + + char nextday[16]; + + tx_log(TX_LOG_INFO, "[PY_OL_0031] 지급 재처리 접수 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , biz_date from py_ledger where key = $1 and status = 'U'", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 37 "app/online/py_ol_0031.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[PY_OL_0031] 실패건 아님 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + if (date_next_business(h_bdate, nextday) != 0) + date_today(nextday); + strncpy(h_bdate, nextday, 8); h_bdate[8] = '\0'; - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0031] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0031] tx_begin 경고"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "update py_ledger set status = 'R' , biz_date = $1 , upd_ts = now ( ) where key = $2 ", + ECPGt_char,(h_bdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 50 "app/online/py_ol_0031.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0031 재처리"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = py_rec_count(bizdate, PY_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0031] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "NEXTDATE", nextday); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_log(TX_LOG_INFO, "[PY_OL_0031] 재처리 접수 key=%s next=%s", key, nextday); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0031.pgc b/app/online/py_ol_0031.pgc index 2b47bc8..753fc43 100644 --- a/app/online/py_ol_0031.pgc +++ b/app/online/py_ol_0031.pgc @@ -1,14 +1,15 @@ /* @tier medium @module py @transform payment-online */ /* - * py_ol_0031.pgc - 지급 온라인 서비스 (PY_OL_0031) [tier=medium] + * py_ol_0031.pgc - 지급 재처리 접수(실패건 재지급 큐 등록) (PY_OL_0031) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,55 +17,47 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0031, ctx) { - py_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0031] 지급 등록 서비스 진입"); - - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0031] 필수 필드 누락"); + char key[16]; + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[PY_OL_0031] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_bdate[9]; + long h_amount; + EXEC SQL END DECLARE SECTION; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[PY_OL_0031] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[PY_OL_0031] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + char nextday[16]; + + tx_log(TX_LOG_INFO, "[PY_OL_0031] 지급 재처리 접수 진입"); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT amount, biz_date INTO :h_amount, :h_bdate + FROM py_ledger WHERE key = :h_key AND status = 'U'; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[PY_OL_0031] 실패건 아님 key=%s", key); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, PY_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + if (date_next_business(h_bdate, nextday) != 0) + date_today(nextday); + strncpy(h_bdate, nextday, 8); h_bdate[8] = '\0'; - rc = py_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[PY_OL_0031] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + if (tx_begin() != TX_OK) tx_log(TX_LOG_WARN, "[PY_OL_0031] tx_begin 경고"); + EXEC SQL UPDATE py_ledger + SET status = 'R', biz_date = :h_bdate, upd_ts = now() + WHERE key = :h_key; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("py_ol_0031 재처리"); tx_abort(); + tx_return(ctx, TX_EDB, ctx->out); return; } - - cnt = py_rec_count(bizdate, PY_ST_RECV); + tx_commit(); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[PY_OL_0031] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "NEXTDATE", nextday); + tx_buf_setlong(ctx->out, "AMOUNT", h_amount); + tx_log(TX_LOG_INFO, "[PY_OL_0031] 재처리 접수 key=%s next=%s", key, nextday); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/py_ol_0032.c b/app/online/py_ol_0032.c index 2a3af6e..2c0ad35 100644 --- a/app/online/py_ol_0032.c +++ b/app/online/py_ol_0032.c @@ -6,17 +6,18 @@ /* End of automatic include section */ #line 1 "app/online/py_ol_0032.pgc" -/* @tier easy @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0032.pgc - 지급 온라인 서비스 (PY_OL_0032) [tier=easy] + * py_ol_0032.pgc - 지급 대사(지급원장 vs 정산요청 검증) (PY_OL_0032) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -89,37 +90,63 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/py_ol_0032.pgc" +#line 16 "app/online/py_ol_0032.pgc" TX_SERVICE(PY_OL_0032, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0032] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0032] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* exec sql begin declare section */ + + + +#line 27 "app/online/py_ol_0032.pgc" + char h_key [ 16 ] , h_status [ 2 ] ; + +#line 28 "app/online/py_ol_0032.pgc" + long h_ledger , h_fee ; +/* exec sql end declare section */ +#line 29 "app/online/py_ol_0032.pgc" - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + + long claim = 0, net, diff; + + tx_log(TX_LOG_INFO, "[PY_OL_0032] 지급 대사 진입"); + tx_buf_getlong(ctx->in, "CLAIMAMT", &claim); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select amount , fee , status from py_ledger where key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_ledger),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 38 "app/online/py_ol_0032.pgc" + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "MATCH", "N"); + tx_buf_sets(ctx->out, "RESULT", "원장없음"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + net = h_ledger - h_fee; + diff = net - claim; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_setlong(ctx->out, "NETAMT", net); + tx_buf_setlong(ctx->out, "CLAIMAMT", claim); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "MATCH", (diff == 0) ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[PY_OL_0032] 지급대사 key=%s net=%ld claim=%ld diff=%ld", + key, net, claim, diff); + tx_return(ctx, (diff == 0) ? TX_OK : TX_FAIL, ctx->out); } diff --git a/app/online/py_ol_0032.pgc b/app/online/py_ol_0032.pgc index fcc742b..d430f8d 100644 --- a/app/online/py_ol_0032.pgc +++ b/app/online/py_ol_0032.pgc @@ -1,14 +1,15 @@ -/* @tier easy @module py @transform payment-online */ +/* @tier medium @module py @transform payment-online */ /* - * py_ol_0032.pgc - 지급 온라인 서비스 (PY_OL_0032) [tier=easy] + * py_ol_0032.pgc - 지급 대사(지급원장 vs 정산요청 검증) (PY_OL_0032) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 py 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 임베디드 SQL / 표준 + * DBIO / tx_call 로 처리한 뒤 응답 TXBUF 를 구성한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "py_core.h" @@ -16,32 +17,41 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(PY_OL_0032, ctx) { - py_rec_t rec; char key[16]; - int rc; - - tx_log(TX_LOG_INFO, "[PY_OL_0032] 지급 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[PY_OL_0032] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16], h_status[2]; + long h_ledger, h_fee; + EXEC SQL END DECLARE SECTION; - memset(&rec, 0, sizeof(rec)); - rc = py_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[PY_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + long claim = 0, net, diff; + + tx_log(TX_LOG_INFO, "[PY_OL_0032] 지급 대사 진입"); + tx_buf_getlong(ctx->in, "CLAIMAMT", &claim); + strncpy(h_key, key, 15); h_key[15] = '\0'; + + EXEC SQL SELECT amount, fee, status INTO :h_ledger, :h_fee, :h_status + FROM py_ledger WHERE key = :h_key; + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "MATCH", "N"); + tx_buf_sets(ctx->out, "RESULT", "원장없음"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } + net = h_ledger - h_fee; + diff = net - claim; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[PY_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_setlong(ctx->out, "NETAMT", net); + tx_buf_setlong(ctx->out, "CLAIMAMT", claim); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "STATUS", h_status); + tx_buf_sets(ctx->out, "MATCH", (diff == 0) ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[PY_OL_0032] 지급대사 key=%s net=%ld claim=%ld diff=%ld", + key, net, claim, diff); + tx_return(ctx, (diff == 0) ? TX_OK : TX_FAIL, ctx->out); } diff --git a/app/online/rc_ol_0002.c b/app/online/rc_ol_0002.c index 32db76d..e160785 100644 --- a/app/online/rc_ol_0002.c +++ b/app/online/rc_ol_0002.c @@ -8,10 +8,11 @@ #line 1 "app/online/rc_ol_0002.pgc" /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0002.pgc - 대사 온라인 서비스 (RC_OL_0002) [tier=medium] + * rc_ol_0002.pgc - 승인-매입 대사 (단건 판정) 온라인 서비스 (RC_OL_0002) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 "승인금액(입력 APPRAMT)" 과 "매입금액 + * (원장 amount)" 을 대사한다. 두 금액이 일치하면 매입확정('M'), + * 불일치하면 불일치('U') 로 상태를 전이하고 차액을 응답에 싣는다. */ #include #include @@ -89,60 +90,65 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0002.pgc" +#line 16 "app/online/rc_ol_0002.pgc" TX_SERVICE(RC_OL_0002, ctx) { rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + long appr_amt = 0; /* 승인계에서 넘어온 승인금액 */ + long diff; int rc; + const char *new_st; - tx_log(TX_LOG_INFO, "[RC_OL_0002] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0002] 승인-매입 대사(단건) 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0002] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0002] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0002] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0002] 영업일 오류 date=%s", bizdate); + if (tx_buf_getlong(ctx->in, "APPRAMT", &appr_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0002] APPRAMT(승인금액) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); + rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0002] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[RC_OL_0002] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + /* 대사 판정: 승인금액 == 매입금액 ? */ + diff = rec.amount - appr_amt; + if (diff == 0) { + new_st = RC_ST_DONE; /* 매입확정 */ + tx_log(TX_LOG_INFO, "[RC_OL_0002] 승인-매입 일치 key=%s amount=%ld", + key, rec.amount); + } else { + new_st = RC_ST_FAIL; /* 금액 불일치 */ + tx_log(TX_LOG_WARN, "[RC_OL_0002] 승인-매입 불일치 key=%s 차액=%ld", + key, diff); + } + + rc = rc_rec_update_status(key, new_st); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0002] 상태전이 실패 key=%s rc=%d", key, rc); + tx_return(ctx, rc, ctx->out); + return; + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0002] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_setlong(ctx->out, "APPRAMT", appr_amt); + tx_buf_setlong(ctx->out, "CAPAMT", rec.amount); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "STATUS", new_st); + tx_buf_sets(ctx->out, "RESULT", (diff == 0) ? "MATCH" : "UNMATCH"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0002.pgc b/app/online/rc_ol_0002.pgc index 8b1c1cb..816cd77 100644 --- a/app/online/rc_ol_0002.pgc +++ b/app/online/rc_ol_0002.pgc @@ -1,9 +1,10 @@ /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0002.pgc - 대사 온라인 서비스 (RC_OL_0002) [tier=medium] + * rc_ol_0002.pgc - 승인-매입 대사 (단건 판정) 온라인 서비스 (RC_OL_0002) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 "승인금액(입력 APPRAMT)" 과 "매입금액 + * (원장 amount)" 을 대사한다. 두 금액이 일치하면 매입확정('M'), + * 불일치하면 불일치('U') 로 상태를 전이하고 차액을 응답에 싣는다. */ #include #include @@ -17,54 +18,59 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0002, ctx) { rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + long appr_amt = 0; /* 승인계에서 넘어온 승인금액 */ + long diff; int rc; + const char *new_st; - tx_log(TX_LOG_INFO, "[RC_OL_0002] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0002] 승인-매입 대사(단건) 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0002] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0002] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0002] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0002] 영업일 오류 date=%s", bizdate); + if (tx_buf_getlong(ctx->in, "APPRAMT", &appr_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0002] APPRAMT(승인금액) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); + rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0002] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[RC_OL_0002] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + /* 대사 판정: 승인금액 == 매입금액 ? */ + diff = rec.amount - appr_amt; + if (diff == 0) { + new_st = RC_ST_DONE; /* 매입확정 */ + tx_log(TX_LOG_INFO, "[RC_OL_0002] 승인-매입 일치 key=%s amount=%ld", + key, rec.amount); + } else { + new_st = RC_ST_FAIL; /* 금액 불일치 */ + tx_log(TX_LOG_WARN, "[RC_OL_0002] 승인-매입 불일치 key=%s 차액=%ld", + key, diff); + } + + rc = rc_rec_update_status(key, new_st); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0002] 상태전이 실패 key=%s rc=%d", key, rc); + tx_return(ctx, rc, ctx->out); + return; + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0002] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_setlong(ctx->out, "APPRAMT", appr_amt); + tx_buf_setlong(ctx->out, "CAPAMT", rec.amount); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "STATUS", new_st); + tx_buf_sets(ctx->out, "RESULT", (diff == 0) ? "MATCH" : "UNMATCH"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0003.c b/app/online/rc_ol_0003.c index 58ce604..888be38 100644 --- a/app/online/rc_ol_0003.c +++ b/app/online/rc_ol_0003.c @@ -6,12 +6,22 @@ /* End of automatic include section */ #line 1 "app/online/rc_ol_0003.pgc" -/* @tier easy @module rc @transform reconciliation-online */ +/* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0003.pgc - 대사 온라인 서비스 (RC_OL_0003) [tier=easy] + * rc_ol_0003.pgc - 매입-입금 대사 (단건) 온라인 서비스 (RC_OL_0003) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 "매입 순액(net = 매입금액 amount - 수수료 + * fee)" 과 매입사로부터 통지된 "실제 입금액(입력 DEPAMT)" 을 대사한다. + * + * 업무규칙: + * - 원장 상태가 매입확정('M') 이고 net == DEPAMT 이면 대사 일치로 보고 + * 정산완료('S') 로 상태를 전이한다(RESULT=MATCH). + * - 그 외에는 대사 불일치로 판정한다(RESULT=UNMATCH). 이 경우 순액과 + * 입금액이 실제로 어긋난 때(net != DEPAMT)에 한해 불일치('U') 로 + * 전이하여 후속 예외처리 대상으로 남기고, 금액은 맞으나 아직 매입 + * 확정 상태가 아닌 경우에는 상태를 바꾸지 않는다. + * + * 응답: KEY, NET, DEPAMT, GAP(=net-DEPAMT), STATUS, RESULT */ #include #include @@ -89,37 +99,100 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0003.pgc" +#line 25 "app/online/rc_ol_0003.pgc" TX_SERVICE(RC_OL_0003, ctx) { rc_rec_t rec; char key[16]; + long dep_amt = 0; /* 매입사 통지 입금액 */ + long net; /* 매입 순액 = amount - fee */ + long gap; /* 차액 = net - dep_amt */ + int matched; /* 대사 일치 여부 */ int rc; + const char *result; + const char *final_st; - tx_log(TX_LOG_INFO, "[RC_OL_0003] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0003] 매입-입금 대사(단건) 서비스 진입"); + /* --- 요청 필드 추출 --- */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0003] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_getlong(ctx->in, "DEPAMT", &dep_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0003] DEPAMT(입금액) 누락 key=%s", key); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!amount_is_valid(dep_amt)) { + tx_log(TX_LOG_WARN, "[RC_OL_0003] 입금액 범위 오류 key=%s dep=%ld", + key, dep_amt); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + /* --- 원장 조회 --- */ memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0003] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0003] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + /* --- 대사 계산 --- */ + net = rec.amount - rec.fee; + gap = net - dep_amt; + matched = (strcmp(rec.status, RC_ST_DONE) == 0) && (gap == 0); + + if (matched) { + /* 매입확정 + 순액 일치 → 정산완료 전이 */ + rc = rc_rec_update_status(key, RC_ST_SETTLED); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0003] 정산완료 전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + final_st = RC_ST_SETTLED; + result = "MATCH"; + tx_log(TX_LOG_INFO, + "[RC_OL_0003] 대사 일치 key=%s net=%ld dep=%ld → 정산완료", + key, net, dep_amt); + } else if (gap != 0) { + /* 금액이 실제로 어긋남 → 불일치 상태로 보류 */ + rc = rc_rec_update_status(key, RC_ST_FAIL); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0003] 불일치 전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + final_st = RC_ST_FAIL; + result = "UNMATCH"; + tx_log(TX_LOG_WARN, + "[RC_OL_0003] 대사 불일치 key=%s 차액=%ld → 보류", + key, gap); + } else { + /* 금액은 맞으나 매입확정 상태가 아님 → 전이 없이 보류 판정 */ + final_st = rec.status; + result = "UNMATCH"; + tx_log(TX_LOG_WARN, + "[RC_OL_0003] 매입확정 前 상태 key=%s status=%s → 전이 보류", + key, rec.status); + } + + /* --- 응답 구성 --- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "NET", net); + tx_buf_setlong(ctx->out, "DEPAMT", dep_amt); + tx_buf_setlong(ctx->out, "GAP", gap); + tx_buf_sets(ctx->out, "STATUS", final_st); + tx_buf_sets(ctx->out, "RESULT", result); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0003.pgc b/app/online/rc_ol_0003.pgc index ee99842..3b544ee 100644 --- a/app/online/rc_ol_0003.pgc +++ b/app/online/rc_ol_0003.pgc @@ -1,9 +1,19 @@ -/* @tier easy @module rc @transform reconciliation-online */ +/* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0003.pgc - 대사 온라인 서비스 (RC_OL_0003) [tier=easy] + * rc_ol_0003.pgc - 매입-입금 대사 (단건) 온라인 서비스 (RC_OL_0003) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 "매입 순액(net = 매입금액 amount - 수수료 + * fee)" 과 매입사로부터 통지된 "실제 입금액(입력 DEPAMT)" 을 대사한다. + * + * 업무규칙: + * - 원장 상태가 매입확정('M') 이고 net == DEPAMT 이면 대사 일치로 보고 + * 정산완료('S') 로 상태를 전이한다(RESULT=MATCH). + * - 그 외에는 대사 불일치로 판정한다(RESULT=UNMATCH). 이 경우 순액과 + * 입금액이 실제로 어긋난 때(net != DEPAMT)에 한해 불일치('U') 로 + * 전이하여 후속 예외처리 대상으로 남기고, 금액은 맞으나 아직 매입 + * 확정 상태가 아닌 경우에는 상태를 바꾸지 않는다. + * + * 응답: KEY, NET, DEPAMT, GAP(=net-DEPAMT), STATUS, RESULT */ #include #include @@ -18,30 +28,93 @@ TX_SERVICE(RC_OL_0003, ctx) { rc_rec_t rec; char key[16]; + long dep_amt = 0; /* 매입사 통지 입금액 */ + long net; /* 매입 순액 = amount - fee */ + long gap; /* 차액 = net - dep_amt */ + int matched; /* 대사 일치 여부 */ int rc; + const char *result; + const char *final_st; - tx_log(TX_LOG_INFO, "[RC_OL_0003] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0003] 매입-입금 대사(단건) 서비스 진입"); + /* --- 요청 필드 추출 --- */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0003] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_getlong(ctx->in, "DEPAMT", &dep_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0003] DEPAMT(입금액) 누락 key=%s", key); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!amount_is_valid(dep_amt)) { + tx_log(TX_LOG_WARN, "[RC_OL_0003] 입금액 범위 오류 key=%s dep=%ld", + key, dep_amt); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + /* --- 원장 조회 --- */ memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0003] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0003] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + /* --- 대사 계산 --- */ + net = rec.amount - rec.fee; + gap = net - dep_amt; + matched = (strcmp(rec.status, RC_ST_DONE) == 0) && (gap == 0); + + if (matched) { + /* 매입확정 + 순액 일치 → 정산완료 전이 */ + rc = rc_rec_update_status(key, RC_ST_SETTLED); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0003] 정산완료 전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + final_st = RC_ST_SETTLED; + result = "MATCH"; + tx_log(TX_LOG_INFO, + "[RC_OL_0003] 대사 일치 key=%s net=%ld dep=%ld → 정산완료", + key, net, dep_amt); + } else if (gap != 0) { + /* 금액이 실제로 어긋남 → 불일치 상태로 보류 */ + rc = rc_rec_update_status(key, RC_ST_FAIL); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0003] 불일치 전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + final_st = RC_ST_FAIL; + result = "UNMATCH"; + tx_log(TX_LOG_WARN, + "[RC_OL_0003] 대사 불일치 key=%s 차액=%ld → 보류", + key, gap); + } else { + /* 금액은 맞으나 매입확정 상태가 아님 → 전이 없이 보류 판정 */ + final_st = rec.status; + result = "UNMATCH"; + tx_log(TX_LOG_WARN, + "[RC_OL_0003] 매입확정 前 상태 key=%s status=%s → 전이 보류", + key, rec.status); + } + + /* --- 응답 구성 --- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "NET", net); + tx_buf_setlong(ctx->out, "DEPAMT", dep_amt); + tx_buf_setlong(ctx->out, "GAP", gap); + tx_buf_sets(ctx->out, "STATUS", final_st); + tx_buf_sets(ctx->out, "RESULT", result); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0004.c b/app/online/rc_ol_0004.c index 54a3bb8..13f5b32 100644 --- a/app/online/rc_ol_0004.c +++ b/app/online/rc_ol_0004.c @@ -8,10 +8,30 @@ #line 1 "app/online/rc_ol_0004.pgc" /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0004.pgc - 대사 온라인 서비스 (RC_OL_0004) [tier=medium] + * rc_ol_0004.pgc - 반송(return) 대사 (단건) 온라인 서비스 (RC_OL_0004) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 매입사에서 통지된 "반송금액(입력 RTNAMT)" + * 을 원거래금액(원장 amount)과 대사한다. + * + * 업무규칙: + * - 반송금액은 0 보다 커야 하며 원거래금액을 초과할 수 없다(TX_EINVAL). + * - 반송금액 == 원거래금액 → 전액반송(매입취소). 상태를 불일치/보류 + * ('U') 로 전이하고 RESULT=FULLRETURN. + * - 0 < 반송금액 < 원거래금액 → 부분반송. 잔액이 남아 있으므로 접수 + * ('R') 상태를 유지하고 RESULT=PARTIAL. + * + * 응답: KEY, RTNAMT, REMAIN(=amount-RTNAMT), STATUS, RESULT + * + * 상태전이 요약: + * [입력 RTNAMT] [원장 상태] [RESULT] + * RTNAMT <= 0 (거부) TX_EINVAL + * RTNAMT > amount (거부) TX_EINVAL + * RTNAMT == amount R/… → U FULLRETURN + * 0 < RTNAMT < amount R 유지 PARTIAL + * + * 주의: 본 서비스는 원장 금액(amount) 자체를 갱신하지 않는다. 부분반송의 + * 잔액(REMAIN)은 응답으로만 통지하며, 실제 금액 조정은 후속 정산 + * 배치에서 반영한다. 따라서 커서/트랜잭션 경계가 필요 없다. */ #include #include @@ -89,60 +109,95 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0004.pgc" +#line 35 "app/online/rc_ol_0004.pgc" TX_SERVICE(RC_OL_0004, ctx) { rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + long rtn_amt = 0; /* 반송금액 */ + long remain; /* 반송 후 잔액 */ int rc; + const char *result; + const char *final_st; - tx_log(TX_LOG_INFO, "[RC_OL_0004] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0004] 반송 대사(단건) 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0004] 필수 필드 누락"); + /* --- 요청 필드 추출 --- */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0004] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0004] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0004] 영업일 오류 date=%s", bizdate); + if (tx_buf_getlong(ctx->in, "RTNAMT", &rtn_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0004] RTNAMT(반송금액) 누락 key=%s", key); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* --- 반송금액 유효성: 0 초과 --- */ + if (rtn_amt <= 0) { + tx_log(TX_LOG_WARN, "[RC_OL_0004] 반송금액 오류 key=%s rtn=%ld", + key, rtn_amt); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* --- 원장 조회 --- */ memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); + rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0004] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[RC_OL_0004] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + /* --- 반송금액이 원거래금액 초과 → 처리 불가 --- */ + if (rtn_amt > rec.amount) { + tx_log(TX_LOG_WARN, + "[RC_OL_0004] 반송금액 초과 key=%s rtn=%ld amount=%ld", + key, rtn_amt, rec.amount); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + remain = rec.amount - rtn_amt; + + tx_log(TX_LOG_INFO, + "[RC_OL_0004] 반송 대사 판정 key=%s 가맹점=%s 원거래=%ld 반송=%ld", + key, rec.merch_id, rec.amount, rtn_amt); + + if (rtn_amt == rec.amount) { + /* 전액반송 → 매입취소, 불일치/보류 전이 */ + rc = rc_rec_update_status(key, RC_ST_FAIL); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0004] 전액반송 전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + final_st = RC_ST_FAIL; + result = "FULLRETURN"; + tx_log(TX_LOG_INFO, + "[RC_OL_0004] 전액반송 key=%s amount=%ld → 매입취소", + key, rec.amount); + } else { + /* 부분반송 → 잔액 존재, 접수상태 유지 */ + final_st = RC_ST_RECV; + result = "PARTIAL"; + tx_log(TX_LOG_INFO, + "[RC_OL_0004] 부분반송 key=%s rtn=%ld 잔액=%ld → 접수유지", + key, rtn_amt, remain); + } + + /* --- 응답 구성 --- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0004] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_setlong(ctx->out, "RTNAMT", rtn_amt); + tx_buf_setlong(ctx->out, "REMAIN", remain); + tx_buf_sets(ctx->out, "STATUS", final_st); + tx_buf_sets(ctx->out, "RESULT", result); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0004.pgc b/app/online/rc_ol_0004.pgc index 0baea69..71dc663 100644 --- a/app/online/rc_ol_0004.pgc +++ b/app/online/rc_ol_0004.pgc @@ -1,9 +1,29 @@ /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0004.pgc - 대사 온라인 서비스 (RC_OL_0004) [tier=medium] + * rc_ol_0004.pgc - 반송(return) 대사 (단건) 온라인 서비스 (RC_OL_0004) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 매입사에서 통지된 "반송금액(입력 RTNAMT)" + * 을 원거래금액(원장 amount)과 대사한다. + * + * 업무규칙: + * - 반송금액은 0 보다 커야 하며 원거래금액을 초과할 수 없다(TX_EINVAL). + * - 반송금액 == 원거래금액 → 전액반송(매입취소). 상태를 불일치/보류 + * ('U') 로 전이하고 RESULT=FULLRETURN. + * - 0 < 반송금액 < 원거래금액 → 부분반송. 잔액이 남아 있으므로 접수 + * ('R') 상태를 유지하고 RESULT=PARTIAL. + * + * 응답: KEY, RTNAMT, REMAIN(=amount-RTNAMT), STATUS, RESULT + * + * 상태전이 요약: + * [입력 RTNAMT] [원장 상태] [RESULT] + * RTNAMT <= 0 (거부) TX_EINVAL + * RTNAMT > amount (거부) TX_EINVAL + * RTNAMT == amount R/… → U FULLRETURN + * 0 < RTNAMT < amount R 유지 PARTIAL + * + * 주의: 본 서비스는 원장 금액(amount) 자체를 갱신하지 않는다. 부분반송의 + * 잔액(REMAIN)은 응답으로만 통지하며, 실제 금액 조정은 후속 정산 + * 배치에서 반영한다. 따라서 커서/트랜잭션 경계가 필요 없다. */ #include #include @@ -17,54 +37,89 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0004, ctx) { rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + long rtn_amt = 0; /* 반송금액 */ + long remain; /* 반송 후 잔액 */ int rc; + const char *result; + const char *final_st; - tx_log(TX_LOG_INFO, "[RC_OL_0004] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0004] 반송 대사(단건) 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0004] 필수 필드 누락"); + /* --- 요청 필드 추출 --- */ + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0004] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0004] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0004] 영업일 오류 date=%s", bizdate); + if (tx_buf_getlong(ctx->in, "RTNAMT", &rtn_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0004] RTNAMT(반송금액) 누락 key=%s", key); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* --- 반송금액 유효성: 0 초과 --- */ + if (rtn_amt <= 0) { + tx_log(TX_LOG_WARN, "[RC_OL_0004] 반송금액 오류 key=%s rtn=%ld", + key, rtn_amt); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* --- 원장 조회 --- */ memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); + rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0004] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[RC_OL_0004] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + /* --- 반송금액이 원거래금액 초과 → 처리 불가 --- */ + if (rtn_amt > rec.amount) { + tx_log(TX_LOG_WARN, + "[RC_OL_0004] 반송금액 초과 key=%s rtn=%ld amount=%ld", + key, rtn_amt, rec.amount); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + remain = rec.amount - rtn_amt; + + tx_log(TX_LOG_INFO, + "[RC_OL_0004] 반송 대사 판정 key=%s 가맹점=%s 원거래=%ld 반송=%ld", + key, rec.merch_id, rec.amount, rtn_amt); + + if (rtn_amt == rec.amount) { + /* 전액반송 → 매입취소, 불일치/보류 전이 */ + rc = rc_rec_update_status(key, RC_ST_FAIL); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0004] 전액반송 전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + final_st = RC_ST_FAIL; + result = "FULLRETURN"; + tx_log(TX_LOG_INFO, + "[RC_OL_0004] 전액반송 key=%s amount=%ld → 매입취소", + key, rec.amount); + } else { + /* 부분반송 → 잔액 존재, 접수상태 유지 */ + final_st = RC_ST_RECV; + result = "PARTIAL"; + tx_log(TX_LOG_INFO, + "[RC_OL_0004] 부분반송 key=%s rtn=%ld 잔액=%ld → 접수유지", + key, rtn_amt, remain); + } + + /* --- 응답 구성 --- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0004] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_setlong(ctx->out, "RTNAMT", rtn_amt); + tx_buf_setlong(ctx->out, "REMAIN", remain); + tx_buf_sets(ctx->out, "STATUS", final_st); + tx_buf_sets(ctx->out, "RESULT", result); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0005.c b/app/online/rc_ol_0005.c index d9dc868..dab209d 100644 --- a/app/online/rc_ol_0005.c +++ b/app/online/rc_ol_0005.c @@ -6,12 +6,32 @@ /* End of automatic include section */ #line 1 "app/online/rc_ol_0005.pgc" -/* @tier easy @module rc @transform reconciliation-online */ +/* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0005.pgc - 대사 온라인 서비스 (RC_OL_0005) [tier=easy] + * rc_ol_0005.pgc - 승인-매입 금액불일치 검출 (단건, read-only) (RC_OL_0005) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 "승인금액(입력 APPRAMT)" 과 "매입금액 + * (원장 amount)" 의 차이를 계산하고 유형을 분류한다. 이 서비스는 검출 + * 전용이며 원장 상태를 절대 변경하지 않는다(read-only). + * + * 분류 규칙(차액 diff = amount - APPRAMT, absd = |diff|): + * - diff == 0 → "EXACT" (완전일치) + * - absd <= 10 → "ROUNDING" (반올림 오차 허용범위) + * - 그 외 → "MISMATCH" (실질 불일치) + * + * 응답: KEY, APPRAMT, CAPAMT(=amount), DIFF, ABSDIFF, CLASS + * + * 분류표: + * [절대차 absd] [CLASS] [의미] + * diff == 0 EXACT 승인/매입 완전일치 + * absd <= 10 ROUNDING 반올림/절사 오차 허용 + * absd > 10 MISMATCH 실질 금액 불일치(정산조정 필요) + * + * 설계 원칙: 본 서비스는 "검출(detection) 전용" 이다. rc_rec_update_status + * 를 호출하지 않으므로 어떤 상태전이도 발생하지 않으며, 반복 + * 호출해도 원장은 불변(idempotent read)이다. 실제 상태전이는 + * 승인-매입 대사 서비스(RC_OL_0002)나 예외처리(RC_OL_0007)에서 + * 수행한다. 따라서 커서/트랜잭션 경계가 필요 없다. */ #include #include @@ -89,37 +109,90 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0005.pgc" +#line 35 "app/online/rc_ol_0005.pgc" +#define RC_ROUNDING_TOL 10L /* 반올림 허용 오차(원) */ + TX_SERVICE(RC_OL_0005, ctx) { rc_rec_t rec; char key[16]; + long appr_amt = 0; /* 승인금액 */ + long diff; /* 차액 = 매입금액 - 승인금액 */ + long absd; /* 차액 절대값 */ int rc; + const char *klass; - tx_log(TX_LOG_INFO, "[RC_OL_0005] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0005] 승인-매입 금액불일치 검출 서비스 진입"); + /* --- 요청 필드 추출 --- */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0005] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_getlong(ctx->in, "APPRAMT", &appr_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0005] APPRAMT(승인금액) 누락 key=%s", key); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + /* --- 원장 조회 (검출 전용, 상태 변경 없음) --- */ memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0005] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0005] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + /* --- 차액 계산 및 분류 --- */ + diff = rec.amount - appr_amt; + absd = (diff < 0) ? -diff : diff; + + tx_log(TX_LOG_INFO, + "[RC_OL_0005] 검출 대상 key=%s 가맹점=%s 매입=%ld 승인=%ld 상태=%s", + key, rec.merch_id, rec.amount, appr_amt, rec.status); + + if (diff == 0) { + klass = "EXACT"; + tx_log(TX_LOG_INFO, "[RC_OL_0005] 완전일치 key=%s amount=%ld", + key, rec.amount); + } else if (absd <= RC_ROUNDING_TOL) { + klass = "ROUNDING"; + tx_log(TX_LOG_INFO, + "[RC_OL_0005] 반올림오차 key=%s 차액=%ld(허용<=%ld)", + key, diff, RC_ROUNDING_TOL); + } else { + klass = "MISMATCH"; + tx_log(TX_LOG_WARN, + "[RC_OL_0005] 실질 불일치 key=%s 차액=%ld 절대차=%ld", + key, diff, absd); + } + + /* --- 차액 방향 보조 로그 (초과매입/과소매입 구분) --- */ + if (diff > 0) + tx_log(TX_LOG_INFO, + "[RC_OL_0005] 매입 초과 key=%s (매입>승인) 초과액=%ld", + key, diff); + else if (diff < 0) + tx_log(TX_LOG_INFO, + "[RC_OL_0005] 매입 과소 key=%s (매입<승인) 부족액=%ld", + key, absd); + + /* --- 응답 구성 (원장 상태는 유지) --- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0005] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "APPRAMT", appr_amt); + tx_buf_setlong(ctx->out, "CAPAMT", rec.amount); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_setlong(ctx->out, "ABSDIFF", absd); + tx_buf_sets(ctx->out, "CLASS", klass); + + tx_log(TX_LOG_INFO, + "[RC_OL_0005] 검출완료 key=%s CLASS=%s DIFF=%ld ABSDIFF=%ld", + key, klass, diff, absd); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0005.pgc b/app/online/rc_ol_0005.pgc index 8e592f2..b3dbee2 100644 --- a/app/online/rc_ol_0005.pgc +++ b/app/online/rc_ol_0005.pgc @@ -1,9 +1,29 @@ -/* @tier easy @module rc @transform reconciliation-online */ +/* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0005.pgc - 대사 온라인 서비스 (RC_OL_0005) [tier=easy] + * rc_ol_0005.pgc - 승인-매입 금액불일치 검출 (단건, read-only) (RC_OL_0005) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 "승인금액(입력 APPRAMT)" 과 "매입금액 + * (원장 amount)" 의 차이를 계산하고 유형을 분류한다. 이 서비스는 검출 + * 전용이며 원장 상태를 절대 변경하지 않는다(read-only). + * + * 분류 규칙(차액 diff = amount - APPRAMT, absd = |diff|): + * - diff == 0 → "EXACT" (완전일치) + * - absd <= 10 → "ROUNDING" (반올림 오차 허용범위) + * - 그 외 → "MISMATCH" (실질 불일치) + * + * 응답: KEY, APPRAMT, CAPAMT(=amount), DIFF, ABSDIFF, CLASS + * + * 분류표: + * [절대차 absd] [CLASS] [의미] + * diff == 0 EXACT 승인/매입 완전일치 + * absd <= 10 ROUNDING 반올림/절사 오차 허용 + * absd > 10 MISMATCH 실질 금액 불일치(정산조정 필요) + * + * 설계 원칙: 본 서비스는 "검출(detection) 전용" 이다. rc_rec_update_status + * 를 호출하지 않으므로 어떤 상태전이도 발생하지 않으며, 반복 + * 호출해도 원장은 불변(idempotent read)이다. 실제 상태전이는 + * 승인-매입 대사 서비스(RC_OL_0002)나 예외처리(RC_OL_0007)에서 + * 수행한다. 따라서 커서/트랜잭션 경계가 필요 없다. */ #include #include @@ -14,34 +34,87 @@ EXEC SQL INCLUDE sqlca; +#define RC_ROUNDING_TOL 10L /* 반올림 허용 오차(원) */ + TX_SERVICE(RC_OL_0005, ctx) { rc_rec_t rec; char key[16]; + long appr_amt = 0; /* 승인금액 */ + long diff; /* 차액 = 매입금액 - 승인금액 */ + long absd; /* 차액 절대값 */ int rc; + const char *klass; - tx_log(TX_LOG_INFO, "[RC_OL_0005] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0005] 승인-매입 금액불일치 검출 서비스 진입"); + /* --- 요청 필드 추출 --- */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0005] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_getlong(ctx->in, "APPRAMT", &appr_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0005] APPRAMT(승인금액) 누락 key=%s", key); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + /* --- 원장 조회 (검출 전용, 상태 변경 없음) --- */ memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0005] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0005] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + /* --- 차액 계산 및 분류 --- */ + diff = rec.amount - appr_amt; + absd = (diff < 0) ? -diff : diff; + + tx_log(TX_LOG_INFO, + "[RC_OL_0005] 검출 대상 key=%s 가맹점=%s 매입=%ld 승인=%ld 상태=%s", + key, rec.merch_id, rec.amount, appr_amt, rec.status); + + if (diff == 0) { + klass = "EXACT"; + tx_log(TX_LOG_INFO, "[RC_OL_0005] 완전일치 key=%s amount=%ld", + key, rec.amount); + } else if (absd <= RC_ROUNDING_TOL) { + klass = "ROUNDING"; + tx_log(TX_LOG_INFO, + "[RC_OL_0005] 반올림오차 key=%s 차액=%ld(허용<=%ld)", + key, diff, RC_ROUNDING_TOL); + } else { + klass = "MISMATCH"; + tx_log(TX_LOG_WARN, + "[RC_OL_0005] 실질 불일치 key=%s 차액=%ld 절대차=%ld", + key, diff, absd); + } + + /* --- 차액 방향 보조 로그 (초과매입/과소매입 구분) --- */ + if (diff > 0) + tx_log(TX_LOG_INFO, + "[RC_OL_0005] 매입 초과 key=%s (매입>승인) 초과액=%ld", + key, diff); + else if (diff < 0) + tx_log(TX_LOG_INFO, + "[RC_OL_0005] 매입 과소 key=%s (매입<승인) 부족액=%ld", + key, absd); + + /* --- 응답 구성 (원장 상태는 유지) --- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0005] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "APPRAMT", appr_amt); + tx_buf_setlong(ctx->out, "CAPAMT", rec.amount); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_setlong(ctx->out, "ABSDIFF", absd); + tx_buf_sets(ctx->out, "CLASS", klass); + + tx_log(TX_LOG_INFO, + "[RC_OL_0005] 검출완료 key=%s CLASS=%s DIFF=%ld ABSDIFF=%ld", + key, klass, diff, absd); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0006.c b/app/online/rc_ol_0006.c index 9e4b5c5..e10d34e 100644 --- a/app/online/rc_ol_0006.c +++ b/app/online/rc_ol_0006.c @@ -6,12 +6,32 @@ /* End of automatic include section */ #line 1 "app/online/rc_ol_0006.pgc" -/* @tier hard @module rc @transform reconciliation-online */ +/* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0006.pgc - 대사 온라인 서비스 (RC_OL_0006) [tier=hard] + * rc_ol_0006.pgc - 건수 불일치 검출 온라인 서비스 (RC_OL_0006) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 영업일(BIZDATE)에 대해 상태별 원장 건수를 집계하여 대사 진행 + * 상황을 점검한다. 상태별로 rc_rec_count 를 호출한다: + * 접수('R'), 매입확정('M'), 불일치('U'), 정산완료('S'). + * + * 업무규칙: + * - total = 접수 + 매입확정 + 불일치 + 정산완료 + * - 접수('R') 건이 하나라도 남아 있으면 아직 처리되지 않은 미처리 + * 잔존이 있으므로 MISMATCH=Y, 없으면 MISMATCH=N. + * + * 응답: BIZDATE, CNTR, CNTM, CNTU, CNTS, TOTAL, MISMATCH + * + * 상태 코드 대응: + * CNTR 접수/승인('R') - 아직 대사되지 않은 미처리 건 + * CNTM 매입확정('M') - 승인-매입 대사 완료 건 + * CNTU 불일치/보류('U') - 예외처리 대기 건 + * CNTS 정산완료('S') - 입금대사까지 종료된 건 + * + * 판정 원칙: 하루의 대사가 완결되면 접수('R') 잔량이 0 이어야 한다. + * CNTR > 0 이면 미처리 잔존으로 보아 MISMATCH=Y 를 통지한다. + * (CNTU 는 예외처리 트랙으로 별도 관리하므로 여기서는 접수 + * 잔량만을 완결 지표로 삼는다.) 상태별 단순 COUNT 집계만 + * 수행하므로 커서/트랜잭션 경계가 필요 없다. */ #include #include @@ -89,88 +109,93 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0006.pgc" +#line 35 "app/online/rc_ol_0006.pgc" TX_SERVICE(RC_OL_0006, ctx) { - rc_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char bizdate[16]; + long cnt_r, cnt_m, cnt_u, cnt_s; + long total; + const char *mismatch; - tx_log(TX_LOG_INFO, "[RC_OL_0006] 대사 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0006] 건수 불일치 검출 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0006] 필수 필드 누락"); + /* --- 요청 필드 추출 및 검증 --- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0006] BIZDATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0006] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0006] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + /* --- 상태별 건수 집계 (음수 리턴은 조회오류) --- */ + cnt_r = rc_rec_count(bizdate, RC_ST_RECV); + cnt_m = rc_rec_count(bizdate, RC_ST_DONE); + cnt_u = rc_rec_count(bizdate, RC_ST_FAIL); + cnt_s = rc_rec_count(bizdate, RC_ST_SETTLED); + + if (cnt_r < 0 || cnt_m < 0 || cnt_u < 0 || cnt_s < 0) { + tx_log(TX_LOG_ERROR, + "[RC_OL_0006] 건수 집계 실패 date=%s R=%ld M=%ld U=%ld S=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + total = cnt_r + cnt_m + cnt_u + cnt_s; - rc = rc_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0006] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; + tx_log(TX_LOG_INFO, + "[RC_OL_0006] 집계 date=%s R=%ld M=%ld U=%ld S=%ld 총=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s, total); + + /* 총건수 0 이면 해당 영업일 대사 대상 자체가 없음 (참고 로그) */ + if (total == 0) + tx_log(TX_LOG_WARN, + "[RC_OL_0006] 대사 대상 없음 date=%s (원장 무건)", bizdate); + + /* 불일치('U') 누적 경고 (완결 지표와 별개로 예외 트랙 가시화) */ + if (cnt_u > 0) + tx_log(TX_LOG_WARN, + "[RC_OL_0006] 예외처리 대기 date=%s 불일치=%ld", bizdate, cnt_u); + + /* --- 미처리(접수) 잔존 여부로 불일치 플래그 결정 --- */ + if (cnt_r > 0) { + mismatch = "Y"; + tx_log(TX_LOG_WARN, + "[RC_OL_0006] 미처리 잔존 date=%s 접수=%ld → MISMATCH", + bizdate, cnt_r); + } else { + mismatch = "N"; + tx_log(TX_LOG_INFO, + "[RC_OL_0006] 미처리 없음 date=%s 총=%ld → 정상", + bizdate, total); } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("RC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[RC_OL_0006] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (rc_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - + /* --- 응답 구성 --- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[RC_OL_0006] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNTR", cnt_r); + tx_buf_setlong(ctx->out, "CNTM", cnt_m); + tx_buf_setlong(ctx->out, "CNTU", cnt_u); + tx_buf_setlong(ctx->out, "CNTS", cnt_s); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_buf_sets(ctx->out, "MISMATCH", mismatch); + + /* 진행률 참고 로그: (매입확정+정산완료) / 총건수 */ + if (total > 0) { + long done_cnt = cnt_m + cnt_s; + tx_log(TX_LOG_INFO, + "[RC_OL_0006] 검출완료 date=%s 완료=%ld/%ld MISMATCH=%s", + bizdate, done_cnt, total, mismatch); + } else { + tx_log(TX_LOG_INFO, + "[RC_OL_0006] 검출완료 date=%s 대상없음 MISMATCH=%s", + bizdate, mismatch); + } tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0006.pgc b/app/online/rc_ol_0006.pgc index ce20689..cfecc0f 100644 --- a/app/online/rc_ol_0006.pgc +++ b/app/online/rc_ol_0006.pgc @@ -1,9 +1,29 @@ -/* @tier hard @module rc @transform reconciliation-online */ +/* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0006.pgc - 대사 온라인 서비스 (RC_OL_0006) [tier=hard] + * rc_ol_0006.pgc - 건수 불일치 검출 온라인 서비스 (RC_OL_0006) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 영업일(BIZDATE)에 대해 상태별 원장 건수를 집계하여 대사 진행 + * 상황을 점검한다. 상태별로 rc_rec_count 를 호출한다: + * 접수('R'), 매입확정('M'), 불일치('U'), 정산완료('S'). + * + * 업무규칙: + * - total = 접수 + 매입확정 + 불일치 + 정산완료 + * - 접수('R') 건이 하나라도 남아 있으면 아직 처리되지 않은 미처리 + * 잔존이 있으므로 MISMATCH=Y, 없으면 MISMATCH=N. + * + * 응답: BIZDATE, CNTR, CNTM, CNTU, CNTS, TOTAL, MISMATCH + * + * 상태 코드 대응: + * CNTR 접수/승인('R') - 아직 대사되지 않은 미처리 건 + * CNTM 매입확정('M') - 승인-매입 대사 완료 건 + * CNTU 불일치/보류('U') - 예외처리 대기 건 + * CNTS 정산완료('S') - 입금대사까지 종료된 건 + * + * 판정 원칙: 하루의 대사가 완결되면 접수('R') 잔량이 0 이어야 한다. + * CNTR > 0 이면 미처리 잔존으로 보아 MISMATCH=Y 를 통지한다. + * (CNTU 는 예외처리 트랙으로 별도 관리하므로 여기서는 접수 + * 잔량만을 완결 지표로 삼는다.) 상태별 단순 COUNT 집계만 + * 수행하므로 커서/트랜잭션 경계가 필요 없다. */ #include #include @@ -16,83 +36,88 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0006, ctx) { - rc_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char bizdate[16]; + long cnt_r, cnt_m, cnt_u, cnt_s; + long total; + const char *mismatch; - tx_log(TX_LOG_INFO, "[RC_OL_0006] 대사 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0006] 건수 불일치 검출 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0006] 필수 필드 누락"); + /* --- 요청 필드 추출 및 검증 --- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0006] BIZDATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0006] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0006] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + /* --- 상태별 건수 집계 (음수 리턴은 조회오류) --- */ + cnt_r = rc_rec_count(bizdate, RC_ST_RECV); + cnt_m = rc_rec_count(bizdate, RC_ST_DONE); + cnt_u = rc_rec_count(bizdate, RC_ST_FAIL); + cnt_s = rc_rec_count(bizdate, RC_ST_SETTLED); + + if (cnt_r < 0 || cnt_m < 0 || cnt_u < 0 || cnt_s < 0) { + tx_log(TX_LOG_ERROR, + "[RC_OL_0006] 건수 집계 실패 date=%s R=%ld M=%ld U=%ld S=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + total = cnt_r + cnt_m + cnt_u + cnt_s; - rc = rc_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0006] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; + tx_log(TX_LOG_INFO, + "[RC_OL_0006] 집계 date=%s R=%ld M=%ld U=%ld S=%ld 총=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s, total); + + /* 총건수 0 이면 해당 영업일 대사 대상 자체가 없음 (참고 로그) */ + if (total == 0) + tx_log(TX_LOG_WARN, + "[RC_OL_0006] 대사 대상 없음 date=%s (원장 무건)", bizdate); + + /* 불일치('U') 누적 경고 (완결 지표와 별개로 예외 트랙 가시화) */ + if (cnt_u > 0) + tx_log(TX_LOG_WARN, + "[RC_OL_0006] 예외처리 대기 date=%s 불일치=%ld", bizdate, cnt_u); + + /* --- 미처리(접수) 잔존 여부로 불일치 플래그 결정 --- */ + if (cnt_r > 0) { + mismatch = "Y"; + tx_log(TX_LOG_WARN, + "[RC_OL_0006] 미처리 잔존 date=%s 접수=%ld → MISMATCH", + bizdate, cnt_r); + } else { + mismatch = "N"; + tx_log(TX_LOG_INFO, + "[RC_OL_0006] 미처리 없음 date=%s 총=%ld → 정상", + bizdate, total); } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("RC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[RC_OL_0006] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (rc_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - + /* --- 응답 구성 --- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[RC_OL_0006] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNTR", cnt_r); + tx_buf_setlong(ctx->out, "CNTM", cnt_m); + tx_buf_setlong(ctx->out, "CNTU", cnt_u); + tx_buf_setlong(ctx->out, "CNTS", cnt_s); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_buf_sets(ctx->out, "MISMATCH", mismatch); + + /* 진행률 참고 로그: (매입확정+정산완료) / 총건수 */ + if (total > 0) { + long done_cnt = cnt_m + cnt_s; + tx_log(TX_LOG_INFO, + "[RC_OL_0006] 검출완료 date=%s 완료=%ld/%ld MISMATCH=%s", + bizdate, done_cnt, total, mismatch); + } else { + tx_log(TX_LOG_INFO, + "[RC_OL_0006] 검출완료 date=%s 대상없음 MISMATCH=%s", + bizdate, mismatch); + } tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0007.c b/app/online/rc_ol_0007.c index b3ecb34..54d91ce 100644 --- a/app/online/rc_ol_0007.c +++ b/app/online/rc_ol_0007.c @@ -6,12 +6,34 @@ /* End of automatic include section */ #line 1 "app/online/rc_ol_0007.pgc" -/* @tier easy @module rc @transform reconciliation-online */ +/* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0007.pgc - 대사 온라인 서비스 (RC_OL_0007) [tier=easy] + * rc_ol_0007.pgc - 대사 불일치 예외처리 온라인 서비스 (RC_OL_0007) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 대사 불일치('U') 건에 대한 예외처리를 + * 수행한다. 불일치 건만 재대사 대기 상태로 되돌린다. + * + * 업무규칙: + * - 원장 상태가 불일치('U') 가 아니면 예외처리 대상이 아니므로 상태를 + * 변경하지 않고 RESULT=SKIP 으로 TX_OK 응답한다. + * - 불일치('U') 인 경우 예외처리 → 접수('R') 로 되돌려 재대사 대기로 + * 전환한다(ACTION=REQUEUE). 사유(REASON)는 금액 부호로 결정한다: + * amount <= 0 → "금액오류" + * amount > 0 → "차액검토" + * + * 응답: KEY, OLDSTATUS, NEWSTATUS, REASON, ACTION + * + * 동작 요약: + * [원장 상태] [ACTION] [NEWSTATUS] [비고] + * U (불일치) REQUEUE R 재대사 대기로 복귀 + * 그 외(R/M/S) SKIP (원상태) 변경 없음, TX_OK + * + * 사유(REASON) 결정: 불일치 건의 원장 금액 부호로 구분한다. + * amount <= 0 → "금액오류" (금액 자체가 비정상 → 원천 데이터 점검) + * amount > 0 → "차액검토" (금액은 정상, 승인/매입 차액 재확인 필요) + * + * 본 서비스는 단건 상태전이 1회만 수행하므로 커서/명시적 트랜잭션 경계가 + * 필요 없다. 재대사는 후속 승인-매입 대사 서비스에서 수행된다. */ #include #include @@ -89,37 +111,90 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0007.pgc" +#line 37 "app/online/rc_ol_0007.pgc" TX_SERVICE(RC_OL_0007, ctx) { rc_rec_t rec; char key[16]; + char old_status[2]; int rc; + const char *reason; - tx_log(TX_LOG_INFO, "[RC_OL_0007] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0007] 대사 불일치 예외처리 서비스 진입"); + /* --- 요청 필드 추출 --- */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0007] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* --- 원장 조회 --- */ memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0007] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0007] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + strncpy(old_status, rec.status, sizeof(old_status) - 1); + old_status[sizeof(old_status) - 1] = '\0'; + + tx_log(TX_LOG_INFO, + "[RC_OL_0007] 예외처리 검토 key=%s 가맹점=%s 금액=%ld 현상태=%s", + key, rec.merch_id, rec.amount, old_status); + + /* --- 예외처리 대상 판별: 불일치('U') 만 --- */ + if (strcmp(rec.status, RC_ST_FAIL) != 0) { + tx_log(TX_LOG_INFO, + "[RC_OL_0007] 예외처리 대상 아님 key=%s status=%s → SKIP", + key, rec.status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "OLDSTATUS", old_status); + tx_buf_sets(ctx->out, "NEWSTATUS", old_status); + tx_buf_sets(ctx->out, "REASON", "대상아님"); + tx_buf_sets(ctx->out, "ACTION", "SKIP"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* --- 사유 결정: 금액 부호 --- */ + if (rec.amount <= 0) { + reason = "금액오류"; + tx_log(TX_LOG_WARN, + "[RC_OL_0007] 비정상 금액 감지 key=%s amount=%ld → 금액오류", + key, rec.amount); + } else { + reason = "차액검토"; + tx_log(TX_LOG_INFO, + "[RC_OL_0007] 정상 금액, 차액 재검토 지정 key=%s amount=%ld", + key, rec.amount); + } + + /* --- 재대사 대기(접수) 로 전이 --- */ + rc = rc_rec_update_status(key, RC_ST_RECV); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0007] 재대사 전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + + tx_log(TX_LOG_INFO, + "[RC_OL_0007] 예외처리 완료 key=%s %s→R 사유=%s", + key, old_status, reason); + + /* --- 응답 구성 --- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0007] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "OLDSTATUS", old_status); + tx_buf_sets(ctx->out, "NEWSTATUS", RC_ST_RECV); + tx_buf_sets(ctx->out, "REASON", reason); + tx_buf_sets(ctx->out, "ACTION", "REQUEUE"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0007.pgc b/app/online/rc_ol_0007.pgc index 0272b8e..8f71105 100644 --- a/app/online/rc_ol_0007.pgc +++ b/app/online/rc_ol_0007.pgc @@ -1,9 +1,31 @@ -/* @tier easy @module rc @transform reconciliation-online */ +/* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0007.pgc - 대사 온라인 서비스 (RC_OL_0007) [tier=easy] + * rc_ol_0007.pgc - 대사 불일치 예외처리 온라인 서비스 (RC_OL_0007) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 대사 불일치('U') 건에 대한 예외처리를 + * 수행한다. 불일치 건만 재대사 대기 상태로 되돌린다. + * + * 업무규칙: + * - 원장 상태가 불일치('U') 가 아니면 예외처리 대상이 아니므로 상태를 + * 변경하지 않고 RESULT=SKIP 으로 TX_OK 응답한다. + * - 불일치('U') 인 경우 예외처리 → 접수('R') 로 되돌려 재대사 대기로 + * 전환한다(ACTION=REQUEUE). 사유(REASON)는 금액 부호로 결정한다: + * amount <= 0 → "금액오류" + * amount > 0 → "차액검토" + * + * 응답: KEY, OLDSTATUS, NEWSTATUS, REASON, ACTION + * + * 동작 요약: + * [원장 상태] [ACTION] [NEWSTATUS] [비고] + * U (불일치) REQUEUE R 재대사 대기로 복귀 + * 그 외(R/M/S) SKIP (원상태) 변경 없음, TX_OK + * + * 사유(REASON) 결정: 불일치 건의 원장 금액 부호로 구분한다. + * amount <= 0 → "금액오류" (금액 자체가 비정상 → 원천 데이터 점검) + * amount > 0 → "차액검토" (금액은 정상, 승인/매입 차액 재확인 필요) + * + * 본 서비스는 단건 상태전이 1회만 수행하므로 커서/명시적 트랜잭션 경계가 + * 필요 없다. 재대사는 후속 승인-매입 대사 서비스에서 수행된다. */ #include #include @@ -18,30 +40,83 @@ TX_SERVICE(RC_OL_0007, ctx) { rc_rec_t rec; char key[16]; + char old_status[2]; int rc; + const char *reason; - tx_log(TX_LOG_INFO, "[RC_OL_0007] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0007] 대사 불일치 예외처리 서비스 진입"); + /* --- 요청 필드 추출 --- */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0007] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* --- 원장 조회 --- */ memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0007] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0007] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + strncpy(old_status, rec.status, sizeof(old_status) - 1); + old_status[sizeof(old_status) - 1] = '\0'; + + tx_log(TX_LOG_INFO, + "[RC_OL_0007] 예외처리 검토 key=%s 가맹점=%s 금액=%ld 현상태=%s", + key, rec.merch_id, rec.amount, old_status); + + /* --- 예외처리 대상 판별: 불일치('U') 만 --- */ + if (strcmp(rec.status, RC_ST_FAIL) != 0) { + tx_log(TX_LOG_INFO, + "[RC_OL_0007] 예외처리 대상 아님 key=%s status=%s → SKIP", + key, rec.status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "OLDSTATUS", old_status); + tx_buf_sets(ctx->out, "NEWSTATUS", old_status); + tx_buf_sets(ctx->out, "REASON", "대상아님"); + tx_buf_sets(ctx->out, "ACTION", "SKIP"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* --- 사유 결정: 금액 부호 --- */ + if (rec.amount <= 0) { + reason = "금액오류"; + tx_log(TX_LOG_WARN, + "[RC_OL_0007] 비정상 금액 감지 key=%s amount=%ld → 금액오류", + key, rec.amount); + } else { + reason = "차액검토"; + tx_log(TX_LOG_INFO, + "[RC_OL_0007] 정상 금액, 차액 재검토 지정 key=%s amount=%ld", + key, rec.amount); + } + + /* --- 재대사 대기(접수) 로 전이 --- */ + rc = rc_rec_update_status(key, RC_ST_RECV); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0007] 재대사 전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + + tx_log(TX_LOG_INFO, + "[RC_OL_0007] 예외처리 완료 key=%s %s→R 사유=%s", + key, old_status, reason); + + /* --- 응답 구성 --- */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0007] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "OLDSTATUS", old_status); + tx_buf_sets(ctx->out, "NEWSTATUS", RC_ST_RECV); + tx_buf_sets(ctx->out, "REASON", reason); + tx_buf_sets(ctx->out, "ACTION", "REQUEUE"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0008.c b/app/online/rc_ol_0008.c index 9666a68..629fd71 100644 --- a/app/online/rc_ol_0008.c +++ b/app/online/rc_ol_0008.c @@ -8,10 +8,14 @@ #line 1 "app/online/rc_ol_0008.pgc" /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0008.pgc - 대사 온라인 서비스 (RC_OL_0008) [tier=easy] + * rc_ol_0008.pgc - 재대사 (단건) 온라인 서비스 (RC_OL_0008) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 원장 1건을 읽어 재대사 대상으로 되돌린다. + * 업무규칙: + * - 정산완료('S') : 이미 정산된 건은 재대사 불가 → REJECT (상태 불변) + * - 불일치('U') / 매입확정('M') : 접수('R') 로 되돌려 재대사 준비 + * - 접수('R') : 이미 재대사 대상 → NOOP (상태 불변) + * 응답 KEY, OLDSTATUS, NEWSTATUS, RESULT 를 구성한다. */ #include #include @@ -89,16 +93,19 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0008.pgc" +#line 19 "app/online/rc_ol_0008.pgc" TX_SERVICE(RC_OL_0008, ctx) { rc_rec_t rec; char key[16]; + char old_status[2]; + const char *new_status; + const char *result; int rc; - tx_log(TX_LOG_INFO, "[RC_OL_0008] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0008] 재대사(단건) 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0008] KEY 누락"); @@ -109,17 +116,52 @@ TX_SERVICE(RC_OL_0008, ctx) memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0008] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0008] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + /* 조회된 현재 상태 보존 */ + memset(old_status, 0, sizeof(old_status)); + strncpy(old_status, rec.status, sizeof(old_status) - 1); + new_status = old_status; /* 기본: 상태 불변 */ + + if (strcmp(old_status, RC_ST_SETTLED) == 0) { + /* 정산완료 건은 재대사 불가 */ + result = "REJECT"; + tx_log(TX_LOG_WARN, "[RC_OL_0008] 정산완료 건 재대사 거부 key=%s", key); + } else if (strcmp(old_status, RC_ST_RECV) == 0) { + /* 이미 접수 상태 → 재대사 필요 없음 */ + result = "NOOP"; + tx_log(TX_LOG_INFO, "[RC_OL_0008] 이미 재대사 대상 key=%s", key); + } else if (strcmp(old_status, RC_ST_FAIL) == 0 || + strcmp(old_status, RC_ST_DONE) == 0) { + /* 불일치/매입확정 → 접수로 되돌려 재대사 준비 */ + rc = rc_rec_update_status(key, RC_ST_RECV); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0008] 상태전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + new_status = RC_ST_RECV; + result = "RERECON"; + tx_log(TX_LOG_INFO, "[RC_OL_0008] 재대사 전이 완료 key=%s %s->R", + key, old_status); + } else { + /* 알 수 없는 상태값 방어 */ + result = "REJECT"; + tx_log(TX_LOG_WARN, "[RC_OL_0008] 알 수 없는 상태 key=%s status=%s", + key, old_status); + } + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "OLDSTATUS", old_status); + tx_buf_sets(ctx->out, "NEWSTATUS", new_status); + tx_buf_sets(ctx->out, "RESULT", result); + tx_log(TX_LOG_INFO, "[RC_OL_0008] 재대사 처리결과 key=%s result=%s", + key, result); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0008.pgc b/app/online/rc_ol_0008.pgc index 1533fc5..3d0d814 100644 --- a/app/online/rc_ol_0008.pgc +++ b/app/online/rc_ol_0008.pgc @@ -1,9 +1,13 @@ /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0008.pgc - 대사 온라인 서비스 (RC_OL_0008) [tier=easy] + * rc_ol_0008.pgc - 재대사 (단건) 온라인 서비스 (RC_OL_0008) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 원장 1건을 읽어 재대사 대상으로 되돌린다. + * 업무규칙: + * - 정산완료('S') : 이미 정산된 건은 재대사 불가 → REJECT (상태 불변) + * - 불일치('U') / 매입확정('M') : 접수('R') 로 되돌려 재대사 준비 + * - 접수('R') : 이미 재대사 대상 → NOOP (상태 불변) + * 응답 KEY, OLDSTATUS, NEWSTATUS, RESULT 를 구성한다. */ #include #include @@ -18,9 +22,12 @@ TX_SERVICE(RC_OL_0008, ctx) { rc_rec_t rec; char key[16]; + char old_status[2]; + const char *new_status; + const char *result; int rc; - tx_log(TX_LOG_INFO, "[RC_OL_0008] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0008] 재대사(단건) 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0008] KEY 누락"); @@ -31,17 +38,52 @@ TX_SERVICE(RC_OL_0008, ctx) memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0008] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0008] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + /* 조회된 현재 상태 보존 */ + memset(old_status, 0, sizeof(old_status)); + strncpy(old_status, rec.status, sizeof(old_status) - 1); + new_status = old_status; /* 기본: 상태 불변 */ + + if (strcmp(old_status, RC_ST_SETTLED) == 0) { + /* 정산완료 건은 재대사 불가 */ + result = "REJECT"; + tx_log(TX_LOG_WARN, "[RC_OL_0008] 정산완료 건 재대사 거부 key=%s", key); + } else if (strcmp(old_status, RC_ST_RECV) == 0) { + /* 이미 접수 상태 → 재대사 필요 없음 */ + result = "NOOP"; + tx_log(TX_LOG_INFO, "[RC_OL_0008] 이미 재대사 대상 key=%s", key); + } else if (strcmp(old_status, RC_ST_FAIL) == 0 || + strcmp(old_status, RC_ST_DONE) == 0) { + /* 불일치/매입확정 → 접수로 되돌려 재대사 준비 */ + rc = rc_rec_update_status(key, RC_ST_RECV); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0008] 상태전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + new_status = RC_ST_RECV; + result = "RERECON"; + tx_log(TX_LOG_INFO, "[RC_OL_0008] 재대사 전이 완료 key=%s %s->R", + key, old_status); + } else { + /* 알 수 없는 상태값 방어 */ + result = "REJECT"; + tx_log(TX_LOG_WARN, "[RC_OL_0008] 알 수 없는 상태 key=%s status=%s", + key, old_status); + } + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "OLDSTATUS", old_status); + tx_buf_sets(ctx->out, "NEWSTATUS", new_status); + tx_buf_sets(ctx->out, "RESULT", result); + tx_log(TX_LOG_INFO, "[RC_OL_0008] 재대사 처리결과 key=%s result=%s", + key, result); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0009.c b/app/online/rc_ol_0009.c index 8a458a2..00a2d5b 100644 --- a/app/online/rc_ol_0009.c +++ b/app/online/rc_ol_0009.c @@ -8,10 +8,14 @@ #line 1 "app/online/rc_ol_0009.pgc" /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0009.pgc - 대사 온라인 서비스 (RC_OL_0009) [tier=medium] + * rc_ol_0009.pgc - 대사 보류(hold) 온라인 서비스 (RC_OL_0009) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 원장 1건을 보류 상태로 전이한다. REASON(사유) 은 선택 입력. + * 업무규칙: + * - 접수('R') / 매입확정('M') : 보류('U') 마커로 전이, 사유 로깅 + * - 이미 보류('U') : ALREADYHELD (상태 불변) + * - 정산완료('S') : REJECT (상태 불변) + * 응답 KEY, OLDSTATUS, NEWSTATUS, REASON, RESULT 를 구성한다. */ #include #include @@ -89,60 +93,81 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0009.pgc" +#line 19 "app/online/rc_ol_0009.pgc" TX_SERVICE(RC_OL_0009, ctx) { rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + char reason[128]; + char old_status[2]; + const char *new_status; + const char *result; int rc; - tx_log(TX_LOG_INFO, "[RC_OL_0009] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0009] 대사 보류(hold) 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0009] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0009] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0009] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0009] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + /* REASON 은 선택 입력 - 없으면 기본 사유 */ + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) { + strncpy(reason, "사유미기재", sizeof(reason) - 1); + reason[sizeof(reason) - 1] = '\0'; } memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); + rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0009] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[RC_OL_0009] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + memset(old_status, 0, sizeof(old_status)); + strncpy(old_status, rec.status, sizeof(old_status) - 1); + new_status = old_status; /* 기본: 상태 불변 */ + + if (strcmp(old_status, RC_ST_RECV) == 0 || + strcmp(old_status, RC_ST_DONE) == 0) { + /* 접수/매입확정 → 보류 마커 전이 */ + rc = rc_rec_update_status(key, RC_ST_FAIL); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0009] 보류 전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + new_status = RC_ST_FAIL; + result = "HELD"; + tx_log(TX_LOG_INFO, "[RC_OL_0009] 보류 처리 key=%s %s->U 사유=%s", + key, old_status, reason); + } else if (strcmp(old_status, RC_ST_FAIL) == 0) { + /* 이미 보류 상태 */ + result = "ALREADYHELD"; + tx_log(TX_LOG_INFO, "[RC_OL_0009] 이미 보류 상태 key=%s", key); + } else if (strcmp(old_status, RC_ST_SETTLED) == 0) { + /* 정산완료 건은 보류 불가 */ + result = "REJECT"; + tx_log(TX_LOG_WARN, "[RC_OL_0009] 정산완료 건 보류 거부 key=%s", key); + } else { + result = "REJECT"; + tx_log(TX_LOG_WARN, "[RC_OL_0009] 알 수 없는 상태 key=%s status=%s", + key, old_status); + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0009] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "OLDSTATUS", old_status); + tx_buf_sets(ctx->out, "NEWSTATUS", new_status); + tx_buf_sets(ctx->out, "REASON", reason); + tx_buf_sets(ctx->out, "RESULT", result); + tx_log(TX_LOG_INFO, "[RC_OL_0009] 보류 처리결과 key=%s result=%s", + key, result); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0009.pgc b/app/online/rc_ol_0009.pgc index 2247550..9b8bdf9 100644 --- a/app/online/rc_ol_0009.pgc +++ b/app/online/rc_ol_0009.pgc @@ -1,9 +1,13 @@ /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0009.pgc - 대사 온라인 서비스 (RC_OL_0009) [tier=medium] + * rc_ol_0009.pgc - 대사 보류(hold) 온라인 서비스 (RC_OL_0009) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 원장 1건을 보류 상태로 전이한다. REASON(사유) 은 선택 입력. + * 업무규칙: + * - 접수('R') / 매입확정('M') : 보류('U') 마커로 전이, 사유 로깅 + * - 이미 보류('U') : ALREADYHELD (상태 불변) + * - 정산완료('S') : REJECT (상태 불변) + * 응답 KEY, OLDSTATUS, NEWSTATUS, REASON, RESULT 를 구성한다. */ #include #include @@ -17,54 +21,75 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0009, ctx) { rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + char reason[128]; + char old_status[2]; + const char *new_status; + const char *result; int rc; - tx_log(TX_LOG_INFO, "[RC_OL_0009] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0009] 대사 보류(hold) 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0009] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0009] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0009] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0009] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + /* REASON 은 선택 입력 - 없으면 기본 사유 */ + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) { + strncpy(reason, "사유미기재", sizeof(reason) - 1); + reason[sizeof(reason) - 1] = '\0'; } memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); + rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0009] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[RC_OL_0009] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + memset(old_status, 0, sizeof(old_status)); + strncpy(old_status, rec.status, sizeof(old_status) - 1); + new_status = old_status; /* 기본: 상태 불변 */ + + if (strcmp(old_status, RC_ST_RECV) == 0 || + strcmp(old_status, RC_ST_DONE) == 0) { + /* 접수/매입확정 → 보류 마커 전이 */ + rc = rc_rec_update_status(key, RC_ST_FAIL); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0009] 보류 전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + new_status = RC_ST_FAIL; + result = "HELD"; + tx_log(TX_LOG_INFO, "[RC_OL_0009] 보류 처리 key=%s %s->U 사유=%s", + key, old_status, reason); + } else if (strcmp(old_status, RC_ST_FAIL) == 0) { + /* 이미 보류 상태 */ + result = "ALREADYHELD"; + tx_log(TX_LOG_INFO, "[RC_OL_0009] 이미 보류 상태 key=%s", key); + } else if (strcmp(old_status, RC_ST_SETTLED) == 0) { + /* 정산완료 건은 보류 불가 */ + result = "REJECT"; + tx_log(TX_LOG_WARN, "[RC_OL_0009] 정산완료 건 보류 거부 key=%s", key); + } else { + result = "REJECT"; + tx_log(TX_LOG_WARN, "[RC_OL_0009] 알 수 없는 상태 key=%s status=%s", + key, old_status); + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0009] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "OLDSTATUS", old_status); + tx_buf_sets(ctx->out, "NEWSTATUS", new_status); + tx_buf_sets(ctx->out, "REASON", reason); + tx_buf_sets(ctx->out, "RESULT", result); + tx_log(TX_LOG_INFO, "[RC_OL_0009] 보류 처리결과 key=%s result=%s", + key, result); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0010.c b/app/online/rc_ol_0010.c index 1870451..763a663 100644 --- a/app/online/rc_ol_0010.c +++ b/app/online/rc_ol_0010.c @@ -8,10 +8,13 @@ #line 1 "app/online/rc_ol_0010.pgc" /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0010.pgc - 대사 온라인 서비스 (RC_OL_0010) [tier=easy] + * rc_ol_0010.pgc - 대사 보류해제(release) 온라인 서비스 (RC_OL_0010) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 원장 1건의 보류를 해제하여 접수 상태로 되돌린다. + * 업무규칙: + * - 보류('U') : 접수('R') 로 전이 (보류해제) + * - 그 외 상태 : NOTHELD (보류 상태가 아님, 상태 불변) + * 응답 KEY, OLDSTATUS, NEWSTATUS, RESULT 를 구성한다. */ #include #include @@ -89,16 +92,19 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0010.pgc" +#line 18 "app/online/rc_ol_0010.pgc" TX_SERVICE(RC_OL_0010, ctx) { rc_rec_t rec; char key[16]; + char old_status[2]; + const char *new_status; + const char *result; int rc; - tx_log(TX_LOG_INFO, "[RC_OL_0010] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0010] 대사 보류해제(release) 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0010] KEY 누락"); @@ -109,17 +115,41 @@ TX_SERVICE(RC_OL_0010, ctx) memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0010] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0010] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + memset(old_status, 0, sizeof(old_status)); + strncpy(old_status, rec.status, sizeof(old_status) - 1); + new_status = old_status; /* 기본: 상태 불변 */ + + if (strcmp(old_status, RC_ST_FAIL) == 0) { + /* 보류(U) → 접수(R) 로 보류해제 */ + rc = rc_rec_update_status(key, RC_ST_RECV); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0010] 보류해제 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + new_status = RC_ST_RECV; + result = "RELEASED"; + tx_log(TX_LOG_INFO, "[RC_OL_0010] 보류해제 완료 key=%s U->R", key); + } else { + /* 보류 상태가 아니면 처리하지 않음 */ + result = "NOTHELD"; + tx_log(TX_LOG_INFO, "[RC_OL_0010] 보류 상태 아님 key=%s status=%s", + key, old_status); + } + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "OLDSTATUS", old_status); + tx_buf_sets(ctx->out, "NEWSTATUS", new_status); + tx_buf_sets(ctx->out, "RESULT", result); + tx_log(TX_LOG_INFO, "[RC_OL_0010] 보류해제 처리결과 key=%s result=%s", + key, result); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0010.pgc b/app/online/rc_ol_0010.pgc index 5da4c7a..9c21048 100644 --- a/app/online/rc_ol_0010.pgc +++ b/app/online/rc_ol_0010.pgc @@ -1,9 +1,12 @@ /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0010.pgc - 대사 온라인 서비스 (RC_OL_0010) [tier=easy] + * rc_ol_0010.pgc - 대사 보류해제(release) 온라인 서비스 (RC_OL_0010) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 원장 1건의 보류를 해제하여 접수 상태로 되돌린다. + * 업무규칙: + * - 보류('U') : 접수('R') 로 전이 (보류해제) + * - 그 외 상태 : NOTHELD (보류 상태가 아님, 상태 불변) + * 응답 KEY, OLDSTATUS, NEWSTATUS, RESULT 를 구성한다. */ #include #include @@ -18,9 +21,12 @@ TX_SERVICE(RC_OL_0010, ctx) { rc_rec_t rec; char key[16]; + char old_status[2]; + const char *new_status; + const char *result; int rc; - tx_log(TX_LOG_INFO, "[RC_OL_0010] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0010] 대사 보류해제(release) 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0010] KEY 누락"); @@ -31,17 +37,41 @@ TX_SERVICE(RC_OL_0010, ctx) memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0010] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0010] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + memset(old_status, 0, sizeof(old_status)); + strncpy(old_status, rec.status, sizeof(old_status) - 1); + new_status = old_status; /* 기본: 상태 불변 */ + + if (strcmp(old_status, RC_ST_FAIL) == 0) { + /* 보류(U) → 접수(R) 로 보류해제 */ + rc = rc_rec_update_status(key, RC_ST_RECV); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0010] 보류해제 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + new_status = RC_ST_RECV; + result = "RELEASED"; + tx_log(TX_LOG_INFO, "[RC_OL_0010] 보류해제 완료 key=%s U->R", key); + } else { + /* 보류 상태가 아니면 처리하지 않음 */ + result = "NOTHELD"; + tx_log(TX_LOG_INFO, "[RC_OL_0010] 보류 상태 아님 key=%s status=%s", + key, old_status); + } + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "OLDSTATUS", old_status); + tx_buf_sets(ctx->out, "NEWSTATUS", new_status); + tx_buf_sets(ctx->out, "RESULT", result); + tx_log(TX_LOG_INFO, "[RC_OL_0010] 보류해제 처리결과 key=%s result=%s", + key, result); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0011.c b/app/online/rc_ol_0011.c index b9454d9..78963bc 100644 --- a/app/online/rc_ol_0011.c +++ b/app/online/rc_ol_0011.c @@ -8,15 +8,22 @@ #line 1 "app/online/rc_ol_0011.pgc" /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0011.pgc - 대사 온라인 서비스 (RC_OL_0011) [tier=medium] + * rc_ol_0011.pgc - 카드사별 대사 조회 (CURSOR) 온라인 서비스 (RC_OL_0011) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 BIZDATE(영업일) 의 원장을 카드번호 순으로 커서 순회하며, 카드번호 + * 접두(BIN, 카드사 식별자) 가 일치하는 건만 집계한다. + * 업무규칙: + * - BIN 은 fetch 된 card_no 의 앞 strlen(BIN) 자리와 비교(전방일치) + * - 일치 건: 총건수(CNT), 금액합계(SUMAMT) 누적 + * - 매입확정('M')/정산완료('S') 는 대사완료 건(MATCHED) 으로 누적 + * - 대사율 RATE = MATCHED * 100 / CNT (0 나눗셈 방어) + * 응답 BIN, CNT, MATCHED, SUMAMT, RATE 를 구성한다. (읽기 전용) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -89,30 +96,47 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0011.pgc" +#line 22 "app/online/rc_ol_0011.pgc" TX_SERVICE(RC_OL_0011, ctx) { - rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + +#line 27 "app/online/rc_ol_0011.pgc" + char h_biz_date [ 9 ] ; + +#line 28 "app/online/rc_ol_0011.pgc" + char h_card_no [ 20 ] ; + +#line 29 "app/online/rc_ol_0011.pgc" + char h_status [ 2 ] ; + +#line 30 "app/online/rc_ol_0011.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 31 "app/online/rc_ol_0011.pgc" - tx_log(TX_LOG_INFO, "[RC_OL_0011] 대사 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0011] 필수 필드 누락"); + char bizdate[16]; + char bin[20]; + size_t binlen; + long cnt = 0, matched = 0, sumamt = 0, rate = 0; + int opened = 0; + + tx_log(TX_LOG_INFO, "[RC_OL_0011] 카드사별 대사 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0011] BIZDATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0011] 금액 범위 오류 amount=%ld", amount); + if (tx_buf_get(ctx->in, "BIN", bin, sizeof(bin)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0011] BIN(카드사 식별 접두) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -122,27 +146,79 @@ TX_SERVICE(RC_OL_0011, ctx) return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0011] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + binlen = strlen(bin); + if (binlen == 0) { + tx_log(TX_LOG_WARN, "[RC_OL_0011] BIN 이 비어있음"); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + ECPGset_var( 0, ( h_biz_date ), __LINE__);\ + /* declare cur_rc_ol_0011 cursor for select card_no , status , amount from rc_ledger where biz_date = $1 order by card_no */ +#line 71 "app/online/rc_ol_0011.pgc" + +#line 71 "app/online/rc_ol_0011.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_ol_0011 cursor for select card_no , status , amount from rc_ledger where biz_date = $1 order by card_no", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 73 "app/online/rc_ol_0011.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0011"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + opened = 1; + + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_ol_0011", ECPGt_EOIT, + ECPGt_char,(h_card_no),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 83 "app/online/rc_ol_0011.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0011"); + break; + } + + /* 카드번호 전방일치: 앞 binlen 자리 비교 */ + if (strncmp(h_card_no, bin, binlen) != 0) + continue; + + cnt++; + sumamt += h_amount; + if (strcmp(h_status, RC_ST_DONE) == 0 || + strcmp(h_status, RC_ST_SETTLED) == 0) + matched++; + } + + if (opened) + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_ol_0011", ECPGt_EOIT, ECPGt_EORT);} +#line 104 "app/online/rc_ol_0011.pgc" + + + rate = (cnt > 0) ? (matched * 100 / cnt) : 0; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0011] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIN", bin); + tx_buf_setlong(ctx->out, "CNT", cnt); + tx_buf_setlong(ctx->out, "MATCHED", matched); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + tx_buf_setlong(ctx->out, "RATE", rate); + tx_log(TX_LOG_INFO, + "[RC_OL_0011] 카드사별 집계 bin=%s 건수=%ld 완료=%ld 대사율=%ld%%", + bin, cnt, matched, rate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0011.pgc b/app/online/rc_ol_0011.pgc index e446736..f1d8b9e 100644 --- a/app/online/rc_ol_0011.pgc +++ b/app/online/rc_ol_0011.pgc @@ -1,14 +1,21 @@ /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0011.pgc - 대사 온라인 서비스 (RC_OL_0011) [tier=medium] + * rc_ol_0011.pgc - 카드사별 대사 조회 (CURSOR) 온라인 서비스 (RC_OL_0011) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 BIZDATE(영업일) 의 원장을 카드번호 순으로 커서 순회하며, 카드번호 + * 접두(BIN, 카드사 식별자) 가 일치하는 건만 집계한다. + * 업무규칙: + * - BIN 은 fetch 된 card_no 의 앞 strlen(BIN) 자리와 비교(전방일치) + * - 일치 건: 총건수(CNT), 금액합계(SUMAMT) 누적 + * - 매입확정('M')/정산완료('S') 는 대사완료 건(MATCHED) 으로 누적 + * - 대사율 RATE = MATCHED * 100 / CNT (0 나눗셈 방어) + * 응답 BIN, CNT, MATCHED, SUMAMT, RATE 를 구성한다. (읽기 전용) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -16,25 +23,28 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0011, ctx) { - rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_card_no[20]; + char h_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[RC_OL_0011] 대사 등록 서비스 진입"); + char bizdate[16]; + char bin[20]; + size_t binlen; + long cnt = 0, matched = 0, sumamt = 0, rate = 0; + int opened = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0011] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[RC_OL_0011] 카드사별 대사 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0011] BIZDATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0011] 금액 범위 오류 amount=%ld", amount); + if (tx_buf_get(ctx->in, "BIN", bin, sizeof(bin)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0011] BIN(카드사 식별 접두) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -44,27 +54,65 @@ TX_SERVICE(RC_OL_0011, ctx) return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0011] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + binlen = strlen(bin); + if (binlen == 0) { + tx_log(TX_LOG_WARN, "[RC_OL_0011] BIN 이 비어있음"); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + EXEC SQL DECLARE cur_rc_ol_0011 CURSOR FOR + SELECT card_no, status, amount + FROM rc_ledger + WHERE biz_date = :h_biz_date + ORDER BY card_no; + + EXEC SQL OPEN cur_rc_ol_0011; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0011"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + opened = 1; + + for (;;) { + EXEC SQL FETCH cur_rc_ol_0011 + INTO :h_card_no, :h_status, :h_amount; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0011"); + break; + } + + /* 카드번호 전방일치: 앞 binlen 자리 비교 */ + if (strncmp(h_card_no, bin, binlen) != 0) + continue; + + cnt++; + sumamt += h_amount; + if (strcmp(h_status, RC_ST_DONE) == 0 || + strcmp(h_status, RC_ST_SETTLED) == 0) + matched++; + } + + if (opened) + EXEC SQL CLOSE cur_rc_ol_0011; + + rate = (cnt > 0) ? (matched * 100 / cnt) : 0; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0011] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIN", bin); + tx_buf_setlong(ctx->out, "CNT", cnt); + tx_buf_setlong(ctx->out, "MATCHED", matched); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + tx_buf_setlong(ctx->out, "RATE", rate); + tx_log(TX_LOG_INFO, + "[RC_OL_0011] 카드사별 집계 bin=%s 건수=%ld 완료=%ld 대사율=%ld%%", + bin, cnt, matched, rate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0012.c b/app/online/rc_ol_0012.c index e166c41..41eff43 100644 --- a/app/online/rc_ol_0012.c +++ b/app/online/rc_ol_0012.c @@ -8,15 +8,22 @@ #line 1 "app/online/rc_ol_0012.pgc" /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0012.pgc - 대사 온라인 서비스 (RC_OL_0012) [tier=easy] + * rc_ol_0012.pgc - 가맹점별 대사 조회 (CURSOR) 온라인 서비스 (RC_OL_0012) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 MERCHID(가맹점번호) 의 원장을 처리키 순으로 커서 순회하며 상태별 + * 건수와 금액을 집계한다. + * 업무규칙: + * - 총건수(CNT), 금액합계(SUMAMT) 누적 + * - 상태별: 매입확정('M')=DONE, 불일치/보류('U')=UNMATCH, + * 정산완료('S')=SETTLED + * - 대사완료율 RECRATE = (DONE + SETTLED) * 100 / CNT (0 나눗셈 방어) + * 응답 MERCHID, CNT, DONE, UNMATCH, SETTLED, SUMAMT, RECRATE. (읽기 전용) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -89,37 +96,107 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0012.pgc" +#line 22 "app/online/rc_ol_0012.pgc" TX_SERVICE(RC_OL_0012, ctx) { - rc_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 27 "app/online/rc_ol_0012.pgc" + char h_merch_id [ 16 ] ; + +#line 28 "app/online/rc_ol_0012.pgc" + char h_status [ 2 ] ; + +#line 29 "app/online/rc_ol_0012.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 30 "app/online/rc_ol_0012.pgc" - tx_log(TX_LOG_INFO, "[RC_OL_0012] 대사 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0012] KEY 누락"); + char merchid[16]; + long cnt = 0, done = 0, unmatch = 0, settled = 0; + long sumamt = 0, recrate = 0; + int opened = 0; + + tx_log(TX_LOG_INFO, "[RC_OL_0012] 가맹점별 대사 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merchid, sizeof(merchid)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0012] MERCHID 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = rc_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_merch_id, 0, sizeof(h_merch_id)); + strncpy(h_merch_id, merchid, sizeof(h_merch_id) - 1); + + ECPGset_var( 0, ( h_merch_id ), __LINE__);\ + /* declare cur_rc_ol_0012 cursor for select status , amount from rc_ledger where merch_id = $1 order by key */ +#line 52 "app/online/rc_ol_0012.pgc" + +#line 52 "app/online/rc_ol_0012.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_ol_0012 cursor for select status , amount from rc_ledger where merch_id = $1 order by key", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 54 "app/online/rc_ol_0012.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0012"); + tx_return(ctx, TX_EDB, ctx->out); return; } + opened = 1; + + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_ol_0012", ECPGt_EOIT, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 63 "app/online/rc_ol_0012.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0012"); + break; + } + + cnt++; + sumamt += h_amount; + + if (strcmp(h_status, RC_ST_DONE) == 0) + done++; + else if (strcmp(h_status, RC_ST_FAIL) == 0) + unmatch++; + else if (strcmp(h_status, RC_ST_SETTLED) == 0) + settled++; + } + + if (opened) + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_ol_0012", ECPGt_EOIT, ECPGt_EORT);} +#line 84 "app/online/rc_ol_0012.pgc" + + + recrate = (cnt > 0) ? ((done + settled) * 100 / cnt) : 0; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merchid); + tx_buf_setlong(ctx->out, "CNT", cnt); + tx_buf_setlong(ctx->out, "DONE", done); + tx_buf_setlong(ctx->out, "UNMATCH", unmatch); + tx_buf_setlong(ctx->out, "SETTLED", settled); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + tx_buf_setlong(ctx->out, "RECRATE", recrate); + tx_log(TX_LOG_INFO, + "[RC_OL_0012] 가맹점별 집계 merch=%s 건수=%ld 완료율=%ld%%", + merchid, cnt, recrate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0012.pgc b/app/online/rc_ol_0012.pgc index 18971eb..2d8217e 100644 --- a/app/online/rc_ol_0012.pgc +++ b/app/online/rc_ol_0012.pgc @@ -1,14 +1,21 @@ /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0012.pgc - 대사 온라인 서비스 (RC_OL_0012) [tier=easy] + * rc_ol_0012.pgc - 가맹점별 대사 조회 (CURSOR) 온라인 서비스 (RC_OL_0012) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 MERCHID(가맹점번호) 의 원장을 처리키 순으로 커서 순회하며 상태별 + * 건수와 금액을 집계한다. + * 업무규칙: + * - 총건수(CNT), 금액합계(SUMAMT) 누적 + * - 상태별: 매입확정('M')=DONE, 불일치/보류('U')=UNMATCH, + * 정산완료('S')=SETTLED + * - 대사완료율 RECRATE = (DONE + SETTLED) * 100 / CNT (0 나눗셈 방어) + * 응답 MERCHID, CNT, DONE, UNMATCH, SETTLED, SUMAMT, RECRATE. (읽기 전용) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -16,32 +23,78 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0012, ctx) { - rc_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[RC_OL_0012] 대사 단건조회 서비스 진입"); + char merchid[16]; + long cnt = 0, done = 0, unmatch = 0, settled = 0; + long sumamt = 0, recrate = 0; + int opened = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0012] KEY 누락"); + tx_log(TX_LOG_INFO, "[RC_OL_0012] 가맹점별 대사 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merchid, sizeof(merchid)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0012] MERCHID 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = rc_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_merch_id, 0, sizeof(h_merch_id)); + strncpy(h_merch_id, merchid, sizeof(h_merch_id) - 1); + + EXEC SQL DECLARE cur_rc_ol_0012 CURSOR FOR + SELECT status, amount + FROM rc_ledger + WHERE merch_id = :h_merch_id + ORDER BY key; + + EXEC SQL OPEN cur_rc_ol_0012; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0012"); + tx_return(ctx, TX_EDB, ctx->out); return; } + opened = 1; + + for (;;) { + EXEC SQL FETCH cur_rc_ol_0012 INTO :h_status, :h_amount; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0012"); + break; + } + + cnt++; + sumamt += h_amount; + + if (strcmp(h_status, RC_ST_DONE) == 0) + done++; + else if (strcmp(h_status, RC_ST_FAIL) == 0) + unmatch++; + else if (strcmp(h_status, RC_ST_SETTLED) == 0) + settled++; + } + + if (opened) + EXEC SQL CLOSE cur_rc_ol_0012; + + recrate = (cnt > 0) ? ((done + settled) * 100 / cnt) : 0; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merchid); + tx_buf_setlong(ctx->out, "CNT", cnt); + tx_buf_setlong(ctx->out, "DONE", done); + tx_buf_setlong(ctx->out, "UNMATCH", unmatch); + tx_buf_setlong(ctx->out, "SETTLED", settled); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + tx_buf_setlong(ctx->out, "RECRATE", recrate); + tx_log(TX_LOG_INFO, + "[RC_OL_0012] 가맹점별 집계 merch=%s 건수=%ld 완료율=%ld%%", + merchid, cnt, recrate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0013.c b/app/online/rc_ol_0013.c index 1fa94e9..e2a00e8 100644 --- a/app/online/rc_ol_0013.c +++ b/app/online/rc_ol_0013.c @@ -8,10 +8,22 @@ #line 1 "app/online/rc_ol_0013.pgc" /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0013.pgc - 대사 온라인 서비스 (RC_OL_0013) [tier=medium] + * rc_ol_0013.pgc - 대사 허용오차(tolerance) 비교 (단건) 온라인 서비스 (RC_OL_0013) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 "매입금액(원장 amount)" 과 승인계에서 + * 넘어온 "승인금액(APPRAMT)" 의 차액을 구한 뒤, 허용오차(TOL) 이내이면 + * 대사일치로 판정한다. + * diff = amount - APPRAMT, absd = |diff| + * absd <= TOL → 일치(MATCH) → 매입확정('M') + * absd > TOL → 불일치(UNMATCH) → 실패('U') + * 소액 정산 오차(반올림/수수료 잔돈)를 흡수하기 위한 판정 로직이다. + * + * 입력 TXBUF: + * KEY (필수) 처리키 + * APPRAMT (필수) 승인금액 + * TOL (선택) 허용오차, 미지정 시 0(완전일치) + * 출력 TXBUF: + * KEY / APPRAMT / CAPAMT / DIFF / TOL / RESULT / STATUS */ #include #include @@ -89,60 +101,100 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0013.pgc" +#line 27 "app/online/rc_ol_0013.pgc" TX_SERVICE(RC_OL_0013, ctx) { rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + long appr_amt = 0; /* 승인금액(APPRAMT) */ + long tol = 0; /* 허용오차(TOL) */ + long diff, absd; + long tol_pct; /* 매입금액 대비 오차율(‱, 만분율) */ + int matched; int rc; + const char *new_st; + const char *result; - tx_log(TX_LOG_INFO, "[RC_OL_0013] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0013] 대사 허용오차 비교(단건) 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0013] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0013] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0013] 금액 범위 오류 amount=%ld", amount); + if (key[0] == '\0') { + tx_log(TX_LOG_ERROR, "[RC_OL_0013] KEY 공백"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0013] 영업일 오류 date=%s", bizdate); + if (tx_buf_getlong(ctx->in, "APPRAMT", &appr_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0013] APPRAMT(승인금액) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 허용오차는 선택 입력(미지정 시 0 = 완전일치 요구) */ + if (tx_buf_getlong(ctx->in, "TOL", &tol) != TX_OK) + tol = 0; + if (tol < 0) { + tx_log(TX_LOG_WARN, "[RC_OL_0013] 음수 허용오차 보정 tol=%ld → 0", tol); + tol = 0; + } memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); + rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0013] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[RC_OL_0013] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + tx_log(TX_LOG_INFO, + "[RC_OL_0013] 대사대상 key=%s 매입=%ld 승인=%ld 허용오차=%ld", + key, rec.amount, appr_amt, tol); + + /* 차액 및 절대차 계산 */ + diff = rec.amount - appr_amt; + absd = (diff < 0) ? -diff : diff; + matched = (absd <= tol); + + /* 참고지표: 매입금액 대비 오차율(만분율). 0 나눗셈 방지 */ + if (rec.amount > 0) + tol_pct = (absd * 10000L) / rec.amount; + else + tol_pct = 0; + tx_log(TX_LOG_INFO, "[RC_OL_0013] |차액|=%ld 오차율=%ld‱", absd, tol_pct); + + if (matched) { + new_st = RC_ST_DONE; /* 매입확정 */ + result = "MATCH"; + tx_log(TX_LOG_INFO, + "[RC_OL_0013] 허용오차 이내 일치 key=%s 차액=%ld 오차=%ld", + key, diff, tol); + } else { + new_st = RC_ST_FAIL; /* 오차 초과 불일치 */ + result = "UNMATCH"; + tx_log(TX_LOG_WARN, + "[RC_OL_0013] 허용오차 초과 불일치 key=%s |차액|=%ld 오차=%ld", + key, absd, tol); + } + + rc = rc_rec_update_status(key, new_st); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0013] 상태전이 실패 key=%s rc=%d", key, rc); + tx_return(ctx, rc, ctx->out); + return; + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0013] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_setlong(ctx->out, "APPRAMT", appr_amt); + tx_buf_setlong(ctx->out, "CAPAMT", rec.amount); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_setlong(ctx->out, "TOL", tol); + tx_buf_sets(ctx->out, "RESULT", result); + tx_buf_sets(ctx->out, "STATUS", new_st); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0013.pgc b/app/online/rc_ol_0013.pgc index 861cdde..33aa170 100644 --- a/app/online/rc_ol_0013.pgc +++ b/app/online/rc_ol_0013.pgc @@ -1,9 +1,21 @@ /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0013.pgc - 대사 온라인 서비스 (RC_OL_0013) [tier=medium] + * rc_ol_0013.pgc - 대사 허용오차(tolerance) 비교 (단건) 온라인 서비스 (RC_OL_0013) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 "매입금액(원장 amount)" 과 승인계에서 + * 넘어온 "승인금액(APPRAMT)" 의 차액을 구한 뒤, 허용오차(TOL) 이내이면 + * 대사일치로 판정한다. + * diff = amount - APPRAMT, absd = |diff| + * absd <= TOL → 일치(MATCH) → 매입확정('M') + * absd > TOL → 불일치(UNMATCH) → 실패('U') + * 소액 정산 오차(반올림/수수료 잔돈)를 흡수하기 위한 판정 로직이다. + * + * 입력 TXBUF: + * KEY (필수) 처리키 + * APPRAMT (필수) 승인금액 + * TOL (선택) 허용오차, 미지정 시 0(완전일치) + * 출력 TXBUF: + * KEY / APPRAMT / CAPAMT / DIFF / TOL / RESULT / STATUS */ #include #include @@ -17,54 +29,94 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0013, ctx) { rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + long appr_amt = 0; /* 승인금액(APPRAMT) */ + long tol = 0; /* 허용오차(TOL) */ + long diff, absd; + long tol_pct; /* 매입금액 대비 오차율(‱, 만분율) */ + int matched; int rc; + const char *new_st; + const char *result; - tx_log(TX_LOG_INFO, "[RC_OL_0013] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0013] 대사 허용오차 비교(단건) 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0013] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0013] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0013] 금액 범위 오류 amount=%ld", amount); + if (key[0] == '\0') { + tx_log(TX_LOG_ERROR, "[RC_OL_0013] KEY 공백"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0013] 영업일 오류 date=%s", bizdate); + if (tx_buf_getlong(ctx->in, "APPRAMT", &appr_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0013] APPRAMT(승인금액) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 허용오차는 선택 입력(미지정 시 0 = 완전일치 요구) */ + if (tx_buf_getlong(ctx->in, "TOL", &tol) != TX_OK) + tol = 0; + if (tol < 0) { + tx_log(TX_LOG_WARN, "[RC_OL_0013] 음수 허용오차 보정 tol=%ld → 0", tol); + tol = 0; + } memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); + rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0013] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[RC_OL_0013] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + tx_log(TX_LOG_INFO, + "[RC_OL_0013] 대사대상 key=%s 매입=%ld 승인=%ld 허용오차=%ld", + key, rec.amount, appr_amt, tol); + + /* 차액 및 절대차 계산 */ + diff = rec.amount - appr_amt; + absd = (diff < 0) ? -diff : diff; + matched = (absd <= tol); + + /* 참고지표: 매입금액 대비 오차율(만분율). 0 나눗셈 방지 */ + if (rec.amount > 0) + tol_pct = (absd * 10000L) / rec.amount; + else + tol_pct = 0; + tx_log(TX_LOG_INFO, "[RC_OL_0013] |차액|=%ld 오차율=%ld‱", absd, tol_pct); + + if (matched) { + new_st = RC_ST_DONE; /* 매입확정 */ + result = "MATCH"; + tx_log(TX_LOG_INFO, + "[RC_OL_0013] 허용오차 이내 일치 key=%s 차액=%ld 오차=%ld", + key, diff, tol); + } else { + new_st = RC_ST_FAIL; /* 오차 초과 불일치 */ + result = "UNMATCH"; + tx_log(TX_LOG_WARN, + "[RC_OL_0013] 허용오차 초과 불일치 key=%s |차액|=%ld 오차=%ld", + key, absd, tol); + } + + rc = rc_rec_update_status(key, new_st); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0013] 상태전이 실패 key=%s rc=%d", key, rc); + tx_return(ctx, rc, ctx->out); + return; + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0013] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_setlong(ctx->out, "APPRAMT", appr_amt); + tx_buf_setlong(ctx->out, "CAPAMT", rec.amount); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_setlong(ctx->out, "TOL", tol); + tx_buf_sets(ctx->out, "RESULT", result); + tx_buf_sets(ctx->out, "STATUS", new_st); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0014.c b/app/online/rc_ol_0014.c index 8a5a9d3..a8fdb57 100644 --- a/app/online/rc_ol_0014.c +++ b/app/online/rc_ol_0014.c @@ -8,15 +8,28 @@ #line 1 "app/online/rc_ol_0014.pgc" /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0014.pgc - 대사 온라인 서비스 (RC_OL_0014) [tier=easy] + * rc_ol_0014.pgc - 미대사(접수 'R') 추출 조회 (CURSOR) 온라인 서비스 (RC_OL_0014) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일(BIZDATE) 의 아직 대사되지 않은 접수건('R') 을 커서로 + * 순회하여 최대 LIMITN 건까지 응답에 실어 반환한다. + * - 각 건은 KEY0/AMT0, KEY1/AMT1 ... 형태로 번호를 붙여 push + * - 실제 push 한 건수(UNRECCNT) 와 금액합계(SUMAMT) 를 집계 + * 순수 조회 서비스이므로 상태전이/커밋 없이 읽기만 수행한다. + * + * 입력 TXBUF: + * BIZDATE (필수) 대상 영업일 YYYYMMDD + * LIMITN (선택) 최대 추출 건수, 미지정/비정상 시 기본 10 + * 출력 TXBUF: + * UNRECCNT / SUMAMT / KEY0..KEYn / AMT0..AMTn + * + * 주의: 커서는 반드시 CLOSE 하며, FETCH 오류 시에도 그때까지 집계한 + * 결과를 정상 응답으로 돌려준다(부분 성공 허용). */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -89,37 +102,124 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0014.pgc" +#line 28 "app/online/rc_ol_0014.pgc" TX_SERVICE(RC_OL_0014, ctx) { - rc_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 33 "app/online/rc_ol_0014.pgc" + char h_biz_date [ 9 ] ; + +#line 34 "app/online/rc_ol_0014.pgc" + char h_key [ 16 ] ; + +#line 35 "app/online/rc_ol_0014.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 36 "app/online/rc_ol_0014.pgc" - tx_log(TX_LOG_INFO, "[RC_OL_0014] 대사 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0014] KEY 누락"); + char bizdate[16]; + char fname[16]; + long limitn = 0; + long pushed = 0; + long sumamt = 0; + int opened = 0; + + tx_log(TX_LOG_INFO, "[RC_OL_0014] 미대사 추출 조회(커서) 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0014] BIZDATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - - memset(&rec, 0, sizeof(rec)); - rc = rc_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0014] 영업일 형식 오류 date=%s", bizdate); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 추출 상한(선택). 미지정/비정상이면 기본 10건, 과대 요청은 상한 clamp */ + if (tx_buf_getlong(ctx->in, "LIMITN", &limitn) != TX_OK || limitn <= 0) + limitn = 10; + if (limitn > 100) { + tx_log(TX_LOG_WARN, "[RC_OL_0014] 추출상한 초과 요청 %ld → 100 clamp", + limitn); + limitn = 100; + } + tx_log(TX_LOG_INFO, "[RC_OL_0014] 추출조건 date=%s 상한=%ld", bizdate, limitn); + + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + + ECPGset_var( 0, ( h_biz_date ), __LINE__);\ + /* declare cur_rc_ol_0014 cursor for select key , amount from rc_ledger where biz_date = $1 and status = 'R' order by key */ +#line 77 "app/online/rc_ol_0014.pgc" + +#line 77 "app/online/rc_ol_0014.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_ol_0014 cursor for select key , amount from rc_ledger where biz_date = $1 and status = 'R' order by key", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 79 "app/online/rc_ol_0014.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0014"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + opened = 1; + + while (pushed < limitn) { + memset(h_key, 0, sizeof(h_key)); + h_amount = 0; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_ol_0014", ECPGt_EOIT, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 91 "app/online/rc_ol_0014.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0014"); + break; + } + + snprintf(fname, sizeof(fname), "KEY%ld", pushed); + tx_buf_sets(ctx->out, fname, h_key); + snprintf(fname, sizeof(fname), "AMT%ld", pushed); + tx_buf_setlong(ctx->out, fname, h_amount); + + sumamt += h_amount; + pushed++; + } + + if (opened) + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_ol_0014", ECPGt_EOIT, ECPGt_EORT);} +#line 110 "app/online/rc_ol_0014.pgc" + + + if (pushed == 0) + tx_log(TX_LOG_INFO, "[RC_OL_0014] 미대사 접수건 없음 date=%s", bizdate); + else if (pushed >= limitn) + tx_log(TX_LOG_INFO, + "[RC_OL_0014] 추출 상한 도달(추가 지연건 존재 가능) date=%s", + bizdate); + + tx_buf_setlong(ctx->out, "UNRECCNT", pushed); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + tx_log(TX_LOG_INFO, "[RC_OL_0014] 추출완료 date=%s 건수=%ld 합계=%ld", + bizdate, pushed, sumamt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0014.pgc b/app/online/rc_ol_0014.pgc index 3943cca..60867f9 100644 --- a/app/online/rc_ol_0014.pgc +++ b/app/online/rc_ol_0014.pgc @@ -1,14 +1,27 @@ /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0014.pgc - 대사 온라인 서비스 (RC_OL_0014) [tier=easy] + * rc_ol_0014.pgc - 미대사(접수 'R') 추출 조회 (CURSOR) 온라인 서비스 (RC_OL_0014) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일(BIZDATE) 의 아직 대사되지 않은 접수건('R') 을 커서로 + * 순회하여 최대 LIMITN 건까지 응답에 실어 반환한다. + * - 각 건은 KEY0/AMT0, KEY1/AMT1 ... 형태로 번호를 붙여 push + * - 실제 push 한 건수(UNRECCNT) 와 금액합계(SUMAMT) 를 집계 + * 순수 조회 서비스이므로 상태전이/커밋 없이 읽기만 수행한다. + * + * 입력 TXBUF: + * BIZDATE (필수) 대상 영업일 YYYYMMDD + * LIMITN (선택) 최대 추출 건수, 미지정/비정상 시 기본 10 + * 출력 TXBUF: + * UNRECCNT / SUMAMT / KEY0..KEYn / AMT0..AMTn + * + * 주의: 커서는 반드시 CLOSE 하며, FETCH 오류 시에도 그때까지 집계한 + * 결과를 정상 응답으로 돌려준다(부분 성공 허용). */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -16,32 +29,96 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0014, ctx) { - rc_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_key[16]; + long h_amount; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[RC_OL_0014] 대사 단건조회 서비스 진입"); + char bizdate[16]; + char fname[16]; + long limitn = 0; + long pushed = 0; + long sumamt = 0; + int opened = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0014] KEY 누락"); + tx_log(TX_LOG_INFO, "[RC_OL_0014] 미대사 추출 조회(커서) 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0014] BIZDATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - - memset(&rec, 0, sizeof(rec)); - rc = rc_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0014] 영업일 형식 오류 date=%s", bizdate); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 추출 상한(선택). 미지정/비정상이면 기본 10건, 과대 요청은 상한 clamp */ + if (tx_buf_getlong(ctx->in, "LIMITN", &limitn) != TX_OK || limitn <= 0) + limitn = 10; + if (limitn > 100) { + tx_log(TX_LOG_WARN, "[RC_OL_0014] 추출상한 초과 요청 %ld → 100 clamp", + limitn); + limitn = 100; + } + tx_log(TX_LOG_INFO, "[RC_OL_0014] 추출조건 date=%s 상한=%ld", bizdate, limitn); + + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + + EXEC SQL DECLARE cur_rc_ol_0014 CURSOR FOR + SELECT key, amount + FROM rc_ledger + WHERE biz_date = :h_biz_date + AND status = 'R' + ORDER BY key; + + EXEC SQL OPEN cur_rc_ol_0014; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0014"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + opened = 1; + + while (pushed < limitn) { + memset(h_key, 0, sizeof(h_key)); + h_amount = 0; + + EXEC SQL FETCH cur_rc_ol_0014 INTO :h_key, :h_amount; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0014"); + break; + } + + snprintf(fname, sizeof(fname), "KEY%ld", pushed); + tx_buf_sets(ctx->out, fname, h_key); + snprintf(fname, sizeof(fname), "AMT%ld", pushed); + tx_buf_setlong(ctx->out, fname, h_amount); + + sumamt += h_amount; + pushed++; + } + + if (opened) + EXEC SQL CLOSE cur_rc_ol_0014; + + if (pushed == 0) + tx_log(TX_LOG_INFO, "[RC_OL_0014] 미대사 접수건 없음 date=%s", bizdate); + else if (pushed >= limitn) + tx_log(TX_LOG_INFO, + "[RC_OL_0014] 추출 상한 도달(추가 지연건 존재 가능) date=%s", + bizdate); + + tx_buf_setlong(ctx->out, "UNRECCNT", pushed); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + tx_log(TX_LOG_INFO, "[RC_OL_0014] 추출완료 date=%s 건수=%ld 합계=%ld", + bizdate, pushed, sumamt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0015.c b/app/online/rc_ol_0015.c index 2e86e11..7c1ccbc 100644 --- a/app/online/rc_ol_0015.c +++ b/app/online/rc_ol_0015.c @@ -8,10 +8,23 @@ #line 1 "app/online/rc_ol_0015.pgc" /* @tier hard @module rc @transform reconciliation-online */ /* - * rc_ol_0015.pgc - 대사 온라인 서비스 (RC_OL_0015) [tier=hard] + * rc_ol_0015.pgc - 대사 결과 집계 조회 온라인 서비스 (RC_OL_0015) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일(BIZDATE) 의 대사 진행 현황을 상태코드별로 집계한다. + * - rc_rec_count 로 접수(R)/매입확정(M)/불일치(U)/정산완료(S) 건수 집계 + * - rc_rec_sum 으로 당일 금액합계(SUMAMT) 산출 + * - totalcnt = R + M + U + S + * 상태전이 없이 카운트/합계만 조회하는 대시보드용 서비스이다. + * + * 입력 TXBUF: + * BIZDATE (필수) 집계 대상 영업일 YYYYMMDD + * 출력 TXBUF: + * BIZDATE / CNTR / CNTM / CNTU / CNTS / TOTALCNT / SUMAMT + * + * 진척도 해석: + * 미처리 = CNTR (아직 접수 상태) + * 처리진행 = CNTM + CNTU (대사 판정 완료) + * 최종정산 = CNTS (정산까지 완료) */ #include #include @@ -89,88 +102,97 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0015.pgc" +#line 28 "app/online/rc_ol_0015.pgc" TX_SERVICE(RC_OL_0015, ctx) { - rc_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; + char bizdate[16]; + long cnt_r, cnt_m, cnt_u, cnt_s, totalcnt; + long done_cnt; /* 판정완료(M+U) */ + long progress; /* 진척률(%) */ + long sumamt = 0; int rc; - TXBUF *sub_in; - TXBUF *sub_out; - tx_log(TX_LOG_INFO, "[RC_OL_0015] 대사 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0015] 대사 결과 집계 조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0015] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0015] BIZDATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0015] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (bizdate[0] == '\0') { + tx_log(TX_LOG_ERROR, "[RC_OL_0015] BIZDATE 공백"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0015] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + tx_log(TX_LOG_INFO, "[RC_OL_0015] 집계 대상 영업일 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[RC_OL_0015] 상태코드별 카운트 조회 시작"); + + /* 상태코드별 건수 집계 (-1 은 조회 오류) */ + cnt_r = rc_rec_count(bizdate, RC_ST_RECV); + cnt_m = rc_rec_count(bizdate, RC_ST_DONE); + cnt_u = rc_rec_count(bizdate, RC_ST_FAIL); + cnt_s = rc_rec_count(bizdate, RC_ST_SETTLED); + + if (cnt_r < 0 || cnt_m < 0 || cnt_u < 0 || cnt_s < 0) { + tx_log(TX_LOG_ERROR, + "[RC_OL_0015] 건수 집계 실패 R=%ld M=%ld U=%ld S=%ld", + cnt_r, cnt_m, cnt_u, cnt_s); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + tx_log(TX_LOG_INFO, "[RC_OL_0015] 접수(R) = %ld", cnt_r); + tx_log(TX_LOG_INFO, "[RC_OL_0015] 매입확정(M) = %ld", cnt_m); + tx_log(TX_LOG_INFO, "[RC_OL_0015] 불일치(U) = %ld", cnt_u); + tx_log(TX_LOG_INFO, "[RC_OL_0015] 정산완료(S) = %ld", cnt_s); - rc = rc_rec_insert(&rec); + totalcnt = cnt_r + cnt_m + cnt_u + cnt_s; + done_cnt = cnt_m + cnt_u; /* 대사 판정이 끝난 건 */ + + /* 진척률 = (판정완료 + 정산완료) / 전체 * 100 */ + if (totalcnt > 0) + progress = ((done_cnt + cnt_s) * 100L) / totalcnt; + else + progress = 0; + tx_log(TX_LOG_INFO, + "[RC_OL_0015] 판정완료=%ld 정산완료=%ld 진척률=%ld%%", + done_cnt, cnt_s, progress); + + /* 불일치 비중이 높으면 경보 로그(운영 모니터링용) */ + if (totalcnt > 0 && (cnt_u * 100L) / totalcnt >= 10) + tx_log(TX_LOG_WARN, + "[RC_OL_0015] 불일치 비중 경보 date=%s U=%ld/%ld", + bizdate, cnt_u, totalcnt); + + /* 당일 금액합계 (실패 시 0 으로 응답) */ + rc = rc_rec_sum(bizdate, &sumamt); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0015] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; + tx_log(TX_LOG_WARN, "[RC_OL_0015] 금액합계 조회 경고 date=%s rc=%d", + bizdate, rc); + sumamt = 0; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("RC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[RC_OL_0015] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (rc_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[RC_OL_0015] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNTR", cnt_r); + tx_buf_setlong(ctx->out, "CNTM", cnt_m); + tx_buf_setlong(ctx->out, "CNTU", cnt_u); + tx_buf_setlong(ctx->out, "CNTS", cnt_s); + tx_buf_setlong(ctx->out, "TOTALCNT", totalcnt); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + if (totalcnt == 0) + tx_log(TX_LOG_INFO, + "[RC_OL_0015] 해당 영업일 원장 없음 date=%s", bizdate); + tx_log(TX_LOG_INFO, + "[RC_OL_0015] 집계완료 date=%s R=%ld M=%ld U=%ld S=%ld 합계=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s, sumamt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0015.pgc b/app/online/rc_ol_0015.pgc index 09916b9..4ea2789 100644 --- a/app/online/rc_ol_0015.pgc +++ b/app/online/rc_ol_0015.pgc @@ -1,9 +1,22 @@ /* @tier hard @module rc @transform reconciliation-online */ /* - * rc_ol_0015.pgc - 대사 온라인 서비스 (RC_OL_0015) [tier=hard] + * rc_ol_0015.pgc - 대사 결과 집계 조회 온라인 서비스 (RC_OL_0015) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일(BIZDATE) 의 대사 진행 현황을 상태코드별로 집계한다. + * - rc_rec_count 로 접수(R)/매입확정(M)/불일치(U)/정산완료(S) 건수 집계 + * - rc_rec_sum 으로 당일 금액합계(SUMAMT) 산출 + * - totalcnt = R + M + U + S + * 상태전이 없이 카운트/합계만 조회하는 대시보드용 서비스이다. + * + * 입력 TXBUF: + * BIZDATE (필수) 집계 대상 영업일 YYYYMMDD + * 출력 TXBUF: + * BIZDATE / CNTR / CNTM / CNTU / CNTS / TOTALCNT / SUMAMT + * + * 진척도 해석: + * 미처리 = CNTR (아직 접수 상태) + * 처리진행 = CNTM + CNTU (대사 판정 완료) + * 최종정산 = CNTS (정산까지 완료) */ #include #include @@ -16,83 +29,92 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0015, ctx) { - rc_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; + char bizdate[16]; + long cnt_r, cnt_m, cnt_u, cnt_s, totalcnt; + long done_cnt; /* 판정완료(M+U) */ + long progress; /* 진척률(%) */ + long sumamt = 0; int rc; - TXBUF *sub_in; - TXBUF *sub_out; - tx_log(TX_LOG_INFO, "[RC_OL_0015] 대사 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0015] 대사 결과 집계 조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0015] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0015] BIZDATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0015] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (bizdate[0] == '\0') { + tx_log(TX_LOG_ERROR, "[RC_OL_0015] BIZDATE 공백"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0015] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + tx_log(TX_LOG_INFO, "[RC_OL_0015] 집계 대상 영업일 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[RC_OL_0015] 상태코드별 카운트 조회 시작"); + + /* 상태코드별 건수 집계 (-1 은 조회 오류) */ + cnt_r = rc_rec_count(bizdate, RC_ST_RECV); + cnt_m = rc_rec_count(bizdate, RC_ST_DONE); + cnt_u = rc_rec_count(bizdate, RC_ST_FAIL); + cnt_s = rc_rec_count(bizdate, RC_ST_SETTLED); + + if (cnt_r < 0 || cnt_m < 0 || cnt_u < 0 || cnt_s < 0) { + tx_log(TX_LOG_ERROR, + "[RC_OL_0015] 건수 집계 실패 R=%ld M=%ld U=%ld S=%ld", + cnt_r, cnt_m, cnt_u, cnt_s); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + tx_log(TX_LOG_INFO, "[RC_OL_0015] 접수(R) = %ld", cnt_r); + tx_log(TX_LOG_INFO, "[RC_OL_0015] 매입확정(M) = %ld", cnt_m); + tx_log(TX_LOG_INFO, "[RC_OL_0015] 불일치(U) = %ld", cnt_u); + tx_log(TX_LOG_INFO, "[RC_OL_0015] 정산완료(S) = %ld", cnt_s); - rc = rc_rec_insert(&rec); + totalcnt = cnt_r + cnt_m + cnt_u + cnt_s; + done_cnt = cnt_m + cnt_u; /* 대사 판정이 끝난 건 */ + + /* 진척률 = (판정완료 + 정산완료) / 전체 * 100 */ + if (totalcnt > 0) + progress = ((done_cnt + cnt_s) * 100L) / totalcnt; + else + progress = 0; + tx_log(TX_LOG_INFO, + "[RC_OL_0015] 판정완료=%ld 정산완료=%ld 진척률=%ld%%", + done_cnt, cnt_s, progress); + + /* 불일치 비중이 높으면 경보 로그(운영 모니터링용) */ + if (totalcnt > 0 && (cnt_u * 100L) / totalcnt >= 10) + tx_log(TX_LOG_WARN, + "[RC_OL_0015] 불일치 비중 경보 date=%s U=%ld/%ld", + bizdate, cnt_u, totalcnt); + + /* 당일 금액합계 (실패 시 0 으로 응답) */ + rc = rc_rec_sum(bizdate, &sumamt); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0015] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; + tx_log(TX_LOG_WARN, "[RC_OL_0015] 금액합계 조회 경고 date=%s rc=%d", + bizdate, rc); + sumamt = 0; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("RC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[RC_OL_0015] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (rc_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[RC_OL_0015] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNTR", cnt_r); + tx_buf_setlong(ctx->out, "CNTM", cnt_m); + tx_buf_setlong(ctx->out, "CNTU", cnt_u); + tx_buf_setlong(ctx->out, "CNTS", cnt_s); + tx_buf_setlong(ctx->out, "TOTALCNT", totalcnt); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + if (totalcnt == 0) + tx_log(TX_LOG_INFO, + "[RC_OL_0015] 해당 영업일 원장 없음 date=%s", bizdate); + tx_log(TX_LOG_INFO, + "[RC_OL_0015] 집계완료 date=%s R=%ld M=%ld U=%ld S=%ld 합계=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s, sumamt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0016.c b/app/online/rc_ol_0016.c index 2872b51..339bddf 100644 --- a/app/online/rc_ol_0016.c +++ b/app/online/rc_ol_0016.c @@ -8,10 +8,24 @@ #line 1 "app/online/rc_ol_0016.pgc" /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0016.pgc - 대사 온라인 서비스 (RC_OL_0016) [tier=easy] + * rc_ol_0016.pgc - 3-way 대사 조회 (승인·매입·입금) 온라인 서비스 (RC_OL_0016) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 세 축을 교차 검증한다. + * leg1 : 승인(APPRAMT) == 매입(원장 amount) + * leg2 : 매입 - 수수료(fee) == 입금(DEPAMT) + * 두 leg 모두 성립하면 3-way 정합(3WAY MATCH) 으로 보고, 상태가 + * 접수('R')/매입확정('M') 이면 정산완료('S') 로 전이한다. 어느 한 + * leg 이라도 깨지면 실패 leg(LEG1FAIL/LEG2FAIL) 를 식별해 실패('U'). + * + * 입력 TXBUF: + * KEY (필수) 처리키 + * APPRAMT (필수) 승인금액 + * DEPAMT (필수) 입금금액 + * 출력 TXBUF: + * KEY / APPRAMT / CAPAMT / DEPAMT / LEG1 / LEG2 / RESULT / STATUS + * + * LEG1/LEG2 는 각각 "OK"/"NG" 로 응답하며, RESULT 는 3WAY MATCH + * 또는 실패 leg 식별값(LEG1FAIL/LEG2FAIL) 이다. */ #include #include @@ -89,37 +103,100 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0016.pgc" +#line 29 "app/online/rc_ol_0016.pgc" TX_SERVICE(RC_OL_0016, ctx) { rc_rec_t rec; char key[16]; + long appr_amt = 0; /* 승인금액(APPRAMT) */ + long dep_amt = 0; /* 입금금액(DEPAMT) */ + long capture; /* 매입금액(원장) */ + long net; /* 매입 - 수수료 */ + int leg1, leg2; int rc; + const char *new_st; + const char *result; - tx_log(TX_LOG_INFO, "[RC_OL_0016] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0016] 3-way 대사 조회 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0016] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_getlong(ctx->in, "APPRAMT", &appr_amt) != TX_OK || + tx_buf_getlong(ctx->in, "DEPAMT", &dep_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0016] APPRAMT/DEPAMT 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0016] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0016] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + capture = rec.amount; + net = rec.amount - rec.fee; + + tx_log(TX_LOG_INFO, + "[RC_OL_0016] 3축 비교 key=%s 승인=%ld 매입=%ld 수수료=%ld 순입금=%ld 입금=%ld", + key, appr_amt, capture, rec.fee, net, dep_amt); + + /* leg1: 승인 == 매입, leg2: 매입-수수료 == 입금 */ + leg1 = (appr_amt == capture); + leg2 = (net == dep_amt); + + if (leg1 && leg2) { + result = "3WAY MATCH"; + /* 접수/매입확정 상태만 정산완료로 전이, 그 외는 현상 유지 */ + if (strcmp(rec.status, RC_ST_RECV) == 0 || + strcmp(rec.status, RC_ST_DONE) == 0) { + new_st = RC_ST_SETTLED; + rc = rc_rec_update_status(key, new_st); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0016] 정산전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + tx_log(TX_LOG_INFO, "[RC_OL_0016] 3-way 정합→정산완료 key=%s", key); + } else { + new_st = rec.status; /* 이미 정산완료 등 → 유지 */ + tx_log(TX_LOG_INFO, + "[RC_OL_0016] 3-way 정합(상태유지) key=%s status=%s", + key, rec.status); + } + } else { + /* 실패 leg 식별: leg1 우선 판정 */ + result = leg1 ? "LEG2FAIL" : "LEG1FAIL"; + new_st = RC_ST_FAIL; + rc = rc_rec_update_status(key, new_st); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0016] 실패전이 오류 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + tx_log(TX_LOG_WARN, + "[RC_OL_0016] 3-way 불일치 key=%s %s (승인=%ld 매입=%ld 입금=%ld)", + key, result, appr_amt, capture, dep_amt); + } + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0016] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "APPRAMT", appr_amt); + tx_buf_setlong(ctx->out, "CAPAMT", capture); + tx_buf_setlong(ctx->out, "DEPAMT", dep_amt); + tx_buf_sets(ctx->out, "LEG1", leg1 ? "OK" : "NG"); + tx_buf_sets(ctx->out, "LEG2", leg2 ? "OK" : "NG"); + tx_buf_sets(ctx->out, "RESULT", result); + tx_buf_sets(ctx->out, "STATUS", new_st); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0016.pgc b/app/online/rc_ol_0016.pgc index 6557590..d31e496 100644 --- a/app/online/rc_ol_0016.pgc +++ b/app/online/rc_ol_0016.pgc @@ -1,9 +1,23 @@ /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0016.pgc - 대사 온라인 서비스 (RC_OL_0016) [tier=easy] + * rc_ol_0016.pgc - 3-way 대사 조회 (승인·매입·입금) 온라인 서비스 (RC_OL_0016) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 세 축을 교차 검증한다. + * leg1 : 승인(APPRAMT) == 매입(원장 amount) + * leg2 : 매입 - 수수료(fee) == 입금(DEPAMT) + * 두 leg 모두 성립하면 3-way 정합(3WAY MATCH) 으로 보고, 상태가 + * 접수('R')/매입확정('M') 이면 정산완료('S') 로 전이한다. 어느 한 + * leg 이라도 깨지면 실패 leg(LEG1FAIL/LEG2FAIL) 를 식별해 실패('U'). + * + * 입력 TXBUF: + * KEY (필수) 처리키 + * APPRAMT (필수) 승인금액 + * DEPAMT (필수) 입금금액 + * 출력 TXBUF: + * KEY / APPRAMT / CAPAMT / DEPAMT / LEG1 / LEG2 / RESULT / STATUS + * + * LEG1/LEG2 는 각각 "OK"/"NG" 로 응답하며, RESULT 는 3WAY MATCH + * 또는 실패 leg 식별값(LEG1FAIL/LEG2FAIL) 이다. */ #include #include @@ -18,30 +32,93 @@ TX_SERVICE(RC_OL_0016, ctx) { rc_rec_t rec; char key[16]; + long appr_amt = 0; /* 승인금액(APPRAMT) */ + long dep_amt = 0; /* 입금금액(DEPAMT) */ + long capture; /* 매입금액(원장) */ + long net; /* 매입 - 수수료 */ + int leg1, leg2; int rc; + const char *new_st; + const char *result; - tx_log(TX_LOG_INFO, "[RC_OL_0016] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0016] 3-way 대사 조회 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0016] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_getlong(ctx->in, "APPRAMT", &appr_amt) != TX_OK || + tx_buf_getlong(ctx->in, "DEPAMT", &dep_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0016] APPRAMT/DEPAMT 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0016] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0016] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + capture = rec.amount; + net = rec.amount - rec.fee; + + tx_log(TX_LOG_INFO, + "[RC_OL_0016] 3축 비교 key=%s 승인=%ld 매입=%ld 수수료=%ld 순입금=%ld 입금=%ld", + key, appr_amt, capture, rec.fee, net, dep_amt); + + /* leg1: 승인 == 매입, leg2: 매입-수수료 == 입금 */ + leg1 = (appr_amt == capture); + leg2 = (net == dep_amt); + + if (leg1 && leg2) { + result = "3WAY MATCH"; + /* 접수/매입확정 상태만 정산완료로 전이, 그 외는 현상 유지 */ + if (strcmp(rec.status, RC_ST_RECV) == 0 || + strcmp(rec.status, RC_ST_DONE) == 0) { + new_st = RC_ST_SETTLED; + rc = rc_rec_update_status(key, new_st); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0016] 정산전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + tx_log(TX_LOG_INFO, "[RC_OL_0016] 3-way 정합→정산완료 key=%s", key); + } else { + new_st = rec.status; /* 이미 정산완료 등 → 유지 */ + tx_log(TX_LOG_INFO, + "[RC_OL_0016] 3-way 정합(상태유지) key=%s status=%s", + key, rec.status); + } + } else { + /* 실패 leg 식별: leg1 우선 판정 */ + result = leg1 ? "LEG2FAIL" : "LEG1FAIL"; + new_st = RC_ST_FAIL; + rc = rc_rec_update_status(key, new_st); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0016] 실패전이 오류 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + tx_log(TX_LOG_WARN, + "[RC_OL_0016] 3-way 불일치 key=%s %s (승인=%ld 매입=%ld 입금=%ld)", + key, result, appr_amt, capture, dep_amt); + } + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0016] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "APPRAMT", appr_amt); + tx_buf_setlong(ctx->out, "CAPAMT", capture); + tx_buf_setlong(ctx->out, "DEPAMT", dep_amt); + tx_buf_sets(ctx->out, "LEG1", leg1 ? "OK" : "NG"); + tx_buf_sets(ctx->out, "LEG2", leg2 ? "OK" : "NG"); + tx_buf_sets(ctx->out, "RESULT", result); + tx_buf_sets(ctx->out, "STATUS", new_st); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0017.c b/app/online/rc_ol_0017.c index 401dcef..6ee0fb6 100644 --- a/app/online/rc_ol_0017.c +++ b/app/online/rc_ol_0017.c @@ -8,15 +8,28 @@ #line 1 "app/online/rc_ol_0017.pgc" /* @tier hard @module rc @transform reconciliation-online */ /* - * rc_ol_0017.pgc - 대사 온라인 서비스 (RC_OL_0017) [tier=hard] + * rc_ol_0017.pgc - 지연대사(미처리 접수) 조회 (CURSOR) 온라인 서비스 (RC_OL_0017) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 기준일(CUTOFF) 이전 영업일에 접수('R') 되었으나 아직 대사되지 않은 + * 지연건을 커서로 순회한다. biz_date 오름차순으로 fetch 하므로 가장 + * 먼저 나온 건이 최고령(oldest) 지연건이다. + * - delaycnt : 지연 건수, sumamt : 지연 금액합계 + * - OLDESTKEY / OLDESTDATE : 최고령 지연건의 KEY 와 영업일 + * 순수 조회 서비스이므로 상태전이/커밋 없이 읽기만 수행한다. + * + * 입력 TXBUF: + * CUTOFF (필수) 기준일 YYYYMMDD (이 날짜 미만 접수건이 지연 대상) + * 출력 TXBUF: + * CUTOFF / DELAYCNT / SUMAMT / OLDESTKEY / OLDESTDATE + * + * 지연건이 한 건도 없으면 DELAYCNT=0, SUMAMT=0, OLDESTKEY/DATE 는 + * 공백으로 응답한다(오류 아님). */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -89,88 +102,129 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0017.pgc" +#line 28 "app/online/rc_ol_0017.pgc" TX_SERVICE(RC_OL_0017, ctx) { - rc_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + +#line 33 "app/online/rc_ol_0017.pgc" + char h_cutoff [ 9 ] ; + +#line 34 "app/online/rc_ol_0017.pgc" + char h_key [ 16 ] ; + +#line 35 "app/online/rc_ol_0017.pgc" + char h_biz_date [ 9 ] ; + +#line 36 "app/online/rc_ol_0017.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 37 "app/online/rc_ol_0017.pgc" - tx_log(TX_LOG_INFO, "[RC_OL_0017] 대사 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0017] 필수 필드 누락"); + char cutoff[16]; + char oldest_key[16]; + char oldest_date[9]; + long delaycnt = 0; + long sumamt = 0; + int opened = 0; + + tx_log(TX_LOG_INFO, "[RC_OL_0017] 지연대사 조회(커서) 서비스 진입"); + + if (tx_buf_get(ctx->in, "CUTOFF", cutoff, sizeof(cutoff)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0017] CUTOFF 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0017] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(cutoff)) { + tx_log(TX_LOG_WARN, "[RC_OL_0017] 기준일 형식 오류 cutoff=%s", cutoff); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + strncpy(h_cutoff, cutoff, sizeof(h_cutoff) - 1); + h_cutoff[sizeof(h_cutoff) - 1] = '\0'; + memset(oldest_key, 0, sizeof(oldest_key)); + memset(oldest_date, 0, sizeof(oldest_date)); + + ECPGset_var( 0, ( h_cutoff ), __LINE__);\ + /* declare cur_rc_ol_0017 cursor for select key , biz_date , amount from rc_ledger where status = 'R' and biz_date < $1 order by biz_date */ +#line 69 "app/online/rc_ol_0017.pgc" + +#line 69 "app/online/rc_ol_0017.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_ol_0017 cursor for select key , biz_date , amount from rc_ledger where status = 'R' and biz_date < $1 order by biz_date", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 71 "app/online/rc_ol_0017.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0017"); + tx_return(ctx, TX_EDB, ctx->out); return; } + opened = 1; - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + for (;;) { + memset(h_key, 0, sizeof(h_key)); + memset(h_biz_date, 0, sizeof(h_biz_date)); + h_amount = 0; - rc = rc_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0017] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_ol_0017", ECPGt_EOIT, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 84 "app/online/rc_ol_0017.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0017"); + break; + } + + /* 최초 fetch 건 = biz_date 최소 = 최고령 지연건 */ + if (delaycnt == 0) { + strncpy(oldest_key, h_key, sizeof(oldest_key) - 1); + strncpy(oldest_date, h_biz_date, sizeof(oldest_date) - 1); + } + + sumamt += h_amount; + delaycnt++; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + if (opened) + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_ol_0017", ECPGt_EOIT, ECPGt_EORT);} +#line 104 "app/online/rc_ol_0017.pgc" + + + if (delaycnt == 0) { + tx_log(TX_LOG_INFO, "[RC_OL_0017] 지연대사 없음 cutoff=%s", cutoff); + } else { + tx_log(TX_LOG_WARN, + "[RC_OL_0017] 지연대사 %ld건 감지 최고령=%s(%s)", + delaycnt, oldest_key, oldest_date); } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("RC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[RC_OL_0017] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (rc_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[RC_OL_0017] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "CUTOFF", cutoff); + tx_buf_setlong(ctx->out, "DELAYCNT", delaycnt); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + tx_buf_sets(ctx->out, "OLDESTKEY", oldest_key); + tx_buf_sets(ctx->out, "OLDESTDATE", oldest_date); + tx_log(TX_LOG_INFO, + "[RC_OL_0017] 지연조회 완료 cutoff=%s 지연=%ld 합계=%ld 최고령=%s(%s)", + cutoff, delaycnt, sumamt, oldest_key, oldest_date); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0017.pgc b/app/online/rc_ol_0017.pgc index 30daa4b..788a164 100644 --- a/app/online/rc_ol_0017.pgc +++ b/app/online/rc_ol_0017.pgc @@ -1,14 +1,27 @@ /* @tier hard @module rc @transform reconciliation-online */ /* - * rc_ol_0017.pgc - 대사 온라인 서비스 (RC_OL_0017) [tier=hard] + * rc_ol_0017.pgc - 지연대사(미처리 접수) 조회 (CURSOR) 온라인 서비스 (RC_OL_0017) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 기준일(CUTOFF) 이전 영업일에 접수('R') 되었으나 아직 대사되지 않은 + * 지연건을 커서로 순회한다. biz_date 오름차순으로 fetch 하므로 가장 + * 먼저 나온 건이 최고령(oldest) 지연건이다. + * - delaycnt : 지연 건수, sumamt : 지연 금액합계 + * - OLDESTKEY / OLDESTDATE : 최고령 지연건의 KEY 와 영업일 + * 순수 조회 서비스이므로 상태전이/커밋 없이 읽기만 수행한다. + * + * 입력 TXBUF: + * CUTOFF (필수) 기준일 YYYYMMDD (이 날짜 미만 접수건이 지연 대상) + * 출력 TXBUF: + * CUTOFF / DELAYCNT / SUMAMT / OLDESTKEY / OLDESTDATE + * + * 지연건이 한 건도 없으면 DELAYCNT=0, SUMAMT=0, OLDESTKEY/DATE 는 + * 공백으로 응답한다(오류 아님). */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -16,83 +29,96 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0017, ctx) { - rc_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_cutoff[9]; + char h_key[16]; + char h_biz_date[9]; + long h_amount; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[RC_OL_0017] 대사 트랜잭션 오케스트레이션 진입"); + char cutoff[16]; + char oldest_key[16]; + char oldest_date[9]; + long delaycnt = 0; + long sumamt = 0; + int opened = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0017] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[RC_OL_0017] 지연대사 조회(커서) 서비스 진입"); + + if (tx_buf_get(ctx->in, "CUTOFF", cutoff, sizeof(cutoff)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0017] CUTOFF 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0017] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(cutoff)) { + tx_log(TX_LOG_WARN, "[RC_OL_0017] 기준일 형식 오류 cutoff=%s", cutoff); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + strncpy(h_cutoff, cutoff, sizeof(h_cutoff) - 1); + h_cutoff[sizeof(h_cutoff) - 1] = '\0'; + memset(oldest_key, 0, sizeof(oldest_key)); + memset(oldest_date, 0, sizeof(oldest_date)); + + EXEC SQL DECLARE cur_rc_ol_0017 CURSOR FOR + SELECT key, biz_date, amount + FROM rc_ledger + WHERE status = 'R' + AND biz_date < :h_cutoff + ORDER BY biz_date; + + EXEC SQL OPEN cur_rc_ol_0017; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0017"); + tx_return(ctx, TX_EDB, ctx->out); return; } + opened = 1; - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + for (;;) { + memset(h_key, 0, sizeof(h_key)); + memset(h_biz_date, 0, sizeof(h_biz_date)); + h_amount = 0; - rc = rc_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0017] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; + EXEC SQL FETCH cur_rc_ol_0017 INTO :h_key, :h_biz_date, :h_amount; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0017"); + break; + } + + /* 최초 fetch 건 = biz_date 최소 = 최고령 지연건 */ + if (delaycnt == 0) { + strncpy(oldest_key, h_key, sizeof(oldest_key) - 1); + strncpy(oldest_date, h_biz_date, sizeof(oldest_date) - 1); + } + + sumamt += h_amount; + delaycnt++; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + if (opened) + EXEC SQL CLOSE cur_rc_ol_0017; + + if (delaycnt == 0) { + tx_log(TX_LOG_INFO, "[RC_OL_0017] 지연대사 없음 cutoff=%s", cutoff); + } else { + tx_log(TX_LOG_WARN, + "[RC_OL_0017] 지연대사 %ld건 감지 최고령=%s(%s)", + delaycnt, oldest_key, oldest_date); } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("RC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[RC_OL_0017] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (rc_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[RC_OL_0017] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "CUTOFF", cutoff); + tx_buf_setlong(ctx->out, "DELAYCNT", delaycnt); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + tx_buf_sets(ctx->out, "OLDESTKEY", oldest_key); + tx_buf_sets(ctx->out, "OLDESTDATE", oldest_date); + tx_log(TX_LOG_INFO, + "[RC_OL_0017] 지연조회 완료 cutoff=%s 지연=%ld 합계=%ld 최고령=%s(%s)", + cutoff, delaycnt, sumamt, oldest_key, oldest_date); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0018.c b/app/online/rc_ol_0018.c index d0f5a27..b348c59 100644 --- a/app/online/rc_ol_0018.c +++ b/app/online/rc_ol_0018.c @@ -8,15 +8,30 @@ #line 1 "app/online/rc_ol_0018.pgc" /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0018.pgc - 대사 온라인 서비스 (RC_OL_0018) [tier=easy] + * rc_ol_0018.pgc - 대사 통계 조회 온라인 서비스 (RC_OL_0018) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일(BIZDATE)의 원장 전체를 커서로 순회하며 대사 통계를 집계한다. + * + * [집계 항목] + * CNT - 대상 건수 + * MINAMT - 최소 거래금액 + * MAXAMT - 최대 거래금액 + * SUMAMT - 거래금액 합계 + * AVGAMT - 평균 거래금액 (SUMAMT / CNT, 0 나눗셈 방지) + * MATCHRATE - 대사완료율 (matched * 100 / CNT) + * + * [대사완료 판정] + * 상태가 매입확정('M') 또는 정산완료('S') 인 건을 대사완료로 본다. + * 접수('R')/불일치('U') 는 미완료로 집계된다. + * + * 원장을 변경하지 않는 순수 조회(read-only) 서비스이며, 커서 기반이므로 + * txcore_dbio.h 의 TX_SQL_OK / TX_SQL_NOTFOUND / TX_DBIO_LOG_ERR 를 사용한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -89,37 +104,139 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0018.pgc" +#line 30 "app/online/rc_ol_0018.pgc" TX_SERVICE(RC_OL_0018, ctx) { - rc_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 35 "app/online/rc_ol_0018.pgc" + char h_biz_date [ 9 ] ; + +#line 36 "app/online/rc_ol_0018.pgc" + long h_amount ; + +#line 37 "app/online/rc_ol_0018.pgc" + char h_status [ 2 ] ; +/* exec sql end declare section */ +#line 38 "app/online/rc_ol_0018.pgc" - tx_log(TX_LOG_INFO, "[RC_OL_0018] 대사 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0018] KEY 누락"); + char bizdate[9]; + long cnt = 0, matched = 0, unmatched = 0; + long minamt = 0, maxamt = 0, sumamt = 0; + long avgamt = 0, matchrate = 0; + int first = 1; + + tx_log(TX_LOG_INFO, "[RC_OL_0018] 대사 통계 조회 서비스 진입"); + + /* 입력 검증: 영업일 필수 + 형식(YYYYMMDD) */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0018] BIZDATE 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0018] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = rc_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0018] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + tx_log(TX_LOG_INFO, "[RC_OL_0018] 통계 집계 시작 date=%s", bizdate); + + ECPGset_var( 0, ( h_biz_date ), __LINE__);\ + /* declare cur_rc_ol_0018 cursor for select amount , status from rc_ledger where biz_date = $1 order by key */ +#line 69 "app/online/rc_ol_0018.pgc" + +#line 69 "app/online/rc_ol_0018.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_ol_0018 cursor for select amount , status from rc_ledger where biz_date = $1 order by key", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 71 "app/online/rc_ol_0018.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0018"); + tx_return(ctx, TX_EDB, ctx->out); return; } + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_ol_0018", ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 79 "app/online/rc_ol_0018.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0018"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_ol_0018", ECPGt_EOIT, ECPGt_EORT);} +#line 85 "app/online/rc_ol_0018.pgc" + + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + cnt++; + sumamt += h_amount; + + /* 최소/최대: 첫 행에서 초기화 후 비교 */ + if (first) { + minamt = maxamt = h_amount; + first = 0; + } else { + if (h_amount < minamt) minamt = h_amount; + if (h_amount > maxamt) maxamt = h_amount; + } + + /* 대사완료 = 매입확정('M') 또는 정산완료('S') */ + if (h_status[0] == RC_ST_DONE[0] || h_status[0] == RC_ST_SETTLED[0]) + matched++; + else + unmatched++; + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_ol_0018", ECPGt_EOIT, ECPGt_EORT);} +#line 109 "app/online/rc_ol_0018.pgc" + + + /* 대상이 없으면 통계 산출 불가 */ + if (cnt == 0) { + tx_log(TX_LOG_WARN, "[RC_OL_0018] 집계대상 없음 date=%s", bizdate); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + /* 0 나눗셈 방지: cnt > 0 확인 후 평균/완료율 산출 */ + avgamt = sumamt / cnt; + matchrate = (matched * 100) / cnt; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0018] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNT", cnt); + tx_buf_setlong(ctx->out, "MINAMT", minamt); + tx_buf_setlong(ctx->out, "MAXAMT", maxamt); + tx_buf_setlong(ctx->out, "AVGAMT", avgamt); + tx_buf_setlong(ctx->out, "MATCHRATE", matchrate); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + + tx_log(TX_LOG_INFO, + "[RC_OL_0018] 집계결과 date=%s 건수=%ld 완료=%ld 미완료=%ld", + bizdate, cnt, matched, unmatched); + tx_log(TX_LOG_INFO, + "[RC_OL_0018] 통계완료 date=%s 최소=%ld 최대=%ld 평균=%ld 합계=%ld 완료율=%ld%%", + bizdate, minamt, maxamt, avgamt, sumamt, matchrate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0018.pgc b/app/online/rc_ol_0018.pgc index f6cff1a..48a3f56 100644 --- a/app/online/rc_ol_0018.pgc +++ b/app/online/rc_ol_0018.pgc @@ -1,14 +1,29 @@ /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0018.pgc - 대사 온라인 서비스 (RC_OL_0018) [tier=easy] + * rc_ol_0018.pgc - 대사 통계 조회 온라인 서비스 (RC_OL_0018) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일(BIZDATE)의 원장 전체를 커서로 순회하며 대사 통계를 집계한다. + * + * [집계 항목] + * CNT - 대상 건수 + * MINAMT - 최소 거래금액 + * MAXAMT - 최대 거래금액 + * SUMAMT - 거래금액 합계 + * AVGAMT - 평균 거래금액 (SUMAMT / CNT, 0 나눗셈 방지) + * MATCHRATE - 대사완료율 (matched * 100 / CNT) + * + * [대사완료 판정] + * 상태가 매입확정('M') 또는 정산완료('S') 인 건을 대사완료로 본다. + * 접수('R')/불일치('U') 는 미완료로 집계된다. + * + * 원장을 변경하지 않는 순수 조회(read-only) 서비스이며, 커서 기반이므로 + * txcore_dbio.h 의 TX_SQL_OK / TX_SQL_NOTFOUND / TX_DBIO_LOG_ERR 를 사용한다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -16,32 +31,108 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0018, ctx) { - rc_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + long h_amount; + char h_status[2]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[RC_OL_0018] 대사 단건조회 서비스 진입"); + char bizdate[9]; + long cnt = 0, matched = 0, unmatched = 0; + long minamt = 0, maxamt = 0, sumamt = 0; + long avgamt = 0, matchrate = 0; + int first = 1; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0018] KEY 누락"); + tx_log(TX_LOG_INFO, "[RC_OL_0018] 대사 통계 조회 서비스 진입"); + + /* 입력 검증: 영업일 필수 + 형식(YYYYMMDD) */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0018] BIZDATE 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0018] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = rc_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0018] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + tx_log(TX_LOG_INFO, "[RC_OL_0018] 통계 집계 시작 date=%s", bizdate); + + EXEC SQL DECLARE cur_rc_ol_0018 CURSOR FOR + SELECT amount, status + FROM rc_ledger + WHERE biz_date = :h_biz_date + ORDER BY key; + + EXEC SQL OPEN cur_rc_ol_0018; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0018"); + tx_return(ctx, TX_EDB, ctx->out); return; } + for (;;) { + EXEC SQL FETCH cur_rc_ol_0018 INTO :h_amount, :h_status; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0018"); + EXEC SQL CLOSE cur_rc_ol_0018; + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + cnt++; + sumamt += h_amount; + + /* 최소/최대: 첫 행에서 초기화 후 비교 */ + if (first) { + minamt = maxamt = h_amount; + first = 0; + } else { + if (h_amount < minamt) minamt = h_amount; + if (h_amount > maxamt) maxamt = h_amount; + } + + /* 대사완료 = 매입확정('M') 또는 정산완료('S') */ + if (h_status[0] == RC_ST_DONE[0] || h_status[0] == RC_ST_SETTLED[0]) + matched++; + else + unmatched++; + } + + EXEC SQL CLOSE cur_rc_ol_0018; + + /* 대상이 없으면 통계 산출 불가 */ + if (cnt == 0) { + tx_log(TX_LOG_WARN, "[RC_OL_0018] 집계대상 없음 date=%s", bizdate); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + + /* 0 나눗셈 방지: cnt > 0 확인 후 평균/완료율 산출 */ + avgamt = sumamt / cnt; + matchrate = (matched * 100) / cnt; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0018] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNT", cnt); + tx_buf_setlong(ctx->out, "MINAMT", minamt); + tx_buf_setlong(ctx->out, "MAXAMT", maxamt); + tx_buf_setlong(ctx->out, "AVGAMT", avgamt); + tx_buf_setlong(ctx->out, "MATCHRATE", matchrate); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + + tx_log(TX_LOG_INFO, + "[RC_OL_0018] 집계결과 date=%s 건수=%ld 완료=%ld 미완료=%ld", + bizdate, cnt, matched, unmatched); + tx_log(TX_LOG_INFO, + "[RC_OL_0018] 통계완료 date=%s 최소=%ld 최대=%ld 평균=%ld 합계=%ld 완료율=%ld%%", + bizdate, minamt, maxamt, avgamt, sumamt, matchrate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0019.c b/app/online/rc_ol_0019.c index d17ea53..7866124 100644 --- a/app/online/rc_ol_0019.c +++ b/app/online/rc_ol_0019.c @@ -8,10 +8,22 @@ #line 1 "app/online/rc_ol_0019.pgc" /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0019.pgc - 대사 온라인 서비스 (RC_OL_0019) [tier=easy] + * rc_ol_0019.pgc - 대사 원장반영 연계 온라인 서비스 (RC_OL_0019) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입확정('M') 된 대사 원장 1건을 정산계로 반영(연계) 한다. + * + * [처리 규칙] + * 1. KEY 로 원장 1건을 조회한다. + * 2. 상태가 매입확정('M') 이 아니면 반영 대상이 아니므로 원장을 변경하지 + * 않고 RESULT=NOTREADY 로 응답한다. + * 3. 매입확정 건이면 원장 반영액(순액) = 거래금액 - 수수료 를 산출하고, + * 상태를 정산완료('S') 로 전이한다. RESULT=SETTLED. + * + * [상태 전이] + * 매입확정('M') --원장반영--> 정산완료('S') + * + * 정산계 연계 금액은 SETTLEAMT 필드로 응답에 실어 후속 정산 배치가 + * 소비할 수 있게 한다. 커서를 사용하지 않는 단건 처리 서비스이다. */ #include #include @@ -89,37 +101,100 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0019.pgc" +#line 27 "app/online/rc_ol_0019.pgc" TX_SERVICE(RC_OL_0019, ctx) { rc_rec_t rec; char key[16]; + long settle_amt = 0; int rc; - tx_log(TX_LOG_INFO, "[RC_OL_0019] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0019] 대사 원장반영 연계 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0019] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (key[0] == '\0') { + tx_log(TX_LOG_WARN, "[RC_OL_0019] KEY 공백"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + tx_log(TX_LOG_INFO, "[RC_OL_0019] 원장반영 대상 key=%s", key); memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0019] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0019] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + tx_log(TX_LOG_INFO, + "[RC_OL_0019] 원장 조회 key=%s 상태=%s 거래액=%ld 수수료=%ld", + key, rec.status, rec.amount, rec.fee); + + /* 반영 대상은 매입확정('M') 건으로 한정 */ + if (rec.status[0] != RC_ST_DONE[0]) { + tx_log(TX_LOG_WARN, + "[RC_OL_0019] 반영대상 아님(매입확정 아님) key=%s status=%s", + key, rec.status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_setlong(ctx->out, "FEE", rec.fee); + tx_buf_setlong(ctx->out, "SETTLEAMT", 0); + tx_buf_sets(ctx->out, "OLDSTATUS", rec.status); + tx_buf_sets(ctx->out, "NEWSTATUS", rec.status); + tx_buf_sets(ctx->out, "RESULT", "NOTREADY"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* 원장 반영액 = 거래금액 - 수수료 (순액) */ + settle_amt = rec.amount - rec.fee; + if (settle_amt < 0) { + /* 수수료가 거래금액을 초과하는 비정상 데이터 방어 */ + tx_log(TX_LOG_ERROR, + "[RC_OL_0019] 반영액 음수(데이터 오류) key=%s amount=%ld fee=%ld", + key, rec.amount, rec.fee); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + tx_log(TX_LOG_INFO, + "[RC_OL_0019] 정산반영 산출 key=%s 거래액=%ld 수수료=%ld 반영액=%ld", + key, rec.amount, rec.fee, settle_amt); + + /* + * 상태 전이는 rc 표준 DBIO(rc_rec_update_status) 로만 수행한다. + * 원장을 매입확정('M')에서 정산완료('S')로 전이하며, 실패 시 응답 없이 + * 원 오류코드를 그대로 상위로 반환한다. + */ + rc = rc_rec_update_status(key, RC_ST_SETTLED); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0019] 정산완료 전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "FEE", rec.fee); + tx_buf_setlong(ctx->out, "SETTLEAMT", settle_amt); + tx_buf_sets(ctx->out, "OLDSTATUS", RC_ST_DONE); + tx_buf_sets(ctx->out, "NEWSTATUS", RC_ST_SETTLED); + tx_buf_sets(ctx->out, "RESULT", "SETTLED"); + + tx_log(TX_LOG_INFO, + "[RC_OL_0019] 원장반영 완료 key=%s 반영액=%ld 상태=%s->%s", + key, settle_amt, RC_ST_DONE, RC_ST_SETTLED); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0019.pgc b/app/online/rc_ol_0019.pgc index b0e37ea..e6dc7fb 100644 --- a/app/online/rc_ol_0019.pgc +++ b/app/online/rc_ol_0019.pgc @@ -1,9 +1,21 @@ /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0019.pgc - 대사 온라인 서비스 (RC_OL_0019) [tier=easy] + * rc_ol_0019.pgc - 대사 원장반영 연계 온라인 서비스 (RC_OL_0019) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입확정('M') 된 대사 원장 1건을 정산계로 반영(연계) 한다. + * + * [처리 규칙] + * 1. KEY 로 원장 1건을 조회한다. + * 2. 상태가 매입확정('M') 이 아니면 반영 대상이 아니므로 원장을 변경하지 + * 않고 RESULT=NOTREADY 로 응답한다. + * 3. 매입확정 건이면 원장 반영액(순액) = 거래금액 - 수수료 를 산출하고, + * 상태를 정산완료('S') 로 전이한다. RESULT=SETTLED. + * + * [상태 전이] + * 매입확정('M') --원장반영--> 정산완료('S') + * + * 정산계 연계 금액은 SETTLEAMT 필드로 응답에 실어 후속 정산 배치가 + * 소비할 수 있게 한다. 커서를 사용하지 않는 단건 처리 서비스이다. */ #include #include @@ -18,30 +30,93 @@ TX_SERVICE(RC_OL_0019, ctx) { rc_rec_t rec; char key[16]; + long settle_amt = 0; int rc; - tx_log(TX_LOG_INFO, "[RC_OL_0019] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0019] 대사 원장반영 연계 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0019] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (key[0] == '\0') { + tx_log(TX_LOG_WARN, "[RC_OL_0019] KEY 공백"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + tx_log(TX_LOG_INFO, "[RC_OL_0019] 원장반영 대상 key=%s", key); memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0019] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0019] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + tx_log(TX_LOG_INFO, + "[RC_OL_0019] 원장 조회 key=%s 상태=%s 거래액=%ld 수수료=%ld", + key, rec.status, rec.amount, rec.fee); + + /* 반영 대상은 매입확정('M') 건으로 한정 */ + if (rec.status[0] != RC_ST_DONE[0]) { + tx_log(TX_LOG_WARN, + "[RC_OL_0019] 반영대상 아님(매입확정 아님) key=%s status=%s", + key, rec.status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_setlong(ctx->out, "FEE", rec.fee); + tx_buf_setlong(ctx->out, "SETTLEAMT", 0); + tx_buf_sets(ctx->out, "OLDSTATUS", rec.status); + tx_buf_sets(ctx->out, "NEWSTATUS", rec.status); + tx_buf_sets(ctx->out, "RESULT", "NOTREADY"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* 원장 반영액 = 거래금액 - 수수료 (순액) */ + settle_amt = rec.amount - rec.fee; + if (settle_amt < 0) { + /* 수수료가 거래금액을 초과하는 비정상 데이터 방어 */ + tx_log(TX_LOG_ERROR, + "[RC_OL_0019] 반영액 음수(데이터 오류) key=%s amount=%ld fee=%ld", + key, rec.amount, rec.fee); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + tx_log(TX_LOG_INFO, + "[RC_OL_0019] 정산반영 산출 key=%s 거래액=%ld 수수료=%ld 반영액=%ld", + key, rec.amount, rec.fee, settle_amt); + + /* + * 상태 전이는 rc 표준 DBIO(rc_rec_update_status) 로만 수행한다. + * 원장을 매입확정('M')에서 정산완료('S')로 전이하며, 실패 시 응답 없이 + * 원 오류코드를 그대로 상위로 반환한다. + */ + rc = rc_rec_update_status(key, RC_ST_SETTLED); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0019] 정산완료 전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "FEE", rec.fee); + tx_buf_setlong(ctx->out, "SETTLEAMT", settle_amt); + tx_buf_sets(ctx->out, "OLDSTATUS", RC_ST_DONE); + tx_buf_sets(ctx->out, "NEWSTATUS", RC_ST_SETTLED); + tx_buf_sets(ctx->out, "RESULT", "SETTLED"); + + tx_log(TX_LOG_INFO, + "[RC_OL_0019] 원장반영 완료 key=%s 반영액=%ld 상태=%s->%s", + key, settle_amt, RC_ST_DONE, RC_ST_SETTLED); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0020.c b/app/online/rc_ol_0020.c index 3f58455..e491d44 100644 --- a/app/online/rc_ol_0020.c +++ b/app/online/rc_ol_0020.c @@ -8,10 +8,24 @@ #line 1 "app/online/rc_ol_0020.pgc" /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0020.pgc - 대사 온라인 서비스 (RC_OL_0020) [tier=medium] + * rc_ol_0020.pgc - 부분대사 온라인 서비스 (RC_OL_0020) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 접수건에 대해 부분매입액(PARTAMT) 을 반영하는 부분대사를 수행한다. + * + * [입력] + * KEY - 처리키(승인/거래 번호) + * PARTAMT - 부분매입액(원) + * + * [처리 규칙] + * - PARTAMT 가 0 이하이거나 원장 거래금액을 초과하면 입력오류(TX_EINVAL). + * - PARTAMT == amount → 전액대사 → 매입확정('M'), 잔여액 0, RESULT=FULL. + * - PARTAMT < amount → 부분대사 → 접수('R') 유지, 잔여액=amount-PARTAMT, + * RESULT=PARTIAL. (남은 금액에 대한 추가 대사 필요) + * + * [응답] + * KEY / AMOUNT / PARTAMT / REMAIN(잔여액) / STATUS / RESULT + * + * 커서를 사용하지 않는 단건 처리 서비스이다. */ #include #include @@ -89,60 +103,98 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0020.pgc" +#line 29 "app/online/rc_ol_0020.pgc" TX_SERVICE(RC_OL_0020, ctx) { rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + long part_amt = 0; + long remain = 0; int rc; + const char *new_st; + const char *result; - tx_log(TX_LOG_INFO, "[RC_OL_0020] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0020] 부분대사 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0020] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0020] KEY 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_getlong(ctx->in, "PARTAMT", &part_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0020] PARTAMT(부분매입액) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0020] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0020] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } + tx_log(TX_LOG_INFO, "[RC_OL_0020] 부분대사 요청 key=%s 부분매입액=%ld", + key, part_amt); memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); + rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0020] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[RC_OL_0020] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + tx_log(TX_LOG_INFO, + "[RC_OL_0020] 원장 조회 key=%s 거래액=%ld 부분매입액=%ld 상태=%s", + key, rec.amount, part_amt, rec.status); + + /* 업무검증: 부분매입액 범위 (0 < PARTAMT <= 거래금액) */ + if (part_amt <= 0) { + tx_log(TX_LOG_WARN, + "[RC_OL_0020] 부분매입액 0 이하 오류 key=%s partamt=%ld", + key, part_amt); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (part_amt > rec.amount) { + tx_log(TX_LOG_WARN, + "[RC_OL_0020] 부분매입액 거래금액 초과 key=%s partamt=%ld amount=%ld", + key, part_amt, rec.amount); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + if (part_amt == rec.amount) { + /* 전액대사 → 매입확정, 잔여액 없음 */ + new_st = RC_ST_DONE; + result = "FULL"; + remain = 0; + tx_log(TX_LOG_INFO, + "[RC_OL_0020] 전액대사 key=%s amount=%ld", key, rec.amount); + } else { + /* 부분대사 → 접수 상태 유지, 잔여액 존재 */ + new_st = RC_ST_RECV; + result = "PARTIAL"; + remain = rec.amount - part_amt; + tx_log(TX_LOG_INFO, + "[RC_OL_0020] 부분대사 key=%s 부분액=%ld 잔여액=%ld", + key, part_amt, remain); + } + + rc = rc_rec_update_status(key, new_st); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0020] 상태전이 실패 key=%s rc=%d", key, rc); + tx_return(ctx, rc, ctx->out); + return; + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0020] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_setlong(ctx->out, "PARTAMT", part_amt); + tx_buf_setlong(ctx->out, "REMAIN", remain); + tx_buf_sets(ctx->out, "STATUS", new_st); + tx_buf_sets(ctx->out, "RESULT", result); + + tx_log(TX_LOG_INFO, + "[RC_OL_0020] 부분대사 완료 key=%s 결과=%s 잔여액=%ld 상태=%s", + key, result, remain, new_st); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0020.pgc b/app/online/rc_ol_0020.pgc index 4bc93e2..9391f19 100644 --- a/app/online/rc_ol_0020.pgc +++ b/app/online/rc_ol_0020.pgc @@ -1,9 +1,23 @@ /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0020.pgc - 대사 온라인 서비스 (RC_OL_0020) [tier=medium] + * rc_ol_0020.pgc - 부분대사 온라인 서비스 (RC_OL_0020) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 접수건에 대해 부분매입액(PARTAMT) 을 반영하는 부분대사를 수행한다. + * + * [입력] + * KEY - 처리키(승인/거래 번호) + * PARTAMT - 부분매입액(원) + * + * [처리 규칙] + * - PARTAMT 가 0 이하이거나 원장 거래금액을 초과하면 입력오류(TX_EINVAL). + * - PARTAMT == amount → 전액대사 → 매입확정('M'), 잔여액 0, RESULT=FULL. + * - PARTAMT < amount → 부분대사 → 접수('R') 유지, 잔여액=amount-PARTAMT, + * RESULT=PARTIAL. (남은 금액에 대한 추가 대사 필요) + * + * [응답] + * KEY / AMOUNT / PARTAMT / REMAIN(잔여액) / STATUS / RESULT + * + * 커서를 사용하지 않는 단건 처리 서비스이다. */ #include #include @@ -17,54 +31,92 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0020, ctx) { rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + long part_amt = 0; + long remain = 0; int rc; + const char *new_st; + const char *result; - tx_log(TX_LOG_INFO, "[RC_OL_0020] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0020] 부분대사 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0020] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0020] KEY 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_getlong(ctx->in, "PARTAMT", &part_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0020] PARTAMT(부분매입액) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0020] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0020] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } + tx_log(TX_LOG_INFO, "[RC_OL_0020] 부분대사 요청 key=%s 부분매입액=%ld", + key, part_amt); memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); + rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0020] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[RC_OL_0020] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + tx_log(TX_LOG_INFO, + "[RC_OL_0020] 원장 조회 key=%s 거래액=%ld 부분매입액=%ld 상태=%s", + key, rec.amount, part_amt, rec.status); + + /* 업무검증: 부분매입액 범위 (0 < PARTAMT <= 거래금액) */ + if (part_amt <= 0) { + tx_log(TX_LOG_WARN, + "[RC_OL_0020] 부분매입액 0 이하 오류 key=%s partamt=%ld", + key, part_amt); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (part_amt > rec.amount) { + tx_log(TX_LOG_WARN, + "[RC_OL_0020] 부분매입액 거래금액 초과 key=%s partamt=%ld amount=%ld", + key, part_amt, rec.amount); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + if (part_amt == rec.amount) { + /* 전액대사 → 매입확정, 잔여액 없음 */ + new_st = RC_ST_DONE; + result = "FULL"; + remain = 0; + tx_log(TX_LOG_INFO, + "[RC_OL_0020] 전액대사 key=%s amount=%ld", key, rec.amount); + } else { + /* 부분대사 → 접수 상태 유지, 잔여액 존재 */ + new_st = RC_ST_RECV; + result = "PARTIAL"; + remain = rec.amount - part_amt; + tx_log(TX_LOG_INFO, + "[RC_OL_0020] 부분대사 key=%s 부분액=%ld 잔여액=%ld", + key, part_amt, remain); + } + + rc = rc_rec_update_status(key, new_st); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0020] 상태전이 실패 key=%s rc=%d", key, rc); + tx_return(ctx, rc, ctx->out); + return; + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0020] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_setlong(ctx->out, "PARTAMT", part_amt); + tx_buf_setlong(ctx->out, "REMAIN", remain); + tx_buf_sets(ctx->out, "STATUS", new_st); + tx_buf_sets(ctx->out, "RESULT", result); + + tx_log(TX_LOG_INFO, + "[RC_OL_0020] 부분대사 완료 key=%s 결과=%s 잔여액=%ld 상태=%s", + key, result, remain, new_st); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0021.c b/app/online/rc_ol_0021.c index 159a942..6b90257 100644 --- a/app/online/rc_ol_0021.c +++ b/app/online/rc_ol_0021.c @@ -8,10 +8,23 @@ #line 1 "app/online/rc_ol_0021.pgc" /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0021.pgc - 대사 온라인 서비스 (RC_OL_0021) [tier=easy] + * rc_ol_0021.pgc - 대사 diff 리포트 조회 온라인 서비스 (RC_OL_0021) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 1건을 읽어 승인계에서 넘어온 값과의 차이를 리포트한다. + * + * [입력] + * KEY - 처리키(승인/거래 번호) + * APPRAMT - 승인금액(원) + * EXPFEE - 예상 수수료(원) + * + * [산출] + * AMTDIFF = amount - APPRAMT (금액 차이) + * FEEDIFF = fee - EXPFEE (수수료 차이) + * RESULT = "OK" (두 차이가 모두 0) + * = "DIFF" (하나라도 0 이 아님) + * + * 이 서비스는 대사 불일치 원인 분석용 리포트이며, 원장 상태를 전혀 + * 변경하지 않는 순수 조회(read-only) 서비스이다. 커서를 사용하지 않는다. */ #include #include @@ -89,37 +102,97 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0021.pgc" +#line 28 "app/online/rc_ol_0021.pgc" TX_SERVICE(RC_OL_0021, ctx) { rc_rec_t rec; char key[16]; + long appr_amt = 0; + long exp_fee = 0; + long amt_diff, fee_diff; int rc; + const char *result; - tx_log(TX_LOG_INFO, "[RC_OL_0021] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0021] 대사 diff 리포트 조회 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0021] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_getlong(ctx->in, "APPRAMT", &appr_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0021] APPRAMT(승인금액) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_getlong(ctx->in, "EXPFEE", &exp_fee) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0021] EXPFEE(예상 수수료) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 승인금액/예상수수료는 음수가 될 수 없다 */ + if (appr_amt < 0 || exp_fee < 0) { + tx_log(TX_LOG_WARN, + "[RC_OL_0021] 입력값 음수 오류 key=%s appramt=%ld expfee=%ld", + key, appr_amt, exp_fee); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + tx_log(TX_LOG_INFO, + "[RC_OL_0021] 대사 diff 입력 key=%s 승인금액=%ld 예상수수료=%ld", + key, appr_amt, exp_fee); memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0021] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0021] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + tx_log(TX_LOG_INFO, + "[RC_OL_0021] 대상 조회 key=%s 매입액=%ld 수수료=%ld 상태=%s", + key, rec.amount, rec.fee, rec.status); + + /* 차이 산출 (원장 무변경) */ + amt_diff = rec.amount - appr_amt; + fee_diff = rec.fee - exp_fee; + + if (amt_diff == 0 && fee_diff == 0) { + result = "OK"; + tx_log(TX_LOG_INFO, "[RC_OL_0021] 차이없음 key=%s", key); + } else { + result = "DIFF"; + /* 차이 유형 세분화 로그: 금액/수수료 어느 항목이 어긋났는지 기록 */ + if (amt_diff != 0 && fee_diff != 0) + tx_log(TX_LOG_WARN, + "[RC_OL_0021] 금액+수수료 동시 불일치 key=%s 금액차=%ld 수수료차=%ld", + key, amt_diff, fee_diff); + else if (amt_diff != 0) + tx_log(TX_LOG_WARN, + "[RC_OL_0021] 금액 불일치 key=%s 금액차=%ld", key, amt_diff); + else + tx_log(TX_LOG_WARN, + "[RC_OL_0021] 수수료 불일치 key=%s 수수료차=%ld", key, fee_diff); + } + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "APPRAMT", appr_amt); + tx_buf_setlong(ctx->out, "AMTDIFF", amt_diff); + tx_buf_setlong(ctx->out, "FEE", rec.fee); + tx_buf_setlong(ctx->out, "EXPFEE", exp_fee); + tx_buf_setlong(ctx->out, "FEEDIFF", fee_diff); + tx_buf_sets(ctx->out, "RESULT", result); + + tx_log(TX_LOG_INFO, + "[RC_OL_0021] 리포트완료 key=%s 결과=%s 금액차=%ld 수수료차=%ld", + key, result, amt_diff, fee_diff); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0021.pgc b/app/online/rc_ol_0021.pgc index 680c013..c84d30a 100644 --- a/app/online/rc_ol_0021.pgc +++ b/app/online/rc_ol_0021.pgc @@ -1,9 +1,22 @@ /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0021.pgc - 대사 온라인 서비스 (RC_OL_0021) [tier=easy] + * rc_ol_0021.pgc - 대사 diff 리포트 조회 온라인 서비스 (RC_OL_0021) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 1건을 읽어 승인계에서 넘어온 값과의 차이를 리포트한다. + * + * [입력] + * KEY - 처리키(승인/거래 번호) + * APPRAMT - 승인금액(원) + * EXPFEE - 예상 수수료(원) + * + * [산출] + * AMTDIFF = amount - APPRAMT (금액 차이) + * FEEDIFF = fee - EXPFEE (수수료 차이) + * RESULT = "OK" (두 차이가 모두 0) + * = "DIFF" (하나라도 0 이 아님) + * + * 이 서비스는 대사 불일치 원인 분석용 리포트이며, 원장 상태를 전혀 + * 변경하지 않는 순수 조회(read-only) 서비스이다. 커서를 사용하지 않는다. */ #include #include @@ -18,30 +31,90 @@ TX_SERVICE(RC_OL_0021, ctx) { rc_rec_t rec; char key[16]; + long appr_amt = 0; + long exp_fee = 0; + long amt_diff, fee_diff; int rc; + const char *result; - tx_log(TX_LOG_INFO, "[RC_OL_0021] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0021] 대사 diff 리포트 조회 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0021] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_getlong(ctx->in, "APPRAMT", &appr_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0021] APPRAMT(승인금액) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_getlong(ctx->in, "EXPFEE", &exp_fee) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0021] EXPFEE(예상 수수료) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 승인금액/예상수수료는 음수가 될 수 없다 */ + if (appr_amt < 0 || exp_fee < 0) { + tx_log(TX_LOG_WARN, + "[RC_OL_0021] 입력값 음수 오류 key=%s appramt=%ld expfee=%ld", + key, appr_amt, exp_fee); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + tx_log(TX_LOG_INFO, + "[RC_OL_0021] 대사 diff 입력 key=%s 승인금액=%ld 예상수수료=%ld", + key, appr_amt, exp_fee); memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0021] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0021] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + tx_log(TX_LOG_INFO, + "[RC_OL_0021] 대상 조회 key=%s 매입액=%ld 수수료=%ld 상태=%s", + key, rec.amount, rec.fee, rec.status); + + /* 차이 산출 (원장 무변경) */ + amt_diff = rec.amount - appr_amt; + fee_diff = rec.fee - exp_fee; + + if (amt_diff == 0 && fee_diff == 0) { + result = "OK"; + tx_log(TX_LOG_INFO, "[RC_OL_0021] 차이없음 key=%s", key); + } else { + result = "DIFF"; + /* 차이 유형 세분화 로그: 금액/수수료 어느 항목이 어긋났는지 기록 */ + if (amt_diff != 0 && fee_diff != 0) + tx_log(TX_LOG_WARN, + "[RC_OL_0021] 금액+수수료 동시 불일치 key=%s 금액차=%ld 수수료차=%ld", + key, amt_diff, fee_diff); + else if (amt_diff != 0) + tx_log(TX_LOG_WARN, + "[RC_OL_0021] 금액 불일치 key=%s 금액차=%ld", key, amt_diff); + else + tx_log(TX_LOG_WARN, + "[RC_OL_0021] 수수료 불일치 key=%s 수수료차=%ld", key, fee_diff); + } + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "APPRAMT", appr_amt); + tx_buf_setlong(ctx->out, "AMTDIFF", amt_diff); + tx_buf_setlong(ctx->out, "FEE", rec.fee); + tx_buf_setlong(ctx->out, "EXPFEE", exp_fee); + tx_buf_setlong(ctx->out, "FEEDIFF", fee_diff); + tx_buf_sets(ctx->out, "RESULT", result); + + tx_log(TX_LOG_INFO, + "[RC_OL_0021] 리포트완료 key=%s 결과=%s 금액차=%ld 수수료차=%ld", + key, result, amt_diff, fee_diff); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0022.c b/app/online/rc_ol_0022.c index 6f896e6..d4b6257 100644 --- a/app/online/rc_ol_0022.c +++ b/app/online/rc_ol_0022.c @@ -8,10 +8,27 @@ #line 1 "app/online/rc_ol_0022.pgc" /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0022.pgc - 대사 온라인 서비스 (RC_OL_0022) [tier=medium] + * rc_ol_0022.pgc - 대사 자동보정 온라인 서비스 (RC_OL_0022) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인금액(APPRAMT) 과 원장 거래금액의 차이를 허용오차(TOL) 와 비교하여 + * 자동보정 여부를 판정한다. + * + * [입력] + * KEY - 처리키(승인/거래 번호) + * APPRAMT - 승인금액(원) + * TOL - 허용오차(원, 0 이상) + * + * [판정] + * diff = amount - APPRAMT, absd = |diff| + * - diff == 0 → ACTION=NOOP, 상태 변경 없음. + * - absd <= TOL → 자동보정(허용오차 이내) → 매입확정('M'), ACTION=CORRECTED, + * 보정액(adj)=diff 를 로그로 남긴다. + * - absd > TOL → 허용오차 초과 → 불일치('U'), ACTION=MANUAL(수기검토). + * + * [응답] + * KEY / DIFF / TOL / ACTION / STATUS + * + * 커서를 사용하지 않는 단건 처리 서비스이다. */ #include #include @@ -89,60 +106,97 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0022.pgc" +#line 32 "app/online/rc_ol_0022.pgc" TX_SERVICE(RC_OL_0022, ctx) { rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + long appr_amt = 0; + long tol = 0; + long diff, absd, adj = 0; int rc; + const char *action; + const char *new_st; + int do_update = 0; - tx_log(TX_LOG_INFO, "[RC_OL_0022] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0022] 대사 자동보정 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0022] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0022] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0022] 금액 범위 오류 amount=%ld", amount); + if (tx_buf_getlong(ctx->in, "APPRAMT", &appr_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0022] APPRAMT(승인금액) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0022] 영업일 오류 date=%s", bizdate); + if (tx_buf_getlong(ctx->in, "TOL", &tol) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0022] TOL(허용오차) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tol < 0) { + tx_log(TX_LOG_WARN, "[RC_OL_0022] 허용오차 음수 오류 tol=%ld", tol); tx_return(ctx, TX_EINVAL, ctx->out); return; } memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); + rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0022] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[RC_OL_0022] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + diff = rec.amount - appr_amt; + absd = (diff < 0) ? -diff : diff; + + if (diff == 0) { + /* 완전일치 → 무처리 */ + action = "NOOP"; + new_st = rec.status; + do_update = 0; + tx_log(TX_LOG_INFO, "[RC_OL_0022] 완전일치 key=%s 변경없음", key); + } else if (absd <= tol) { + /* 허용오차 이내 → 자동보정 후 매입확정 */ + action = "CORRECTED"; + new_st = RC_ST_DONE; + adj = diff; + do_update = 1; + tx_log(TX_LOG_INFO, + "[RC_OL_0022] 자동보정 key=%s 차이=%ld 허용오차=%ld 보정액=%ld", + key, diff, tol, adj); + } else { + /* 허용오차 초과 → 불일치, 수기검토 대상 */ + action = "MANUAL"; + new_st = RC_ST_FAIL; + do_update = 1; + tx_log(TX_LOG_WARN, + "[RC_OL_0022] 허용오차 초과(수기검토) key=%s 차이=%ld 허용오차=%ld", + key, diff, tol); + } + + if (do_update) { + rc = rc_rec_update_status(key, new_st); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0022] 상태전이 실패 key=%s rc=%d", key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0022] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_setlong(ctx->out, "TOL", tol); + tx_buf_sets(ctx->out, "ACTION", action); + tx_buf_sets(ctx->out, "STATUS", new_st); + + tx_log(TX_LOG_INFO, "[RC_OL_0022] 자동보정 완료 key=%s 조치=%s", key, action); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0022.pgc b/app/online/rc_ol_0022.pgc index 502d0b7..9c84f6c 100644 --- a/app/online/rc_ol_0022.pgc +++ b/app/online/rc_ol_0022.pgc @@ -1,9 +1,26 @@ /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0022.pgc - 대사 온라인 서비스 (RC_OL_0022) [tier=medium] + * rc_ol_0022.pgc - 대사 자동보정 온라인 서비스 (RC_OL_0022) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인금액(APPRAMT) 과 원장 거래금액의 차이를 허용오차(TOL) 와 비교하여 + * 자동보정 여부를 판정한다. + * + * [입력] + * KEY - 처리키(승인/거래 번호) + * APPRAMT - 승인금액(원) + * TOL - 허용오차(원, 0 이상) + * + * [판정] + * diff = amount - APPRAMT, absd = |diff| + * - diff == 0 → ACTION=NOOP, 상태 변경 없음. + * - absd <= TOL → 자동보정(허용오차 이내) → 매입확정('M'), ACTION=CORRECTED, + * 보정액(adj)=diff 를 로그로 남긴다. + * - absd > TOL → 허용오차 초과 → 불일치('U'), ACTION=MANUAL(수기검토). + * + * [응답] + * KEY / DIFF / TOL / ACTION / STATUS + * + * 커서를 사용하지 않는 단건 처리 서비스이다. */ #include #include @@ -17,54 +34,91 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0022, ctx) { rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + long appr_amt = 0; + long tol = 0; + long diff, absd, adj = 0; int rc; + const char *action; + const char *new_st; + int do_update = 0; - tx_log(TX_LOG_INFO, "[RC_OL_0022] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0022] 대사 자동보정 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0022] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0022] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0022] 금액 범위 오류 amount=%ld", amount); + if (tx_buf_getlong(ctx->in, "APPRAMT", &appr_amt) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0022] APPRAMT(승인금액) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0022] 영업일 오류 date=%s", bizdate); + if (tx_buf_getlong(ctx->in, "TOL", &tol) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0022] TOL(허용오차) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tol < 0) { + tx_log(TX_LOG_WARN, "[RC_OL_0022] 허용오차 음수 오류 tol=%ld", tol); tx_return(ctx, TX_EINVAL, ctx->out); return; } memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); + rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0022] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[RC_OL_0022] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + diff = rec.amount - appr_amt; + absd = (diff < 0) ? -diff : diff; + + if (diff == 0) { + /* 완전일치 → 무처리 */ + action = "NOOP"; + new_st = rec.status; + do_update = 0; + tx_log(TX_LOG_INFO, "[RC_OL_0022] 완전일치 key=%s 변경없음", key); + } else if (absd <= tol) { + /* 허용오차 이내 → 자동보정 후 매입확정 */ + action = "CORRECTED"; + new_st = RC_ST_DONE; + adj = diff; + do_update = 1; + tx_log(TX_LOG_INFO, + "[RC_OL_0022] 자동보정 key=%s 차이=%ld 허용오차=%ld 보정액=%ld", + key, diff, tol, adj); + } else { + /* 허용오차 초과 → 불일치, 수기검토 대상 */ + action = "MANUAL"; + new_st = RC_ST_FAIL; + do_update = 1; + tx_log(TX_LOG_WARN, + "[RC_OL_0022] 허용오차 초과(수기검토) key=%s 차이=%ld 허용오차=%ld", + key, diff, tol); + } + + if (do_update) { + rc = rc_rec_update_status(key, new_st); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0022] 상태전이 실패 key=%s rc=%d", key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0022] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_setlong(ctx->out, "TOL", tol); + tx_buf_sets(ctx->out, "ACTION", action); + tx_buf_sets(ctx->out, "STATUS", new_st); + + tx_log(TX_LOG_INFO, "[RC_OL_0022] 자동보정 완료 key=%s 조치=%s", key, action); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0023.c b/app/online/rc_ol_0023.c index 99a86c9..46bd8ed 100644 --- a/app/online/rc_ol_0023.c +++ b/app/online/rc_ol_0023.c @@ -8,15 +8,19 @@ #line 1 "app/online/rc_ol_0023.pgc" /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0023.pgc - 대사 온라인 서비스 (RC_OL_0023) [tier=easy] + * rc_ol_0023.pgc - 대사 예외 알림 조회 온라인 서비스 (RC_OL_0023) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일(BIZDATE) 의 불일치('U') 원장을 커서로 순회하며 예외 건을 + * 집계한다. 예외 건수(ALERTCNT) 와 금액합계(SUMAMT) 를 누적하고, 먼저 + * 조회된 최대 3건의 처리키(KEY0/KEY1/KEY2) 를 알림 표본으로 확보한다. + * 예외 규모에 따라 알림등급(ALERTLEVEL) 을 NONE/LOW/HIGH 로 판정한다. + * 원장을 변경하지 않는 읽기전용 서비스이다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -89,37 +93,128 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0023.pgc" +#line 19 "app/online/rc_ol_0023.pgc" TX_SERVICE(RC_OL_0023, ctx) { - rc_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 24 "app/online/rc_ol_0023.pgc" + char h_biz_date [ 9 ] ; + +#line 25 "app/online/rc_ol_0023.pgc" + char h_key [ 16 ] ; + +#line 26 "app/online/rc_ol_0023.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 27 "app/online/rc_ol_0023.pgc" - tx_log(TX_LOG_INFO, "[RC_OL_0023] 대사 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0023] KEY 누락"); + char bizdate[9]; + char sample[3][16]; + const char *alertlevel; + long alertcnt = 0; + long sumamt = 0; + int captured = 0; + int i; + + tx_log(TX_LOG_INFO, "[RC_OL_0023] 대사 예외 알림 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0023] BIZDATE(영업일) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = rc_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0023] 영업일 형식 오류 date=%s", bizdate); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + for (i = 0; i < 3; i++) + sample[i][0] = '\0'; + + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 불일치(U) 예외 원장을 처리키 순으로 순회 */ + ECPGset_var( 0, ( h_biz_date ), __LINE__);\ + /* declare cur_rc_ol_0023 cursor for select key , amount from rc_ledger where biz_date = $1 and status = 'U' order by key */ +#line 63 "app/online/rc_ol_0023.pgc" + +#line 63 "app/online/rc_ol_0023.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_ol_0023 cursor for select key , amount from rc_ledger where biz_date = $1 and status = 'U' order by key", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 65 "app/online/rc_ol_0023.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0023"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_ol_0023", ECPGt_EOIT, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 73 "app/online/rc_ol_0023.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0023"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_ol_0023", ECPGt_EOIT, ECPGt_EORT);} +#line 79 "app/online/rc_ol_0023.pgc" + + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + alertcnt++; + sumamt += h_amount; + + if (captured < 3) { + strncpy(sample[captured], h_key, sizeof(sample[captured]) - 1); + sample[captured][sizeof(sample[captured]) - 1] = '\0'; + captured++; + } + } + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_ol_0023", ECPGt_EOIT, ECPGt_EORT);} +#line 94 "app/online/rc_ol_0023.pgc" + + + /* 예외 규모에 따른 알림등급 판정 */ + if (alertcnt == 0) + alertlevel = "NONE"; + else if (alertcnt <= 5) + alertlevel = "LOW"; + else + alertlevel = "HIGH"; + + tx_log(TX_LOG_INFO, + "[RC_OL_0023] 예외집계 date=%s 건수=%ld 합계=%ld 등급=%s", + bizdate, alertcnt, sumamt, alertlevel); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "ALERTCNT", alertcnt); + tx_buf_sets(ctx->out, "ALERTLEVEL", alertlevel); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + tx_buf_sets(ctx->out, "KEY0", sample[0]); + tx_buf_sets(ctx->out, "KEY1", sample[1]); + tx_buf_sets(ctx->out, "KEY2", sample[2]); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0023.pgc b/app/online/rc_ol_0023.pgc index 6d00a12..5870424 100644 --- a/app/online/rc_ol_0023.pgc +++ b/app/online/rc_ol_0023.pgc @@ -1,14 +1,18 @@ /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0023.pgc - 대사 온라인 서비스 (RC_OL_0023) [tier=easy] + * rc_ol_0023.pgc - 대사 예외 알림 조회 온라인 서비스 (RC_OL_0023) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일(BIZDATE) 의 불일치('U') 원장을 커서로 순회하며 예외 건을 + * 집계한다. 예외 건수(ALERTCNT) 와 금액합계(SUMAMT) 를 누적하고, 먼저 + * 조회된 최대 3건의 처리키(KEY0/KEY1/KEY2) 를 알림 표본으로 확보한다. + * 예외 규모에 따라 알림등급(ALERTLEVEL) 을 NONE/LOW/HIGH 로 판정한다. + * 원장을 변경하지 않는 읽기전용 서비스이다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -16,32 +20,98 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0023, ctx) { - rc_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_key[16]; + long h_amount; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[RC_OL_0023] 대사 단건조회 서비스 진입"); + char bizdate[9]; + char sample[3][16]; + const char *alertlevel; + long alertcnt = 0; + long sumamt = 0; + int captured = 0; + int i; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0023] KEY 누락"); + tx_log(TX_LOG_INFO, "[RC_OL_0023] 대사 예외 알림 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0023] BIZDATE(영업일) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = rc_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0023] 영업일 형식 오류 date=%s", bizdate); + tx_return(ctx, TX_EINVAL, ctx->out); return; } + for (i = 0; i < 3; i++) + sample[i][0] = '\0'; + + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + + /* 불일치(U) 예외 원장을 처리키 순으로 순회 */ + EXEC SQL DECLARE cur_rc_ol_0023 CURSOR FOR + SELECT key, amount + FROM rc_ledger + WHERE biz_date = :h_biz_date + AND status = 'U' + ORDER BY key; + + EXEC SQL OPEN cur_rc_ol_0023; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0023"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + for (;;) { + EXEC SQL FETCH cur_rc_ol_0023 INTO :h_key, :h_amount; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0023"); + EXEC SQL CLOSE cur_rc_ol_0023; + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + alertcnt++; + sumamt += h_amount; + + if (captured < 3) { + strncpy(sample[captured], h_key, sizeof(sample[captured]) - 1); + sample[captured][sizeof(sample[captured]) - 1] = '\0'; + captured++; + } + } + + EXEC SQL CLOSE cur_rc_ol_0023; + + /* 예외 규모에 따른 알림등급 판정 */ + if (alertcnt == 0) + alertlevel = "NONE"; + else if (alertcnt <= 5) + alertlevel = "LOW"; + else + alertlevel = "HIGH"; + + tx_log(TX_LOG_INFO, + "[RC_OL_0023] 예외집계 date=%s 건수=%ld 합계=%ld 등급=%s", + bizdate, alertcnt, sumamt, alertlevel); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "ALERTCNT", alertcnt); + tx_buf_sets(ctx->out, "ALERTLEVEL", alertlevel); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + tx_buf_sets(ctx->out, "KEY0", sample[0]); + tx_buf_sets(ctx->out, "KEY1", sample[1]); + tx_buf_sets(ctx->out, "KEY2", sample[2]); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0024.c b/app/online/rc_ol_0024.c index 0dfaf5c..4287475 100644 --- a/app/online/rc_ol_0024.c +++ b/app/online/rc_ol_0024.c @@ -8,10 +8,15 @@ #line 1 "app/online/rc_ol_0024.pgc" /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0024.pgc - 대사 온라인 서비스 (RC_OL_0024) [tier=medium] + * rc_ol_0024.pgc - 대사 상태조회 (상세) 온라인 서비스 (RC_OL_0024) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 표준 DBIO 로 조회하여 대사 진행 상세를 응답한다. + * 상태코드를 업무단계(STAGE) 와 진행률(PROGRESS,%) 로 사상한다. + * R(접수) → "접수" 25% + * M(매입확정) → "매입확정" 50% + * S(정산완료) → "정산완료" 100% + * U(불일치) → "불일치" 0% + * 원장을 변경하지 않는 읽기전용 조회 서비스이다. (커서 미사용) */ #include #include @@ -89,60 +94,65 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0024.pgc" +#line 20 "app/online/rc_ol_0024.pgc" TX_SERVICE(RC_OL_0024, ctx) { rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + const char *stage; + long progress; int rc; - tx_log(TX_LOG_INFO, "[RC_OL_0024] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0024] 대사 상태조회(상세) 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0024] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0024] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0024] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0024] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); + rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0024] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[RC_OL_0024] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + /* 상태코드 → 업무단계/진행률 사상 */ + if (strcmp(rec.status, RC_ST_RECV) == 0) { + stage = "접수"; + progress = 25; + } else if (strcmp(rec.status, RC_ST_DONE) == 0) { + stage = "매입확정"; + progress = 50; + } else if (strcmp(rec.status, RC_ST_SETTLED) == 0) { + stage = "정산완료"; + progress = 100; + } else if (strcmp(rec.status, RC_ST_FAIL) == 0) { + stage = "불일치"; + progress = 0; + } else { + tx_log(TX_LOG_WARN, "[RC_OL_0024] 알수없는 상태 key=%s status=%s", + key, rec.status); + stage = "미확인"; + progress = 0; + } + + tx_log(TX_LOG_INFO, + "[RC_OL_0024] 상세 key=%s status=%s stage=%s progress=%ld%%", + key, rec.status, stage, progress); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0024] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_setlong(ctx->out, "FEE", rec.fee); + tx_buf_sets(ctx->out, "STATUS", rec.status); + tx_buf_sets(ctx->out, "STAGE", stage); + tx_buf_setlong(ctx->out, "PROGRESS", progress); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0024.pgc b/app/online/rc_ol_0024.pgc index b7a31b6..bfd7a5e 100644 --- a/app/online/rc_ol_0024.pgc +++ b/app/online/rc_ol_0024.pgc @@ -1,9 +1,14 @@ /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0024.pgc - 대사 온라인 서비스 (RC_OL_0024) [tier=medium] + * rc_ol_0024.pgc - 대사 상태조회 (상세) 온라인 서비스 (RC_OL_0024) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 표준 DBIO 로 조회하여 대사 진행 상세를 응답한다. + * 상태코드를 업무단계(STAGE) 와 진행률(PROGRESS,%) 로 사상한다. + * R(접수) → "접수" 25% + * M(매입확정) → "매입확정" 50% + * S(정산완료) → "정산완료" 100% + * U(불일치) → "불일치" 0% + * 원장을 변경하지 않는 읽기전용 조회 서비스이다. (커서 미사용) */ #include #include @@ -17,54 +22,59 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0024, ctx) { rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + const char *stage; + long progress; int rc; - tx_log(TX_LOG_INFO, "[RC_OL_0024] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0024] 대사 상태조회(상세) 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0024] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0024] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0024] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0024] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = rc_rec_insert(&rec); + rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0024] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[RC_OL_0024] 원장 조회 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + /* 상태코드 → 업무단계/진행률 사상 */ + if (strcmp(rec.status, RC_ST_RECV) == 0) { + stage = "접수"; + progress = 25; + } else if (strcmp(rec.status, RC_ST_DONE) == 0) { + stage = "매입확정"; + progress = 50; + } else if (strcmp(rec.status, RC_ST_SETTLED) == 0) { + stage = "정산완료"; + progress = 100; + } else if (strcmp(rec.status, RC_ST_FAIL) == 0) { + stage = "불일치"; + progress = 0; + } else { + tx_log(TX_LOG_WARN, "[RC_OL_0024] 알수없는 상태 key=%s status=%s", + key, rec.status); + stage = "미확인"; + progress = 0; + } + + tx_log(TX_LOG_INFO, + "[RC_OL_0024] 상세 key=%s status=%s stage=%s progress=%ld%%", + key, rec.status, stage, progress); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0024] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_setlong(ctx->out, "FEE", rec.fee); + tx_buf_sets(ctx->out, "STATUS", rec.status); + tx_buf_sets(ctx->out, "STAGE", stage); + tx_buf_setlong(ctx->out, "PROGRESS", progress); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0025.c b/app/online/rc_ol_0025.c index e6df54c..bc3520b 100644 --- a/app/online/rc_ol_0025.c +++ b/app/online/rc_ol_0025.c @@ -8,12 +8,17 @@ #line 1 "app/online/rc_ol_0025.pgc" /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0025.pgc - 대사 온라인 서비스 (RC_OL_0025) [tier=easy] + * rc_ol_0025.pgc - 수수료 대사 온라인 서비스 (RC_OL_0025) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 수수료를 대사한다. 입력 수수료율(RATEBP) + * 은 basis point 단위(250 = 2.5%)로, 기대수수료는 + * EXPFEE = amount * RATEBP / 10000 + * 로 계산한다. 실제수수료(fee) 와의 차이(FEEDIFF = fee - EXPFEE) 절대값이 + * 1원 이하이면 일치로 보아 매입확정('M')/MATCH 로, 아니면 불일치('U')/ + * FEEMISMATCH 로 상태를 전이한다. (커서 미사용) */ #include +#include #include #include "txcore.h" @@ -89,37 +94,81 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0025.pgc" +#line 20 "app/online/rc_ol_0025.pgc" TX_SERVICE(RC_OL_0025, ctx) { rc_rec_t rec; char key[16]; + long ratebp = 0; + long expfee; + long feediff; + int matched; int rc; + const char *new_st; + const char *result; - tx_log(TX_LOG_INFO, "[RC_OL_0025] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0025] 수수료 대사 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0025] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_getlong(ctx->in, "RATEBP", &ratebp) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0025] RATEBP(수수료율) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (ratebp < 0) { + tx_log(TX_LOG_WARN, "[RC_OL_0025] 수수료율 음수 ratebp=%ld", ratebp); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0025] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0025] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + /* 기대수수료 및 차액 산출 */ + expfee = rec.amount * ratebp / 10000; + feediff = rec.fee - expfee; + matched = (labs(feediff) <= 1); + + if (matched) { + new_st = RC_ST_DONE; + result = "MATCH"; + tx_log(TX_LOG_INFO, + "[RC_OL_0025] 수수료 일치 key=%s expfee=%ld fee=%ld", + key, expfee, rec.fee); + } else { + new_st = RC_ST_FAIL; + result = "FEEMISMATCH"; + tx_log(TX_LOG_WARN, + "[RC_OL_0025] 수수료 불일치 key=%s 차액=%ld", key, feediff); + } + + rc = rc_rec_update_status(key, new_st); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0025] 상태전이 실패 key=%s rc=%d", key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0025] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "FEE", rec.fee); + tx_buf_setlong(ctx->out, "RATEBP", ratebp); + tx_buf_setlong(ctx->out, "EXPFEE", expfee); + tx_buf_setlong(ctx->out, "FEEDIFF", feediff); + tx_buf_sets(ctx->out, "RESULT", result); + tx_buf_sets(ctx->out, "STATUS", new_st); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0025.pgc b/app/online/rc_ol_0025.pgc index e579684..a168ac5 100644 --- a/app/online/rc_ol_0025.pgc +++ b/app/online/rc_ol_0025.pgc @@ -1,11 +1,16 @@ /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0025.pgc - 대사 온라인 서비스 (RC_OL_0025) [tier=easy] + * rc_ol_0025.pgc - 수수료 대사 온라인 서비스 (RC_OL_0025) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건을 읽어 수수료를 대사한다. 입력 수수료율(RATEBP) + * 은 basis point 단위(250 = 2.5%)로, 기대수수료는 + * EXPFEE = amount * RATEBP / 10000 + * 로 계산한다. 실제수수료(fee) 와의 차이(FEEDIFF = fee - EXPFEE) 절대값이 + * 1원 이하이면 일치로 보아 매입확정('M')/MATCH 로, 아니면 불일치('U')/ + * FEEMISMATCH 로 상태를 전이한다. (커서 미사용) */ #include +#include #include #include "txcore.h" @@ -18,30 +23,74 @@ TX_SERVICE(RC_OL_0025, ctx) { rc_rec_t rec; char key[16]; + long ratebp = 0; + long expfee; + long feediff; + int matched; int rc; + const char *new_st; + const char *result; - tx_log(TX_LOG_INFO, "[RC_OL_0025] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0025] 수수료 대사 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0025] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_getlong(ctx->in, "RATEBP", &ratebp) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0025] RATEBP(수수료율) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (ratebp < 0) { + tx_log(TX_LOG_WARN, "[RC_OL_0025] 수수료율 음수 ratebp=%ld", ratebp); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0025] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0025] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + /* 기대수수료 및 차액 산출 */ + expfee = rec.amount * ratebp / 10000; + feediff = rec.fee - expfee; + matched = (labs(feediff) <= 1); + + if (matched) { + new_st = RC_ST_DONE; + result = "MATCH"; + tx_log(TX_LOG_INFO, + "[RC_OL_0025] 수수료 일치 key=%s expfee=%ld fee=%ld", + key, expfee, rec.fee); + } else { + new_st = RC_ST_FAIL; + result = "FEEMISMATCH"; + tx_log(TX_LOG_WARN, + "[RC_OL_0025] 수수료 불일치 key=%s 차액=%ld", key, feediff); + } + + rc = rc_rec_update_status(key, new_st); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0025] 상태전이 실패 key=%s rc=%d", key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0025] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_setlong(ctx->out, "FEE", rec.fee); + tx_buf_setlong(ctx->out, "RATEBP", ratebp); + tx_buf_setlong(ctx->out, "EXPFEE", expfee); + tx_buf_setlong(ctx->out, "FEEDIFF", feediff); + tx_buf_sets(ctx->out, "RESULT", result); + tx_buf_sets(ctx->out, "STATUS", new_st); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0026.c b/app/online/rc_ol_0026.c index 4fef5ea..7c7a93c 100644 --- a/app/online/rc_ol_0026.c +++ b/app/online/rc_ol_0026.c @@ -8,15 +8,21 @@ #line 1 "app/online/rc_ol_0026.pgc" /* @tier hard @module rc @transform reconciliation-online */ /* - * rc_ol_0026.pgc - 대사 온라인 서비스 (RC_OL_0026) [tier=hard] + * rc_ol_0026.pgc - 중복거래 대사 온라인 서비스 (RC_OL_0026) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 동일 가맹점(MERCHID)/동일 금액(AMOUNT)/동일 영업일(BIZDATE) 조합의 + * 원장을 커서로 순회하여 중복 여부를 판정한다. 일치 건수(DUPCNT) 를 + * 세고 먼저 조회된 최대 2건의 처리키(KEY0/KEY1) 를 확보한다. + * DUPCNT > 1 → "DUPLICATE" + * DUPCNT == 1 → "UNIQUE" + * DUPCNT == 0 → "NOTFOUND" + * 원장을 변경하지 않는 읽기전용 서비스이다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -89,88 +95,154 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0026.pgc" +#line 21 "app/online/rc_ol_0026.pgc" TX_SERVICE(RC_OL_0026, ctx) { - rc_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + +#line 26 "app/online/rc_ol_0026.pgc" + char h_merch_id [ 16 ] ; + +#line 27 "app/online/rc_ol_0026.pgc" + char h_biz_date [ 9 ] ; + +#line 28 "app/online/rc_ol_0026.pgc" + long h_amount ; + +#line 29 "app/online/rc_ol_0026.pgc" + char h_key [ 16 ] ; +/* exec sql end declare section */ +#line 30 "app/online/rc_ol_0026.pgc" - tx_log(TX_LOG_INFO, "[RC_OL_0026] 대사 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0026] 필수 필드 누락"); + char merch[16]; + char bizdate[9]; + char sample[2][16]; + long amount = 0; + long dupcnt = 0; + int captured = 0; + int i; + const char *result; + + tx_log(TX_LOG_INFO, "[RC_OL_0026] 중복거래 대사 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0026] MERCHID(가맹점번호) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0026] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (tx_buf_getlong(ctx->in, "AMOUNT", &amount) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0026] AMOUNT(거래금액) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0026] BIZDATE(영업일) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + if (!amount_is_valid(amount)) { + tx_log(TX_LOG_WARN, "[RC_OL_0026] 금액 유효성 오류 amount=%ld", amount); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0026] 영업일 형식 오류 date=%s", bizdate); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + for (i = 0; i < 2; i++) + sample[i][0] = '\0'; - rc = rc_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0026] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + h_amount = amount; + + /* 가맹점/금액/영업일이 동일한 원장을 처리키 순으로 순회 */ + ECPGset_var( 0, ( h_biz_date ), __LINE__);\ + ECPGset_var( 1, &( h_amount ), __LINE__);\ + ECPGset_var( 2, ( h_merch_id ), __LINE__);\ + /* declare cur_rc_ol_0026 cursor for select key from rc_ledger where merch_id = $1 and amount = $2 and biz_date = $3 order by key */ +#line 86 "app/online/rc_ol_0026.pgc" + +#line 86 "app/online/rc_ol_0026.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_ol_0026 cursor for select key from rc_ledger where merch_id = $1 and amount = $2 and biz_date = $3 order by key", + ECPGt_char,(h_merch_id),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 88 "app/online/rc_ol_0026.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0026"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_ol_0026", ECPGt_EOIT, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 96 "app/online/rc_ol_0026.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0026"); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_ol_0026", ECPGt_EOIT, ECPGt_EORT);} +#line 102 "app/online/rc_ol_0026.pgc" + + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + dupcnt++; + + if (captured < 2) { + strncpy(sample[captured], h_key, sizeof(sample[captured]) - 1); + sample[captured][sizeof(sample[captured]) - 1] = '\0'; + captured++; + } } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("RC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[RC_OL_0026] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_ol_0026", ECPGt_EOIT, ECPGt_EORT);} +#line 116 "app/online/rc_ol_0026.pgc" - /* 교차 검증: 당일 합계 재계산 */ - if (rc_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - tx_commit(); + /* 중복 판정 */ + if (dupcnt > 1) + result = "DUPLICATE"; + else if (dupcnt == 1) + result = "UNIQUE"; + else + result = "NOTFOUND"; + + tx_log(TX_LOG_INFO, + "[RC_OL_0026] 중복대사 merch=%s amount=%ld date=%s 건수=%ld 결과=%s", + merch, amount, bizdate, dupcnt, result); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[RC_OL_0026] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "DUPCNT", dupcnt); + tx_buf_sets(ctx->out, "RESULT", result); + tx_buf_sets(ctx->out, "KEY0", sample[0]); + tx_buf_sets(ctx->out, "KEY1", sample[1]); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0026.pgc b/app/online/rc_ol_0026.pgc index 384998b..35f8722 100644 --- a/app/online/rc_ol_0026.pgc +++ b/app/online/rc_ol_0026.pgc @@ -1,14 +1,20 @@ /* @tier hard @module rc @transform reconciliation-online */ /* - * rc_ol_0026.pgc - 대사 온라인 서비스 (RC_OL_0026) [tier=hard] + * rc_ol_0026.pgc - 중복거래 대사 온라인 서비스 (RC_OL_0026) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 동일 가맹점(MERCHID)/동일 금액(AMOUNT)/동일 영업일(BIZDATE) 조합의 + * 원장을 커서로 순회하여 중복 여부를 판정한다. 일치 건수(DUPCNT) 를 + * 세고 먼저 조회된 최대 2건의 처리키(KEY0/KEY1) 를 확보한다. + * DUPCNT > 1 → "DUPLICATE" + * DUPCNT == 1 → "UNIQUE" + * DUPCNT == 0 → "NOTFOUND" + * 원장을 변경하지 않는 읽기전용 서비스이다. */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -16,83 +22,118 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0026, ctx) { - rc_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch_id[16]; + char h_biz_date[9]; + long h_amount; + char h_key[16]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[RC_OL_0026] 대사 트랜잭션 오케스트레이션 진입"); + char merch[16]; + char bizdate[9]; + char sample[2][16]; + long amount = 0; + long dupcnt = 0; + int captured = 0; + int i; + const char *result; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0026] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[RC_OL_0026] 중복거래 대사 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0026] MERCHID(가맹점번호) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0026] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (tx_buf_getlong(ctx->in, "AMOUNT", &amount) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0026] AMOUNT(거래금액) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0026] BIZDATE(영업일) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + if (!amount_is_valid(amount)) { + tx_log(TX_LOG_WARN, "[RC_OL_0026] 금액 유효성 오류 amount=%ld", amount); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0026] 영업일 형식 오류 date=%s", bizdate); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + for (i = 0; i < 2; i++) + sample[i][0] = '\0'; - rc = rc_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0026] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch_id, merch, sizeof(h_merch_id) - 1); + h_merch_id[sizeof(h_merch_id) - 1] = '\0'; + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + h_biz_date[sizeof(h_biz_date) - 1] = '\0'; + h_amount = amount; + + /* 가맹점/금액/영업일이 동일한 원장을 처리키 순으로 순회 */ + EXEC SQL DECLARE cur_rc_ol_0026 CURSOR FOR + SELECT key + FROM rc_ledger + WHERE merch_id = :h_merch_id + AND amount = :h_amount + AND biz_date = :h_biz_date + ORDER BY key; + + EXEC SQL OPEN cur_rc_ol_0026; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0026"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + for (;;) { + EXEC SQL FETCH cur_rc_ol_0026 INTO :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0026"); + EXEC SQL CLOSE cur_rc_ol_0026; + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + dupcnt++; + + if (captured < 2) { + strncpy(sample[captured], h_key, sizeof(sample[captured]) - 1); + sample[captured][sizeof(sample[captured]) - 1] = '\0'; + captured++; + } } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("RC_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[RC_OL_0026] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); + EXEC SQL CLOSE cur_rc_ol_0026; - /* 교차 검증: 당일 합계 재계산 */ - if (rc_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; + /* 중복 판정 */ + if (dupcnt > 1) + result = "DUPLICATE"; + else if (dupcnt == 1) + result = "UNIQUE"; + else + result = "NOTFOUND"; - tx_commit(); + tx_log(TX_LOG_INFO, + "[RC_OL_0026] 중복대사 merch=%s amount=%ld date=%s 건수=%ld 결과=%s", + merch, amount, bizdate, dupcnt, result); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[RC_OL_0026] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "DUPCNT", dupcnt); + tx_buf_sets(ctx->out, "RESULT", result); + tx_buf_sets(ctx->out, "KEY0", sample[0]); + tx_buf_sets(ctx->out, "KEY1", sample[1]); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0027.c b/app/online/rc_ol_0027.c index 555c6fa..cf47fb6 100644 --- a/app/online/rc_ol_0027.c +++ b/app/online/rc_ol_0027.c @@ -8,10 +8,25 @@ #line 1 "app/online/rc_ol_0027.pgc" /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0027.pgc - 대사 온라인 서비스 (RC_OL_0027) [tier=easy] + * rc_ol_0027.pgc - 대사율 조회 온라인 서비스 (RC_OL_0027) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 영업일(BIZDATE)에 대해 상태별 원장 건수를 집계하여 그날의 + * 대사율(완료 비율)과 미대사율(잔여 비율)을 산출한다. + * 상태별로 rc_rec_count 를 호출한다: + * 접수('R'), 매입확정('M'), 불일치('U'), 정산완료('S'). + * + * 업무규칙: + * - total = 접수 + 매입확정 + 불일치 + 정산완료 (0 방어) + * - 대사율 RECRATE = (매입확정 + 정산완료) * 100 / total + * - 미대사율 UNRECRATE = (접수 + 불일치) * 100 / total + * + * 응답: BIZDATE, TOTAL, CNTR, CNTM, CNTU, CNTS, RECRATE, UNRECRATE + * + * 비고: + * - 원장(rc_ledger) 을 직접 순회하지 않고 상태별 집계 API 만 사용하므로 + * 커서 없이 O(상태수) 회의 count 호출로 대사율을 계산한다 (읽기 전용). + * - 백분율은 정수 나눗셈이라 절삭되며, RECRATE + UNRECRATE 가 100 미만이 + * 될 수 있다. 이는 표시 목적의 근사치이며 감사에는 원건수를 사용한다. */ #include #include @@ -89,37 +104,95 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0027.pgc" +#line 30 "app/online/rc_ol_0027.pgc" TX_SERVICE(RC_OL_0027, ctx) { - rc_rec_t rec; - char key[16]; - int rc; + char bizdate[16]; + long cnt_r, cnt_m, cnt_u, cnt_s; + long total; + long recrate, unrecrate; - tx_log(TX_LOG_INFO, "[RC_OL_0027] 대사 단건조회 서비스 진입"); + long recdone; /* 대사완료 건수 (M + S) */ + long recopen; /* 미대사 건수 (R + U) */ - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0027] KEY 누락"); + tx_log(TX_LOG_INFO, "[RC_OL_0027] 대사율 조회 서비스 진입"); + + /* --- 요청 필드 추출 및 검증 --- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0027] BIZDATE 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0027] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = rc_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0027] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* --- 상태별 건수 집계 (음수 리턴은 조회오류) --- */ + cnt_r = rc_rec_count(bizdate, RC_ST_RECV); + cnt_m = rc_rec_count(bizdate, RC_ST_DONE); + cnt_u = rc_rec_count(bizdate, RC_ST_FAIL); + cnt_s = rc_rec_count(bizdate, RC_ST_SETTLED); + + if (cnt_r < 0 || cnt_m < 0 || cnt_u < 0 || cnt_s < 0) { + tx_log(TX_LOG_ERROR, + "[RC_OL_0027] 건수 집계 실패 date=%s R=%ld M=%ld U=%ld S=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s); + tx_return(ctx, TX_EDB, ctx->out); return; } + total = cnt_r + cnt_m + cnt_u + cnt_s; + recdone = cnt_m + cnt_s; /* 매입확정 + 정산완료 = 대사완료 */ + recopen = cnt_r + cnt_u; /* 접수 + 불일치 = 미대사 */ + + /* 상태별 내역을 감사 로그로 남겨 이후 대사 추적을 돕는다. */ + tx_log(TX_LOG_INFO, + "[RC_OL_0027] 상태내역 date=%s 접수=%ld 매입=%ld 불일치=%ld 정산=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s); + + /* --- 대사율/미대사율 산출 (0 나눗셈 방어) --- */ + if (total > 0) { + recrate = recdone * 100 / total; + unrecrate = recopen * 100 / total; + } else { + recrate = 0; + unrecrate = 0; + tx_log(TX_LOG_WARN, + "[RC_OL_0027] 대상 원장 없음 date=%s → 대사율 0%%", bizdate); + } + + /* 대사율과 미대사율의 합은 정상적으로 100(잔차 제외) 이어야 한다. + * 정수 나눗셈 절삭으로 100 미만이 될 수 있어 참고용으로만 기록한다. */ + tx_log(TX_LOG_INFO, + "[RC_OL_0027] date=%s 총=%ld 완료=%ld 잔여=%ld 대사율=%ld%% 미대사율=%ld%%", + bizdate, total, recdone, recopen, recrate, unrecrate); + + /* --- 대사율 수준별 정성 등급을 로그로 남긴다 (응답에는 미포함) --- */ + if (total == 0) + tx_log(TX_LOG_INFO, "[RC_OL_0027] 등급=N/A (대상 없음)"); + else if (recrate >= 100) + tx_log(TX_LOG_INFO, "[RC_OL_0027] 등급=완전대사 (100%%)"); + else if (recrate >= 90) + tx_log(TX_LOG_INFO, "[RC_OL_0027] 등급=양호 (대사율 %ld%%)", recrate); + else if (recrate >= 70) + tx_log(TX_LOG_WARN, "[RC_OL_0027] 등급=주의 (대사율 %ld%%)", recrate); + else + tx_log(TX_LOG_WARN, "[RC_OL_0027] 등급=미흡 (대사율 %ld%%)", recrate); + + /* --- 응답 구성 --- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0027] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_buf_setlong(ctx->out, "CNTR", cnt_r); + tx_buf_setlong(ctx->out, "CNTM", cnt_m); + tx_buf_setlong(ctx->out, "CNTU", cnt_u); + tx_buf_setlong(ctx->out, "CNTS", cnt_s); + tx_buf_setlong(ctx->out, "RECRATE", recrate); + tx_buf_setlong(ctx->out, "UNRECRATE", unrecrate); + tx_log(TX_LOG_INFO, "[RC_OL_0027] 대사율 조회 서비스 정상 종료 date=%s", bizdate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0027.pgc b/app/online/rc_ol_0027.pgc index b48f969..d69554a 100644 --- a/app/online/rc_ol_0027.pgc +++ b/app/online/rc_ol_0027.pgc @@ -1,9 +1,24 @@ /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0027.pgc - 대사 온라인 서비스 (RC_OL_0027) [tier=easy] + * rc_ol_0027.pgc - 대사율 조회 온라인 서비스 (RC_OL_0027) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 영업일(BIZDATE)에 대해 상태별 원장 건수를 집계하여 그날의 + * 대사율(완료 비율)과 미대사율(잔여 비율)을 산출한다. + * 상태별로 rc_rec_count 를 호출한다: + * 접수('R'), 매입확정('M'), 불일치('U'), 정산완료('S'). + * + * 업무규칙: + * - total = 접수 + 매입확정 + 불일치 + 정산완료 (0 방어) + * - 대사율 RECRATE = (매입확정 + 정산완료) * 100 / total + * - 미대사율 UNRECRATE = (접수 + 불일치) * 100 / total + * + * 응답: BIZDATE, TOTAL, CNTR, CNTM, CNTU, CNTS, RECRATE, UNRECRATE + * + * 비고: + * - 원장(rc_ledger) 을 직접 순회하지 않고 상태별 집계 API 만 사용하므로 + * 커서 없이 O(상태수) 회의 count 호출로 대사율을 계산한다 (읽기 전용). + * - 백분율은 정수 나눗셈이라 절삭되며, RECRATE + UNRECRATE 가 100 미만이 + * 될 수 있다. 이는 표시 목적의 근사치이며 감사에는 원건수를 사용한다. */ #include #include @@ -16,32 +31,90 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0027, ctx) { - rc_rec_t rec; - char key[16]; - int rc; + char bizdate[16]; + long cnt_r, cnt_m, cnt_u, cnt_s; + long total; + long recrate, unrecrate; - tx_log(TX_LOG_INFO, "[RC_OL_0027] 대사 단건조회 서비스 진입"); + long recdone; /* 대사완료 건수 (M + S) */ + long recopen; /* 미대사 건수 (R + U) */ - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0027] KEY 누락"); + tx_log(TX_LOG_INFO, "[RC_OL_0027] 대사율 조회 서비스 진입"); + + /* --- 요청 필드 추출 및 검증 --- */ + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0027] BIZDATE 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0027] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = rc_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0027] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* --- 상태별 건수 집계 (음수 리턴은 조회오류) --- */ + cnt_r = rc_rec_count(bizdate, RC_ST_RECV); + cnt_m = rc_rec_count(bizdate, RC_ST_DONE); + cnt_u = rc_rec_count(bizdate, RC_ST_FAIL); + cnt_s = rc_rec_count(bizdate, RC_ST_SETTLED); + + if (cnt_r < 0 || cnt_m < 0 || cnt_u < 0 || cnt_s < 0) { + tx_log(TX_LOG_ERROR, + "[RC_OL_0027] 건수 집계 실패 date=%s R=%ld M=%ld U=%ld S=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s); + tx_return(ctx, TX_EDB, ctx->out); return; } + total = cnt_r + cnt_m + cnt_u + cnt_s; + recdone = cnt_m + cnt_s; /* 매입확정 + 정산완료 = 대사완료 */ + recopen = cnt_r + cnt_u; /* 접수 + 불일치 = 미대사 */ + + /* 상태별 내역을 감사 로그로 남겨 이후 대사 추적을 돕는다. */ + tx_log(TX_LOG_INFO, + "[RC_OL_0027] 상태내역 date=%s 접수=%ld 매입=%ld 불일치=%ld 정산=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s); + + /* --- 대사율/미대사율 산출 (0 나눗셈 방어) --- */ + if (total > 0) { + recrate = recdone * 100 / total; + unrecrate = recopen * 100 / total; + } else { + recrate = 0; + unrecrate = 0; + tx_log(TX_LOG_WARN, + "[RC_OL_0027] 대상 원장 없음 date=%s → 대사율 0%%", bizdate); + } + + /* 대사율과 미대사율의 합은 정상적으로 100(잔차 제외) 이어야 한다. + * 정수 나눗셈 절삭으로 100 미만이 될 수 있어 참고용으로만 기록한다. */ + tx_log(TX_LOG_INFO, + "[RC_OL_0027] date=%s 총=%ld 완료=%ld 잔여=%ld 대사율=%ld%% 미대사율=%ld%%", + bizdate, total, recdone, recopen, recrate, unrecrate); + + /* --- 대사율 수준별 정성 등급을 로그로 남긴다 (응답에는 미포함) --- */ + if (total == 0) + tx_log(TX_LOG_INFO, "[RC_OL_0027] 등급=N/A (대상 없음)"); + else if (recrate >= 100) + tx_log(TX_LOG_INFO, "[RC_OL_0027] 등급=완전대사 (100%%)"); + else if (recrate >= 90) + tx_log(TX_LOG_INFO, "[RC_OL_0027] 등급=양호 (대사율 %ld%%)", recrate); + else if (recrate >= 70) + tx_log(TX_LOG_WARN, "[RC_OL_0027] 등급=주의 (대사율 %ld%%)", recrate); + else + tx_log(TX_LOG_WARN, "[RC_OL_0027] 등급=미흡 (대사율 %ld%%)", recrate); + + /* --- 응답 구성 --- */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0027] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_buf_setlong(ctx->out, "CNTR", cnt_r); + tx_buf_setlong(ctx->out, "CNTM", cnt_m); + tx_buf_setlong(ctx->out, "CNTU", cnt_u); + tx_buf_setlong(ctx->out, "CNTS", cnt_s); + tx_buf_setlong(ctx->out, "RECRATE", recrate); + tx_buf_setlong(ctx->out, "UNRECRATE", unrecrate); + tx_log(TX_LOG_INFO, "[RC_OL_0027] 대사율 조회 서비스 정상 종료 date=%s", bizdate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0028.c b/app/online/rc_ol_0028.c index 4782c1f..3504f65 100644 --- a/app/online/rc_ol_0028.c +++ b/app/online/rc_ol_0028.c @@ -8,15 +8,24 @@ #line 1 "app/online/rc_ol_0028.pgc" /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0028.pgc - 대사 온라인 서비스 (RC_OL_0028) [tier=easy] + * rc_ol_0028.pgc - 대사 균형검증 (CURSOR) 온라인 서비스 (RC_OL_0028) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 영업일(BIZDATE)의 원장을 커서로 순회하며 승인 잔여금액과 + * 매입/정산금액을 각각 누적하여 두 축의 균형(밸런스) 여부를 검증한다. + * + * 업무규칙: + * - 접수('R') → 승인 잔여 SUMAPPR 에 amount 누적 + * - 매입확정('M')/정산('S') → 매입/정산 SUMCAP 에 amount 누적 + * - 차이 GAP = SUMAPPR - SUMCAP + * - GAP 이 0 이면 BALANCED=Y, 아니면 BALANCED=N + * + * 응답: BIZDATE, SUMAPPR, SUMCAP, GAP, BALANCED (읽기 전용) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -89,37 +98,132 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0028.pgc" +#line 24 "app/online/rc_ol_0028.pgc" TX_SERVICE(RC_OL_0028, ctx) { - rc_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 29 "app/online/rc_ol_0028.pgc" + char h_biz_date [ 9 ] ; + +#line 30 "app/online/rc_ol_0028.pgc" + char h_status [ 2 ] ; + +#line 31 "app/online/rc_ol_0028.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 32 "app/online/rc_ol_0028.pgc" - tx_log(TX_LOG_INFO, "[RC_OL_0028] 대사 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0028] KEY 누락"); + char bizdate[16]; + long sumappr = 0, sumcap = 0, gap = 0; + long scanned = 0; + const char *balanced; + int opened = 0; + + tx_log(TX_LOG_INFO, "[RC_OL_0028] 대사 균형검증 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0028] BIZDATE 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0028] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = rc_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + ECPGset_var( 0, ( h_biz_date ), __LINE__);\ + /* declare cur_rc_ol_0028 cursor for select amount , status from rc_ledger where biz_date = $1 order by key */ +#line 60 "app/online/rc_ol_0028.pgc" + +#line 60 "app/online/rc_ol_0028.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_ol_0028 cursor for select amount , status from rc_ledger where biz_date = $1 order by key", + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 62 "app/online/rc_ol_0028.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0028"); + tx_return(ctx, TX_EDB, ctx->out); return; } + opened = 1; + + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_ol_0028", ECPGt_EOIT, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 71 "app/online/rc_ol_0028.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0028"); + break; + } + + scanned++; + + /* 승인 잔여(접수) vs 매입/정산 완료로 양측 누적 */ + if (strcmp(h_status, RC_ST_RECV) == 0) { + sumappr += h_amount; + } else if (strcmp(h_status, RC_ST_DONE) == 0 || + strcmp(h_status, RC_ST_SETTLED) == 0) { + sumcap += h_amount; + } + } + + if (opened) + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_ol_0028", ECPGt_EOIT, ECPGt_EORT);} +#line 92 "app/online/rc_ol_0028.pgc" + + + gap = sumappr - sumcap; + balanced = (gap == 0) ? "Y" : "N"; + + tx_log(TX_LOG_INFO, + "[RC_OL_0028] date=%s 순회=%ld 승인잔여=%ld 매입정산=%ld GAP=%ld 균형=%s", + bizdate, scanned, sumappr, sumcap, gap, balanced); + + /* --- 불균형 방향을 진단하여 후속 조치에 참고할 로그를 남긴다 --- */ + if (scanned == 0) { + tx_log(TX_LOG_WARN, + "[RC_OL_0028] 순회 대상 없음 date=%s → 검증 생략", bizdate); + } else if (gap == 0) { + tx_log(TX_LOG_INFO, + "[RC_OL_0028] 승인/매입 균형 일치 date=%s", bizdate); + } else if (gap > 0) { + tx_log(TX_LOG_WARN, + "[RC_OL_0028] 승인잔여 초과 date=%s 미매입 추정=%ld", bizdate, gap); + } else { + tx_log(TX_LOG_WARN, + "[RC_OL_0028] 매입정산 초과 date=%s 과매입 추정=%ld", + bizdate, -gap); + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SUMAPPR", sumappr); + tx_buf_setlong(ctx->out, "SUMCAP", sumcap); + tx_buf_setlong(ctx->out, "GAP", gap); + tx_buf_sets(ctx->out, "BALANCED", balanced); + tx_log(TX_LOG_INFO, + "[RC_OL_0028] 대사 균형검증 서비스 정상 종료 date=%s 균형=%s", + bizdate, balanced); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0028.pgc b/app/online/rc_ol_0028.pgc index 4ef4206..56c3c33 100644 --- a/app/online/rc_ol_0028.pgc +++ b/app/online/rc_ol_0028.pgc @@ -1,14 +1,23 @@ /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0028.pgc - 대사 온라인 서비스 (RC_OL_0028) [tier=easy] + * rc_ol_0028.pgc - 대사 균형검증 (CURSOR) 온라인 서비스 (RC_OL_0028) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 영업일(BIZDATE)의 원장을 커서로 순회하며 승인 잔여금액과 + * 매입/정산금액을 각각 누적하여 두 축의 균형(밸런스) 여부를 검증한다. + * + * 업무규칙: + * - 접수('R') → 승인 잔여 SUMAPPR 에 amount 누적 + * - 매입확정('M')/정산('S') → 매입/정산 SUMCAP 에 amount 누적 + * - 차이 GAP = SUMAPPR - SUMCAP + * - GAP 이 0 이면 BALANCED=Y, 아니면 BALANCED=N + * + * 응답: BIZDATE, SUMAPPR, SUMCAP, GAP, BALANCED (읽기 전용) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -16,32 +25,103 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0028, ctx) { - rc_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_biz_date[9]; + char h_status[2]; + long h_amount; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[RC_OL_0028] 대사 단건조회 서비스 진입"); + char bizdate[16]; + long sumappr = 0, sumcap = 0, gap = 0; + long scanned = 0; + const char *balanced; + int opened = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0028] KEY 누락"); + tx_log(TX_LOG_INFO, "[RC_OL_0028] 대사 균형검증 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0028] BIZDATE 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0028] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = rc_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + memset(h_biz_date, 0, sizeof(h_biz_date)); + strncpy(h_biz_date, bizdate, sizeof(h_biz_date) - 1); + + EXEC SQL DECLARE cur_rc_ol_0028 CURSOR FOR + SELECT amount, status + FROM rc_ledger + WHERE biz_date = :h_biz_date + ORDER BY key; + + EXEC SQL OPEN cur_rc_ol_0028; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0028"); + tx_return(ctx, TX_EDB, ctx->out); return; } + opened = 1; + + for (;;) { + EXEC SQL FETCH cur_rc_ol_0028 INTO :h_amount, :h_status; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0028"); + break; + } + + scanned++; + + /* 승인 잔여(접수) vs 매입/정산 완료로 양측 누적 */ + if (strcmp(h_status, RC_ST_RECV) == 0) { + sumappr += h_amount; + } else if (strcmp(h_status, RC_ST_DONE) == 0 || + strcmp(h_status, RC_ST_SETTLED) == 0) { + sumcap += h_amount; + } + } + + if (opened) + EXEC SQL CLOSE cur_rc_ol_0028; + + gap = sumappr - sumcap; + balanced = (gap == 0) ? "Y" : "N"; + + tx_log(TX_LOG_INFO, + "[RC_OL_0028] date=%s 순회=%ld 승인잔여=%ld 매입정산=%ld GAP=%ld 균형=%s", + bizdate, scanned, sumappr, sumcap, gap, balanced); + + /* --- 불균형 방향을 진단하여 후속 조치에 참고할 로그를 남긴다 --- */ + if (scanned == 0) { + tx_log(TX_LOG_WARN, + "[RC_OL_0028] 순회 대상 없음 date=%s → 검증 생략", bizdate); + } else if (gap == 0) { + tx_log(TX_LOG_INFO, + "[RC_OL_0028] 승인/매입 균형 일치 date=%s", bizdate); + } else if (gap > 0) { + tx_log(TX_LOG_WARN, + "[RC_OL_0028] 승인잔여 초과 date=%s 미매입 추정=%ld", bizdate, gap); + } else { + tx_log(TX_LOG_WARN, + "[RC_OL_0028] 매입정산 초과 date=%s 과매입 추정=%ld", + bizdate, -gap); + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "SUMAPPR", sumappr); + tx_buf_setlong(ctx->out, "SUMCAP", sumcap); + tx_buf_setlong(ctx->out, "GAP", gap); + tx_buf_sets(ctx->out, "BALANCED", balanced); + tx_log(TX_LOG_INFO, + "[RC_OL_0028] 대사 균형검증 서비스 정상 종료 date=%s 균형=%s", + bizdate, balanced); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0029.c b/app/online/rc_ol_0029.c index 2473fe9..f604206 100644 --- a/app/online/rc_ol_0029.c +++ b/app/online/rc_ol_0029.c @@ -8,15 +8,24 @@ #line 1 "app/online/rc_ol_0029.pgc" /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0029.pgc - 대사 온라인 서비스 (RC_OL_0029) [tier=medium] + * rc_ol_0029.pgc - 미매입 추출 (CURSOR) 온라인 서비스 (RC_OL_0029) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 기준일(CUTOFF, YYYYMMDD) 이하로 승인('R')되었으나 아직 매입이 + * 일어나지 않은(수수료 fee=0) 건을 커서로 순회하여 잔존 미매입 현황을 + * 추출한다. 오래된 건부터 확인하기 위해 영업일 오름차순으로 순회한다. + * + * 업무규칙: + * - 대상: status='R' AND fee=0 AND biz_date <= :cutoff + * - 미매입 건수 UNCAPCNT 와 금액합계 SUMAMT 누적 + * - 가장 먼저(가장 오래된) 만난 건의 KEY 를 FIRSTKEY 로 캡처 + * + * 응답: CUTOFF, UNCAPCNT, SUMAMT, FIRSTKEY (읽기 전용) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -89,60 +98,131 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0029.pgc" +#line 24 "app/online/rc_ol_0029.pgc" TX_SERVICE(RC_OL_0029, ctx) { - rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + +#line 29 "app/online/rc_ol_0029.pgc" + char h_cutoff [ 9 ] ; + +#line 30 "app/online/rc_ol_0029.pgc" + char h_key [ 16 ] ; + +#line 31 "app/online/rc_ol_0029.pgc" + char h_biz_date [ 9 ] ; + +#line 32 "app/online/rc_ol_0029.pgc" + long h_amount ; +/* exec sql end declare section */ +#line 33 "app/online/rc_ol_0029.pgc" - tx_log(TX_LOG_INFO, "[RC_OL_0029] 대사 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0029] 필수 필드 누락"); + char cutoff[16]; + char firstkey[16]; + long uncapcnt = 0, sumamt = 0; + int captured = 0; + int opened = 0; + + tx_log(TX_LOG_INFO, "[RC_OL_0029] 미매입 추출 서비스 진입"); + + if (tx_buf_get(ctx->in, "CUTOFF", cutoff, sizeof(cutoff)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0029] CUTOFF(기준일) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0029] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0029] 영업일 오류 date=%s", bizdate); + if (!date_is_valid(cutoff)) { + tx_log(TX_LOG_WARN, "[RC_OL_0029] 기준일 형식 오류 cutoff=%s", cutoff); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + memset(firstkey, 0, sizeof(firstkey)); + memset(h_cutoff, 0, sizeof(h_cutoff)); + strncpy(h_cutoff, cutoff, sizeof(h_cutoff) - 1); - rc = rc_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0029] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + ECPGset_var( 0, ( h_cutoff ), __LINE__);\ + /* declare cur_rc_ol_0029 cursor for select key , amount , biz_date from rc_ledger where status = 'R' and fee = 0 and biz_date <= $1 order by biz_date */ +#line 64 "app/online/rc_ol_0029.pgc" + +#line 64 "app/online/rc_ol_0029.pgc" + + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare cur_rc_ol_0029 cursor for select key , amount , biz_date from rc_ledger where status = 'R' and fee = 0 and biz_date <= $1 order by biz_date", + ECPGt_char,(h_cutoff),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 66 "app/online/rc_ol_0029.pgc" + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0029"); + tx_return(ctx, TX_EDB, ctx->out); return; } + opened = 1; - cnt = rc_rec_count(bizdate, RC_ST_RECV); + for (;;) { + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "fetch cur_rc_ol_0029", ECPGt_EOIT, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 76 "app/online/rc_ol_0029.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0029"); + break; + } + + uncapcnt++; + sumamt += h_amount; + + /* 가장 오래된(첫) 미매입 건의 KEY 캡처 */ + if (!captured) { + strncpy(firstkey, h_key, sizeof(firstkey) - 1); + firstkey[sizeof(firstkey) - 1] = '\0'; + captured = 1; + tx_log(TX_LOG_INFO, + "[RC_OL_0029] 최선두 미매입 key=%s date=%s", + h_key, h_biz_date); + } + } + + if (opened) + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "close cur_rc_ol_0029", ECPGt_EOIT, ECPGt_EORT);} +#line 100 "app/online/rc_ol_0029.pgc" + + + if (uncapcnt == 0) { + tx_log(TX_LOG_INFO, + "[RC_OL_0029] 미매입 없음 cutoff=%s", cutoff); + } else { + long avgamt = sumamt / uncapcnt; /* 미매입 건당 평균금액 (참고) */ + tx_log(TX_LOG_WARN, + "[RC_OL_0029] 미매입 잔존 cutoff=%s 건수=%ld 합계=%ld 평균=%ld", + cutoff, uncapcnt, sumamt, avgamt); + if (uncapcnt >= 100) + tx_log(TX_LOG_WARN, + "[RC_OL_0029] 미매입 다량 잔존 → 매입배치 재점검 필요"); + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0029] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "CUTOFF", cutoff); + tx_buf_setlong(ctx->out, "UNCAPCNT", uncapcnt); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + tx_buf_sets(ctx->out, "FIRSTKEY", firstkey); + tx_log(TX_LOG_INFO, + "[RC_OL_0029] 미매입 추출 서비스 정상 종료 cutoff=%s 추출=%ld건", + cutoff, uncapcnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0029.pgc b/app/online/rc_ol_0029.pgc index 3f82d9d..6818b7c 100644 --- a/app/online/rc_ol_0029.pgc +++ b/app/online/rc_ol_0029.pgc @@ -1,14 +1,23 @@ /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0029.pgc - 대사 온라인 서비스 (RC_OL_0029) [tier=medium] + * rc_ol_0029.pgc - 미매입 추출 (CURSOR) 온라인 서비스 (RC_OL_0029) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 기준일(CUTOFF, YYYYMMDD) 이하로 승인('R')되었으나 아직 매입이 + * 일어나지 않은(수수료 fee=0) 건을 커서로 순회하여 잔존 미매입 현황을 + * 추출한다. 오래된 건부터 확인하기 위해 영업일 오름차순으로 순회한다. + * + * 업무규칙: + * - 대상: status='R' AND fee=0 AND biz_date <= :cutoff + * - 미매입 건수 UNCAPCNT 와 금액합계 SUMAMT 누적 + * - 가장 먼저(가장 오래된) 만난 건의 KEY 를 FIRSTKEY 로 캡처 + * + * 응답: CUTOFF, UNCAPCNT, SUMAMT, FIRSTKEY (읽기 전용) */ #include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" @@ -16,55 +25,100 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0029, ctx) { - rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_cutoff[9]; + char h_key[16]; + char h_biz_date[9]; + long h_amount; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[RC_OL_0029] 대사 등록 서비스 진입"); + char cutoff[16]; + char firstkey[16]; + long uncapcnt = 0, sumamt = 0; + int captured = 0; + int opened = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0029] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[RC_OL_0029] 미매입 추출 서비스 진입"); + + if (tx_buf_get(ctx->in, "CUTOFF", cutoff, sizeof(cutoff)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0029] CUTOFF(기준일) 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0029] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0029] 영업일 오류 date=%s", bizdate); + if (!date_is_valid(cutoff)) { + tx_log(TX_LOG_WARN, "[RC_OL_0029] 기준일 형식 오류 cutoff=%s", cutoff); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + memset(firstkey, 0, sizeof(firstkey)); + memset(h_cutoff, 0, sizeof(h_cutoff)); + strncpy(h_cutoff, cutoff, sizeof(h_cutoff) - 1); - rc = rc_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0029] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + EXEC SQL DECLARE cur_rc_ol_0029 CURSOR FOR + SELECT key, amount, biz_date + FROM rc_ledger + WHERE status = 'R' + AND fee = 0 + AND biz_date <= :h_cutoff + ORDER BY biz_date; + + EXEC SQL OPEN cur_rc_ol_0029; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("OPEN cur_rc_ol_0029"); + tx_return(ctx, TX_EDB, ctx->out); return; } + opened = 1; - cnt = rc_rec_count(bizdate, RC_ST_RECV); + for (;;) { + EXEC SQL FETCH cur_rc_ol_0029 + INTO :h_key, :h_amount, :h_biz_date; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) + break; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("FETCH cur_rc_ol_0029"); + break; + } + + uncapcnt++; + sumamt += h_amount; + + /* 가장 오래된(첫) 미매입 건의 KEY 캡처 */ + if (!captured) { + strncpy(firstkey, h_key, sizeof(firstkey) - 1); + firstkey[sizeof(firstkey) - 1] = '\0'; + captured = 1; + tx_log(TX_LOG_INFO, + "[RC_OL_0029] 최선두 미매입 key=%s date=%s", + h_key, h_biz_date); + } + } + + if (opened) + EXEC SQL CLOSE cur_rc_ol_0029; + + if (uncapcnt == 0) { + tx_log(TX_LOG_INFO, + "[RC_OL_0029] 미매입 없음 cutoff=%s", cutoff); + } else { + long avgamt = sumamt / uncapcnt; /* 미매입 건당 평균금액 (참고) */ + tx_log(TX_LOG_WARN, + "[RC_OL_0029] 미매입 잔존 cutoff=%s 건수=%ld 합계=%ld 평균=%ld", + cutoff, uncapcnt, sumamt, avgamt); + if (uncapcnt >= 100) + tx_log(TX_LOG_WARN, + "[RC_OL_0029] 미매입 다량 잔존 → 매입배치 재점검 필요"); + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0029] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "CUTOFF", cutoff); + tx_buf_setlong(ctx->out, "UNCAPCNT", uncapcnt); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + tx_buf_sets(ctx->out, "FIRSTKEY", firstkey); + tx_log(TX_LOG_INFO, + "[RC_OL_0029] 미매입 추출 서비스 정상 종료 cutoff=%s 추출=%ld건", + cutoff, uncapcnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0030.c b/app/online/rc_ol_0030.c index b69ab54..03123f5 100644 --- a/app/online/rc_ol_0030.c +++ b/app/online/rc_ol_0030.c @@ -8,10 +8,18 @@ #line 1 "app/online/rc_ol_0030.pgc" /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0030.pgc - 대사 온라인 서비스 (RC_OL_0030) [tier=easy] + * rc_ol_0030.pgc - 대사 취소 온라인 서비스 (RC_OL_0030) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건에 대해 대사(매입/불일치) 결과를 취소하여 접수('R') + * 상태로 되돌린다. 오취소를 막기 위해 확인 플래그(CFMFLAG="Y") 를 요구한다. + * + * 업무규칙: + * - CFMFLAG != "Y" → RESULT=NOCONFIRM, 상태 변경 없음 (TX_OK) + * - status == 정산완료('S') → RESULT=REJECT, 취소 불가 (상태 유지) + * - status in {'M','U'} → 접수('R') 로 전이, RESULT=CANCELLED + * - 그 외(이미 'R' 등) → RESULT=REJECT (취소 대상 아님) + * + * 응답: KEY, OLDSTATUS, NEWSTATUS, RESULT (조건부 갱신) */ #include #include @@ -89,37 +97,102 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0030.pgc" +#line 23 "app/online/rc_ol_0030.pgc" TX_SERVICE(RC_OL_0030, ctx) { rc_rec_t rec; char key[16]; + char cfmflag[4]; + char oldstatus[2]; + const char *newstatus; + const char *result; int rc; - tx_log(TX_LOG_INFO, "[RC_OL_0030] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0030] 대사 취소 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0030] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_get(ctx->in, "CFMFLAG", cfmflag, sizeof(cfmflag)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0030] CFMFLAG(확인 플래그) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0030] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0030] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + memset(oldstatus, 0, sizeof(oldstatus)); + strncpy(oldstatus, rec.status, sizeof(oldstatus) - 1); + newstatus = oldstatus; /* 기본: 변경 없음 */ + + /* --- 확인 플래그 미승인 → 변경 없이 종료 --- */ + if (strcmp(cfmflag, "Y") != 0) { + result = "NOCONFIRM"; + tx_log(TX_LOG_WARN, + "[RC_OL_0030] 확인 플래그 미승인 key=%s flag=%s → 취소 보류", + key, cfmflag); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "OLDSTATUS", oldstatus); + tx_buf_sets(ctx->out, "NEWSTATUS", newstatus); + tx_buf_sets(ctx->out, "RESULT", result); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* --- 정산완료 건은 취소 불가 --- */ + if (strcmp(rec.status, RC_ST_SETTLED) == 0) { + result = "REJECT"; + tx_log(TX_LOG_WARN, + "[RC_OL_0030] 정산완료 취소불가 key=%s status=S", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "OLDSTATUS", oldstatus); + tx_buf_sets(ctx->out, "NEWSTATUS", newstatus); + tx_buf_sets(ctx->out, "RESULT", result); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* --- 매입확정/불일치 건만 접수('R') 로 되돌림 --- */ + if (strcmp(rec.status, RC_ST_DONE) == 0 || + strcmp(rec.status, RC_ST_FAIL) == 0) { + rc = rc_rec_update_status(key, RC_ST_RECV); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0030] 취소 전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + newstatus = RC_ST_RECV; + result = "CANCELLED"; + tx_log(TX_LOG_INFO, + "[RC_OL_0030] 대사 취소 완료 key=%s %s→R", key, oldstatus); + } else { + result = "REJECT"; + tx_log(TX_LOG_WARN, + "[RC_OL_0030] 취소 대상 아님 key=%s status=%s", + key, oldstatus); + } + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "OLDSTATUS", oldstatus); + tx_buf_sets(ctx->out, "NEWSTATUS", newstatus); + tx_buf_sets(ctx->out, "RESULT", result); + tx_log(TX_LOG_INFO, + "[RC_OL_0030] 대사 취소 서비스 종료 key=%s %s→%s 결과=%s", + key, oldstatus, newstatus, result); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0030.pgc b/app/online/rc_ol_0030.pgc index 9daf2b9..ac2abf1 100644 --- a/app/online/rc_ol_0030.pgc +++ b/app/online/rc_ol_0030.pgc @@ -1,9 +1,17 @@ /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0030.pgc - 대사 온라인 서비스 (RC_OL_0030) [tier=easy] + * rc_ol_0030.pgc - 대사 취소 온라인 서비스 (RC_OL_0030) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 KEY 의 원장 1건에 대해 대사(매입/불일치) 결과를 취소하여 접수('R') + * 상태로 되돌린다. 오취소를 막기 위해 확인 플래그(CFMFLAG="Y") 를 요구한다. + * + * 업무규칙: + * - CFMFLAG != "Y" → RESULT=NOCONFIRM, 상태 변경 없음 (TX_OK) + * - status == 정산완료('S') → RESULT=REJECT, 취소 불가 (상태 유지) + * - status in {'M','U'} → 접수('R') 로 전이, RESULT=CANCELLED + * - 그 외(이미 'R' 등) → RESULT=REJECT (취소 대상 아님) + * + * 응답: KEY, OLDSTATUS, NEWSTATUS, RESULT (조건부 갱신) */ #include #include @@ -18,30 +26,95 @@ TX_SERVICE(RC_OL_0030, ctx) { rc_rec_t rec; char key[16]; + char cfmflag[4]; + char oldstatus[2]; + const char *newstatus; + const char *result; int rc; - tx_log(TX_LOG_INFO, "[RC_OL_0030] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0030] 대사 취소 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[RC_OL_0030] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + if (tx_buf_get(ctx->in, "CFMFLAG", cfmflag, sizeof(cfmflag)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0030] CFMFLAG(확인 플래그) 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } memset(&rec, 0, sizeof(rec)); rc = rc_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0030] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[RC_OL_0030] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + memset(oldstatus, 0, sizeof(oldstatus)); + strncpy(oldstatus, rec.status, sizeof(oldstatus) - 1); + newstatus = oldstatus; /* 기본: 변경 없음 */ + + /* --- 확인 플래그 미승인 → 변경 없이 종료 --- */ + if (strcmp(cfmflag, "Y") != 0) { + result = "NOCONFIRM"; + tx_log(TX_LOG_WARN, + "[RC_OL_0030] 확인 플래그 미승인 key=%s flag=%s → 취소 보류", + key, cfmflag); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "OLDSTATUS", oldstatus); + tx_buf_sets(ctx->out, "NEWSTATUS", newstatus); + tx_buf_sets(ctx->out, "RESULT", result); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* --- 정산완료 건은 취소 불가 --- */ + if (strcmp(rec.status, RC_ST_SETTLED) == 0) { + result = "REJECT"; + tx_log(TX_LOG_WARN, + "[RC_OL_0030] 정산완료 취소불가 key=%s status=S", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "OLDSTATUS", oldstatus); + tx_buf_sets(ctx->out, "NEWSTATUS", newstatus); + tx_buf_sets(ctx->out, "RESULT", result); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* --- 매입확정/불일치 건만 접수('R') 로 되돌림 --- */ + if (strcmp(rec.status, RC_ST_DONE) == 0 || + strcmp(rec.status, RC_ST_FAIL) == 0) { + rc = rc_rec_update_status(key, RC_ST_RECV); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0030] 취소 전이 실패 key=%s rc=%d", + key, rc); + tx_return(ctx, rc, ctx->out); + return; + } + newstatus = RC_ST_RECV; + result = "CANCELLED"; + tx_log(TX_LOG_INFO, + "[RC_OL_0030] 대사 취소 완료 key=%s %s→R", key, oldstatus); + } else { + result = "REJECT"; + tx_log(TX_LOG_WARN, + "[RC_OL_0030] 취소 대상 아님 key=%s status=%s", + key, oldstatus); + } + tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "OLDSTATUS", oldstatus); + tx_buf_sets(ctx->out, "NEWSTATUS", newstatus); + tx_buf_sets(ctx->out, "RESULT", result); + tx_log(TX_LOG_INFO, + "[RC_OL_0030] 대사 취소 서비스 종료 key=%s %s→%s 결과=%s", + key, oldstatus, newstatus, result); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0031.c b/app/online/rc_ol_0031.c index 194f90f..648cfe2 100644 --- a/app/online/rc_ol_0031.c +++ b/app/online/rc_ol_0031.c @@ -8,10 +8,27 @@ #line 1 "app/online/rc_ol_0031.pgc" /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0031.pgc - 대사 온라인 서비스 (RC_OL_0031) [tier=medium] + * rc_ol_0031.pgc - 대사 진행률 조회 온라인 서비스 (RC_OL_0031) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 영업일(BIZDATE)에 대해 상태별 원장 건수를 집계하여 대사 처리의 + * 진행률과 완료율, 잔여 미처리 건수를 산출한다. + * 상태별로 rc_rec_count 를 호출한다: + * 접수('R'), 매입확정('M'), 불일치('U'), 정산완료('S'). + * + * 업무규칙: + * - total = 접수 + 매입확정 + 불일치 + 정산완료 (0 방어) + * - 처리완료 PROCESSED = 매입확정 + 불일치 + 정산완료 (접수를 제외한 건) + * - 진행률 PROGRESS = PROCESSED * 100 / total + * - 최종완료율 COMPLETION = 정산완료 * 100 / total + * - 잔여 PENDING = 접수('R') 건수 + * + * 응답: BIZDATE, TOTAL, PROCESSED, PROGRESS, COMPLETION, PENDING + * + * 비고: + * - PROGRESS 는 "접수를 벗어난" 모든 건(매입/불일치/정산) 을 처리로 보고, + * COMPLETION 은 최종 정산완료('S') 만을 완료로 본다. 따라서 항상 + * COMPLETION <= PROGRESS 관계가 성립한다. + * - 상태별 집계 API 만 사용하며 커서를 열지 않는다 (읽기 전용). */ #include #include @@ -89,60 +106,93 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0031.pgc" +#line 32 "app/online/rc_ol_0031.pgc" TX_SERVICE(RC_OL_0031, ctx) { - rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char bizdate[16]; + long cnt_r, cnt_m, cnt_u, cnt_s; + long total, processed; + long progress; /* 처리 진행률 (%) */ + long completion; /* 최종 완료율 (%) */ + long pending; /* 잔여 접수 건수 */ - tx_log(TX_LOG_INFO, "[RC_OL_0031] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0031] 대사 진행률 조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0031] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0031] 금액 범위 오류 amount=%ld", amount); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0031] BIZDATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0031] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[RC_OL_0031] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + cnt_r = rc_rec_count(bizdate, RC_ST_RECV); + cnt_m = rc_rec_count(bizdate, RC_ST_DONE); + cnt_u = rc_rec_count(bizdate, RC_ST_FAIL); + cnt_s = rc_rec_count(bizdate, RC_ST_SETTLED); - rc = rc_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0031] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (cnt_r < 0 || cnt_m < 0 || cnt_u < 0 || cnt_s < 0) { + tx_log(TX_LOG_ERROR, + "[RC_OL_0031] 건수 집계 실패 date=%s R=%ld M=%ld U=%ld S=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + total = cnt_r + cnt_m + cnt_u + cnt_s; + processed = cnt_m + cnt_u + cnt_s; /* 접수를 제외한 처리 진행 건 */ + pending = cnt_r; + + /* 상태 내역을 감사 로그로 남겨 진행 추이를 추적한다. */ + tx_log(TX_LOG_INFO, + "[RC_OL_0031] 상태내역 date=%s 접수=%ld 매입=%ld 불일치=%ld 정산=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s); + + /* --- 진행률/완료율 산출 (0 나눗셈 방어) --- */ + if (total > 0) { + progress = processed * 100 / total; + completion = cnt_s * 100 / total; + } else { + progress = 0; + completion = 0; + tx_log(TX_LOG_WARN, + "[RC_OL_0031] 대상 원장 없음 date=%s → 진행률 0%%", bizdate); + } + + tx_log(TX_LOG_INFO, + "[RC_OL_0031] date=%s 총=%ld 처리=%ld 진행률=%ld%% 완료율=%ld%% 잔여=%ld", + bizdate, total, processed, progress, completion, pending); + + /* --- 진행 단계별 정성 상태를 로그로 남긴다 (응답에는 미포함) --- */ + if (total == 0) + tx_log(TX_LOG_INFO, "[RC_OL_0031] 단계=대상없음"); + else if (pending == 0 && completion >= 100) + tx_log(TX_LOG_INFO, "[RC_OL_0031] 단계=정산마감 (100%% 완료)"); + else if (pending == 0) + tx_log(TX_LOG_INFO, + "[RC_OL_0031] 단계=대사완료 정산진행 (완료율 %ld%%)", completion); + else if (progress >= 50) + tx_log(TX_LOG_INFO, + "[RC_OL_0031] 단계=진행중 (진행률 %ld%% 잔여 %ld건)", + progress, pending); + else + tx_log(TX_LOG_WARN, + "[RC_OL_0031] 단계=초기 (진행률 %ld%% 잔여 %ld건)", + progress, pending); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0031] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_buf_setlong(ctx->out, "PROCESSED", processed); + tx_buf_setlong(ctx->out, "PROGRESS", progress); + tx_buf_setlong(ctx->out, "COMPLETION", completion); + tx_buf_setlong(ctx->out, "PENDING", pending); + tx_log(TX_LOG_INFO, + "[RC_OL_0031] 대사 진행률 조회 서비스 정상 종료 date=%s", bizdate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0031.pgc b/app/online/rc_ol_0031.pgc index 2be47fa..29e9dd5 100644 --- a/app/online/rc_ol_0031.pgc +++ b/app/online/rc_ol_0031.pgc @@ -1,9 +1,26 @@ /* @tier medium @module rc @transform reconciliation-online */ /* - * rc_ol_0031.pgc - 대사 온라인 서비스 (RC_OL_0031) [tier=medium] + * rc_ol_0031.pgc - 대사 진행률 조회 온라인 서비스 (RC_OL_0031) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 영업일(BIZDATE)에 대해 상태별 원장 건수를 집계하여 대사 처리의 + * 진행률과 완료율, 잔여 미처리 건수를 산출한다. + * 상태별로 rc_rec_count 를 호출한다: + * 접수('R'), 매입확정('M'), 불일치('U'), 정산완료('S'). + * + * 업무규칙: + * - total = 접수 + 매입확정 + 불일치 + 정산완료 (0 방어) + * - 처리완료 PROCESSED = 매입확정 + 불일치 + 정산완료 (접수를 제외한 건) + * - 진행률 PROGRESS = PROCESSED * 100 / total + * - 최종완료율 COMPLETION = 정산완료 * 100 / total + * - 잔여 PENDING = 접수('R') 건수 + * + * 응답: BIZDATE, TOTAL, PROCESSED, PROGRESS, COMPLETION, PENDING + * + * 비고: + * - PROGRESS 는 "접수를 벗어난" 모든 건(매입/불일치/정산) 을 처리로 보고, + * COMPLETION 은 최종 정산완료('S') 만을 완료로 본다. 따라서 항상 + * COMPLETION <= PROGRESS 관계가 성립한다. + * - 상태별 집계 API 만 사용하며 커서를 열지 않는다 (읽기 전용). */ #include #include @@ -16,55 +33,88 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0031, ctx) { - rc_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char bizdate[16]; + long cnt_r, cnt_m, cnt_u, cnt_s; + long total, processed; + long progress; /* 처리 진행률 (%) */ + long completion; /* 최종 완료율 (%) */ + long pending; /* 잔여 접수 건수 */ - tx_log(TX_LOG_INFO, "[RC_OL_0031] 대사 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0031] 대사 진행률 조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0031] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[RC_OL_0031] 금액 범위 오류 amount=%ld", amount); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0031] BIZDATE 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[RC_OL_0031] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[RC_OL_0031] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, RC_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + cnt_r = rc_rec_count(bizdate, RC_ST_RECV); + cnt_m = rc_rec_count(bizdate, RC_ST_DONE); + cnt_u = rc_rec_count(bizdate, RC_ST_FAIL); + cnt_s = rc_rec_count(bizdate, RC_ST_SETTLED); - rc = rc_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0031] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (cnt_r < 0 || cnt_m < 0 || cnt_u < 0 || cnt_s < 0) { + tx_log(TX_LOG_ERROR, + "[RC_OL_0031] 건수 집계 실패 date=%s R=%ld M=%ld U=%ld S=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = rc_rec_count(bizdate, RC_ST_RECV); + total = cnt_r + cnt_m + cnt_u + cnt_s; + processed = cnt_m + cnt_u + cnt_s; /* 접수를 제외한 처리 진행 건 */ + pending = cnt_r; + + /* 상태 내역을 감사 로그로 남겨 진행 추이를 추적한다. */ + tx_log(TX_LOG_INFO, + "[RC_OL_0031] 상태내역 date=%s 접수=%ld 매입=%ld 불일치=%ld 정산=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s); + + /* --- 진행률/완료율 산출 (0 나눗셈 방어) --- */ + if (total > 0) { + progress = processed * 100 / total; + completion = cnt_s * 100 / total; + } else { + progress = 0; + completion = 0; + tx_log(TX_LOG_WARN, + "[RC_OL_0031] 대상 원장 없음 date=%s → 진행률 0%%", bizdate); + } + + tx_log(TX_LOG_INFO, + "[RC_OL_0031] date=%s 총=%ld 처리=%ld 진행률=%ld%% 완료율=%ld%% 잔여=%ld", + bizdate, total, processed, progress, completion, pending); + + /* --- 진행 단계별 정성 상태를 로그로 남긴다 (응답에는 미포함) --- */ + if (total == 0) + tx_log(TX_LOG_INFO, "[RC_OL_0031] 단계=대상없음"); + else if (pending == 0 && completion >= 100) + tx_log(TX_LOG_INFO, "[RC_OL_0031] 단계=정산마감 (100%% 완료)"); + else if (pending == 0) + tx_log(TX_LOG_INFO, + "[RC_OL_0031] 단계=대사완료 정산진행 (완료율 %ld%%)", completion); + else if (progress >= 50) + tx_log(TX_LOG_INFO, + "[RC_OL_0031] 단계=진행중 (진행률 %ld%% 잔여 %ld건)", + progress, pending); + else + tx_log(TX_LOG_WARN, + "[RC_OL_0031] 단계=초기 (진행률 %ld%% 잔여 %ld건)", + progress, pending); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[RC_OL_0031] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_buf_setlong(ctx->out, "PROCESSED", processed); + tx_buf_setlong(ctx->out, "PROGRESS", progress); + tx_buf_setlong(ctx->out, "COMPLETION", completion); + tx_buf_setlong(ctx->out, "PENDING", pending); + tx_log(TX_LOG_INFO, + "[RC_OL_0031] 대사 진행률 조회 서비스 정상 종료 date=%s", bizdate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0032.c b/app/online/rc_ol_0032.c index 4d98079..a191f82 100644 --- a/app/online/rc_ol_0032.c +++ b/app/online/rc_ol_0032.c @@ -8,10 +8,25 @@ #line 1 "app/online/rc_ol_0032.pgc" /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0032.pgc - 대사 온라인 서비스 (RC_OL_0032) [tier=easy] + * rc_ol_0032.pgc - 대사 요약 리포트 조회 온라인 서비스 (RC_OL_0032) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 영업일(BIZDATE)에 대해 상태별 원장 건수와 금액 합계를 집계하여 + * 하루치 대사 요약 리포트를 구성한다. + * 상태별 건수는 rc_rec_count(R/M/U/S), 금액 합계는 rc_rec_sum 을 사용한다. + * + * 업무규칙: + * - total = 접수 + 매입확정 + 불일치 + 정산완료 (0 방어) + * - 대사율 RECRATE = (매입확정 + 정산완료) * 100 / total + * - 총평 OVERALL = 접수('R')==0 && 불일치('U')==0 → "CLOSED" + * (미처리/불일치 잔존 시) → "OPEN" + * + * 응답: BIZDATE, CNTR, CNTM, CNTU, CNTS, SUMAMT, RECRATE, OVERALL + * + * 비고: + * - 건수는 상태별 rc_rec_count, 금액은 rc_rec_sum 으로 집계하며 원장을 + * 커서로 순회하지 않는다 (읽기 전용). + * - OVERALL 은 접수/불일치가 모두 소진되어야 "CLOSED" 가 되며, 하나라도 + * 남아 있으면 "OPEN" 으로 마감 대기 상태임을 알린다. */ #include #include @@ -89,37 +104,95 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/rc_ol_0032.pgc" +#line 30 "app/online/rc_ol_0032.pgc" TX_SERVICE(RC_OL_0032, ctx) { - rc_rec_t rec; - char key[16]; + char bizdate[16]; + long cnt_r, cnt_m, cnt_u, cnt_s; + long total, sumamt = 0; + long recrate; + const char *overall; int rc; - tx_log(TX_LOG_INFO, "[RC_OL_0032] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0032] 대사 요약 리포트 조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0032] KEY 누락"); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0032] BIZDATE 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0032] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = rc_rec_select(key, &rec); + /* --- 상태별 건수 집계 --- */ + cnt_r = rc_rec_count(bizdate, RC_ST_RECV); + cnt_m = rc_rec_count(bizdate, RC_ST_DONE); + cnt_u = rc_rec_count(bizdate, RC_ST_FAIL); + cnt_s = rc_rec_count(bizdate, RC_ST_SETTLED); + + if (cnt_r < 0 || cnt_m < 0 || cnt_u < 0 || cnt_s < 0) { + tx_log(TX_LOG_ERROR, + "[RC_OL_0032] 건수 집계 실패 date=%s R=%ld M=%ld U=%ld S=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* --- 금액 합계 집계 --- */ + rc = rc_rec_sum(bizdate, &sumamt); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_ERROR, + "[RC_OL_0032] 금액 합계 실패 date=%s rc=%d(%s)", + bizdate, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + total = cnt_r + cnt_m + cnt_u + cnt_s; + + /* 리포트 본문에 앞서 상태 내역과 금액 합계를 감사 로그로 남긴다. */ + tx_log(TX_LOG_INFO, + "[RC_OL_0032] 집계 date=%s 접수=%ld 매입=%ld 불일치=%ld 정산=%ld 합계=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s, sumamt); + + /* --- 대사율 산출 (0 나눗셈 방어) --- */ + if (total > 0) { + recrate = (cnt_m + cnt_s) * 100 / total; + } else { + recrate = 0; + tx_log(TX_LOG_WARN, + "[RC_OL_0032] 대상 원장 없음 date=%s → 대사율 0%%", bizdate); + } + + /* --- 총평: 미처리/불일치 잔존 여부로 마감 상태 판정 --- */ + if (cnt_r == 0 && cnt_u == 0) { + overall = "CLOSED"; + tx_log(TX_LOG_INFO, + "[RC_OL_0032] 마감 완료 date=%s 총=%ld 대사율=%ld%%", + bizdate, total, recrate); + } else { + overall = "OPEN"; + tx_log(TX_LOG_WARN, + "[RC_OL_0032] 미결 잔존 date=%s 접수=%ld 불일치=%ld", + bizdate, cnt_r, cnt_u); + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNTR", cnt_r); + tx_buf_setlong(ctx->out, "CNTM", cnt_m); + tx_buf_setlong(ctx->out, "CNTU", cnt_u); + tx_buf_setlong(ctx->out, "CNTS", cnt_s); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + tx_buf_setlong(ctx->out, "RECRATE", recrate); + tx_buf_sets(ctx->out, "OVERALL", overall); + tx_log(TX_LOG_INFO, + "[RC_OL_0032] 대사 요약 리포트 서비스 정상 종료 date=%s 총평=%s 대사율=%ld%%", + bizdate, overall, recrate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/rc_ol_0032.pgc b/app/online/rc_ol_0032.pgc index c7e366a..1f36334 100644 --- a/app/online/rc_ol_0032.pgc +++ b/app/online/rc_ol_0032.pgc @@ -1,9 +1,24 @@ /* @tier easy @module rc @transform reconciliation-online */ /* - * rc_ol_0032.pgc - 대사 온라인 서비스 (RC_OL_0032) [tier=easy] + * rc_ol_0032.pgc - 대사 요약 리포트 조회 온라인 서비스 (RC_OL_0032) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 rc 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 요청 영업일(BIZDATE)에 대해 상태별 원장 건수와 금액 합계를 집계하여 + * 하루치 대사 요약 리포트를 구성한다. + * 상태별 건수는 rc_rec_count(R/M/U/S), 금액 합계는 rc_rec_sum 을 사용한다. + * + * 업무규칙: + * - total = 접수 + 매입확정 + 불일치 + 정산완료 (0 방어) + * - 대사율 RECRATE = (매입확정 + 정산완료) * 100 / total + * - 총평 OVERALL = 접수('R')==0 && 불일치('U')==0 → "CLOSED" + * (미처리/불일치 잔존 시) → "OPEN" + * + * 응답: BIZDATE, CNTR, CNTM, CNTU, CNTS, SUMAMT, RECRATE, OVERALL + * + * 비고: + * - 건수는 상태별 rc_rec_count, 금액은 rc_rec_sum 으로 집계하며 원장을 + * 커서로 순회하지 않는다 (읽기 전용). + * - OVERALL 은 접수/불일치가 모두 소진되어야 "CLOSED" 가 되며, 하나라도 + * 남아 있으면 "OPEN" 으로 마감 대기 상태임을 알린다. */ #include #include @@ -16,32 +31,90 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(RC_OL_0032, ctx) { - rc_rec_t rec; - char key[16]; + char bizdate[16]; + long cnt_r, cnt_m, cnt_u, cnt_s; + long total, sumamt = 0; + long recrate; + const char *overall; int rc; - tx_log(TX_LOG_INFO, "[RC_OL_0032] 대사 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[RC_OL_0032] 대사 요약 리포트 조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[RC_OL_0032] KEY 누락"); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[RC_OL_0032] BIZDATE 누락"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[RC_OL_0032] 영업일 형식 오류 date=%s", bizdate); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = rc_rec_select(key, &rec); + /* --- 상태별 건수 집계 --- */ + cnt_r = rc_rec_count(bizdate, RC_ST_RECV); + cnt_m = rc_rec_count(bizdate, RC_ST_DONE); + cnt_u = rc_rec_count(bizdate, RC_ST_FAIL); + cnt_s = rc_rec_count(bizdate, RC_ST_SETTLED); + + if (cnt_r < 0 || cnt_m < 0 || cnt_u < 0 || cnt_s < 0) { + tx_log(TX_LOG_ERROR, + "[RC_OL_0032] 건수 집계 실패 date=%s R=%ld M=%ld U=%ld S=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* --- 금액 합계 집계 --- */ + rc = rc_rec_sum(bizdate, &sumamt); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[RC_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_ERROR, + "[RC_OL_0032] 금액 합계 실패 date=%s rc=%d(%s)", + bizdate, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + total = cnt_r + cnt_m + cnt_u + cnt_s; + + /* 리포트 본문에 앞서 상태 내역과 금액 합계를 감사 로그로 남긴다. */ + tx_log(TX_LOG_INFO, + "[RC_OL_0032] 집계 date=%s 접수=%ld 매입=%ld 불일치=%ld 정산=%ld 합계=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s, sumamt); + + /* --- 대사율 산출 (0 나눗셈 방어) --- */ + if (total > 0) { + recrate = (cnt_m + cnt_s) * 100 / total; + } else { + recrate = 0; + tx_log(TX_LOG_WARN, + "[RC_OL_0032] 대상 원장 없음 date=%s → 대사율 0%%", bizdate); + } + + /* --- 총평: 미처리/불일치 잔존 여부로 마감 상태 판정 --- */ + if (cnt_r == 0 && cnt_u == 0) { + overall = "CLOSED"; + tx_log(TX_LOG_INFO, + "[RC_OL_0032] 마감 완료 date=%s 총=%ld 대사율=%ld%%", + bizdate, total, recrate); + } else { + overall = "OPEN"; + tx_log(TX_LOG_WARN, + "[RC_OL_0032] 미결 잔존 date=%s 접수=%ld 불일치=%ld", + bizdate, cnt_r, cnt_u); + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[RC_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNTR", cnt_r); + tx_buf_setlong(ctx->out, "CNTM", cnt_m); + tx_buf_setlong(ctx->out, "CNTU", cnt_u); + tx_buf_setlong(ctx->out, "CNTS", cnt_s); + tx_buf_setlong(ctx->out, "SUMAMT", sumamt); + tx_buf_setlong(ctx->out, "RECRATE", recrate); + tx_buf_sets(ctx->out, "OVERALL", overall); + tx_log(TX_LOG_INFO, + "[RC_OL_0032] 대사 요약 리포트 서비스 정상 종료 date=%s 총평=%s 대사율=%ld%%", + bizdate, overall, recrate); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0002.c b/app/online/st_ol_0002.c index ab00cb0..69ea53b 100644 --- a/app/online/st_ol_0002.c +++ b/app/online/st_ol_0002.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0002.pgc" /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0002.pgc - 정산수수료 온라인 서비스 (ST_OL_0002) [tier=medium] + * st_ol_0002.pgc - 정산대상 접수 집계 (ST_OL_0002) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 신규 정산대상 건을 접수(status=R)로 원장에 등록하고, 당일 접수 건수와 + * 접수 총액을 재집계하여 응답한다. */ #include #include @@ -92,32 +92,28 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0002.pgc" + TX_SERVICE(ST_OL_0002, ctx) { st_rec_t rec; char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + long amount = 0, daycnt, daysum = 0; int rc; - tx_log(TX_LOG_INFO, "[ST_OL_0002] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0002] 정산대상 접수 집계 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0002] 필수 필드 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0002] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0002] 영업일 오류 date=%s", bizdate); + if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[ST_OL_0002] 입력 검증 실패 amount=%ld date=%s", amount, bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -128,21 +124,24 @@ TX_SERVICE(ST_OL_0002, ctx) strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + rec.fee = 0; rc = st_rec_insert(&rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0002] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_ERROR, "[ST_OL_0002] 접수 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = st_rec_count(bizdate, ST_ST_RECV); + daycnt = st_rec_count(bizdate, ST_ST_RECV); + st_rec_sum(bizdate, &daysum); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0002] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "DAYCNT", (daycnt < 0) ? 0 : daycnt); + tx_buf_setlong(ctx->out, "DAYSUM", (daysum < 0) ? 0 : daysum); + tx_log(TX_LOG_INFO, "[ST_OL_0002] 접수완료 key=%s 당일건수=%ld 당일합=%ld", + key, daycnt, daysum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0002.pgc b/app/online/st_ol_0002.pgc index 42b5058..2fbf3e9 100644 --- a/app/online/st_ol_0002.pgc +++ b/app/online/st_ol_0002.pgc @@ -1,9 +1,9 @@ /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0002.pgc - 정산수수료 온라인 서비스 (ST_OL_0002) [tier=medium] + * st_ol_0002.pgc - 정산대상 접수 집계 (ST_OL_0002) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 신규 정산대상 건을 접수(status=R)로 원장에 등록하고, 당일 접수 건수와 + * 접수 총액을 재집계하여 응답한다. */ #include #include @@ -14,32 +14,28 @@ EXEC SQL INCLUDE sqlca; + TX_SERVICE(ST_OL_0002, ctx) { st_rec_t rec; char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + long amount = 0, daycnt, daysum = 0; int rc; - tx_log(TX_LOG_INFO, "[ST_OL_0002] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0002] 정산대상 접수 집계 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0002] 필수 필드 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0002] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0002] 영업일 오류 date=%s", bizdate); + if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[ST_OL_0002] 입력 검증 실패 amount=%ld date=%s", amount, bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -50,21 +46,24 @@ TX_SERVICE(ST_OL_0002, ctx) strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + rec.fee = 0; rc = st_rec_insert(&rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0002] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_ERROR, "[ST_OL_0002] 접수 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = st_rec_count(bizdate, ST_ST_RECV); + daycnt = st_rec_count(bizdate, ST_ST_RECV); + st_rec_sum(bizdate, &daysum); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0002] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "DAYCNT", (daycnt < 0) ? 0 : daycnt); + tx_buf_setlong(ctx->out, "DAYSUM", (daysum < 0) ? 0 : daysum); + tx_log(TX_LOG_INFO, "[ST_OL_0002] 접수완료 key=%s 당일건수=%ld 당일합=%ld", + key, daycnt, daysum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0003.c b/app/online/st_ol_0003.c index 102531f..9a8375a 100644 --- a/app/online/st_ol_0003.c +++ b/app/online/st_ol_0003.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0003.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0003.pgc - 정산수수료 온라인 서비스 (ST_OL_0003) [tier=easy] + * st_ol_0003.pgc - 가맹점 할인수수료(MDR) 계산 (ST_OL_0003) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 거래금액에 MDR(가맹점 할인수수료) 요율(만분율)을 적용하여 + * 수수료와 정산순액(net)을 원단위 반올림으로 산출한다. */ #include #include @@ -92,34 +92,48 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0003.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0003, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long amount = 0, mdr_bp = 0, fee, net; - tx_log(TX_LOG_INFO, "[ST_OL_0003] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0003] MDR 수수료 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0003] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "MDRBP", &mdr_bp); + + if (!amount_is_valid(amount) || mdr_bp <= 0 || mdr_bp > 10000) { + tx_log(TX_LOG_WARN, "[ST_OL_0003] 요율/금액 오류 amount=%ld bp=%ld", amount, mdr_bp); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + fee = st_fee_round(amount, mdr_bp); + net = amount - fee; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_setlong(ctx->out, "MDRBP", mdr_bp); + tx_buf_setlong(ctx->out, "FEE", fee); + tx_buf_setlong(ctx->out, "NET", net); + tx_log(TX_LOG_INFO, "[ST_OL_0003] MDR 산출 amount=%ld bp=%ld fee=%ld net=%ld", + amount, mdr_bp, fee, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0003.pgc b/app/online/st_ol_0003.pgc index af86c0d..8a0fa68 100644 --- a/app/online/st_ol_0003.pgc +++ b/app/online/st_ol_0003.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0003.pgc - 정산수수료 온라인 서비스 (ST_OL_0003) [tier=easy] + * st_ol_0003.pgc - 가맹점 할인수수료(MDR) 계산 (ST_OL_0003) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 거래금액에 MDR(가맹점 할인수수료) 요율(만분율)을 적용하여 + * 수수료와 정산순액(net)을 원단위 반올림으로 산출한다. */ #include #include @@ -14,34 +14,48 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0003, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long amount = 0, mdr_bp = 0, fee, net; - tx_log(TX_LOG_INFO, "[ST_OL_0003] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0003] MDR 수수료 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0003] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "MDRBP", &mdr_bp); + + if (!amount_is_valid(amount) || mdr_bp <= 0 || mdr_bp > 10000) { + tx_log(TX_LOG_WARN, "[ST_OL_0003] 요율/금액 오류 amount=%ld bp=%ld", amount, mdr_bp); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + fee = st_fee_round(amount, mdr_bp); + net = amount - fee; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_setlong(ctx->out, "MDRBP", mdr_bp); + tx_buf_setlong(ctx->out, "FEE", fee); + tx_buf_setlong(ctx->out, "NET", net); + tx_log(TX_LOG_INFO, "[ST_OL_0003] MDR 산출 amount=%ld bp=%ld fee=%ld net=%ld", + amount, mdr_bp, fee, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0004.c b/app/online/st_ol_0004.c index fb7a283..86b49c2 100644 --- a/app/online/st_ol_0004.c +++ b/app/online/st_ol_0004.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0004.pgc" /* @tier hard @module st @transform settlement-online */ /* - * st_ol_0004.pgc - 정산수수료 온라인 서비스 (ST_OL_0004) [tier=hard] + * st_ol_0004.pgc - VAN 수수료(건당 정액) 계산 (ST_OL_0004) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인 건수에 VAN 건당 정액 수수료를 곱하여 총 VAN 수수료를 산출한다. + * 정률 요소가 없는 건당 과금(전표/통신비) 모델. */ #include #include @@ -92,85 +92,42 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0004.pgc" + TX_SERVICE(ST_OL_0004, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char merch[16]; + long amount = 0, txn_cnt = 0, unit_fee = 0, van_fee, net; - tx_log(TX_LOG_INFO, "[ST_OL_0004] 정산수수료 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0004] VAN 건당정액 수수료 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0004] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "TXNCNT", &txn_cnt); + if (tx_buf_getlong(ctx->in, "UNITFEE", &unit_fee) != TX_OK || unit_fee <= 0) + unit_fee = 120; /* VAN 건당 기본 정액 120원 */ - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0004] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (txn_cnt <= 0) { + tx_log(TX_LOG_WARN, "[ST_OL_0004] 승인건수 오류 cnt=%ld", txn_cnt); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0004] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("ST_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[ST_OL_0004] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (st_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + van_fee = unit_fee * txn_cnt; + net = amount - van_fee; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[ST_OL_0004] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "TXNCNT", txn_cnt); + tx_buf_setlong(ctx->out, "UNITFEE", unit_fee); + tx_buf_setlong(ctx->out, "VANFEE", van_fee); + tx_buf_setlong(ctx->out, "NET", net); + tx_log(TX_LOG_INFO, "[ST_OL_0004] VAN 수수료 cnt=%ld unit=%ld total=%ld", + txn_cnt, unit_fee, van_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0004.pgc b/app/online/st_ol_0004.pgc index 7bff7dd..eaaa598 100644 --- a/app/online/st_ol_0004.pgc +++ b/app/online/st_ol_0004.pgc @@ -1,9 +1,9 @@ /* @tier hard @module st @transform settlement-online */ /* - * st_ol_0004.pgc - 정산수수료 온라인 서비스 (ST_OL_0004) [tier=hard] + * st_ol_0004.pgc - VAN 수수료(건당 정액) 계산 (ST_OL_0004) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인 건수에 VAN 건당 정액 수수료를 곱하여 총 VAN 수수료를 산출한다. + * 정률 요소가 없는 건당 과금(전표/통신비) 모델. */ #include #include @@ -14,85 +14,42 @@ EXEC SQL INCLUDE sqlca; + TX_SERVICE(ST_OL_0004, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char merch[16]; + long amount = 0, txn_cnt = 0, unit_fee = 0, van_fee, net; - tx_log(TX_LOG_INFO, "[ST_OL_0004] 정산수수료 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0004] VAN 건당정액 수수료 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0004] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "TXNCNT", &txn_cnt); + if (tx_buf_getlong(ctx->in, "UNITFEE", &unit_fee) != TX_OK || unit_fee <= 0) + unit_fee = 120; /* VAN 건당 기본 정액 120원 */ - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0004] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (txn_cnt <= 0) { + tx_log(TX_LOG_WARN, "[ST_OL_0004] 승인건수 오류 cnt=%ld", txn_cnt); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0004] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("ST_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[ST_OL_0004] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (st_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + van_fee = unit_fee * txn_cnt; + net = amount - van_fee; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[ST_OL_0004] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "TXNCNT", txn_cnt); + tx_buf_setlong(ctx->out, "UNITFEE", unit_fee); + tx_buf_setlong(ctx->out, "VANFEE", van_fee); + tx_buf_setlong(ctx->out, "NET", net); + tx_log(TX_LOG_INFO, "[ST_OL_0004] VAN 수수료 cnt=%ld unit=%ld total=%ld", + txn_cnt, unit_fee, van_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0005.c b/app/online/st_ol_0005.c index 7cfd6fb..a0e1a03 100644 --- a/app/online/st_ol_0005.c +++ b/app/online/st_ol_0005.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0005.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0005.pgc - 정산수수료 온라인 서비스 (ST_OL_0005) [tier=easy] + * st_ol_0005.pgc - 중계 수수료 계산 (ST_OL_0005) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 결제 중계사(PG/중계) 수수료 = 정률(만분율) + 건당 정액 합산 모델. + * 정률분과 정액분을 각각 산출하여 합산한다. */ #include #include @@ -92,34 +92,54 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0005.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0005, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long amount = 0, rate_bp = 0, txn_cnt = 0, flat = 0; + long rate_part, flat_part, relay_fee, net; - tx_log(TX_LOG_INFO, "[ST_OL_0005] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0005] 중계 수수료 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0005] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "RATEBP", &rate_bp); + tx_buf_getlong(ctx->in, "TXNCNT", &txn_cnt); + if (tx_buf_getlong(ctx->in, "FLATFEE", &flat) != TX_OK || flat < 0) + flat = 30; /* 중계 건당 기본 30원 */ + if (txn_cnt <= 0) txn_cnt = 1; + + if (!amount_is_valid(amount) || rate_bp < 0 || rate_bp > 10000) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0005] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + rate_part = st_fee_round(amount, rate_bp); + flat_part = flat * txn_cnt; + relay_fee = rate_part + flat_part; + net = amount - relay_fee; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0005] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "RATEPART", rate_part); + tx_buf_setlong(ctx->out, "FLATPART", flat_part); + tx_buf_setlong(ctx->out, "RELAYFEE", relay_fee); + tx_buf_setlong(ctx->out, "NET", net); + tx_log(TX_LOG_INFO, "[ST_OL_0005] 중계수수료 정률=%ld 정액=%ld 합=%ld", + rate_part, flat_part, relay_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0005.pgc b/app/online/st_ol_0005.pgc index 795ad5f..ad14bf7 100644 --- a/app/online/st_ol_0005.pgc +++ b/app/online/st_ol_0005.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0005.pgc - 정산수수료 온라인 서비스 (ST_OL_0005) [tier=easy] + * st_ol_0005.pgc - 중계 수수료 계산 (ST_OL_0005) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 결제 중계사(PG/중계) 수수료 = 정률(만분율) + 건당 정액 합산 모델. + * 정률분과 정액분을 각각 산출하여 합산한다. */ #include #include @@ -14,34 +14,54 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0005, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long amount = 0, rate_bp = 0, txn_cnt = 0, flat = 0; + long rate_part, flat_part, relay_fee, net; - tx_log(TX_LOG_INFO, "[ST_OL_0005] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0005] 중계 수수료 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0005] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "RATEBP", &rate_bp); + tx_buf_getlong(ctx->in, "TXNCNT", &txn_cnt); + if (tx_buf_getlong(ctx->in, "FLATFEE", &flat) != TX_OK || flat < 0) + flat = 30; /* 중계 건당 기본 30원 */ + if (txn_cnt <= 0) txn_cnt = 1; + + if (!amount_is_valid(amount) || rate_bp < 0 || rate_bp > 10000) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0005] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + rate_part = st_fee_round(amount, rate_bp); + flat_part = flat * txn_cnt; + relay_fee = rate_part + flat_part; + net = amount - relay_fee; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0005] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "RATEPART", rate_part); + tx_buf_setlong(ctx->out, "FLATPART", flat_part); + tx_buf_setlong(ctx->out, "RELAYFEE", relay_fee); + tx_buf_setlong(ctx->out, "NET", net); + tx_log(TX_LOG_INFO, "[ST_OL_0005] 중계수수료 정률=%ld 정액=%ld 합=%ld", + rate_part, flat_part, relay_fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0006.c b/app/online/st_ol_0006.c index e1582f7..455759d 100644 --- a/app/online/st_ol_0006.c +++ b/app/online/st_ol_0006.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0006.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0006.pgc - 정산수수료 온라인 서비스 (ST_OL_0006) [tier=easy] + * st_ol_0006.pgc - 정산금 netting 계산 (ST_OL_0006) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매출 - 환불 - 수수료 - 조정 을 상계(netting)하여 최종 지급 정산금을 + * 산출한다. 음수(가맹점 채무) 방향도 부호로 반환한다. */ #include #include @@ -92,34 +92,43 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0006.pgc" + TX_SERVICE(ST_OL_0006, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long gross = 0, refund = 0, fee = 0, adj = 0, net; + const char *dir; - tx_log(TX_LOG_INFO, "[ST_OL_0006] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0006] 정산금 netting 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0006] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "GROSS", &gross); + tx_buf_getlong(ctx->in, "REFUND", &refund); + tx_buf_getlong(ctx->in, "FEE", &fee); + tx_buf_getlong(ctx->in, "ADJ", &adj); + + if (gross < 0 || refund < 0 || fee < 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0006] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + net = gross - refund - fee + adj; + dir = (net >= 0) ? "PAY" : "CLAWBACK"; /* 지급 / 회수 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0006] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "GROSS", gross); + tx_buf_setlong(ctx->out, "REFUND", refund); + tx_buf_setlong(ctx->out, "FEE", fee); + tx_buf_setlong(ctx->out, "NET", net); + tx_buf_sets(ctx->out, "DIR", dir); + tx_log(TX_LOG_INFO, "[ST_OL_0006] netting gross=%ld refund=%ld fee=%ld net=%ld(%s)", + gross, refund, fee, net, dir); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0006.pgc b/app/online/st_ol_0006.pgc index 120574b..61d6ea4 100644 --- a/app/online/st_ol_0006.pgc +++ b/app/online/st_ol_0006.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0006.pgc - 정산수수료 온라인 서비스 (ST_OL_0006) [tier=easy] + * st_ol_0006.pgc - 정산금 netting 계산 (ST_OL_0006) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매출 - 환불 - 수수료 - 조정 을 상계(netting)하여 최종 지급 정산금을 + * 산출한다. 음수(가맹점 채무) 방향도 부호로 반환한다. */ #include #include @@ -14,34 +14,43 @@ EXEC SQL INCLUDE sqlca; + TX_SERVICE(ST_OL_0006, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long gross = 0, refund = 0, fee = 0, adj = 0, net; + const char *dir; - tx_log(TX_LOG_INFO, "[ST_OL_0006] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0006] 정산금 netting 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0006] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "GROSS", &gross); + tx_buf_getlong(ctx->in, "REFUND", &refund); + tx_buf_getlong(ctx->in, "FEE", &fee); + tx_buf_getlong(ctx->in, "ADJ", &adj); + + if (gross < 0 || refund < 0 || fee < 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0006] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + net = gross - refund - fee + adj; + dir = (net >= 0) ? "PAY" : "CLAWBACK"; /* 지급 / 회수 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0006] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "GROSS", gross); + tx_buf_setlong(ctx->out, "REFUND", refund); + tx_buf_setlong(ctx->out, "FEE", fee); + tx_buf_setlong(ctx->out, "NET", net); + tx_buf_sets(ctx->out, "DIR", dir); + tx_log(TX_LOG_INFO, "[ST_OL_0006] netting gross=%ld refund=%ld fee=%ld net=%ld(%s)", + gross, refund, fee, net, dir); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0007.c b/app/online/st_ol_0007.c index df345f7..4987b37 100644 --- a/app/online/st_ol_0007.c +++ b/app/online/st_ol_0007.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0007.pgc" /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0007.pgc - 정산수수료 온라인 서비스 (ST_OL_0007) [tier=medium] + * st_ol_0007.pgc - 정산 부가세(VAT) 처리 (ST_OL_0007) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 수수료(공급가액) 기준 부가세 10% 를 산출한다. 공급가/세액/합계를 + * 원단위 반올림으로 분해하여 세금계산서 항목을 구성한다. */ #include #include @@ -92,57 +92,44 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0007.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0007, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char merch[16]; + long fee_supply = 0, vat, total; - tx_log(TX_LOG_INFO, "[ST_OL_0007] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0007] 부가세(VAT) 처리 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0007] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0007] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0007] 영업일 오류 date=%s", bizdate); + tx_buf_getlong(ctx->in, "FEESUPPLY", &fee_supply); + if (fee_supply < 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0007] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = st_rec_count(bizdate, ST_ST_RECV); + vat = st_fee_round(fee_supply, 1000); /* 10% = 1000 만분율 */ + total = fee_supply + vat; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0007] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "SUPPLY", fee_supply); + tx_buf_setlong(ctx->out, "VAT", vat); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_log(TX_LOG_INFO, "[ST_OL_0007] VAT 공급가=%ld 세액=%ld 합계=%ld", + fee_supply, vat, total); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0007.pgc b/app/online/st_ol_0007.pgc index 020b88c..0b417cb 100644 --- a/app/online/st_ol_0007.pgc +++ b/app/online/st_ol_0007.pgc @@ -1,9 +1,9 @@ /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0007.pgc - 정산수수료 온라인 서비스 (ST_OL_0007) [tier=medium] + * st_ol_0007.pgc - 정산 부가세(VAT) 처리 (ST_OL_0007) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 수수료(공급가액) 기준 부가세 10% 를 산출한다. 공급가/세액/합계를 + * 원단위 반올림으로 분해하여 세금계산서 항목을 구성한다. */ #include #include @@ -14,57 +14,44 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0007, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char merch[16]; + long fee_supply = 0, vat, total; - tx_log(TX_LOG_INFO, "[ST_OL_0007] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0007] 부가세(VAT) 처리 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0007] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0007] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0007] 영업일 오류 date=%s", bizdate); + tx_buf_getlong(ctx->in, "FEESUPPLY", &fee_supply); + if (fee_supply < 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0007] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = st_rec_count(bizdate, ST_ST_RECV); + vat = st_fee_round(fee_supply, 1000); /* 10% = 1000 만분율 */ + total = fee_supply + vat; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0007] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "SUPPLY", fee_supply); + tx_buf_setlong(ctx->out, "VAT", vat); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_log(TX_LOG_INFO, "[ST_OL_0007] VAT 공급가=%ld 세액=%ld 합계=%ld", + fee_supply, vat, total); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0008.c b/app/online/st_ol_0008.c index e34fb53..7ac6658 100644 --- a/app/online/st_ol_0008.c +++ b/app/online/st_ol_0008.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0008.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0008.pgc - 정산수수료 온라인 서비스 (ST_OL_0008) [tier=easy] + * st_ol_0008.pgc - 정산 원천징수 계산 (ST_OL_0008) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 사업소득 원천징수 3.3%(소득세 3% + 지방소득세 0.3%) 를 산출하고 + * 원천징수 후 실지급액을 계산한다. */ #include #include @@ -92,34 +92,47 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0008.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0008, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long base = 0, income_tax, local_tax, withhold, payable; - tx_log(TX_LOG_INFO, "[ST_OL_0008] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0008] 원천징수 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0008] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "BASE", &base); + if (base < 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0008] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + income_tax = st_fee_round(base, 300); /* 소득세 3.0% */ + local_tax = st_fee_round(income_tax, 1000);/* 지방세 = 소득세의 10% */ + withhold = income_tax + local_tax; + payable = base - withhold; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "INCOMETAX", income_tax); + tx_buf_setlong(ctx->out, "LOCALTAX", local_tax); + tx_buf_setlong(ctx->out, "WITHHOLD", withhold); + tx_buf_setlong(ctx->out, "PAYABLE", payable); + tx_log(TX_LOG_INFO, "[ST_OL_0008] 원천징수 base=%ld 세액=%ld 실지급=%ld", + base, withhold, payable); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0008.pgc b/app/online/st_ol_0008.pgc index 7fde71f..fb02850 100644 --- a/app/online/st_ol_0008.pgc +++ b/app/online/st_ol_0008.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0008.pgc - 정산수수료 온라인 서비스 (ST_OL_0008) [tier=easy] + * st_ol_0008.pgc - 정산 원천징수 계산 (ST_OL_0008) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 사업소득 원천징수 3.3%(소득세 3% + 지방소득세 0.3%) 를 산출하고 + * 원천징수 후 실지급액을 계산한다. */ #include #include @@ -14,34 +14,47 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0008, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long base = 0, income_tax, local_tax, withhold, payable; - tx_log(TX_LOG_INFO, "[ST_OL_0008] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0008] 원천징수 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0008] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "BASE", &base); + if (base < 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0008] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + income_tax = st_fee_round(base, 300); /* 소득세 3.0% */ + local_tax = st_fee_round(income_tax, 1000);/* 지방세 = 소득세의 10% */ + withhold = income_tax + local_tax; + payable = base - withhold; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "INCOMETAX", income_tax); + tx_buf_setlong(ctx->out, "LOCALTAX", local_tax); + tx_buf_setlong(ctx->out, "WITHHOLD", withhold); + tx_buf_setlong(ctx->out, "PAYABLE", payable); + tx_log(TX_LOG_INFO, "[ST_OL_0008] 원천징수 base=%ld 세액=%ld 실지급=%ld", + base, withhold, payable); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0009.c b/app/online/st_ol_0009.c index c7ad37f..60855f6 100644 --- a/app/online/st_ol_0009.c +++ b/app/online/st_ol_0009.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0009.pgc" /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0009.pgc - 정산수수료 온라인 서비스 (ST_OL_0009) [tier=medium] + * st_ol_0009.pgc - 정산 조정(가감) 처리 (ST_OL_0009) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 확정 정산금에 조정사유코드별 가산/차감 조정을 반영한다. + * 차감이 정산금을 초과하면 조정 불가로 반려한다. */ #include #include @@ -92,57 +92,43 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0009.pgc" + TX_SERVICE(ST_OL_0009, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char merch[16], reason[8]; + long base = 0, adj = 0, result; + int sign; - tx_log(TX_LOG_INFO, "[ST_OL_0009] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0009] 정산 조정 처리 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0009] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) + strncpy(reason, "MANUAL", sizeof(reason) - 1); + tx_buf_getlong(ctx->in, "BASE", &base); + tx_buf_getlong(ctx->in, "ADJ", &adj); /* 부호 포함 */ - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0009] 금액 범위 오류 amount=%ld", amount); + result = base + adj; + if (result < 0) { + tx_log(TX_LOG_WARN, "[ST_OL_0009] 차감 과다 base=%ld adj=%ld", base, adj); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0009] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0009] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = st_rec_count(bizdate, ST_ST_RECV); + sign = (adj >= 0) ? 1 : -1; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0009] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "REASON", reason); + tx_buf_setlong(ctx->out, "BASE", base); + tx_buf_setlong(ctx->out, "ADJ", adj); + tx_buf_setlong(ctx->out, "RESULT", result); + tx_buf_sets(ctx->out, "ADJTYPE", (sign > 0) ? "ADD" : "SUB"); + tx_log(TX_LOG_INFO, "[ST_OL_0009] 조정 base=%ld adj=%ld -> %ld (%s)", + base, adj, result, reason); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0009.pgc b/app/online/st_ol_0009.pgc index 03eed82..49e7d00 100644 --- a/app/online/st_ol_0009.pgc +++ b/app/online/st_ol_0009.pgc @@ -1,9 +1,9 @@ /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0009.pgc - 정산수수료 온라인 서비스 (ST_OL_0009) [tier=medium] + * st_ol_0009.pgc - 정산 조정(가감) 처리 (ST_OL_0009) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 확정 정산금에 조정사유코드별 가산/차감 조정을 반영한다. + * 차감이 정산금을 초과하면 조정 불가로 반려한다. */ #include #include @@ -14,57 +14,43 @@ EXEC SQL INCLUDE sqlca; + TX_SERVICE(ST_OL_0009, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char merch[16], reason[8]; + long base = 0, adj = 0, result; + int sign; - tx_log(TX_LOG_INFO, "[ST_OL_0009] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0009] 정산 조정 처리 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0009] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) + strncpy(reason, "MANUAL", sizeof(reason) - 1); + tx_buf_getlong(ctx->in, "BASE", &base); + tx_buf_getlong(ctx->in, "ADJ", &adj); /* 부호 포함 */ - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0009] 금액 범위 오류 amount=%ld", amount); + result = base + adj; + if (result < 0) { + tx_log(TX_LOG_WARN, "[ST_OL_0009] 차감 과다 base=%ld adj=%ld", base, adj); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0009] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0009] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = st_rec_count(bizdate, ST_ST_RECV); + sign = (adj >= 0) ? 1 : -1; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0009] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "REASON", reason); + tx_buf_setlong(ctx->out, "BASE", base); + tx_buf_setlong(ctx->out, "ADJ", adj); + tx_buf_setlong(ctx->out, "RESULT", result); + tx_buf_sets(ctx->out, "ADJTYPE", (sign > 0) ? "ADD" : "SUB"); + tx_log(TX_LOG_INFO, "[ST_OL_0009] 조정 base=%ld adj=%ld -> %ld (%s)", + base, adj, result, reason); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0010.c b/app/online/st_ol_0010.c index b3ad1f1..0423b91 100644 --- a/app/online/st_ol_0010.c +++ b/app/online/st_ol_0010.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0010.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0010.pgc - 정산수수료 온라인 서비스 (ST_OL_0010) [tier=easy] + * st_ol_0010.pgc - 선정산 한도/금액 산정 (ST_OL_0010) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 예상 정산순액에 선정산 인출비율을 적용하여 선지급 가능액을 산정하고 + * 선이자(일할)를 차감한 실 선지급액을 산출한다. */ #include #include @@ -92,34 +92,53 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0010.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0010, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long expected = 0, advance_rate_bp = 0, days = 0, annual_bp = 0; + long advance, interest, payout; - tx_log(TX_LOG_INFO, "[ST_OL_0010] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0010] 선정산 산정 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0010] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "EXPECTED", &expected); + tx_buf_getlong(ctx->in, "ADVRATE", &advance_rate_bp); + tx_buf_getlong(ctx->in, "DAYS", &days); + tx_buf_getlong(ctx->in, "ANNUALBP", &annual_bp); + if (advance_rate_bp <= 0 || advance_rate_bp > 10000) advance_rate_bp = 8000; /* 80% */ + if (annual_bp <= 0) annual_bp = 700; /* 연 7% */ + if (days < 0) days = 0; + + if (expected <= 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + advance = st_fee_round(expected, advance_rate_bp); + interest = st_fee_round(advance, annual_bp) * days / 365; + payout = advance - interest; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "ADVANCE", advance); + tx_buf_setlong(ctx->out, "INTEREST", interest); + tx_buf_setlong(ctx->out, "PAYOUT", payout); + tx_log(TX_LOG_INFO, "[ST_OL_0010] 선정산 advance=%ld 선이자=%ld 지급=%ld", + advance, interest, payout); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0010.pgc b/app/online/st_ol_0010.pgc index 0897f0e..faff6a1 100644 --- a/app/online/st_ol_0010.pgc +++ b/app/online/st_ol_0010.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0010.pgc - 정산수수료 온라인 서비스 (ST_OL_0010) [tier=easy] + * st_ol_0010.pgc - 선정산 한도/금액 산정 (ST_OL_0010) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 예상 정산순액에 선정산 인출비율을 적용하여 선지급 가능액을 산정하고 + * 선이자(일할)를 차감한 실 선지급액을 산출한다. */ #include #include @@ -14,34 +14,53 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0010, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long expected = 0, advance_rate_bp = 0, days = 0, annual_bp = 0; + long advance, interest, payout; - tx_log(TX_LOG_INFO, "[ST_OL_0010] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0010] 선정산 산정 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0010] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "EXPECTED", &expected); + tx_buf_getlong(ctx->in, "ADVRATE", &advance_rate_bp); + tx_buf_getlong(ctx->in, "DAYS", &days); + tx_buf_getlong(ctx->in, "ANNUALBP", &annual_bp); + if (advance_rate_bp <= 0 || advance_rate_bp > 10000) advance_rate_bp = 8000; /* 80% */ + if (annual_bp <= 0) annual_bp = 700; /* 연 7% */ + if (days < 0) days = 0; + + if (expected <= 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + advance = st_fee_round(expected, advance_rate_bp); + interest = st_fee_round(advance, annual_bp) * days / 365; + payout = advance - interest; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "ADVANCE", advance); + tx_buf_setlong(ctx->out, "INTEREST", interest); + tx_buf_setlong(ctx->out, "PAYOUT", payout); + tx_log(TX_LOG_INFO, "[ST_OL_0010] 선정산 advance=%ld 선이자=%ld 지급=%ld", + advance, interest, payout); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0011.c b/app/online/st_ol_0011.c index ddb3732..e6d627b 100644 --- a/app/online/st_ol_0011.c +++ b/app/online/st_ol_0011.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0011.pgc" /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0011.pgc - 정산수수료 온라인 서비스 (ST_OL_0011) [tier=medium] + * st_ol_0011.pgc - 지연정산 이자 계산 (ST_OL_0011) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산 지급 지연일수에 대한 지연이자(연리 일할)를 산출한다. + * 이자 = 원금 x 연리(만분율) x 지연일수 / 365 (원단위 반올림). */ #include #include @@ -92,57 +92,49 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0011.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0011, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char merch[16]; + long principal = 0, annual_bp = 0, delay_days = 0, interest, total; - tx_log(TX_LOG_INFO, "[ST_OL_0011] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0011] 지연정산 이자 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0011] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "PRINCIPAL", &principal); + tx_buf_getlong(ctx->in, "ANNUALBP", &annual_bp); + tx_buf_getlong(ctx->in, "DELAYDAYS", &delay_days); + if (annual_bp <= 0) annual_bp = 1500; /* 지연이자 연 15% */ - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0011] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0011] 영업일 오류 date=%s", bizdate); + if (principal <= 0 || delay_days < 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0011] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = st_rec_count(bizdate, ST_ST_RECV); + interest = st_fee_round(principal, annual_bp) * delay_days / 365; + total = principal + interest; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0011] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "PRINCIPAL", principal); + tx_buf_setlong(ctx->out, "DELAYDAYS", delay_days); + tx_buf_setlong(ctx->out, "INTEREST", interest); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_log(TX_LOG_INFO, "[ST_OL_0011] 지연이자 원금=%ld 일수=%ld 이자=%ld", + principal, delay_days, interest); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0011.pgc b/app/online/st_ol_0011.pgc index 174919f..5107edc 100644 --- a/app/online/st_ol_0011.pgc +++ b/app/online/st_ol_0011.pgc @@ -1,9 +1,9 @@ /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0011.pgc - 정산수수료 온라인 서비스 (ST_OL_0011) [tier=medium] + * st_ol_0011.pgc - 지연정산 이자 계산 (ST_OL_0011) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산 지급 지연일수에 대한 지연이자(연리 일할)를 산출한다. + * 이자 = 원금 x 연리(만분율) x 지연일수 / 365 (원단위 반올림). */ #include #include @@ -14,57 +14,49 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0011, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char merch[16]; + long principal = 0, annual_bp = 0, delay_days = 0, interest, total; - tx_log(TX_LOG_INFO, "[ST_OL_0011] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0011] 지연정산 이자 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0011] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "PRINCIPAL", &principal); + tx_buf_getlong(ctx->in, "ANNUALBP", &annual_bp); + tx_buf_getlong(ctx->in, "DELAYDAYS", &delay_days); + if (annual_bp <= 0) annual_bp = 1500; /* 지연이자 연 15% */ - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0011] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0011] 영업일 오류 date=%s", bizdate); + if (principal <= 0 || delay_days < 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0011] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = st_rec_count(bizdate, ST_ST_RECV); + interest = st_fee_round(principal, annual_bp) * delay_days / 365; + total = principal + interest; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0011] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "PRINCIPAL", principal); + tx_buf_setlong(ctx->out, "DELAYDAYS", delay_days); + tx_buf_setlong(ctx->out, "INTEREST", interest); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_log(TX_LOG_INFO, "[ST_OL_0011] 지연이자 원금=%ld 일수=%ld 이자=%ld", + principal, delay_days, interest); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0012.c b/app/online/st_ol_0012.c index 6aecec0..711e817 100644 --- a/app/online/st_ol_0012.c +++ b/app/online/st_ol_0012.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0012.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0012.pgc - 정산수수료 온라인 서비스 (ST_OL_0012) [tier=easy] + * st_ol_0012.pgc - 정산 보류/해제 처리 (ST_OL_0012) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산 원장 건에 대해 지급 보류(HOLD) 또는 보류 해제(RELEASE)를 수행한다. + * 보류는 상태 U, 해제는 상태 R 로 전이한다. */ #include #include @@ -92,16 +92,18 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0012.pgc" + TX_SERVICE(ST_OL_0012, ctx) { st_rec_t rec; - char key[16]; + char key[16], action[12]; + const char *newst; int rc; - tx_log(TX_LOG_INFO, "[ST_OL_0012] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0012] 정산 보류/해제 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0012] KEY 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "ACTION", action, sizeof(action)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -109,17 +111,32 @@ TX_SERVICE(ST_OL_0012, ctx) memset(&rec, 0, sizeof(rec)); rc = st_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_return(ctx, rc, ctx->out); + return; + } + + if (strcmp(action, "HOLD") == 0) { + newst = ST_ST_FAIL; /* 보류 = U */ + } else if (strcmp(action, "RELEASE") == 0) { + newst = ST_ST_RECV; /* 해제 = R */ + } else { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + rc = st_rec_update_status(key, newst); + if (rc != TX_OK) { tx_return(ctx, rc, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "ACTION", action); + tx_buf_sets(ctx->out, "STATUS", newst); + tx_log(TX_LOG_INFO, "[ST_OL_0012] %s key=%s -> status=%s", action, key, newst); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0012.pgc b/app/online/st_ol_0012.pgc index 986e0ce..eb6d4e2 100644 --- a/app/online/st_ol_0012.pgc +++ b/app/online/st_ol_0012.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0012.pgc - 정산수수료 온라인 서비스 (ST_OL_0012) [tier=easy] + * st_ol_0012.pgc - 정산 보류/해제 처리 (ST_OL_0012) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산 원장 건에 대해 지급 보류(HOLD) 또는 보류 해제(RELEASE)를 수행한다. + * 보류는 상태 U, 해제는 상태 R 로 전이한다. */ #include #include @@ -14,16 +14,18 @@ EXEC SQL INCLUDE sqlca; + TX_SERVICE(ST_OL_0012, ctx) { st_rec_t rec; - char key[16]; + char key[16], action[12]; + const char *newst; int rc; - tx_log(TX_LOG_INFO, "[ST_OL_0012] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0012] 정산 보류/해제 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0012] KEY 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "ACTION", action, sizeof(action)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -31,17 +33,32 @@ TX_SERVICE(ST_OL_0012, ctx) memset(&rec, 0, sizeof(rec)); rc = st_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_return(ctx, rc, ctx->out); + return; + } + + if (strcmp(action, "HOLD") == 0) { + newst = ST_ST_FAIL; /* 보류 = U */ + } else if (strcmp(action, "RELEASE") == 0) { + newst = ST_ST_RECV; /* 해제 = R */ + } else { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + rc = st_rec_update_status(key, newst); + if (rc != TX_OK) { tx_return(ctx, rc, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "ACTION", action); + tx_buf_sets(ctx->out, "STATUS", newst); + tx_log(TX_LOG_INFO, "[ST_OL_0012] %s key=%s -> status=%s", action, key, newst); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0013.c b/app/online/st_ol_0013.c index f619637..d5820be 100644 --- a/app/online/st_ol_0013.c +++ b/app/online/st_ol_0013.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0013.pgc" /* @tier hard @module st @transform settlement-online */ /* - * st_ol_0013.pgc - 정산수수료 온라인 서비스 (ST_OL_0013) [tier=hard] + * st_ol_0013.pgc - 정산금 분할(N분할) 계산 (ST_OL_0013) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 총 정산금을 N 회차로 균등 분할하되, 원단위 잔여(remainder)를 마지막 + * 회차에 몰아 합계가 원금과 일치하도록 배분한다. */ #include #include @@ -92,85 +92,41 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0013.pgc" + TX_SERVICE(ST_OL_0013, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char merch[16]; + long total = 0, parts = 0, base_part, last_part, i, acc = 0; - tx_log(TX_LOG_INFO, "[ST_OL_0013] 정산수수료 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0013] 정산금 분할 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0013] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "TOTAL", &total); + tx_buf_getlong(ctx->in, "PARTS", &parts); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0013] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (total <= 0 || parts <= 0 || parts > 60) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0013] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("ST_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[ST_OL_0013] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (st_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + base_part = total / parts; + for (i = 0; i < parts - 1; i++) + acc += base_part; + last_part = total - acc; /* 잔여 마지막 회차 흡수 */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[ST_OL_0013] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_buf_setlong(ctx->out, "PARTS", parts); + tx_buf_setlong(ctx->out, "BASEPART", base_part); + tx_buf_setlong(ctx->out, "LASTPART", last_part); + tx_log(TX_LOG_INFO, "[ST_OL_0013] 분할 total=%ld parts=%ld base=%ld last=%ld", + total, parts, base_part, last_part); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0013.pgc b/app/online/st_ol_0013.pgc index 9fc6884..d6ac9ca 100644 --- a/app/online/st_ol_0013.pgc +++ b/app/online/st_ol_0013.pgc @@ -1,9 +1,9 @@ /* @tier hard @module st @transform settlement-online */ /* - * st_ol_0013.pgc - 정산수수료 온라인 서비스 (ST_OL_0013) [tier=hard] + * st_ol_0013.pgc - 정산금 분할(N분할) 계산 (ST_OL_0013) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 총 정산금을 N 회차로 균등 분할하되, 원단위 잔여(remainder)를 마지막 + * 회차에 몰아 합계가 원금과 일치하도록 배분한다. */ #include #include @@ -14,85 +14,41 @@ EXEC SQL INCLUDE sqlca; + TX_SERVICE(ST_OL_0013, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char merch[16]; + long total = 0, parts = 0, base_part, last_part, i, acc = 0; - tx_log(TX_LOG_INFO, "[ST_OL_0013] 정산수수료 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0013] 정산금 분할 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0013] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "TOTAL", &total); + tx_buf_getlong(ctx->in, "PARTS", &parts); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0013] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (total <= 0 || parts <= 0 || parts > 60) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0013] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("ST_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[ST_OL_0013] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (st_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + base_part = total / parts; + for (i = 0; i < parts - 1; i++) + acc += base_part; + last_part = total - acc; /* 잔여 마지막 회차 흡수 */ tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[ST_OL_0013] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "TOTAL", total); + tx_buf_setlong(ctx->out, "PARTS", parts); + tx_buf_setlong(ctx->out, "BASEPART", base_part); + tx_buf_setlong(ctx->out, "LASTPART", last_part); + tx_log(TX_LOG_INFO, "[ST_OL_0013] 분할 total=%ld parts=%ld base=%ld last=%ld", + total, parts, base_part, last_part); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0014.c b/app/online/st_ol_0014.c index 4d38bfb..ec5dc0c 100644 --- a/app/online/st_ol_0014.c +++ b/app/online/st_ol_0014.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0014.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0014.pgc - 정산수수료 온라인 서비스 (ST_OL_0014) [tier=easy] + * st_ol_0014.pgc - 일자별 정산 집계 조회 (ST_OL_0014) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일의 상태별 건수(R/M/S/U)와 총 거래금액을 집계하여 + * 정산 진행 현황(처리율)을 응답한다. */ #include #include @@ -92,34 +92,41 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0014.pgc" + TX_SERVICE(ST_OL_0014, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char bizdate[16]; + long c_recv, c_done, c_settled, c_fail, total = 0, sum = 0; - tx_log(TX_LOG_INFO, "[ST_OL_0014] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0014] 일자별 정산 집계 조회 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0014] KEY 누락"); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + c_recv = st_rec_count(bizdate, ST_ST_RECV); + c_done = st_rec_count(bizdate, ST_ST_DONE); + c_settled = st_rec_count(bizdate, ST_ST_SETTLED); + c_fail = st_rec_count(bizdate, ST_ST_FAIL); + if (c_recv < 0) c_recv = 0; + if (c_done < 0) c_done = 0; + if (c_settled < 0) c_settled = 0; + if (c_fail < 0) c_fail = 0; + total = c_recv + c_done + c_settled + c_fail; + st_rec_sum(bizdate, &sum); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CRECV", c_recv); + tx_buf_setlong(ctx->out, "CDONE", c_done); + tx_buf_setlong(ctx->out, "CSETTLED", c_settled); + tx_buf_setlong(ctx->out, "CFAIL", c_fail); + tx_buf_setlong(ctx->out, "TOTALCNT", total); + tx_buf_setlong(ctx->out, "SUMAMT", (sum < 0) ? 0 : sum); + tx_log(TX_LOG_INFO, "[ST_OL_0014] 집계 date=%s 총=%ld 완료=%ld 정산=%ld", + bizdate, total, c_done, c_settled); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0014.pgc b/app/online/st_ol_0014.pgc index bc1d9f1..ba893d6 100644 --- a/app/online/st_ol_0014.pgc +++ b/app/online/st_ol_0014.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0014.pgc - 정산수수료 온라인 서비스 (ST_OL_0014) [tier=easy] + * st_ol_0014.pgc - 일자별 정산 집계 조회 (ST_OL_0014) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일의 상태별 건수(R/M/S/U)와 총 거래금액을 집계하여 + * 정산 진행 현황(처리율)을 응답한다. */ #include #include @@ -14,34 +14,41 @@ EXEC SQL INCLUDE sqlca; + TX_SERVICE(ST_OL_0014, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char bizdate[16]; + long c_recv, c_done, c_settled, c_fail, total = 0, sum = 0; - tx_log(TX_LOG_INFO, "[ST_OL_0014] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0014] 일자별 정산 집계 조회 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0014] KEY 누락"); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + c_recv = st_rec_count(bizdate, ST_ST_RECV); + c_done = st_rec_count(bizdate, ST_ST_DONE); + c_settled = st_rec_count(bizdate, ST_ST_SETTLED); + c_fail = st_rec_count(bizdate, ST_ST_FAIL); + if (c_recv < 0) c_recv = 0; + if (c_done < 0) c_done = 0; + if (c_settled < 0) c_settled = 0; + if (c_fail < 0) c_fail = 0; + total = c_recv + c_done + c_settled + c_fail; + st_rec_sum(bizdate, &sum); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CRECV", c_recv); + tx_buf_setlong(ctx->out, "CDONE", c_done); + tx_buf_setlong(ctx->out, "CSETTLED", c_settled); + tx_buf_setlong(ctx->out, "CFAIL", c_fail); + tx_buf_setlong(ctx->out, "TOTALCNT", total); + tx_buf_setlong(ctx->out, "SUMAMT", (sum < 0) ? 0 : sum); + tx_log(TX_LOG_INFO, "[ST_OL_0014] 집계 date=%s 총=%ld 완료=%ld 정산=%ld", + bizdate, total, c_done, c_settled); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0015.c b/app/online/st_ol_0015.c index ff66341..f17b766 100644 --- a/app/online/st_ol_0015.c +++ b/app/online/st_ol_0015.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0015.pgc" /* @tier hard @module st @transform settlement-online */ /* - * st_ol_0015.pgc - 정산수수료 온라인 서비스 (ST_OL_0015) [tier=hard] + * st_ol_0015.pgc - 영업일 지급일(T+n) 산정 (ST_OL_0015) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 거래 영업일 기준 T+n 영업일 지급일을 산정한다. 주말/비영업일을 + * 건너뛰며 date_next_business 로 n 영업일을 전진시킨다. */ #include #include @@ -92,85 +92,38 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0015.pgc" + TX_SERVICE(ST_OL_0015, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char bizdate[16], cur[9], nxt[9]; + long nplus = 0, i; - tx_log(TX_LOG_INFO, "[ST_OL_0015] 정산수수료 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0015] 지급일 T+n 산정 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0015] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "NPLUS", &nplus); + if (nplus < 0 || nplus > 30) nplus = 2; /* 기본 T+2 */ - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0015] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + memset(cur, 0, sizeof(cur)); + strncpy(cur, bizdate, sizeof(cur) - 1); + for (i = 0; i < nplus; i++) { + if (date_next_business(cur, nxt) != 0) { + tx_log(TX_LOG_ERROR, "[ST_OL_0015] 다음영업일 계산 실패 cur=%s", cur); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + strncpy(cur, nxt, sizeof(cur) - 1); } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0015] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("ST_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[ST_OL_0015] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (st_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[ST_OL_0015] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "NPLUS", nplus); + tx_buf_sets(ctx->out, "PAYDATE", cur); + tx_log(TX_LOG_INFO, "[ST_OL_0015] T+%ld 지급일 %s -> %s", nplus, bizdate, cur); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0015.pgc b/app/online/st_ol_0015.pgc index 2b57c3c..93cbac1 100644 --- a/app/online/st_ol_0015.pgc +++ b/app/online/st_ol_0015.pgc @@ -1,9 +1,9 @@ /* @tier hard @module st @transform settlement-online */ /* - * st_ol_0015.pgc - 정산수수료 온라인 서비스 (ST_OL_0015) [tier=hard] + * st_ol_0015.pgc - 영업일 지급일(T+n) 산정 (ST_OL_0015) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 거래 영업일 기준 T+n 영업일 지급일을 산정한다. 주말/비영업일을 + * 건너뛰며 date_next_business 로 n 영업일을 전진시킨다. */ #include #include @@ -14,85 +14,38 @@ EXEC SQL INCLUDE sqlca; + TX_SERVICE(ST_OL_0015, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char bizdate[16], cur[9], nxt[9]; + long nplus = 0, i; - tx_log(TX_LOG_INFO, "[ST_OL_0015] 정산수수료 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0015] 지급일 T+n 산정 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0015] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "NPLUS", &nplus); + if (nplus < 0 || nplus > 30) nplus = 2; /* 기본 T+2 */ - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0015] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + memset(cur, 0, sizeof(cur)); + strncpy(cur, bizdate, sizeof(cur) - 1); + for (i = 0; i < nplus; i++) { + if (date_next_business(cur, nxt) != 0) { + tx_log(TX_LOG_ERROR, "[ST_OL_0015] 다음영업일 계산 실패 cur=%s", cur); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + strncpy(cur, nxt, sizeof(cur) - 1); } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0015] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("ST_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[ST_OL_0015] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (st_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[ST_OL_0015] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "NPLUS", nplus); + tx_buf_sets(ctx->out, "PAYDATE", cur); + tx_log(TX_LOG_INFO, "[ST_OL_0015] T+%ld 지급일 %s -> %s", nplus, bizdate, cur); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0016.c b/app/online/st_ol_0016.c index 6fad329..df6efe3 100644 --- a/app/online/st_ol_0016.c +++ b/app/online/st_ol_0016.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0016.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0016.pgc - 정산수수료 온라인 서비스 (ST_OL_0016) [tier=easy] + * st_ol_0016.pgc - 카드사(발급사) 정산 계산 (ST_OL_0016) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 발급 카드사로부터 수취할 정산금 = 매출총액 - 정산수수료(인터체인지). + * 인터체인지 요율(만분율)을 적용해 발급사 정산순액을 산출한다. */ #include #include @@ -92,34 +92,47 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0016.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0016, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char issuer[12]; + long gross = 0, ic_bp = 0, ic_fee, net; - tx_log(TX_LOG_INFO, "[ST_OL_0016] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0016] 카드사 정산 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0016] KEY 누락"); + if (tx_buf_get(ctx->in, "ISSUER", issuer, sizeof(issuer)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "GROSS", &gross); + tx_buf_getlong(ctx->in, "ICBP", &ic_bp); + if (ic_bp <= 0 || ic_bp > 10000) ic_bp = 120; /* 인터체인지 1.2% */ + + if (gross < 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0016] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + ic_fee = st_fee_round(gross, ic_bp); + net = gross - ic_fee; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0016] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "ISSUER", issuer); + tx_buf_setlong(ctx->out, "GROSS", gross); + tx_buf_setlong(ctx->out, "ICFEE", ic_fee); + tx_buf_setlong(ctx->out, "NET", net); + tx_log(TX_LOG_INFO, "[ST_OL_0016] 카드사 정산 issuer=%s gross=%ld fee=%ld net=%ld", + issuer, gross, ic_fee, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0016.pgc b/app/online/st_ol_0016.pgc index c98542a..71adb53 100644 --- a/app/online/st_ol_0016.pgc +++ b/app/online/st_ol_0016.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0016.pgc - 정산수수료 온라인 서비스 (ST_OL_0016) [tier=easy] + * st_ol_0016.pgc - 카드사(발급사) 정산 계산 (ST_OL_0016) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 발급 카드사로부터 수취할 정산금 = 매출총액 - 정산수수료(인터체인지). + * 인터체인지 요율(만분율)을 적용해 발급사 정산순액을 산출한다. */ #include #include @@ -14,34 +14,47 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0016, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char issuer[12]; + long gross = 0, ic_bp = 0, ic_fee, net; - tx_log(TX_LOG_INFO, "[ST_OL_0016] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0016] 카드사 정산 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0016] KEY 누락"); + if (tx_buf_get(ctx->in, "ISSUER", issuer, sizeof(issuer)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "GROSS", &gross); + tx_buf_getlong(ctx->in, "ICBP", &ic_bp); + if (ic_bp <= 0 || ic_bp > 10000) ic_bp = 120; /* 인터체인지 1.2% */ + + if (gross < 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0016] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + ic_fee = st_fee_round(gross, ic_bp); + net = gross - ic_fee; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0016] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "ISSUER", issuer); + tx_buf_setlong(ctx->out, "GROSS", gross); + tx_buf_setlong(ctx->out, "ICFEE", ic_fee); + tx_buf_setlong(ctx->out, "NET", net); + tx_log(TX_LOG_INFO, "[ST_OL_0016] 카드사 정산 issuer=%s gross=%ld fee=%ld net=%ld", + issuer, gross, ic_fee, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0017.c b/app/online/st_ol_0017.c index 1e03e94..a022514 100644 --- a/app/online/st_ol_0017.c +++ b/app/online/st_ol_0017.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0017.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0017.pgc - 정산수수료 온라인 서비스 (ST_OL_0017) [tier=easy] + * st_ol_0017.pgc - 구간요율 수수료율 적용 (ST_OL_0017) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 거래금액 구간(band)별 차등 수수료율(만분율)을 적용하여 수수료를 + * 산출한다. 소액/중액/고액 3구간 슬라브 요율 모델. */ #include #include @@ -92,34 +92,52 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0017.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0017, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long amount = 0, bp, fee, net; - tx_log(TX_LOG_INFO, "[ST_OL_0017] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0017] 구간요율 적용 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0017] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (!amount_is_valid(amount)) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0017] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + if (amount < 50000L) + bp = 250; /* 5만원 미만 2.50% */ + else if (amount < 500000L) + bp = 180; /* 50만원 미만 1.80% */ + else + bp = 140; /* 그 이상 1.40% */ + + fee = st_fee_round(amount, bp); + net = amount - fee; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0017] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_setlong(ctx->out, "BPUSED", bp); + tx_buf_setlong(ctx->out, "FEE", fee); + tx_buf_setlong(ctx->out, "NET", net); + tx_log(TX_LOG_INFO, "[ST_OL_0017] 구간요율 amount=%ld bp=%ld fee=%ld", + amount, bp, fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0017.pgc b/app/online/st_ol_0017.pgc index 95b13e1..d0cf577 100644 --- a/app/online/st_ol_0017.pgc +++ b/app/online/st_ol_0017.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0017.pgc - 정산수수료 온라인 서비스 (ST_OL_0017) [tier=easy] + * st_ol_0017.pgc - 구간요율 수수료율 적용 (ST_OL_0017) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 거래금액 구간(band)별 차등 수수료율(만분율)을 적용하여 수수료를 + * 산출한다. 소액/중액/고액 3구간 슬라브 요율 모델. */ #include #include @@ -14,34 +14,52 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0017, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long amount = 0, bp, fee, net; - tx_log(TX_LOG_INFO, "[ST_OL_0017] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0017] 구간요율 적용 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0017] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (!amount_is_valid(amount)) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0017] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + if (amount < 50000L) + bp = 250; /* 5만원 미만 2.50% */ + else if (amount < 500000L) + bp = 180; /* 50만원 미만 1.80% */ + else + bp = 140; /* 그 이상 1.40% */ + + fee = st_fee_round(amount, bp); + net = amount - fee; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0017] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_setlong(ctx->out, "BPUSED", bp); + tx_buf_setlong(ctx->out, "FEE", fee); + tx_buf_setlong(ctx->out, "NET", net); + tx_log(TX_LOG_INFO, "[ST_OL_0017] 구간요율 amount=%ld bp=%ld fee=%ld", + amount, bp, fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0018.c b/app/online/st_ol_0018.c index 48a00b9..a0eb409 100644 --- a/app/online/st_ol_0018.c +++ b/app/online/st_ol_0018.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0018.pgc" /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0018.pgc - 정산수수료 온라인 서비스 (ST_OL_0018) [tier=medium] + * st_ol_0018.pgc - 정산 상계(offset) 처리 (ST_OL_0018) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 채권(수취)과 채무(지급)를 상계하여 잔액과 방향을 산출한다. + * 상계 후 잔여채권/잔여채무를 구분해 반환한다. */ #include #include @@ -92,57 +92,39 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0018.pgc" + TX_SERVICE(ST_OL_0018, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char merch[16]; + long receivable = 0, payable = 0, offset, residual; + const char *dir; - tx_log(TX_LOG_INFO, "[ST_OL_0018] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0018] 정산 상계 처리 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0018] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0018] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0018] 영업일 오류 date=%s", bizdate); + tx_buf_getlong(ctx->in, "RECEIVABLE", &receivable); + tx_buf_getlong(ctx->in, "PAYABLE", &payable); + if (receivable < 0 || payable < 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0018] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = st_rec_count(bizdate, ST_ST_RECV); + offset = (receivable < payable) ? receivable : payable; + residual = receivable - payable; + dir = (residual > 0) ? "RECV" : (residual < 0 ? "PAY" : "EVEN"); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0018] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "OFFSET", offset); + tx_buf_setlong(ctx->out, "RESIDUAL", residual); + tx_buf_sets(ctx->out, "DIR", dir); + tx_log(TX_LOG_INFO, "[ST_OL_0018] 상계 recv=%ld pay=%ld offset=%ld 잔액=%ld(%s)", + receivable, payable, offset, residual, dir); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0018.pgc b/app/online/st_ol_0018.pgc index 9fec94c..e73ce00 100644 --- a/app/online/st_ol_0018.pgc +++ b/app/online/st_ol_0018.pgc @@ -1,9 +1,9 @@ /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0018.pgc - 정산수수료 온라인 서비스 (ST_OL_0018) [tier=medium] + * st_ol_0018.pgc - 정산 상계(offset) 처리 (ST_OL_0018) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 채권(수취)과 채무(지급)를 상계하여 잔액과 방향을 산출한다. + * 상계 후 잔여채권/잔여채무를 구분해 반환한다. */ #include #include @@ -14,57 +14,39 @@ EXEC SQL INCLUDE sqlca; + TX_SERVICE(ST_OL_0018, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char merch[16]; + long receivable = 0, payable = 0, offset, residual; + const char *dir; - tx_log(TX_LOG_INFO, "[ST_OL_0018] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0018] 정산 상계 처리 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0018] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0018] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0018] 영업일 오류 date=%s", bizdate); + tx_buf_getlong(ctx->in, "RECEIVABLE", &receivable); + tx_buf_getlong(ctx->in, "PAYABLE", &payable); + if (receivable < 0 || payable < 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0018] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = st_rec_count(bizdate, ST_ST_RECV); + offset = (receivable < payable) ? receivable : payable; + residual = receivable - payable; + dir = (residual > 0) ? "RECV" : (residual < 0 ? "PAY" : "EVEN"); tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0018] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "OFFSET", offset); + tx_buf_setlong(ctx->out, "RESIDUAL", residual); + tx_buf_sets(ctx->out, "DIR", dir); + tx_log(TX_LOG_INFO, "[ST_OL_0018] 상계 recv=%ld pay=%ld offset=%ld 잔액=%ld(%s)", + receivable, payable, offset, residual, dir); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0019.c b/app/online/st_ol_0019.c index 0daba17..ee38c8d 100644 --- a/app/online/st_ol_0019.c +++ b/app/online/st_ol_0019.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0019.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0019.pgc - 정산수수료 온라인 서비스 (ST_OL_0019) [tier=easy] + * st_ol_0019.pgc - 정산 diff 검증(예상 vs 실제) (ST_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 표준요율로 재산출한 예상 수수료와 원장에 기록된 실제 수수료의 차이를 + * 검증한다. 허용오차(tolerance)를 넘으면 불일치로 판정한다. */ #include #include @@ -92,34 +92,52 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0019.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0019, ctx) { st_rec_t rec; char key[16]; - int rc; + long std_bp = 0, tol = 0, expected, diff; + int rc, mismatch; - tx_log(TX_LOG_INFO, "[ST_OL_0019] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0019] 정산 diff 검증 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0019] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + tx_buf_getlong(ctx->in, "STDBP", &std_bp); + tx_buf_getlong(ctx->in, "TOL", &tol); + if (std_bp <= 0) std_bp = 180; /* 표준요율 1.80% */ + if (tol < 0) tol = 0; memset(&rec, 0, sizeof(rec)); rc = st_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + expected = st_fee_round(rec.amount, std_bp); + diff = rec.fee - expected; + mismatch = ((diff < 0 ? -diff : diff) > tol); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", mismatch ? RESP_INVALID : RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "EXPECTED", expected); + tx_buf_setlong(ctx->out, "ACTUAL", rec.fee); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "VERDICT", mismatch ? "MISMATCH" : "OK"); + tx_log(TX_LOG_INFO, "[ST_OL_0019] diff key=%s 예상=%ld 실제=%ld diff=%ld %s", + key, expected, rec.fee, diff, mismatch ? "불일치" : "일치"); + tx_return(ctx, mismatch ? TX_FAIL : TX_OK, ctx->out); } diff --git a/app/online/st_ol_0019.pgc b/app/online/st_ol_0019.pgc index b331590..e86df81 100644 --- a/app/online/st_ol_0019.pgc +++ b/app/online/st_ol_0019.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0019.pgc - 정산수수료 온라인 서비스 (ST_OL_0019) [tier=easy] + * st_ol_0019.pgc - 정산 diff 검증(예상 vs 실제) (ST_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 표준요율로 재산출한 예상 수수료와 원장에 기록된 실제 수수료의 차이를 + * 검증한다. 허용오차(tolerance)를 넘으면 불일치로 판정한다. */ #include #include @@ -14,34 +14,52 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0019, ctx) { st_rec_t rec; char key[16]; - int rc; + long std_bp = 0, tol = 0, expected, diff; + int rc, mismatch; - tx_log(TX_LOG_INFO, "[ST_OL_0019] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0019] 정산 diff 검증 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0019] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + tx_buf_getlong(ctx->in, "STDBP", &std_bp); + tx_buf_getlong(ctx->in, "TOL", &tol); + if (std_bp <= 0) std_bp = 180; /* 표준요율 1.80% */ + if (tol < 0) tol = 0; memset(&rec, 0, sizeof(rec)); rc = st_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } + expected = st_fee_round(rec.amount, std_bp); + diff = rec.fee - expected; + mismatch = ((diff < 0 ? -diff : diff) > tol); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); - tx_return(ctx, TX_OK, ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", mismatch ? RESP_INVALID : RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "EXPECTED", expected); + tx_buf_setlong(ctx->out, "ACTUAL", rec.fee); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_sets(ctx->out, "VERDICT", mismatch ? "MISMATCH" : "OK"); + tx_log(TX_LOG_INFO, "[ST_OL_0019] diff key=%s 예상=%ld 실제=%ld diff=%ld %s", + key, expected, rec.fee, diff, mismatch ? "불일치" : "일치"); + tx_return(ctx, mismatch ? TX_FAIL : TX_OK, ctx->out); } diff --git a/app/online/st_ol_0020.c b/app/online/st_ol_0020.c index 7a37415..b900893 100644 --- a/app/online/st_ol_0020.c +++ b/app/online/st_ol_0020.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0020.pgc" /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0020.pgc - 정산수수료 온라인 서비스 (ST_OL_0020) [tier=medium] + * st_ol_0020.pgc - 정산 수수료 재계산 (ST_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 건을 조회하여 표준요율로 수수료를 재계산하고, 재계산 전/후 + * 수수료와 정산순액 변동분(delta)을 응답한다. */ #include #include @@ -92,57 +92,50 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0020.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0020, ctx) { st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + long std_bp = 0, new_fee, delta, new_net; int rc; - tx_log(TX_LOG_INFO, "[ST_OL_0020] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0020] 정산 재계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0020] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0020] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0020] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } + tx_buf_getlong(ctx->in, "STDBP", &std_bp); + if (std_bp <= 0) std_bp = 180; memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); + rc = st_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0020] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = st_rec_count(bizdate, ST_ST_RECV); + new_fee = st_fee_round(rec.amount, std_bp); + delta = new_fee - rec.fee; + new_net = rec.amount - new_fee; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0020] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "OLDFEE", rec.fee); + tx_buf_setlong(ctx->out, "NEWFEE", new_fee); + tx_buf_setlong(ctx->out, "DELTA", delta); + tx_buf_setlong(ctx->out, "NEWNET", new_net); + tx_log(TX_LOG_INFO, "[ST_OL_0020] 재계산 key=%s old=%ld new=%ld delta=%ld", + key, rec.fee, new_fee, delta); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0020.pgc b/app/online/st_ol_0020.pgc index ad13a2d..14c8fa4 100644 --- a/app/online/st_ol_0020.pgc +++ b/app/online/st_ol_0020.pgc @@ -1,9 +1,9 @@ /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0020.pgc - 정산수수료 온라인 서비스 (ST_OL_0020) [tier=medium] + * st_ol_0020.pgc - 정산 수수료 재계산 (ST_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 건을 조회하여 표준요율로 수수료를 재계산하고, 재계산 전/후 + * 수수료와 정산순액 변동분(delta)을 응답한다. */ #include #include @@ -14,57 +14,50 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0020, ctx) { st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + long std_bp = 0, new_fee, delta, new_net; int rc; - tx_log(TX_LOG_INFO, "[ST_OL_0020] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0020] 정산 재계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0020] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0020] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0020] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } + tx_buf_getlong(ctx->in, "STDBP", &std_bp); + if (std_bp <= 0) std_bp = 180; memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); + rc = st_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0020] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = st_rec_count(bizdate, ST_ST_RECV); + new_fee = st_fee_round(rec.amount, std_bp); + delta = new_fee - rec.fee; + new_net = rec.amount - new_fee; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0020] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "OLDFEE", rec.fee); + tx_buf_setlong(ctx->out, "NEWFEE", new_fee); + tx_buf_setlong(ctx->out, "DELTA", delta); + tx_buf_setlong(ctx->out, "NEWNET", new_net); + tx_log(TX_LOG_INFO, "[ST_OL_0020] 재계산 key=%s old=%ld new=%ld delta=%ld", + key, rec.fee, new_fee, delta); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0021.c b/app/online/st_ol_0021.c index 7224485..c91ecd4 100644 --- a/app/online/st_ol_0021.c +++ b/app/online/st_ol_0021.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0021.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0021.pgc - 정산수수료 온라인 서비스 (ST_OL_0021) [tier=easy] + * st_ol_0021.pgc - 정산 취소 처리 (ST_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산 원장 건을 취소한다. 이미 정산완료(S) 건은 취소 불가로 반려하고, + * 그 외 상태는 취소(U)로 전이한다. */ #include #include @@ -92,16 +92,16 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0021.pgc" + TX_SERVICE(ST_OL_0021, ctx) { st_rec_t rec; char key[16]; int rc; - tx_log(TX_LOG_INFO, "[ST_OL_0021] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0021] 정산 취소 처리 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0021] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -109,17 +109,29 @@ TX_SERVICE(ST_OL_0021, ctx) memset(&rec, 0, sizeof(rec)); rc = st_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_return(ctx, rc, ctx->out); + return; + } + + if (strcmp(rec.status, ST_ST_SETTLED) == 0) { + tx_log(TX_LOG_WARN, "[ST_OL_0021] 정산완료 건 취소불가 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "REASON", "ALREADY_SETTLED"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + rc = st_rec_update_status(key, ST_ST_FAIL); + if (rc != TX_OK) { tx_return(ctx, rc, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", ST_ST_FAIL); + tx_log(TX_LOG_INFO, "[ST_OL_0021] 취소완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0021.pgc b/app/online/st_ol_0021.pgc index 0afc3a0..8958598 100644 --- a/app/online/st_ol_0021.pgc +++ b/app/online/st_ol_0021.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0021.pgc - 정산수수료 온라인 서비스 (ST_OL_0021) [tier=easy] + * st_ol_0021.pgc - 정산 취소 처리 (ST_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산 원장 건을 취소한다. 이미 정산완료(S) 건은 취소 불가로 반려하고, + * 그 외 상태는 취소(U)로 전이한다. */ #include #include @@ -14,16 +14,16 @@ EXEC SQL INCLUDE sqlca; + TX_SERVICE(ST_OL_0021, ctx) { st_rec_t rec; char key[16]; int rc; - tx_log(TX_LOG_INFO, "[ST_OL_0021] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0021] 정산 취소 처리 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0021] KEY 누락"); tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -31,17 +31,29 @@ TX_SERVICE(ST_OL_0021, ctx) memset(&rec, 0, sizeof(rec)); rc = st_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_return(ctx, rc, ctx->out); + return; + } + + if (strcmp(rec.status, ST_ST_SETTLED) == 0) { + tx_log(TX_LOG_WARN, "[ST_OL_0021] 정산완료 건 취소불가 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "REASON", "ALREADY_SETTLED"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + rc = st_rec_update_status(key, ST_ST_FAIL); + if (rc != TX_OK) { tx_return(ctx, rc, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", ST_ST_FAIL); + tx_log(TX_LOG_INFO, "[ST_OL_0021] 취소완료 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0022.c b/app/online/st_ol_0022.c index 0c3e338..4370356 100644 --- a/app/online/st_ol_0022.c +++ b/app/online/st_ol_0022.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0022.pgc" /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0022.pgc - 정산수수료 온라인 서비스 (ST_OL_0022) [tier=medium] + * st_ol_0022.pgc - 정산 통계 조회 (ST_OL_0022) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일의 정산 통계를 산출한다. 총건수/정산완료건수 기반 + * 정산완료율(bp) 과 건당 평균 거래금액을 계산한다. */ #include #include @@ -92,57 +92,43 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0022.pgc" + TX_SERVICE(ST_OL_0022, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char bizdate[16]; + long total, settled, sum = 0, rate_bp = 0, avg = 0; - tx_log(TX_LOG_INFO, "[ST_OL_0022] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0022] 정산 통계 조회 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0022] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0022] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0022] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + total = st_rec_count(bizdate, ST_ST_RECV) + + st_rec_count(bizdate, ST_ST_DONE) + + st_rec_count(bizdate, ST_ST_SETTLED) + + st_rec_count(bizdate, ST_ST_FAIL); + settled = st_rec_count(bizdate, ST_ST_SETTLED); + if (total < 0) total = 0; + if (settled < 0) settled = 0; + st_rec_sum(bizdate, &sum); + if (sum < 0) sum = 0; - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0022] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + if (total > 0) { + rate_bp = settled * 10000 / total; + avg = sum / total; } - cnt = st_rec_count(bizdate, ST_ST_RECV); - tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0022] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "TOTALCNT", total); + tx_buf_setlong(ctx->out, "SETTLEDCNT", settled); + tx_buf_setlong(ctx->out, "SETTLERATEBP", rate_bp); + tx_buf_setlong(ctx->out, "AVGAMT", avg); + tx_log(TX_LOG_INFO, "[ST_OL_0022] 통계 date=%s 총=%ld 정산율bp=%ld 평균=%ld", + bizdate, total, rate_bp, avg); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0022.pgc b/app/online/st_ol_0022.pgc index 8bf3505..69de96b 100644 --- a/app/online/st_ol_0022.pgc +++ b/app/online/st_ol_0022.pgc @@ -1,9 +1,9 @@ /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0022.pgc - 정산수수료 온라인 서비스 (ST_OL_0022) [tier=medium] + * st_ol_0022.pgc - 정산 통계 조회 (ST_OL_0022) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일의 정산 통계를 산출한다. 총건수/정산완료건수 기반 + * 정산완료율(bp) 과 건당 평균 거래금액을 계산한다. */ #include #include @@ -14,57 +14,43 @@ EXEC SQL INCLUDE sqlca; + TX_SERVICE(ST_OL_0022, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char bizdate[16]; + long total, settled, sum = 0, rate_bp = 0, avg = 0; - tx_log(TX_LOG_INFO, "[ST_OL_0022] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0022] 정산 통계 조회 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0022] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0022] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0022] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + total = st_rec_count(bizdate, ST_ST_RECV) + + st_rec_count(bizdate, ST_ST_DONE) + + st_rec_count(bizdate, ST_ST_SETTLED) + + st_rec_count(bizdate, ST_ST_FAIL); + settled = st_rec_count(bizdate, ST_ST_SETTLED); + if (total < 0) total = 0; + if (settled < 0) settled = 0; + st_rec_sum(bizdate, &sum); + if (sum < 0) sum = 0; - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0022] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + if (total > 0) { + rate_bp = settled * 10000 / total; + avg = sum / total; } - cnt = st_rec_count(bizdate, ST_ST_RECV); - tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0022] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "TOTALCNT", total); + tx_buf_setlong(ctx->out, "SETTLEDCNT", settled); + tx_buf_setlong(ctx->out, "SETTLERATEBP", rate_bp); + tx_buf_setlong(ctx->out, "AVGAMT", avg); + tx_log(TX_LOG_INFO, "[ST_OL_0022] 통계 date=%s 총=%ld 정산율bp=%ld 평균=%ld", + bizdate, total, rate_bp, avg); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0023.c b/app/online/st_ol_0023.c index 3353dd3..f45ed76 100644 --- a/app/online/st_ol_0023.c +++ b/app/online/st_ol_0023.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0023.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0023.pgc - 정산수수료 온라인 서비스 (ST_OL_0023) [tier=easy] + * st_ol_0023.pgc - 정산 이체연계 요청 (ST_OL_0023) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 확정 정산금에 대한 은행 이체(펌뱅킹) 연계 요청 전문을 구성한다. + * 계좌/은행/금액 검증 후 이체 요청 식별자를 생성한다. */ #include #include @@ -92,34 +92,42 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0023.pgc" + TX_SERVICE(ST_OL_0023, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16], bank[8], acct[24]; + long amount = 0; + char reqid[32]; - tx_log(TX_LOG_INFO, "[ST_OL_0023] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0023] 정산 이체연계 요청 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0023] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "BANK", bank, sizeof(bank)) != TX_OK || + tx_buf_get(ctx->in, "ACCT", acct, sizeof(acct)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + if (amount <= 0 || strlen(acct) < 6) { + tx_log(TX_LOG_WARN, "[ST_OL_0023] 이체 요청 검증 실패 amt=%ld acct=%s", amount, acct); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + snprintf(reqid, sizeof(reqid), "TR%s%03ld", bank, (long)(amount % 1000)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "BANK", bank); + tx_buf_sets(ctx->out, "ACCT", acct); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "XFERSTATE", "REQUESTED"); + tx_log(TX_LOG_INFO, "[ST_OL_0023] 이체요청 merch=%s amt=%ld reqid=%s", + merch, amount, reqid); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0023.pgc b/app/online/st_ol_0023.pgc index 70e481c..84cb0f4 100644 --- a/app/online/st_ol_0023.pgc +++ b/app/online/st_ol_0023.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0023.pgc - 정산수수료 온라인 서비스 (ST_OL_0023) [tier=easy] + * st_ol_0023.pgc - 정산 이체연계 요청 (ST_OL_0023) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 확정 정산금에 대한 은행 이체(펌뱅킹) 연계 요청 전문을 구성한다. + * 계좌/은행/금액 검증 후 이체 요청 식별자를 생성한다. */ #include #include @@ -14,34 +14,42 @@ EXEC SQL INCLUDE sqlca; + TX_SERVICE(ST_OL_0023, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16], bank[8], acct[24]; + long amount = 0; + char reqid[32]; - tx_log(TX_LOG_INFO, "[ST_OL_0023] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0023] 정산 이체연계 요청 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0023] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "BANK", bank, sizeof(bank)) != TX_OK || + tx_buf_get(ctx->in, "ACCT", acct, sizeof(acct)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + + if (amount <= 0 || strlen(acct) < 6) { + tx_log(TX_LOG_WARN, "[ST_OL_0023] 이체 요청 검증 실패 amt=%ld acct=%s", amount, acct); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + snprintf(reqid, sizeof(reqid), "TR%s%03ld", bank, (long)(amount % 1000)); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "BANK", bank); + tx_buf_sets(ctx->out, "ACCT", acct); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_sets(ctx->out, "REQID", reqid); + tx_buf_sets(ctx->out, "XFERSTATE", "REQUESTED"); + tx_log(TX_LOG_INFO, "[ST_OL_0023] 이체요청 merch=%s amt=%ld reqid=%s", + merch, amount, reqid); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0024.c b/app/online/st_ol_0024.c index 25f8641..d830711 100644 --- a/app/online/st_ol_0024.c +++ b/app/online/st_ol_0024.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0024.pgc" /* @tier hard @module st @transform settlement-online */ /* - * st_ol_0024.pgc - 정산수수료 온라인 서비스 (ST_OL_0024) [tier=hard] + * st_ol_0024.pgc - 정산 리포트 헤더 생성 (ST_OL_0024) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일의 정산 리포트 헤더(총건수/총매출/총수수료/총순액)를 + * 집계 구성한다. 수수료는 표준요율로 추정 합산한다. */ #include #include @@ -92,85 +92,49 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0024.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0024, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char bizdate[16]; + long total, sum = 0, std_bp = 0, est_fee, net; - tx_log(TX_LOG_INFO, "[ST_OL_0024] 정산수수료 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0024] 정산 리포트 헤더 생성 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0024] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "STDBP", &std_bp); + if (std_bp <= 0) std_bp = 180; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0024] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } + total = st_rec_count(bizdate, ST_ST_RECV) + + st_rec_count(bizdate, ST_ST_DONE) + + st_rec_count(bizdate, ST_ST_SETTLED) + + st_rec_count(bizdate, ST_ST_FAIL); + if (total < 0) total = 0; + st_rec_sum(bizdate, &sum); + if (sum < 0) sum = 0; - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0024] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("ST_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[ST_OL_0024] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (st_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + est_fee = st_fee_round(sum, std_bp); + net = sum - est_fee; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[ST_OL_0024] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "REPORTID", bizdate); + tx_buf_setlong(ctx->out, "TOTALCNT", total); + tx_buf_setlong(ctx->out, "GROSSSUM", sum); + tx_buf_setlong(ctx->out, "FEESUM", est_fee); + tx_buf_setlong(ctx->out, "NETSUM", net); + tx_log(TX_LOG_INFO, "[ST_OL_0024] 리포트 date=%s 건수=%ld 매출=%ld 수수료=%ld 순액=%ld", + bizdate, total, sum, est_fee, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0024.pgc b/app/online/st_ol_0024.pgc index 148e822..e28c256 100644 --- a/app/online/st_ol_0024.pgc +++ b/app/online/st_ol_0024.pgc @@ -1,9 +1,9 @@ /* @tier hard @module st @transform settlement-online */ /* - * st_ol_0024.pgc - 정산수수료 온라인 서비스 (ST_OL_0024) [tier=hard] + * st_ol_0024.pgc - 정산 리포트 헤더 생성 (ST_OL_0024) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 지정 영업일의 정산 리포트 헤더(총건수/총매출/총수수료/총순액)를 + * 집계 구성한다. 수수료는 표준요율로 추정 합산한다. */ #include #include @@ -14,85 +14,49 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0024, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + char bizdate[16]; + long total, sum = 0, std_bp = 0, est_fee, net; - tx_log(TX_LOG_INFO, "[ST_OL_0024] 정산수수료 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0024] 정산 리포트 헤더 생성 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0024] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "STDBP", &std_bp); + if (std_bp <= 0) std_bp = 180; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0024] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } + total = st_rec_count(bizdate, ST_ST_RECV) + + st_rec_count(bizdate, ST_ST_DONE) + + st_rec_count(bizdate, ST_ST_SETTLED) + + st_rec_count(bizdate, ST_ST_FAIL); + if (total < 0) total = 0; + st_rec_sum(bizdate, &sum); + if (sum < 0) sum = 0; - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0024] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("ST_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[ST_OL_0024] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (st_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + est_fee = st_fee_round(sum, std_bp); + net = sum - est_fee; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[ST_OL_0024] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "REPORTID", bizdate); + tx_buf_setlong(ctx->out, "TOTALCNT", total); + tx_buf_setlong(ctx->out, "GROSSSUM", sum); + tx_buf_setlong(ctx->out, "FEESUM", est_fee); + tx_buf_setlong(ctx->out, "NETSUM", net); + tx_log(TX_LOG_INFO, "[ST_OL_0024] 리포트 date=%s 건수=%ld 매출=%ld 수수료=%ld 순액=%ld", + bizdate, total, sum, est_fee, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0025.c b/app/online/st_ol_0025.c index f2623c7..5a75d0e 100644 --- a/app/online/st_ol_0025.c +++ b/app/online/st_ol_0025.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0025.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0025.pgc - 정산수수료 온라인 서비스 (ST_OL_0025) [tier=easy] + * st_ol_0025.pgc - 정산 원장연계 기표 (ST_OL_0025) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산 확정건을 회계원장에 연계 기표한다. 차변 미지급금 / 대변 수수료수익 + * + 부가세예수금 으로 분개하여 대차 균형을 검증한다. */ #include #include @@ -92,34 +92,48 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0025.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0025, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long net = 0, fee = 0, vat, dr, cr; - tx_log(TX_LOG_INFO, "[ST_OL_0025] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0025] 정산 원장연계 기표 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0025] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "NET", &net); + tx_buf_getlong(ctx->in, "FEE", &fee); + if (net < 0 || fee < 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0025] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + vat = st_fee_round(fee, 1000); /* 수수료수익의 부가세 10% */ + dr = net + fee + vat; /* 차변: 미지급금(정산부채) */ + cr = net + fee + vat; /* 대변: 예수금 + 수수료수익 + VAT예수금 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0025] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "DRAMT", dr); + tx_buf_setlong(ctx->out, "CRAMT", cr); + tx_buf_setlong(ctx->out, "FEEREV", fee); + tx_buf_setlong(ctx->out, "VATPAYABLE", vat); + tx_buf_sets(ctx->out, "BALANCED", (dr == cr) ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[ST_OL_0025] 기표 merch=%s 차변=%ld 대변=%ld 균형=%s", + merch, dr, cr, (dr == cr) ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0025.pgc b/app/online/st_ol_0025.pgc index 8a5d997..6892726 100644 --- a/app/online/st_ol_0025.pgc +++ b/app/online/st_ol_0025.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0025.pgc - 정산수수료 온라인 서비스 (ST_OL_0025) [tier=easy] + * st_ol_0025.pgc - 정산 원장연계 기표 (ST_OL_0025) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산 확정건을 회계원장에 연계 기표한다. 차변 미지급금 / 대변 수수료수익 + * + 부가세예수금 으로 분개하여 대차 균형을 검증한다. */ #include #include @@ -14,34 +14,48 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0025, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long net = 0, fee = 0, vat, dr, cr; - tx_log(TX_LOG_INFO, "[ST_OL_0025] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0025] 정산 원장연계 기표 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0025] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "NET", &net); + tx_buf_getlong(ctx->in, "FEE", &fee); + if (net < 0 || fee < 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0025] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + vat = st_fee_round(fee, 1000); /* 수수료수익의 부가세 10% */ + dr = net + fee + vat; /* 차변: 미지급금(정산부채) */ + cr = net + fee + vat; /* 대변: 예수금 + 수수료수익 + VAT예수금 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0025] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "DRAMT", dr); + tx_buf_setlong(ctx->out, "CRAMT", cr); + tx_buf_setlong(ctx->out, "FEEREV", fee); + tx_buf_setlong(ctx->out, "VATPAYABLE", vat); + tx_buf_sets(ctx->out, "BALANCED", (dr == cr) ? "Y" : "N"); + tx_log(TX_LOG_INFO, "[ST_OL_0025] 기표 merch=%s 차변=%ld 대변=%ld 균형=%s", + merch, dr, cr, (dr == cr) ? "Y" : "N"); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0026.c b/app/online/st_ol_0026.c index 80eb41d..bd4a40b 100644 --- a/app/online/st_ol_0026.c +++ b/app/online/st_ol_0026.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0026.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0026.pgc - 정산수수료 온라인 서비스 (ST_OL_0026) [tier=easy] + * st_ol_0026.pgc - 캐시백/리워드 수수료 계산 (ST_OL_0026) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매출금액에 리워드 적립요율(만분율)을 적용해 캐시백을 산출하되, + * 회차 적립한도(cap)로 상한을 적용한다. */ #include #include @@ -92,34 +92,49 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0026.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0026, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long amount = 0, reward_bp = 0, cap = 0, cashback; - tx_log(TX_LOG_INFO, "[ST_OL_0026] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0026] 캐시백/리워드 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0026] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "REWARDBP", &reward_bp); + tx_buf_getlong(ctx->in, "CAP", &cap); + if (reward_bp <= 0) reward_bp = 100; /* 1% 적립 */ + if (cap <= 0) cap = 20000; /* 회차 한도 2만원 */ + + if (!amount_is_valid(amount)) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0026] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + cashback = st_fee_round(amount, reward_bp); + if (cashback > cap) cashback = cap; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0026] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_setlong(ctx->out, "CASHBACK", cashback); + tx_buf_setlong(ctx->out, "CAP", cap); + tx_log(TX_LOG_INFO, "[ST_OL_0026] 캐시백 amount=%ld bp=%ld cashback=%ld", + amount, reward_bp, cashback); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0026.pgc b/app/online/st_ol_0026.pgc index c3d8926..53073e8 100644 --- a/app/online/st_ol_0026.pgc +++ b/app/online/st_ol_0026.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0026.pgc - 정산수수료 온라인 서비스 (ST_OL_0026) [tier=easy] + * st_ol_0026.pgc - 캐시백/리워드 수수료 계산 (ST_OL_0026) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매출금액에 리워드 적립요율(만분율)을 적용해 캐시백을 산출하되, + * 회차 적립한도(cap)로 상한을 적용한다. */ #include #include @@ -14,34 +14,49 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0026, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long amount = 0, reward_bp = 0, cap = 0, cashback; - tx_log(TX_LOG_INFO, "[ST_OL_0026] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0026] 캐시백/리워드 계산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0026] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "REWARDBP", &reward_bp); + tx_buf_getlong(ctx->in, "CAP", &cap); + if (reward_bp <= 0) reward_bp = 100; /* 1% 적립 */ + if (cap <= 0) cap = 20000; /* 회차 한도 2만원 */ + + if (!amount_is_valid(amount)) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0026] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + cashback = st_fee_round(amount, reward_bp); + if (cashback > cap) cashback = cap; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0026] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_setlong(ctx->out, "CASHBACK", cashback); + tx_buf_setlong(ctx->out, "CAP", cap); + tx_log(TX_LOG_INFO, "[ST_OL_0026] 캐시백 amount=%ld bp=%ld cashback=%ld", + amount, reward_bp, cashback); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0027.c b/app/online/st_ol_0027.c index d73f389..b88ca04 100644 --- a/app/online/st_ol_0027.c +++ b/app/online/st_ol_0027.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0027.pgc" /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0027.pgc - 정산수수료 온라인 서비스 (ST_OL_0027) [tier=medium] + * st_ol_0027.pgc - 부분취소 정산 반영 (ST_OL_0027) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원거래 부분취소 시 취소금액을 차감한 잔여금액을 산출하고, 수수료를 + * 잔여금액 기준으로 비례 재계산한다. */ #include #include @@ -92,57 +92,51 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0027.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0027, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char merch[16]; + long orig = 0, cancel = 0, std_bp = 0, remain, new_fee, fee_refund, orig_fee; - tx_log(TX_LOG_INFO, "[ST_OL_0027] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0027] 부분취소 정산 반영 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0027] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "ORIG", &orig); + tx_buf_getlong(ctx->in, "CANCEL", &cancel); + tx_buf_getlong(ctx->in, "STDBP", &std_bp); + if (std_bp <= 0) std_bp = 180; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0027] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0027] 영업일 오류 date=%s", bizdate); + if (orig <= 0 || cancel < 0 || cancel > orig) { + tx_log(TX_LOG_WARN, "[ST_OL_0027] 취소금액 오류 orig=%ld cancel=%ld", orig, cancel); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0027] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = st_rec_count(bizdate, ST_ST_RECV); + remain = orig - cancel; + orig_fee = st_fee_round(orig, std_bp); + new_fee = st_fee_round(remain, std_bp); + fee_refund = orig_fee - new_fee; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0027] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "REMAIN", remain); + tx_buf_setlong(ctx->out, "NEWFEE", new_fee); + tx_buf_setlong(ctx->out, "FEEREFUND", fee_refund); + tx_log(TX_LOG_INFO, "[ST_OL_0027] 부분취소 orig=%ld cancel=%ld 잔여=%ld 수수료환급=%ld", + orig, cancel, remain, fee_refund); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0027.pgc b/app/online/st_ol_0027.pgc index 4645112..525c8f3 100644 --- a/app/online/st_ol_0027.pgc +++ b/app/online/st_ol_0027.pgc @@ -1,9 +1,9 @@ /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0027.pgc - 정산수수료 온라인 서비스 (ST_OL_0027) [tier=medium] + * st_ol_0027.pgc - 부분취소 정산 반영 (ST_OL_0027) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원거래 부분취소 시 취소금액을 차감한 잔여금액을 산출하고, 수수료를 + * 잔여금액 기준으로 비례 재계산한다. */ #include #include @@ -14,57 +14,51 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0027, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char merch[16]; + long orig = 0, cancel = 0, std_bp = 0, remain, new_fee, fee_refund, orig_fee; - tx_log(TX_LOG_INFO, "[ST_OL_0027] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0027] 부분취소 정산 반영 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0027] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "ORIG", &orig); + tx_buf_getlong(ctx->in, "CANCEL", &cancel); + tx_buf_getlong(ctx->in, "STDBP", &std_bp); + if (std_bp <= 0) std_bp = 180; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0027] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0027] 영업일 오류 date=%s", bizdate); + if (orig <= 0 || cancel < 0 || cancel > orig) { + tx_log(TX_LOG_WARN, "[ST_OL_0027] 취소금액 오류 orig=%ld cancel=%ld", orig, cancel); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0027] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = st_rec_count(bizdate, ST_ST_RECV); + remain = orig - cancel; + orig_fee = st_fee_round(orig, std_bp); + new_fee = st_fee_round(remain, std_bp); + fee_refund = orig_fee - new_fee; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0027] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "REMAIN", remain); + tx_buf_setlong(ctx->out, "NEWFEE", new_fee); + tx_buf_setlong(ctx->out, "FEEREFUND", fee_refund); + tx_log(TX_LOG_INFO, "[ST_OL_0027] 부분취소 orig=%ld cancel=%ld 잔여=%ld 수수료환급=%ld", + orig, cancel, remain, fee_refund); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0028.c b/app/online/st_ol_0028.c index 21ee0ad..7ffdae4 100644 --- a/app/online/st_ol_0028.c +++ b/app/online/st_ol_0028.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0028.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0028.pgc - 정산수수료 온라인 서비스 (ST_OL_0028) [tier=easy] + * st_ol_0028.pgc - 프로모션 할인 정산 (ST_OL_0028) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 프로모션 할인요율(만분율)을 매출에 적용하여 할인액을 산출하되, + * 프로모션 예산 한도(cap)를 상한으로 실 청구금액을 계산한다. */ #include #include @@ -92,34 +92,52 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0028.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0028, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16], promo[12]; + long amount = 0, promo_bp = 0, cap = 0, discount, charged; - tx_log(TX_LOG_INFO, "[ST_OL_0028] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0028] 프로모션 할인 정산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0028] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "PROMO", promo, sizeof(promo)) != TX_OK) + strncpy(promo, "DEFAULT", sizeof(promo) - 1); + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "PROMOBP", &promo_bp); + tx_buf_getlong(ctx->in, "CAP", &cap); + if (promo_bp <= 0) promo_bp = 500; /* 5% 할인 */ + if (cap <= 0) cap = 10000; /* 프로모션 상한 1만원 */ + + if (!amount_is_valid(amount)) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + discount = st_fee_round(amount, promo_bp); + if (discount > cap) discount = cap; + charged = amount - discount; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "PROMO", promo); + tx_buf_setlong(ctx->out, "DISCOUNT", discount); + tx_buf_setlong(ctx->out, "CHARGED", charged); + tx_log(TX_LOG_INFO, "[ST_OL_0028] 프로모션 %s amount=%ld 할인=%ld 청구=%ld", + promo, amount, discount, charged); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0028.pgc b/app/online/st_ol_0028.pgc index f00d0d4..0344dd1 100644 --- a/app/online/st_ol_0028.pgc +++ b/app/online/st_ol_0028.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0028.pgc - 정산수수료 온라인 서비스 (ST_OL_0028) [tier=easy] + * st_ol_0028.pgc - 프로모션 할인 정산 (ST_OL_0028) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 프로모션 할인요율(만분율)을 매출에 적용하여 할인액을 산출하되, + * 프로모션 예산 한도(cap)를 상한으로 실 청구금액을 계산한다. */ #include #include @@ -14,34 +14,52 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0028, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16], promo[12]; + long amount = 0, promo_bp = 0, cap = 0, discount, charged; - tx_log(TX_LOG_INFO, "[ST_OL_0028] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0028] 프로모션 할인 정산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0028] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "PROMO", promo, sizeof(promo)) != TX_OK) + strncpy(promo, "DEFAULT", sizeof(promo) - 1); + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "PROMOBP", &promo_bp); + tx_buf_getlong(ctx->in, "CAP", &cap); + if (promo_bp <= 0) promo_bp = 500; /* 5% 할인 */ + if (cap <= 0) cap = 10000; /* 프로모션 상한 1만원 */ + + if (!amount_is_valid(amount)) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + discount = st_fee_round(amount, promo_bp); + if (discount > cap) discount = cap; + charged = amount - discount; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "PROMO", promo); + tx_buf_setlong(ctx->out, "DISCOUNT", discount); + tx_buf_setlong(ctx->out, "CHARGED", charged); + tx_log(TX_LOG_INFO, "[ST_OL_0028] 프로모션 %s amount=%ld 할인=%ld 청구=%ld", + promo, amount, discount, charged); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0029.c b/app/online/st_ol_0029.c index 9305d7f..368e4dc 100644 --- a/app/online/st_ol_0029.c +++ b/app/online/st_ol_0029.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0029.pgc" /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0029.pgc - 정산수수료 온라인 서비스 (ST_OL_0029) [tier=medium] + * st_ol_0029.pgc - 수수료 상/하한(cap/floor) 적용 (ST_OL_0029) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정률 수수료 산출 후 건별 최소수수료(floor)와 최대수수료(cap) 로 + * 클램핑하여 최종 수수료를 확정한다. */ #include #include @@ -92,57 +92,54 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0029.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0029, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char merch[16]; + long amount = 0, bp = 0, floor = 0, cap = 0, raw, fee; + const char *clamp; - tx_log(TX_LOG_INFO, "[ST_OL_0029] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0029] 수수료 상/하한 적용 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0029] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "BP", &bp); + tx_buf_getlong(ctx->in, "FLOOR", &floor); + tx_buf_getlong(ctx->in, "CAP", &cap); + if (bp <= 0) bp = 180; + if (floor <= 0) floor = 100; /* 최소 100원 */ + if (cap <= 0) cap = 50000; /* 최대 5만원 */ - /* 업무검증: 금액 범위 + 영업일 형식 */ if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0029] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0029] 영업일 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0029] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = st_rec_count(bizdate, ST_ST_RECV); + raw = st_fee_round(amount, bp); + if (raw < floor) { fee = floor; clamp = "FLOOR"; } + else if (raw > cap) { fee = cap; clamp = "CAP"; } + else { fee = raw; clamp = "NONE"; } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0029] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "RAWFEE", raw); + tx_buf_setlong(ctx->out, "FEE", fee); + tx_buf_sets(ctx->out, "CLAMP", clamp); + tx_log(TX_LOG_INFO, "[ST_OL_0029] cap/floor raw=%ld fee=%ld clamp=%s", + raw, fee, clamp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0029.pgc b/app/online/st_ol_0029.pgc index 18a3fe8..46865d6 100644 --- a/app/online/st_ol_0029.pgc +++ b/app/online/st_ol_0029.pgc @@ -1,9 +1,9 @@ /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0029.pgc - 정산수수료 온라인 서비스 (ST_OL_0029) [tier=medium] + * st_ol_0029.pgc - 수수료 상/하한(cap/floor) 적용 (ST_OL_0029) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정률 수수료 산출 후 건별 최소수수료(floor)와 최대수수료(cap) 로 + * 클램핑하여 최종 수수료를 확정한다. */ #include #include @@ -14,57 +14,54 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0029, ctx) { - st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + char merch[16]; + long amount = 0, bp = 0, floor = 0, cap = 0, raw, fee; + const char *clamp; - tx_log(TX_LOG_INFO, "[ST_OL_0029] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0029] 수수료 상/하한 적용 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0029] 필수 필드 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_buf_getlong(ctx->in, "BP", &bp); + tx_buf_getlong(ctx->in, "FLOOR", &floor); + tx_buf_getlong(ctx->in, "CAP", &cap); + if (bp <= 0) bp = 180; + if (floor <= 0) floor = 100; /* 최소 100원 */ + if (cap <= 0) cap = 50000; /* 최대 5만원 */ - /* 업무검증: 금액 범위 + 영업일 형식 */ if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0029] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0029] 영업일 오류 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0029] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } - - cnt = st_rec_count(bizdate, ST_ST_RECV); + raw = st_fee_round(amount, bp); + if (raw < floor) { fee = floor; clamp = "FLOOR"; } + else if (raw > cap) { fee = cap; clamp = "CAP"; } + else { fee = raw; clamp = "NONE"; } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0029] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "RAWFEE", raw); + tx_buf_setlong(ctx->out, "FEE", fee); + tx_buf_sets(ctx->out, "CLAMP", clamp); + tx_log(TX_LOG_INFO, "[ST_OL_0029] cap/floor raw=%ld fee=%ld clamp=%s", + raw, fee, clamp); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0030.c b/app/online/st_ol_0030.c index 88f9e19..6910bfa 100644 --- a/app/online/st_ol_0030.c +++ b/app/online/st_ol_0030.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0030.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0030.pgc - 정산수수료 온라인 서비스 (ST_OL_0030) [tier=easy] + * st_ol_0030.pgc - 정산 통화환산(원화) (ST_OL_0030) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 외화 매출을 적용환율(1e4 스케일)로 원화 환산한다. 원화 환산액은 + * 원단위 미만을 절사하고, 수수료는 환산 후 원화 기준으로 산출한다. */ #include #include @@ -92,34 +92,52 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0030.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0030, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16], cur[4]; + long fx_amount = 0, rate = 0, fee_bp = 0, krw, fee, net; - tx_log(TX_LOG_INFO, "[ST_OL_0030] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0030] 정산 통화환산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0030] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "CUR", cur, sizeof(cur)) != TX_OK) + strncpy(cur, "USD", sizeof(cur) - 1); + tx_buf_getlong(ctx->in, "FXAMT", &fx_amount); /* 외화 최소단위 */ + tx_buf_getlong(ctx->in, "RATE", &rate); /* 환율 x 10000 */ + tx_buf_getlong(ctx->in, "FEEBP", &fee_bp); + if (fee_bp <= 0) fee_bp = 200; /* 2% */ + + if (fx_amount <= 0 || rate <= 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + krw = fx_amount * rate / 10000; /* 원단위 미만 절사 */ + fee = st_fee_round(krw, fee_bp); + net = krw - fee; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "CUR", cur); + tx_buf_setlong(ctx->out, "KRW", krw); + tx_buf_setlong(ctx->out, "FEE", fee); + tx_buf_setlong(ctx->out, "NET", net); + tx_log(TX_LOG_INFO, "[ST_OL_0030] 환산 %s fx=%ld rate=%ld krw=%ld net=%ld", + cur, fx_amount, rate, krw, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0030.pgc b/app/online/st_ol_0030.pgc index 0527360..7b2bd5b 100644 --- a/app/online/st_ol_0030.pgc +++ b/app/online/st_ol_0030.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0030.pgc - 정산수수료 온라인 서비스 (ST_OL_0030) [tier=easy] + * st_ol_0030.pgc - 정산 통화환산(원화) (ST_OL_0030) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 외화 매출을 적용환율(1e4 스케일)로 원화 환산한다. 원화 환산액은 + * 원단위 미만을 절사하고, 수수료는 환산 후 원화 기준으로 산출한다. */ #include #include @@ -14,34 +14,52 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0030, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16], cur[4]; + long fx_amount = 0, rate = 0, fee_bp = 0, krw, fee, net; - tx_log(TX_LOG_INFO, "[ST_OL_0030] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0030] 정산 통화환산 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0030] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "CUR", cur, sizeof(cur)) != TX_OK) + strncpy(cur, "USD", sizeof(cur) - 1); + tx_buf_getlong(ctx->in, "FXAMT", &fx_amount); /* 외화 최소단위 */ + tx_buf_getlong(ctx->in, "RATE", &rate); /* 환율 x 10000 */ + tx_buf_getlong(ctx->in, "FEEBP", &fee_bp); + if (fee_bp <= 0) fee_bp = 200; /* 2% */ + + if (fx_amount <= 0 || rate <= 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0030] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + krw = fx_amount * rate / 10000; /* 원단위 미만 절사 */ + fee = st_fee_round(krw, fee_bp); + net = krw - fee; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_sets(ctx->out, "CUR", cur); + tx_buf_setlong(ctx->out, "KRW", krw); + tx_buf_setlong(ctx->out, "FEE", fee); + tx_buf_setlong(ctx->out, "NET", net); + tx_log(TX_LOG_INFO, "[ST_OL_0030] 환산 %s fx=%ld rate=%ld krw=%ld net=%ld", + cur, fx_amount, rate, krw, net); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0031.c b/app/online/st_ol_0031.c index 21408d8..d1d8d81 100644 --- a/app/online/st_ol_0031.c +++ b/app/online/st_ol_0031.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0031.pgc" /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0031.pgc - 정산수수료 온라인 서비스 (ST_OL_0031) [tier=medium] + * st_ol_0031.pgc - 정산 확정 처리 (ST_OL_0031) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리완료(M) 건을 정산완료(S)로 확정 전이한다. 완료 상태가 아닌 건은 + * 확정 불가로 반려한다. */ #include #include @@ -92,57 +92,47 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0031.pgc" + TX_SERVICE(ST_OL_0031, ctx) { st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; int rc; - tx_log(TX_LOG_INFO, "[ST_OL_0031] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0031] 정산 확정 처리 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0031] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0031] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0031] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); + rc = st_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0031] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = st_rec_count(bizdate, ST_ST_RECV); + if (strcmp(rec.status, ST_ST_DONE) != 0) { + tx_log(TX_LOG_WARN, "[ST_OL_0031] 확정 불가 상태 key=%s st=%s", key, rec.status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "REASON", "NOT_DONE"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + rc = st_rec_update_status(key, ST_ST_SETTLED); + if (rc != TX_OK) { + tx_return(ctx, rc, ctx->out); + return; + } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0031] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "STATUS", ST_ST_SETTLED); + tx_buf_setlong(ctx->out, "NET", rec.amount - rec.fee); + tx_log(TX_LOG_INFO, "[ST_OL_0031] 정산확정 key=%s net=%ld", key, rec.amount - rec.fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0031.pgc b/app/online/st_ol_0031.pgc index 71edaec..1cf5265 100644 --- a/app/online/st_ol_0031.pgc +++ b/app/online/st_ol_0031.pgc @@ -1,9 +1,9 @@ /* @tier medium @module st @transform settlement-online */ /* - * st_ol_0031.pgc - 정산수수료 온라인 서비스 (ST_OL_0031) [tier=medium] + * st_ol_0031.pgc - 정산 확정 처리 (ST_OL_0031) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리완료(M) 건을 정산완료(S)로 확정 전이한다. 완료 상태가 아닌 건은 + * 확정 불가로 반려한다. */ #include #include @@ -14,57 +14,47 @@ EXEC SQL INCLUDE sqlca; + TX_SERVICE(ST_OL_0031, ctx) { st_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; int rc; - tx_log(TX_LOG_INFO, "[ST_OL_0031] 정산수수료 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0031] 정산 확정 처리 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0031] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[ST_OL_0031] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[ST_OL_0031] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_return(ctx, TX_EINVAL, ctx->out); return; } memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, ST_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = st_rec_insert(&rec); + rc = st_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0031] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); tx_return(ctx, rc, ctx->out); return; } - cnt = st_rec_count(bizdate, ST_ST_RECV); + if (strcmp(rec.status, ST_ST_DONE) != 0) { + tx_log(TX_LOG_WARN, "[ST_OL_0031] 확정 불가 상태 key=%s st=%s", key, rec.status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); + tx_buf_sets(ctx->out, "REASON", "NOT_DONE"); + tx_return(ctx, TX_FAIL, ctx->out); + return; + } + + rc = st_rec_update_status(key, ST_ST_SETTLED); + if (rc != TX_OK) { + tx_return(ctx, rc, ctx->out); + return; + } tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[ST_OL_0031] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "STATUS", ST_ST_SETTLED); + tx_buf_setlong(ctx->out, "NET", rec.amount - rec.fee); + tx_log(TX_LOG_INFO, "[ST_OL_0031] 정산확정 key=%s net=%ld", key, rec.amount - rec.fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0032.c b/app/online/st_ol_0032.c index 321d619..cc4ec9d 100644 --- a/app/online/st_ol_0032.c +++ b/app/online/st_ol_0032.c @@ -8,10 +8,10 @@ #line 1 "app/online/st_ol_0032.pgc" /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0032.pgc - 정산수수료 온라인 서비스 (ST_OL_0032) [tier=easy] + * st_ol_0032.pgc - 정산 예치금(보증금) 산정 (ST_OL_0032) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산순액의 일부를 지급유보(리스크 예치금)로 적립한다. 예치요율 + * (만분율)을 적용하여 예치금과 실지급액을 산출한다. */ #include #include @@ -92,34 +92,47 @@ struct sqlca_t *ECPGget_sqlca(void); #line 15 "app/online/st_ol_0032.pgc" + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0032, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long net = 0, reserve_bp = 0, reserve, payout; - tx_log(TX_LOG_INFO, "[ST_OL_0032] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0032] 정산 예치금 산정 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0032] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "NET", &net); + tx_buf_getlong(ctx->in, "RESERVEBP", &reserve_bp); + if (reserve_bp < 0 || reserve_bp > 10000) reserve_bp = 500; /* 5% 예치 */ + + if (net <= 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + reserve = st_fee_round(net, reserve_bp); + payout = net - reserve; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "NET", net); + tx_buf_setlong(ctx->out, "RESERVE", reserve); + tx_buf_setlong(ctx->out, "PAYOUT", payout); + tx_log(TX_LOG_INFO, "[ST_OL_0032] 예치금 net=%ld reserve=%ld payout=%ld", + net, reserve, payout); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/st_ol_0032.pgc b/app/online/st_ol_0032.pgc index 1a2796a..877fc64 100644 --- a/app/online/st_ol_0032.pgc +++ b/app/online/st_ol_0032.pgc @@ -1,9 +1,9 @@ /* @tier easy @module st @transform settlement-online */ /* - * st_ol_0032.pgc - 정산수수료 온라인 서비스 (ST_OL_0032) [tier=easy] + * st_ol_0032.pgc - 정산 예치금(보증금) 산정 (ST_OL_0032) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 st 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산순액의 일부를 지급유보(리스크 예치금)로 적립한다. 예치요율 + * (만분율)을 적용하여 예치금과 실지급액을 산출한다. */ #include #include @@ -14,34 +14,47 @@ EXEC SQL INCLUDE sqlca; + +static long st_fee_round(long amount, long rate_bp) +{ + /* 요율(만분율, basis point) x 금액 -> 원단위 반올림 (BigDecimal HALF_UP 대응) */ + if (amount < 0) + return -(((-amount) * rate_bp + 5000) / 10000); + return (amount * rate_bp + 5000) / 10000; +} + TX_SERVICE(ST_OL_0032, ctx) { - st_rec_t rec; - char key[16]; - int rc; + char merch[16]; + long net = 0, reserve_bp = 0, reserve, payout; - tx_log(TX_LOG_INFO, "[ST_OL_0032] 정산수수료 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[ST_OL_0032] 정산 예치금 산정 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[ST_OL_0032] KEY 누락"); + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "NET", &net); + tx_buf_getlong(ctx->in, "RESERVEBP", &reserve_bp); + if (reserve_bp < 0 || reserve_bp > 10000) reserve_bp = 500; /* 5% 예치 */ + + if (net <= 0) { + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "RESPCODE", RESP_INVALID); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = st_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[ST_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; - } + reserve = st_fee_round(net, reserve_bp); + payout = net - reserve; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[ST_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "NET", net); + tx_buf_setlong(ctx->out, "RESERVE", reserve); + tx_buf_setlong(ctx->out, "PAYOUT", payout); + tx_log(TX_LOG_INFO, "[ST_OL_0032] 예치금 net=%ld reserve=%ld payout=%ld", + net, reserve, payout); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0002.c b/app/online/vl_ol_0002.c index e12aa15..90bedec 100644 --- a/app/online/vl_ol_0002.c +++ b/app/online/vl_ol_0002.c @@ -8,15 +8,19 @@ #line 1 "app/online/vl_ol_0002.pgc" /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0002.pgc - 정합성 온라인 서비스 (VL_OL_0002) [tier=medium] + * vl_ol_0002.pgc - 정합성 접수등록 서비스 (VL_OL_0002) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 정합성 대상 거래를 접수한다. 필수필드(처리키/가맹점/영업일/금액)를 + * 검증한 뒤 표준 DBIO 로 원장에 접수(vl_rec_insert)하고, 접수이력 테이블 + * (vl_recv_hist) 에 감사 로그를 남긴다. 마지막으로 당일 접수건수를 집계하여 + * 응답에 실어 반환한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,60 +93,129 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0002.pgc" +#line 19 "app/online/vl_ol_0002.pgc" TX_SERVICE(VL_OL_0002, ctx) { + /* exec sql begin declare section */ + + + + + + + +#line 24 "app/online/vl_ol_0002.pgc" + char h_key [ 16 ] ; + +#line 25 "app/online/vl_ol_0002.pgc" + char h_merch [ 16 ] ; + +#line 26 "app/online/vl_ol_0002.pgc" + char h_bizdate [ 9 ] ; + +#line 27 "app/online/vl_ol_0002.pgc" + char h_status [ 2 ] ; + +#line 28 "app/online/vl_ol_0002.pgc" + long h_amount ; + +#line 29 "app/online/vl_ol_0002.pgc" + char h_recv_time [ 7 ] ; +/* exec sql end declare section */ +#line 30 "app/online/vl_ol_0002.pgc" + + vl_rec_t rec; char key[16], merch[16], bizdate[16]; long amount = 0, cnt; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0002] 정합성 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0002] 정합성 접수등록 서비스 진입"); + /* 1) 필수필드 검증 */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { tx_log(TX_LOG_ERROR, "[VL_OL_0002] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0200"); tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0002] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0002] 영업일 오류 date=%s", bizdate); + if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[VL_OL_0002] 입력값 검증 실패 amount=%ld date=%s", + amount, bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "VL0201"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2) 원장 접수 (표준 DBIO) */ memset(&rec, 0, sizeof(rec)); strncpy(rec.key, key, sizeof(rec.key) - 1); strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + rec.fee = amount / 100; rc = vl_rec_insert(&rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0002] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_ERROR, "[VL_OL_0002] 원장접수 실패 rc=%d(%s)", + rc, tx_strerror(rc)); + tx_buf_sets(ctx->out, "ERRCODE", "VL0202"); tx_return(ctx, rc, ctx->out); return; } + /* 3) 접수이력 INSERT (감사 로그) */ + strncpy(h_key, key, sizeof(h_key) - 1); h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_merch, merch, sizeof(h_merch) - 1); h_merch[sizeof(h_merch) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + strncpy(h_status, VL_ST_RECV, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + h_amount = amount; + strncpy(h_recv_time, "000000", sizeof(h_recv_time) - 1); + h_recv_time[sizeof(h_recv_time) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into vl_recv_hist ( recv_key , merch_id , biz_date , recv_amount , recv_status , recv_time ) values ( $1 , $2 , $3 , $4 , $5 , $6 )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_recv_time),(long)7,(long)1,(7)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 91 "app/online/vl_ol_0002.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT vl_recv_hist"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0209"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 4) 당일 접수건수 집계 */ cnt = vl_rec_count(bizdate, VL_ST_RECV); + if (cnt < 0) + cnt = 0; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0002] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "RECVAMT", amount); + tx_buf_setlong(ctx->out, "DAYCNT", cnt); + tx_log(TX_LOG_INFO, "[VL_OL_0002] 접수완료 key=%s 당일건수=%ld", key, cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0002.pgc b/app/online/vl_ol_0002.pgc index 630f406..42ed7ef 100644 --- a/app/online/vl_ol_0002.pgc +++ b/app/online/vl_ol_0002.pgc @@ -1,14 +1,18 @@ /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0002.pgc - 정합성 온라인 서비스 (VL_OL_0002) [tier=medium] + * vl_ol_0002.pgc - 정합성 접수등록 서비스 (VL_OL_0002) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 매입 정합성 대상 거래를 접수한다. 필수필드(처리키/가맹점/영업일/금액)를 + * 검증한 뒤 표준 DBIO 로 원장에 접수(vl_rec_insert)하고, 접수이력 테이블 + * (vl_recv_hist) 에 감사 로그를 남긴다. 마지막으로 당일 접수건수를 집계하여 + * 응답에 실어 반환한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,55 +20,94 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0002, ctx) { + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_merch[16]; + char h_bizdate[9]; + char h_status[2]; + long h_amount; + char h_recv_time[7]; + EXEC SQL END DECLARE SECTION; + vl_rec_t rec; char key[16], merch[16], bizdate[16]; long amount = 0, cnt; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0002] 정합성 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0002] 정합성 접수등록 서비스 진입"); + /* 1) 필수필드 검증 */ if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { tx_log(TX_LOG_ERROR, "[VL_OL_0002] 필수 필드 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0200"); tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0002] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0002] 영업일 오류 date=%s", bizdate); + if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[VL_OL_0002] 입력값 검증 실패 amount=%ld date=%s", + amount, bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "VL0201"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 2) 원장 접수 (표준 DBIO) */ memset(&rec, 0, sizeof(rec)); strncpy(rec.key, key, sizeof(rec.key) - 1); strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + rec.fee = amount / 100; rc = vl_rec_insert(&rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0002] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_ERROR, "[VL_OL_0002] 원장접수 실패 rc=%d(%s)", + rc, tx_strerror(rc)); + tx_buf_sets(ctx->out, "ERRCODE", "VL0202"); tx_return(ctx, rc, ctx->out); return; } + /* 3) 접수이력 INSERT (감사 로그) */ + strncpy(h_key, key, sizeof(h_key) - 1); h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_merch, merch, sizeof(h_merch) - 1); h_merch[sizeof(h_merch) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + strncpy(h_status, VL_ST_RECV, sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + h_amount = amount; + strncpy(h_recv_time, "000000", sizeof(h_recv_time) - 1); + h_recv_time[sizeof(h_recv_time) - 1] = '\0'; + + EXEC SQL + INSERT INTO vl_recv_hist + (recv_key, merch_id, biz_date, recv_amount, recv_status, recv_time) + VALUES + (:h_key, :h_merch, :h_bizdate, :h_amount, :h_status, :h_recv_time); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT vl_recv_hist"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0209"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 4) 당일 접수건수 집계 */ cnt = vl_rec_count(bizdate, VL_ST_RECV); + if (cnt < 0) + cnt = 0; tx_buf_reset(ctx->out); tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0002] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "RECVAMT", amount); + tx_buf_setlong(ctx->out, "DAYCNT", cnt); + tx_log(TX_LOG_INFO, "[VL_OL_0002] 접수완료 key=%s 당일건수=%ld", key, cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0003.c b/app/online/vl_ol_0003.c index fb506d4..bd42ed5 100644 --- a/app/online/vl_ol_0003.c +++ b/app/online/vl_ol_0003.c @@ -6,17 +6,19 @@ /* End of automatic include section */ #line 1 "app/online/vl_ol_0003.pgc" -/* @tier easy @module vl @transform validation-online */ +/* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0003.pgc - 정합성 온라인 서비스 (VL_OL_0003) [tier=easy] + * vl_ol_0003.pgc - 매출 정합성 검증 서비스 (VL_OL_0003) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장의 거래금액과 매출원천(매출액/승인액) 을 대사하여 정합/불일치를 + * 판정한다. 불일치 시 표준 에러코드(VL03xx) 를 응답에 실어 반환한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,19 +91,38 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0003.pgc" +#line 17 "app/online/vl_ol_0003.pgc" TX_SERVICE(VL_OL_0003, ctx) { + /* exec sql begin declare section */ + + + + +#line 22 "app/online/vl_ol_0003.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/vl_ol_0003.pgc" + long h_sale_amt ; + +#line 24 "app/online/vl_ol_0003.pgc" + long h_appr_amt ; +/* exec sql end declare section */ +#line 25 "app/online/vl_ol_0003.pgc" + + vl_rec_t rec; char key[16]; int rc; + long diff; - tx_log(TX_LOG_INFO, "[VL_OL_0003] 정합성 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0003] 매출 정합성 검증 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[VL_OL_0003] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0300"); tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -109,17 +130,57 @@ TX_SERVICE(VL_OL_0003, ctx) memset(&rec, 0, sizeof(rec)); rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[VL_OL_0003] 원장 조회 실패 key=%s rc=%d", key, rc); + tx_buf_sets(ctx->out, "ERRCODE", "VL0301"); tx_return(ctx, rc, ctx->out); return; } + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select sale_amount , approval_amount from vl_sale_source where txn_key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_sale_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_appr_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 57 "app/online/vl_ol_0003.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0003] 매출원천 없음 key=%s", key); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0302"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_sale_source"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0309"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + diff = rec.amount - h_sale_amt; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "LEDGERAMT", rec.amount); + tx_buf_setlong(ctx->out, "SALEAMT", h_sale_amt); + tx_buf_setlong(ctx->out, "APPRAMT", h_appr_amt); + + if (diff != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0003] 매출 불일치 key=%s diff=%ld", key, diff); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0303"); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0003] 매출 정합 확인 key=%s amount=%ld", key, rec.amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0003.pgc b/app/online/vl_ol_0003.pgc index a18df3b..cee6559 100644 --- a/app/online/vl_ol_0003.pgc +++ b/app/online/vl_ol_0003.pgc @@ -1,14 +1,16 @@ -/* @tier easy @module vl @transform validation-online */ +/* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0003.pgc - 정합성 온라인 서비스 (VL_OL_0003) [tier=easy] + * vl_ol_0003.pgc - 매출 정합성 검증 서비스 (VL_OL_0003) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장의 거래금액과 매출원천(매출액/승인액) 을 대사하여 정합/불일치를 + * 판정한다. 불일치 시 표준 에러코드(VL03xx) 를 응답에 실어 반환한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,14 +18,22 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0003, ctx) { + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_sale_amt; + long h_appr_amt; + EXEC SQL END DECLARE SECTION; + vl_rec_t rec; char key[16]; int rc; + long diff; - tx_log(TX_LOG_INFO, "[VL_OL_0003] 정합성 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0003] 매출 정합성 검증 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[VL_OL_0003] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0300"); tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -31,17 +41,53 @@ TX_SERVICE(VL_OL_0003, ctx) memset(&rec, 0, sizeof(rec)); rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0003] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[VL_OL_0003] 원장 조회 실패 key=%s rc=%d", key, rc); + tx_buf_sets(ctx->out, "ERRCODE", "VL0301"); tx_return(ctx, rc, ctx->out); return; } + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + EXEC SQL + SELECT sale_amount, approval_amount + INTO :h_sale_amt, :h_appr_amt + FROM vl_sale_source + WHERE txn_key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0003] 매출원천 없음 key=%s", key); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0302"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_sale_source"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0309"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + diff = rec.amount - h_sale_amt; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0003] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "LEDGERAMT", rec.amount); + tx_buf_setlong(ctx->out, "SALEAMT", h_sale_amt); + tx_buf_setlong(ctx->out, "APPRAMT", h_appr_amt); + + if (diff != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0003] 매출 불일치 key=%s diff=%ld", key, diff); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0303"); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0003] 매출 정합 확인 key=%s amount=%ld", key, rec.amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0004.c b/app/online/vl_ol_0004.c index 14634e5..8254d84 100644 --- a/app/online/vl_ol_0004.c +++ b/app/online/vl_ol_0004.c @@ -8,15 +8,18 @@ #line 1 "app/online/vl_ol_0004.pgc" /* @tier hard @module vl @transform validation-online */ /* - * vl_ol_0004.pgc - 정합성 온라인 서비스 (VL_OL_0004) [tier=hard] + * vl_ol_0004.pgc - 중복거래 검출 서비스 (VL_OL_0004) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 동일 카드번호 + 동일 금액 + 동일 거래일자로 접수된 원장(vl_ledger) 건수를 + * 집계하여 2건 이상이면 중복거래로 판정한다. 중복 판정 시 표준 에러코드 + * (VL04xx) 를 응답에 실어 반환하고, 정상(1건 이하)이면 정합으로 처리한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,88 +92,99 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0004.pgc" +#line 18 "app/online/vl_ol_0004.pgc" TX_SERVICE(VL_OL_0004, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + +#line 23 "app/online/vl_ol_0004.pgc" + char h_card [ 20 ] ; + +#line 24 "app/online/vl_ol_0004.pgc" + char h_txndate [ 9 ] ; + +#line 25 "app/online/vl_ol_0004.pgc" + long h_amount ; + +#line 26 "app/online/vl_ol_0004.pgc" + long h_dupcnt ; +/* exec sql end declare section */ +#line 27 "app/online/vl_ol_0004.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0004] 정합성 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0004] 필수 필드 누락"); + char card[20], txndate[16]; + long amount = 0; + + tx_log(TX_LOG_INFO, "[VL_OL_0004] 중복거래 검출 서비스 진입"); + + /* 1) 입력 검증: 카드번호 + 거래일자 + 금액 */ + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK || + tx_buf_get(ctx->in, "TXNDATE", txndate, sizeof(txndate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0004] 필수 필드 누락(CARDNO/TXNDATE)"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0400"); tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0004] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (strlen(card) < 12 || !date_is_valid(txndate) || !amount_is_valid(amount)) { + tx_log(TX_LOG_WARN, "[VL_OL_0004] 입력값 형식 오류 card_len=%d date=%s amt=%ld", + (int)strlen(card), txndate, amount); + tx_buf_sets(ctx->out, "ERRCODE", "VL0401"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + /* 2) 동일 키 조합 건수 집계 */ + strncpy(h_card, card, sizeof(h_card) - 1); h_card[sizeof(h_card) - 1] = '\0'; + strncpy(h_txndate, txndate, sizeof(h_txndate) - 1); + h_txndate[sizeof(h_txndate) - 1] = '\0'; + h_amount = amount; + h_dupcnt = 0; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from vl_ledger where card_no = $1 and amount = $2 and txn_date = $3 ", + ECPGt_char,(h_card),(long)20,(long)1,(20)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_txndate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_dupcnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 65 "app/online/vl_ol_0004.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT COUNT vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0409"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0004] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("VL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[VL_OL_0004] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (vl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - + /* 3) 중복 판정 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[VL_OL_0004] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "CARDNO", card); + tx_buf_sets(ctx->out, "TXNDATE", txndate); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_setlong(ctx->out, "DUPCNT", h_dupcnt); + + if (h_dupcnt >= 2) { + tx_log(TX_LOG_WARN, "[VL_OL_0004] 중복거래 검출 card=%s cnt=%ld", + card, h_dupcnt); + tx_buf_sets(ctx->out, "RESULT", "DUPLICATE"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0403"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "UNIQUE"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0004] 중복 없음 card=%s cnt=%ld", card, h_dupcnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0004.pgc b/app/online/vl_ol_0004.pgc index 8d23685..2448e5f 100644 --- a/app/online/vl_ol_0004.pgc +++ b/app/online/vl_ol_0004.pgc @@ -1,14 +1,17 @@ /* @tier hard @module vl @transform validation-online */ /* - * vl_ol_0004.pgc - 정합성 온라인 서비스 (VL_OL_0004) [tier=hard] + * vl_ol_0004.pgc - 중복거래 검출 서비스 (VL_OL_0004) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 동일 카드번호 + 동일 금액 + 동일 거래일자로 접수된 원장(vl_ledger) 건수를 + * 집계하여 2건 이상이면 중복거래로 판정한다. 중복 판정 시 표준 에러코드 + * (VL04xx) 를 응답에 실어 반환하고, 정상(1건 이하)이면 정합으로 처리한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,83 +19,76 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0004, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_card[20]; + char h_txndate[9]; + long h_amount; + long h_dupcnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0004] 정합성 트랜잭션 오케스트레이션 진입"); + char card[20], txndate[16]; + long amount = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0004] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[VL_OL_0004] 중복거래 검출 서비스 진입"); + + /* 1) 입력 검증: 카드번호 + 거래일자 + 금액 */ + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK || + tx_buf_get(ctx->in, "TXNDATE", txndate, sizeof(txndate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0004] 필수 필드 누락(CARDNO/TXNDATE)"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0400"); tx_return(ctx, TX_EINVAL, ctx->out); return; } tx_buf_getlong(ctx->in, "AMOUNT", &amount); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0004] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (strlen(card) < 12 || !date_is_valid(txndate) || !amount_is_valid(amount)) { + tx_log(TX_LOG_WARN, "[VL_OL_0004] 입력값 형식 오류 card_len=%d date=%s amt=%ld", + (int)strlen(card), txndate, amount); + tx_buf_sets(ctx->out, "ERRCODE", "VL0401"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + /* 2) 동일 키 조합 건수 집계 */ + strncpy(h_card, card, sizeof(h_card) - 1); h_card[sizeof(h_card) - 1] = '\0'; + strncpy(h_txndate, txndate, sizeof(h_txndate) - 1); + h_txndate[sizeof(h_txndate) - 1] = '\0'; + h_amount = amount; + h_dupcnt = 0; + + EXEC SQL + SELECT COUNT(*) + INTO :h_dupcnt + FROM vl_ledger + WHERE card_no = :h_card + AND amount = :h_amount + AND txn_date = :h_txndate; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT COUNT vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0409"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0004] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("VL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[VL_OL_0004] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (vl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - + /* 3) 중복 판정 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[VL_OL_0004] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "CARDNO", card); + tx_buf_sets(ctx->out, "TXNDATE", txndate); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_setlong(ctx->out, "DUPCNT", h_dupcnt); + + if (h_dupcnt >= 2) { + tx_log(TX_LOG_WARN, "[VL_OL_0004] 중복거래 검출 card=%s cnt=%ld", + card, h_dupcnt); + tx_buf_sets(ctx->out, "RESULT", "DUPLICATE"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0403"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "UNIQUE"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0004] 중복 없음 card=%s cnt=%ld", card, h_dupcnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0005.c b/app/online/vl_ol_0005.c index 13ee48b..738c25f 100644 --- a/app/online/vl_ol_0005.c +++ b/app/online/vl_ol_0005.c @@ -8,15 +8,18 @@ #line 1 "app/online/vl_ol_0005.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0005.pgc - 정합성 온라인 서비스 (VL_OL_0005) [tier=easy] + * vl_ol_0005.pgc - 가맹점 유효성 검증 서비스 (VL_OL_0005) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장(vl_rec_select) 에서 처리 대상의 가맹점번호를 얻은 뒤, 가맹점 마스터 + * (mm_merchant) 의 계약상태/계약만료일을 조회한다. 상태가 정상('A') 이 아니거나 + * 계약이 만료(만료일 < 오늘) 되었으면 불일치(VL05xx) 로 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,37 +92,109 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0005.pgc" +#line 18 "app/online/vl_ol_0005.pgc" TX_SERVICE(VL_OL_0005, ctx) { + /* exec sql begin declare section */ + + + + +#line 23 "app/online/vl_ol_0005.pgc" + char h_merch [ 16 ] ; + +#line 24 "app/online/vl_ol_0005.pgc" + char h_status [ 2 ] ; + +#line 25 "app/online/vl_ol_0005.pgc" + char h_contract_end [ 9 ] ; +/* exec sql end declare section */ +#line 26 "app/online/vl_ol_0005.pgc" + + vl_rec_t rec; char key[16]; + char today[9]; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0005] 정합성 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0005] 가맹점 유효성 검증 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[VL_OL_0005] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0500"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 1) 원장 조회로 가맹점번호 확보 */ memset(&rec, 0, sizeof(rec)); rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0005] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[VL_OL_0005] 원장 조회 실패 key=%s rc=%d", key, rc); + tx_buf_sets(ctx->out, "ERRCODE", "VL0501"); tx_return(ctx, rc, ctx->out); return; } + /* 2) 가맹점 마스터 상태/만료일 조회 */ + strncpy(h_merch, rec.merch_id, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + memset(h_status, 0, sizeof(h_status)); + memset(h_contract_end, 0, sizeof(h_contract_end)); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select merch_status , contract_end from mm_merchant where merch_id = $1 ", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_contract_end),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 62 "app/online/vl_ol_0005.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0005] 미등록 가맹점 merch=%s", rec.merch_id); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0502"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0509"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 상태/만료 판정 */ + date_today(today); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "KEY", key); tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0005] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MSTATUS", h_status); + tx_buf_sets(ctx->out, "CONTRACTEND", h_contract_end); + + if (memcmp(h_status, "A", 1) != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0005] 가맹점 상태 비정상 merch=%s status=%s", + rec.merch_id, h_status); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0503"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + if (strcmp(h_contract_end, today) < 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0005] 계약 만료 merch=%s end=%s today=%s", + rec.merch_id, h_contract_end, today); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0504"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0005] 가맹점 유효 merch=%s", rec.merch_id); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0005.pgc b/app/online/vl_ol_0005.pgc index b0be2b9..bfaddf5 100644 --- a/app/online/vl_ol_0005.pgc +++ b/app/online/vl_ol_0005.pgc @@ -1,14 +1,17 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0005.pgc - 정합성 온라인 서비스 (VL_OL_0005) [tier=easy] + * vl_ol_0005.pgc - 가맹점 유효성 검증 서비스 (VL_OL_0005) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장(vl_rec_select) 에서 처리 대상의 가맹점번호를 얻은 뒤, 가맹점 마스터 + * (mm_merchant) 의 계약상태/계약만료일을 조회한다. 상태가 정상('A') 이 아니거나 + * 계약이 만료(만료일 < 오늘) 되었으면 불일치(VL05xx) 로 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,32 +19,89 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0005, ctx) { + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16]; + char h_status[2]; + char h_contract_end[9]; + EXEC SQL END DECLARE SECTION; + vl_rec_t rec; char key[16]; + char today[9]; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0005] 정합성 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0005] 가맹점 유효성 검증 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[VL_OL_0005] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0500"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 1) 원장 조회로 가맹점번호 확보 */ memset(&rec, 0, sizeof(rec)); rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0005] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[VL_OL_0005] 원장 조회 실패 key=%s rc=%d", key, rc); + tx_buf_sets(ctx->out, "ERRCODE", "VL0501"); tx_return(ctx, rc, ctx->out); return; } + /* 2) 가맹점 마스터 상태/만료일 조회 */ + strncpy(h_merch, rec.merch_id, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + memset(h_status, 0, sizeof(h_status)); + memset(h_contract_end, 0, sizeof(h_contract_end)); + + EXEC SQL + SELECT merch_status, contract_end + INTO :h_status, :h_contract_end + FROM mm_merchant + WHERE merch_id = :h_merch; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0005] 미등록 가맹점 merch=%s", rec.merch_id); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0502"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT mm_merchant"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0509"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 상태/만료 판정 */ + date_today(today); tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "KEY", key); tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0005] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MSTATUS", h_status); + tx_buf_sets(ctx->out, "CONTRACTEND", h_contract_end); + + if (memcmp(h_status, "A", 1) != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0005] 가맹점 상태 비정상 merch=%s status=%s", + rec.merch_id, h_status); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0503"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + if (strcmp(h_contract_end, today) < 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0005] 계약 만료 merch=%s end=%s today=%s", + rec.merch_id, h_contract_end, today); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0504"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0005] 가맹점 유효 merch=%s", rec.merch_id); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0006.c b/app/online/vl_ol_0006.c index 46133bb..8f8c800 100644 --- a/app/online/vl_ol_0006.c +++ b/app/online/vl_ol_0006.c @@ -8,15 +8,18 @@ #line 1 "app/online/vl_ol_0006.pgc" /* @tier hard @module vl @transform validation-online */ /* - * vl_ol_0006.pgc - 정합성 온라인 서비스 (VL_OL_0006) [tier=hard] + * vl_ol_0006.pgc - 카드 유효성(BIN/유효기간) 검증 서비스 (VL_OL_0006) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 카드번호의 앞 6자리(BIN)로 카드 BIN 마스터(vl_card_bin) 를 조회하여 + * 발급사/카드상태/유효기간(expire_ym, YYYYMM)을 확인한다. BIN 미등록이거나 + * 카드상태가 정상('N') 이 아니거나 유효기간이 만료되었으면 불일치(VL06xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,88 +92,116 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0006.pgc" +#line 18 "app/online/vl_ol_0006.pgc" TX_SERVICE(VL_OL_0006, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + +#line 23 "app/online/vl_ol_0006.pgc" + char h_bin [ 7 ] ; + +#line 24 "app/online/vl_ol_0006.pgc" + char h_issuer [ 8 ] ; + +#line 25 "app/online/vl_ol_0006.pgc" + char h_card_status [ 2 ] ; + +#line 26 "app/online/vl_ol_0006.pgc" + char h_expire_ym [ 7 ] ; +/* exec sql end declare section */ +#line 27 "app/online/vl_ol_0006.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0006] 정합성 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0006] 필수 필드 누락"); + char card[20]; + char cur_ym[7]; + char today[9]; + + tx_log(TX_LOG_INFO, "[VL_OL_0006] 카드 유효성 검증 서비스 진입"); + + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0006] CARDNO 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0600"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0006] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (strlen(card) < 6) { + tx_log(TX_LOG_WARN, "[VL_OL_0006] 카드번호 길이 오류 len=%d", (int)strlen(card)); + tx_buf_sets(ctx->out, "ERRCODE", "VL0601"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + /* 1) BIN 6자리 추출 */ + memset(h_bin, 0, sizeof(h_bin)); + memcpy(h_bin, card, 6); + h_bin[6] = '\0'; + memset(h_issuer, 0, sizeof(h_issuer)); + memset(h_card_status, 0, sizeof(h_card_status)); + memset(h_expire_ym, 0, sizeof(h_expire_ym)); + + /* 2) BIN 마스터 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select issuer_code , card_status , expire_ym from vl_card_bin where bin = $1 ", + ECPGt_char,(h_bin),(long)7,(long)1,(7)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_issuer),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_card_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_expire_ym),(long)7,(long)1,(7)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 61 "app/online/vl_ol_0006.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0006] 미등록 BIN bin=%s", h_bin); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0602"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_card_bin"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0609"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0006] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("VL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[VL_OL_0006] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (vl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 3) 상태/유효기간 판정 (YYYYMM 비교) */ + date_today(today); + memcpy(cur_ym, today, 6); + cur_ym[6] = '\0'; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[VL_OL_0006] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIN", h_bin); + tx_buf_sets(ctx->out, "ISSUER", h_issuer); + tx_buf_sets(ctx->out, "CARDSTATUS", h_card_status); + tx_buf_sets(ctx->out, "EXPIREYM", h_expire_ym); + + if (memcmp(h_card_status, "N", 1) != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0006] 카드상태 비정상 bin=%s status=%s", + h_bin, h_card_status); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0603"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + if (strcmp(h_expire_ym, cur_ym) < 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0006] 유효기간 만료 bin=%s expire=%s cur=%s", + h_bin, h_expire_ym, cur_ym); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0604"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0006] 카드 유효 bin=%s issuer=%s", h_bin, h_issuer); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0006.pgc b/app/online/vl_ol_0006.pgc index 590bc97..d3c4438 100644 --- a/app/online/vl_ol_0006.pgc +++ b/app/online/vl_ol_0006.pgc @@ -1,14 +1,17 @@ /* @tier hard @module vl @transform validation-online */ /* - * vl_ol_0006.pgc - 정합성 온라인 서비스 (VL_OL_0006) [tier=hard] + * vl_ol_0006.pgc - 카드 유효성(BIN/유효기간) 검증 서비스 (VL_OL_0006) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 카드번호의 앞 6자리(BIN)로 카드 BIN 마스터(vl_card_bin) 를 조회하여 + * 발급사/카드상태/유효기간(expire_ym, YYYYMM)을 확인한다. BIN 미등록이거나 + * 카드상태가 정상('N') 이 아니거나 유효기간이 만료되었으면 불일치(VL06xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,83 +19,91 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0006, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_bin[7]; + char h_issuer[8]; + char h_card_status[2]; + char h_expire_ym[7]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0006] 정합성 트랜잭션 오케스트레이션 진입"); + char card[20]; + char cur_ym[7]; + char today[9]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0006] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[VL_OL_0006] 카드 유효성 검증 서비스 진입"); + + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0006] CARDNO 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0600"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0006] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (strlen(card) < 6) { + tx_log(TX_LOG_WARN, "[VL_OL_0006] 카드번호 길이 오류 len=%d", (int)strlen(card)); + tx_buf_sets(ctx->out, "ERRCODE", "VL0601"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + /* 1) BIN 6자리 추출 */ + memset(h_bin, 0, sizeof(h_bin)); + memcpy(h_bin, card, 6); + h_bin[6] = '\0'; + memset(h_issuer, 0, sizeof(h_issuer)); + memset(h_card_status, 0, sizeof(h_card_status)); + memset(h_expire_ym, 0, sizeof(h_expire_ym)); + + /* 2) BIN 마스터 조회 */ + EXEC SQL + SELECT issuer_code, card_status, expire_ym + INTO :h_issuer, :h_card_status, :h_expire_ym + FROM vl_card_bin + WHERE bin = :h_bin; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0006] 미등록 BIN bin=%s", h_bin); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0602"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_card_bin"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0609"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0006] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); - return; - } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; - } - - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("VL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[VL_OL_0006] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (vl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); + /* 3) 상태/유효기간 판정 (YYYYMM 비교) */ + date_today(today); + memcpy(cur_ym, today, 6); + cur_ym[6] = '\0'; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[VL_OL_0006] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIN", h_bin); + tx_buf_sets(ctx->out, "ISSUER", h_issuer); + tx_buf_sets(ctx->out, "CARDSTATUS", h_card_status); + tx_buf_sets(ctx->out, "EXPIREYM", h_expire_ym); + + if (memcmp(h_card_status, "N", 1) != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0006] 카드상태 비정상 bin=%s status=%s", + h_bin, h_card_status); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0603"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + if (strcmp(h_expire_ym, cur_ym) < 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0006] 유효기간 만료 bin=%s expire=%s cur=%s", + h_bin, h_expire_ym, cur_ym); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0604"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0006] 카드 유효 bin=%s issuer=%s", h_bin, h_issuer); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0007.c b/app/online/vl_ol_0007.c index a8e5015..be9074a 100644 --- a/app/online/vl_ol_0007.c +++ b/app/online/vl_ol_0007.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0007.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0007.pgc - 정합성 온라인 서비스 (VL_OL_0007) [tier=easy] + * vl_ol_0007.pgc - 금액 tolerance 정합 검증 서비스 (VL_OL_0007) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장(vl_rec_select) 의 거래금액과 승인원천(vl_authorized) 의 승인금액을 + * 대사한다. 입력 허용오차(TOL, 기본 0원) 이내면 정합, 초과하면 불일치(VL07xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,37 +91,100 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0007.pgc" +#line 17 "app/online/vl_ol_0007.pgc" TX_SERVICE(VL_OL_0007, ctx) { + /* exec sql begin declare section */ + + + +#line 22 "app/online/vl_ol_0007.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/vl_ol_0007.pgc" + long h_appr_amt ; +/* exec sql end declare section */ +#line 24 "app/online/vl_ol_0007.pgc" + + vl_rec_t rec; char key[16]; + long tol = 0, diff, absdiff; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0007] 정합성 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0007] 금액 tolerance 검증 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[VL_OL_0007] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0700"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + tx_buf_getlong(ctx->in, "TOL", &tol); + if (tol < 0) + tol = 0; + /* 1) 원장 금액 확보 */ memset(&rec, 0, sizeof(rec)); rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0007] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[VL_OL_0007] 원장 조회 실패 key=%s rc=%d", key, rc); + tx_buf_sets(ctx->out, "ERRCODE", "VL0701"); tx_return(ctx, rc, ctx->out); return; } + /* 2) 승인금액 조회 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + h_appr_amt = 0; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select approval_amount from vl_authorized where txn_key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_appr_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 62 "app/online/vl_ol_0007.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0007] 승인원천 없음 key=%s", key); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0702"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_authorized"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0709"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 허용오차 판정 */ + diff = rec.amount - h_appr_amt; + absdiff = labs(diff); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0007] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "LEDGERAMT", rec.amount); + tx_buf_setlong(ctx->out, "APPRAMT", h_appr_amt); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_setlong(ctx->out, "TOL", tol); + + if (absdiff > tol) { + tx_log(TX_LOG_WARN, "[VL_OL_0007] 허용오차 초과 key=%s diff=%ld tol=%ld", + key, diff, tol); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0703"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0007] 금액 정합 key=%s diff=%ld", key, diff); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0007.pgc b/app/online/vl_ol_0007.pgc index 4ddce1f..0a79d6d 100644 --- a/app/online/vl_ol_0007.pgc +++ b/app/online/vl_ol_0007.pgc @@ -1,14 +1,16 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0007.pgc - 정합성 온라인 서비스 (VL_OL_0007) [tier=easy] + * vl_ol_0007.pgc - 금액 tolerance 정합 검증 서비스 (VL_OL_0007) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장(vl_rec_select) 의 거래금액과 승인원천(vl_authorized) 의 승인금액을 + * 대사한다. 입력 허용오차(TOL, 기본 0원) 이내면 정합, 초과하면 불일치(VL07xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,32 +18,85 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0007, ctx) { + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_appr_amt; + EXEC SQL END DECLARE SECTION; + vl_rec_t rec; char key[16]; + long tol = 0, diff, absdiff; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0007] 정합성 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0007] 금액 tolerance 검증 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[VL_OL_0007] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0700"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + tx_buf_getlong(ctx->in, "TOL", &tol); + if (tol < 0) + tol = 0; + /* 1) 원장 금액 확보 */ memset(&rec, 0, sizeof(rec)); rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0007] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[VL_OL_0007] 원장 조회 실패 key=%s rc=%d", key, rc); + tx_buf_sets(ctx->out, "ERRCODE", "VL0701"); tx_return(ctx, rc, ctx->out); return; } + /* 2) 승인금액 조회 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + h_appr_amt = 0; + + EXEC SQL + SELECT approval_amount + INTO :h_appr_amt + FROM vl_authorized + WHERE txn_key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0007] 승인원천 없음 key=%s", key); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0702"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_authorized"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0709"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 허용오차 판정 */ + diff = rec.amount - h_appr_amt; + absdiff = labs(diff); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0007] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "LEDGERAMT", rec.amount); + tx_buf_setlong(ctx->out, "APPRAMT", h_appr_amt); + tx_buf_setlong(ctx->out, "DIFF", diff); + tx_buf_setlong(ctx->out, "TOL", tol); + + if (absdiff > tol) { + tx_log(TX_LOG_WARN, "[VL_OL_0007] 허용오차 초과 key=%s diff=%ld tol=%ld", + key, diff, tol); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0703"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0007] 금액 정합 key=%s diff=%ld", key, diff); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0008.c b/app/online/vl_ol_0008.c index be436f0..2616316 100644 --- a/app/online/vl_ol_0008.c +++ b/app/online/vl_ol_0008.c @@ -8,15 +8,18 @@ #line 1 "app/online/vl_ol_0008.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0008.pgc - 정합성 온라인 서비스 (VL_OL_0008) [tier=easy] + * vl_ol_0008.pgc - 일자 정합성 검증 서비스 (VL_OL_0008) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리키에 매핑된 일자정보(vl_date_map) 의 거래일/영업일/정산예정일을 조회하여 + * 각 일자의 형식(date_is_valid) 과 순서(거래일 <= 정산예정일) 정합성을 검증한다. + * 형식 오류 또는 역전 시 불일치(VL08xx) 로 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,37 +92,106 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0008.pgc" +#line 18 "app/online/vl_ol_0008.pgc" TX_SERVICE(VL_OL_0008, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 23 "app/online/vl_ol_0008.pgc" + char h_key [ 16 ] ; + +#line 24 "app/online/vl_ol_0008.pgc" + char h_txn_date [ 9 ] ; + +#line 25 "app/online/vl_ol_0008.pgc" + char h_biz_date [ 9 ] ; + +#line 26 "app/online/vl_ol_0008.pgc" + char h_settle_date [ 9 ] ; +/* exec sql end declare section */ +#line 27 "app/online/vl_ol_0008.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0008] 정합성 단건조회 서비스 진입"); + + char key[16]; + + tx_log(TX_LOG_INFO, "[VL_OL_0008] 일자 정합성 검증 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[VL_OL_0008] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0800"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0008] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 일자정보 조회 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + memset(h_txn_date, 0, sizeof(h_txn_date)); + memset(h_biz_date, 0, sizeof(h_biz_date)); + memset(h_settle_date, 0, sizeof(h_settle_date)); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select txn_date , biz_date , settle_date from vl_date_map where txn_key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_txn_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_biz_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_settle_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 51 "app/online/vl_ol_0008.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0008] 일자정보 없음 key=%s", key); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0801"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_date_map"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0809"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "TXNDATE", h_txn_date); + tx_buf_sets(ctx->out, "BIZDATE", h_biz_date); + tx_buf_sets(ctx->out, "SETTLEDATE", h_settle_date); + + /* 2) 형식 검증 */ + if (!date_is_valid(h_txn_date) || !date_is_valid(h_biz_date) || + !date_is_valid(h_settle_date)) { + tx_log(TX_LOG_WARN, "[VL_OL_0008] 일자 형식 오류 key=%s txn=%s settle=%s", + key, h_txn_date, h_settle_date); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0802"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* 3) 순서 검증: 거래일 <= 정산예정일 */ + if (strcmp(h_txn_date, h_settle_date) > 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0008] 일자 역전 key=%s txn=%s > settle=%s", + key, h_txn_date, h_settle_date); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0803"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0008] 일자 정합 key=%s txn=%s settle=%s", + key, h_txn_date, h_settle_date); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0008.pgc b/app/online/vl_ol_0008.pgc index 36f011e..f220b2d 100644 --- a/app/online/vl_ol_0008.pgc +++ b/app/online/vl_ol_0008.pgc @@ -1,14 +1,17 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0008.pgc - 정합성 온라인 서비스 (VL_OL_0008) [tier=easy] + * vl_ol_0008.pgc - 일자 정합성 검증 서비스 (VL_OL_0008) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리키에 매핑된 일자정보(vl_date_map) 의 거래일/영업일/정산예정일을 조회하여 + * 각 일자의 형식(date_is_valid) 과 순서(거래일 <= 정산예정일) 정합성을 검증한다. + * 형식 오류 또는 역전 시 불일치(VL08xx) 로 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,32 +19,81 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0008, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_txn_date[9]; + char h_biz_date[9]; + char h_settle_date[9]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0008] 정합성 단건조회 서비스 진입"); + char key[16]; + + tx_log(TX_LOG_INFO, "[VL_OL_0008] 일자 정합성 검증 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[VL_OL_0008] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0800"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0008] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 일자정보 조회 */ + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + memset(h_txn_date, 0, sizeof(h_txn_date)); + memset(h_biz_date, 0, sizeof(h_biz_date)); + memset(h_settle_date, 0, sizeof(h_settle_date)); + + EXEC SQL + SELECT txn_date, biz_date, settle_date + INTO :h_txn_date, :h_biz_date, :h_settle_date + FROM vl_date_map + WHERE txn_key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0008] 일자정보 없음 key=%s", key); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0801"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_date_map"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0809"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0008] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "TXNDATE", h_txn_date); + tx_buf_sets(ctx->out, "BIZDATE", h_biz_date); + tx_buf_sets(ctx->out, "SETTLEDATE", h_settle_date); + + /* 2) 형식 검증 */ + if (!date_is_valid(h_txn_date) || !date_is_valid(h_biz_date) || + !date_is_valid(h_settle_date)) { + tx_log(TX_LOG_WARN, "[VL_OL_0008] 일자 형식 오류 key=%s txn=%s settle=%s", + key, h_txn_date, h_settle_date); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0802"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + /* 3) 순서 검증: 거래일 <= 정산예정일 */ + if (strcmp(h_txn_date, h_settle_date) > 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0008] 일자 역전 key=%s txn=%s > settle=%s", + key, h_txn_date, h_settle_date); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0803"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0008] 일자 정합 key=%s txn=%s settle=%s", + key, h_txn_date, h_settle_date); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0009.c b/app/online/vl_ol_0009.c index c08321d..0e4a97a 100644 --- a/app/online/vl_ol_0009.c +++ b/app/online/vl_ol_0009.c @@ -8,15 +8,18 @@ #line 1 "app/online/vl_ol_0009.pgc" /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0009.pgc - 정합성 온라인 서비스 (VL_OL_0009) [tier=medium] + * vl_ol_0009.pgc - 필수항목 존재/형식 검증 서비스 (VL_OL_0009) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 전문 필수항목(가맹점ID/카드번호/승인번호/금액) 의 존재 및 형식(길이/숫자)을 + * 검증한다. 누락/형식오류 항목명을 누적하여 목록으로 응답하고, 하나라도 실패면 + * 불일치(VL09xx). 검증 결과는 항목검증 이력(vl_field_chk_hist) 에 적재한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,60 +92,129 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0009.pgc" +#line 18 "app/online/vl_ol_0009.pgc" +static int is_all_digit(const char *s) +{ + int i; + if (s == NULL || s[0] == '\0') + return 0; + for (i = 0; s[i] != '\0'; i++) { + if (s[i] < '0' || s[i] > '9') + return 0; + } + return 1; +} + TX_SERVICE(VL_OL_0009, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + +#line 35 "app/online/vl_ol_0009.pgc" + char h_key [ 16 ] ; + +#line 36 "app/online/vl_ol_0009.pgc" + char h_missing [ 128 ] ; + +#line 37 "app/online/vl_ol_0009.pgc" + long h_fail_cnt ; +/* exec sql end declare section */ +#line 38 "app/online/vl_ol_0009.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0009] 정합성 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0009] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char key[16], merch[16], card[20], approval[12], amtstr[16]; + char missing[128]; + long amount = 0; + int fail = 0; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0009] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0009] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[VL_OL_0009] 필수항목 검증 서비스 진입"); + + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0009] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0900"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + missing[0] = '\0'; - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0009] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 가맹점ID: 존재 + 숫자/영숫자 길이 */ + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + strlen(merch) < 6) { + strncat(missing, "MERCHID;", sizeof(missing) - strlen(missing) - 1); + fail++; + } + + /* 2) 카드번호: 존재 + 12자리 이상 숫자 */ + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK || + strlen(card) < 12 || !is_all_digit(card)) { + strncat(missing, "CARDNO;", sizeof(missing) - strlen(missing) - 1); + fail++; + } + + /* 3) 승인번호: 존재 + 숫자 */ + if (tx_buf_get(ctx->in, "APPROVALNO", approval, sizeof(approval)) != TX_OK || + !is_all_digit(approval)) { + strncat(missing, "APPROVALNO;", sizeof(missing) - strlen(missing) - 1); + fail++; + } + + /* 4) 금액: 존재 + 범위 유효 */ + if (tx_buf_get(ctx->in, "AMOUNT", amtstr, sizeof(amtstr)) != TX_OK) { + strncat(missing, "AMOUNT;", sizeof(missing) - strlen(missing) - 1); + fail++; + } else { + amount = atol(amtstr); + if (!amount_is_valid(amount)) { + strncat(missing, "AMOUNT;", sizeof(missing) - strlen(missing) - 1); + fail++; + } + } + + /* 5) 항목검증 이력 적재 */ + strncpy(h_key, key, sizeof(h_key) - 1); h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_missing, (missing[0] ? missing : "-"), sizeof(h_missing) - 1); + h_missing[sizeof(h_missing) - 1] = '\0'; + h_fail_cnt = fail; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into vl_field_chk_hist ( chk_key , fail_count , missing_fields ) values ( $1 , $2 , $3 )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fail_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_missing),(long)128,(long)1,(128)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 99 "app/online/vl_ol_0009.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT vl_field_chk_hist"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0909"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); - + /* 6) 판정 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0009] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "FAILCNT", fail); + tx_buf_sets(ctx->out, "MISSING", (missing[0] ? missing : "-")); + + if (fail > 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0009] 필수항목 검증 실패 key=%s missing=%s", + key, missing); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0903"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0009] 필수항목 정상 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0009.pgc b/app/online/vl_ol_0009.pgc index 208b06a..d1d4a3a 100644 --- a/app/online/vl_ol_0009.pgc +++ b/app/online/vl_ol_0009.pgc @@ -1,70 +1,127 @@ /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0009.pgc - 정합성 온라인 서비스 (VL_OL_0009) [tier=medium] + * vl_ol_0009.pgc - 필수항목 존재/형식 검증 서비스 (VL_OL_0009) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 전문 필수항목(가맹점ID/카드번호/승인번호/금액) 의 존재 및 형식(길이/숫자)을 + * 검증한다. 누락/형식오류 항목명을 누적하여 목록으로 응답하고, 하나라도 실패면 + * 불일치(VL09xx). 검증 결과는 항목검증 이력(vl_field_chk_hist) 에 적재한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" EXEC SQL INCLUDE sqlca; +static int is_all_digit(const char *s) +{ + int i; + if (s == NULL || s[0] == '\0') + return 0; + for (i = 0; s[i] != '\0'; i++) { + if (s[i] < '0' || s[i] > '9') + return 0; + } + return 1; +} + TX_SERVICE(VL_OL_0009, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_missing[128]; + long h_fail_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0009] 정합성 등록 서비스 진입"); + char key[16], merch[16], card[20], approval[12], amtstr[16]; + char missing[128]; + long amount = 0; + int fail = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0009] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[VL_OL_0009] 필수항목 검증 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0009] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0009] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0009] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0900"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + missing[0] = '\0'; - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0009] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 가맹점ID: 존재 + 숫자/영숫자 길이 */ + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + strlen(merch) < 6) { + strncat(missing, "MERCHID;", sizeof(missing) - strlen(missing) - 1); + fail++; + } + + /* 2) 카드번호: 존재 + 12자리 이상 숫자 */ + if (tx_buf_get(ctx->in, "CARDNO", card, sizeof(card)) != TX_OK || + strlen(card) < 12 || !is_all_digit(card)) { + strncat(missing, "CARDNO;", sizeof(missing) - strlen(missing) - 1); + fail++; + } + + /* 3) 승인번호: 존재 + 숫자 */ + if (tx_buf_get(ctx->in, "APPROVALNO", approval, sizeof(approval)) != TX_OK || + !is_all_digit(approval)) { + strncat(missing, "APPROVALNO;", sizeof(missing) - strlen(missing) - 1); + fail++; + } + + /* 4) 금액: 존재 + 범위 유효 */ + if (tx_buf_get(ctx->in, "AMOUNT", amtstr, sizeof(amtstr)) != TX_OK) { + strncat(missing, "AMOUNT;", sizeof(missing) - strlen(missing) - 1); + fail++; + } else { + amount = atol(amtstr); + if (!amount_is_valid(amount)) { + strncat(missing, "AMOUNT;", sizeof(missing) - strlen(missing) - 1); + fail++; + } + } + + /* 5) 항목검증 이력 적재 */ + strncpy(h_key, key, sizeof(h_key) - 1); h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_missing, (missing[0] ? missing : "-"), sizeof(h_missing) - 1); + h_missing[sizeof(h_missing) - 1] = '\0'; + h_fail_cnt = fail; + + EXEC SQL + INSERT INTO vl_field_chk_hist + (chk_key, fail_count, missing_fields) + VALUES + (:h_key, :h_fail_cnt, :h_missing); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT vl_field_chk_hist"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0909"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); - + /* 6) 판정 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0009] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_setlong(ctx->out, "FAILCNT", fail); + tx_buf_sets(ctx->out, "MISSING", (missing[0] ? missing : "-")); + + if (fail > 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0009] 필수항목 검증 실패 key=%s missing=%s", + key, missing); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL0903"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0009] 필수항목 정상 key=%s", key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0010.c b/app/online/vl_ol_0010.c index 6f9e309..aa6a775 100644 --- a/app/online/vl_ol_0010.c +++ b/app/online/vl_ol_0010.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0010.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0010.pgc - 정합성 온라인 서비스 (VL_OL_0010) [tier=easy] + * vl_ol_0010.pgc - 코드값 유효성 검증 서비스 (VL_OL_0010) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 코드그룹 + 코드값을 공통코드(vl_common_code) 에서 조회하여 등록 여부와 + * 사용여부(use_yn='Y') 를 확인한다. 미등록이거나 비활성이면 불일치(VL10xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,37 +91,95 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0010.pgc" +#line 17 "app/online/vl_ol_0010.pgc" TX_SERVICE(VL_OL_0010, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 22 "app/online/vl_ol_0010.pgc" + char h_grp [ 16 ] ; + +#line 23 "app/online/vl_ol_0010.pgc" + char h_val [ 16 ] ; + +#line 24 "app/online/vl_ol_0010.pgc" + char h_use_yn [ 2 ] ; + +#line 25 "app/online/vl_ol_0010.pgc" + char h_code_name [ 41 ] ; +/* exec sql end declare section */ +#line 26 "app/online/vl_ol_0010.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0010] 정합성 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0010] KEY 누락"); + char grp[16], val[16]; + + tx_log(TX_LOG_INFO, "[VL_OL_0010] 코드값 검증 서비스 진입"); + + if (tx_buf_get(ctx->in, "CODEGRP", grp, sizeof(grp)) != TX_OK || + tx_buf_get(ctx->in, "CODEVAL", val, sizeof(val)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0010] 필수 필드 누락(CODEGRP/CODEVAL)"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1000"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 공통코드 조회 */ + strncpy(h_grp, grp, sizeof(h_grp) - 1); h_grp[sizeof(h_grp) - 1] = '\0'; + strncpy(h_val, val, sizeof(h_val) - 1); h_val[sizeof(h_val) - 1] = '\0'; + memset(h_use_yn, 0, sizeof(h_use_yn)); + memset(h_code_name, 0, sizeof(h_code_name)); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select use_yn , code_name from vl_common_code where code_group = $1 and code_value = $2 ", + ECPGt_char,(h_grp),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_val),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_use_yn),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_code_name),(long)41,(long)1,(41)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 51 "app/online/vl_ol_0010.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0010] 미등록 코드 grp=%s val=%s", grp, val); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1002"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_common_code"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1009"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 2) 사용여부 판정 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "CODEGRP", grp); + tx_buf_sets(ctx->out, "CODEVAL", val); + tx_buf_sets(ctx->out, "CODENAME", h_code_name); + tx_buf_sets(ctx->out, "USEYN", h_use_yn); + + if (memcmp(h_use_yn, "Y", 1) != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0010] 비활성 코드 grp=%s val=%s use=%s", + grp, val, h_use_yn); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1003"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0010] 코드 유효 grp=%s val=%s name=%s", + grp, val, h_code_name); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0010.pgc b/app/online/vl_ol_0010.pgc index aa8c5a9..db217d7 100644 --- a/app/online/vl_ol_0010.pgc +++ b/app/online/vl_ol_0010.pgc @@ -1,14 +1,16 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0010.pgc - 정합성 온라인 서비스 (VL_OL_0010) [tier=easy] + * vl_ol_0010.pgc - 코드값 유효성 검증 서비스 (VL_OL_0010) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 코드그룹 + 코드값을 공통코드(vl_common_code) 에서 조회하여 등록 여부와 + * 사용여부(use_yn='Y') 를 확인한다. 미등록이거나 비활성이면 불일치(VL10xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,32 +18,71 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0010, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_grp[16]; + char h_val[16]; + char h_use_yn[2]; + char h_code_name[41]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0010] 정합성 단건조회 서비스 진입"); + char grp[16], val[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0010] KEY 누락"); + tx_log(TX_LOG_INFO, "[VL_OL_0010] 코드값 검증 서비스 진입"); + + if (tx_buf_get(ctx->in, "CODEGRP", grp, sizeof(grp)) != TX_OK || + tx_buf_get(ctx->in, "CODEVAL", val, sizeof(val)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0010] 필수 필드 누락(CODEGRP/CODEVAL)"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1000"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0010] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 공통코드 조회 */ + strncpy(h_grp, grp, sizeof(h_grp) - 1); h_grp[sizeof(h_grp) - 1] = '\0'; + strncpy(h_val, val, sizeof(h_val) - 1); h_val[sizeof(h_val) - 1] = '\0'; + memset(h_use_yn, 0, sizeof(h_use_yn)); + memset(h_code_name, 0, sizeof(h_code_name)); + + EXEC SQL + SELECT use_yn, code_name + INTO :h_use_yn, :h_code_name + FROM vl_common_code + WHERE code_group = :h_grp + AND code_value = :h_val; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0010] 미등록 코드 grp=%s val=%s", grp, val); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1002"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_common_code"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1009"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 2) 사용여부 판정 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0010] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "CODEGRP", grp); + tx_buf_sets(ctx->out, "CODEVAL", val); + tx_buf_sets(ctx->out, "CODENAME", h_code_name); + tx_buf_sets(ctx->out, "USEYN", h_use_yn); + + if (memcmp(h_use_yn, "Y", 1) != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0010] 비활성 코드 grp=%s val=%s use=%s", + grp, val, h_use_yn); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1003"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0010] 코드 유효 grp=%s val=%s name=%s", + grp, val, h_code_name); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0011.c b/app/online/vl_ol_0011.c index c3fa365..44f2819 100644 --- a/app/online/vl_ol_0011.c +++ b/app/online/vl_ol_0011.c @@ -8,15 +8,18 @@ #line 1 "app/online/vl_ol_0011.pgc" /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0011.pgc - 정합성 온라인 서비스 (VL_OL_0011) [tier=medium] + * vl_ol_0011.pgc - 승인-매입 정합 검증 서비스 (VL_OL_0011) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인번호를 기준으로 승인원장(vl_approval) 과 매입원장(vl_purchase) 을 각각 + * 조회하여 금액 합계와 건수를 대사한다. 승인/매입 금액 또는 건수가 불일치하면 + * 미대사(VL11xx) 로 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,60 +92,119 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0011.pgc" +#line 18 "app/online/vl_ol_0011.pgc" TX_SERVICE(VL_OL_0011, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/vl_ol_0011.pgc" + char h_appr_no [ 16 ] ; + +#line 24 "app/online/vl_ol_0011.pgc" + long h_appr_amt ; + +#line 25 "app/online/vl_ol_0011.pgc" + long h_appr_cnt ; + +#line 26 "app/online/vl_ol_0011.pgc" + long h_pur_amt ; + +#line 27 "app/online/vl_ol_0011.pgc" + long h_pur_cnt ; +/* exec sql end declare section */ +#line 28 "app/online/vl_ol_0011.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0011] 정합성 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0011] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char approval[16]; + long amt_diff, cnt_diff; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0011] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0011] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_INFO, "[VL_OL_0011] 승인-매입 정합 검증 서비스 진입"); + + if (tx_buf_get(ctx->in, "APPRNO", approval, sizeof(approval)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0011] APPRNO 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1100"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_appr_no, approval, sizeof(h_appr_no) - 1); + h_appr_no[sizeof(h_appr_no) - 1] = '\0'; + h_appr_amt = 0; h_appr_cnt = 0; h_pur_amt = 0; h_pur_cnt = 0; - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0011] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 승인원장 집계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( SUM ( appr_amount ) , 0 ) , count ( * ) from vl_approval where approval_no = $1 ", + ECPGt_char,(h_appr_no),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_appr_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_appr_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 51 "app/online/vl_ol_0011.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT vl_approval"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1109"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); + /* 2) 매입원장 집계 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( SUM ( purchase_amount ) , 0 ) , count ( * ) from vl_purchase where approval_no = $1 ", + ECPGt_char,(h_appr_no),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_pur_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_pur_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 65 "app/online/vl_ol_0011.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT vl_purchase"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1119"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 대사 판정 */ + amt_diff = h_appr_amt - h_pur_amt; + cnt_diff = h_appr_cnt - h_pur_cnt; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0011] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "APPRNO", approval); + tx_buf_setlong(ctx->out, "APPRAMT", h_appr_amt); + tx_buf_setlong(ctx->out, "APPRCNT", h_appr_cnt); + tx_buf_setlong(ctx->out, "PURAMT", h_pur_amt); + tx_buf_setlong(ctx->out, "PURCNT", h_pur_cnt); + tx_buf_setlong(ctx->out, "AMTDIFF", amt_diff); + + if (h_appr_cnt == 0 && h_pur_cnt == 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0011] 승인/매입 원장 없음 appr=%s", approval); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1101"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (amt_diff != 0 || cnt_diff != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0011] 승인-매입 불일치 appr=%s amtdiff=%ld cntdiff=%ld", + approval, amt_diff, cnt_diff); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1103"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0011] 승인-매입 정합 appr=%s amt=%ld cnt=%ld", + approval, h_appr_amt, h_appr_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0011.pgc b/app/online/vl_ol_0011.pgc index 0d09b04..2577834 100644 --- a/app/online/vl_ol_0011.pgc +++ b/app/online/vl_ol_0011.pgc @@ -1,14 +1,17 @@ /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0011.pgc - 정합성 온라인 서비스 (VL_OL_0011) [tier=medium] + * vl_ol_0011.pgc - 승인-매입 정합 검증 서비스 (VL_OL_0011) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 승인번호를 기준으로 승인원장(vl_approval) 과 매입원장(vl_purchase) 을 각각 + * 조회하여 금액 합계와 건수를 대사한다. 승인/매입 금액 또는 건수가 불일치하면 + * 미대사(VL11xx) 로 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,55 +19,89 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0011, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_appr_no[16]; + long h_appr_amt; + long h_appr_cnt; + long h_pur_amt; + long h_pur_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0011] 정합성 등록 서비스 진입"); + char approval[16]; + long amt_diff, cnt_diff; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0011] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[VL_OL_0011] 승인-매입 정합 검증 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0011] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0011] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "APPRNO", approval, sizeof(approval)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0011] APPRNO 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1100"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_appr_no, approval, sizeof(h_appr_no) - 1); + h_appr_no[sizeof(h_appr_no) - 1] = '\0'; + h_appr_amt = 0; h_appr_cnt = 0; h_pur_amt = 0; h_pur_cnt = 0; - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0011] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 승인원장 집계 */ + EXEC SQL + SELECT COALESCE(SUM(appr_amount), 0), COUNT(*) + INTO :h_appr_amt, :h_appr_cnt + FROM vl_approval + WHERE approval_no = :h_appr_no; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT vl_approval"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1109"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); + /* 2) 매입원장 집계 */ + EXEC SQL + SELECT COALESCE(SUM(purchase_amount), 0), COUNT(*) + INTO :h_pur_amt, :h_pur_cnt + FROM vl_purchase + WHERE approval_no = :h_appr_no; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT vl_purchase"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1119"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 대사 판정 */ + amt_diff = h_appr_amt - h_pur_amt; + cnt_diff = h_appr_cnt - h_pur_cnt; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0011] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "APPRNO", approval); + tx_buf_setlong(ctx->out, "APPRAMT", h_appr_amt); + tx_buf_setlong(ctx->out, "APPRCNT", h_appr_cnt); + tx_buf_setlong(ctx->out, "PURAMT", h_pur_amt); + tx_buf_setlong(ctx->out, "PURCNT", h_pur_cnt); + tx_buf_setlong(ctx->out, "AMTDIFF", amt_diff); + + if (h_appr_cnt == 0 && h_pur_cnt == 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0011] 승인/매입 원장 없음 appr=%s", approval); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1101"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (amt_diff != 0 || cnt_diff != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0011] 승인-매입 불일치 appr=%s amtdiff=%ld cntdiff=%ld", + approval, amt_diff, cnt_diff); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1103"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0011] 승인-매입 정합 appr=%s amt=%ld cnt=%ld", + approval, h_appr_amt, h_appr_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0012.c b/app/online/vl_ol_0012.c index a7ca898..9390483 100644 --- a/app/online/vl_ol_0012.c +++ b/app/online/vl_ol_0012.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0012.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0012.pgc - 정합성 온라인 서비스 (VL_OL_0012) [tier=easy] + * vl_ol_0012.pgc - 가맹점 한도 정합 검증 서비스 (VL_OL_0012) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 일한도(vl_limit) 를 조회하고, 당일 원장(vl_ledger) 누적금액을 합산한 + * 뒤 신규 요청금액을 더해 한도 초과 여부를 판정한다. 초과 시 불일치(VL12xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,37 +91,121 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0012.pgc" +#line 17 "app/online/vl_ol_0012.pgc" TX_SERVICE(VL_OL_0012, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 22 "app/online/vl_ol_0012.pgc" + char h_merch [ 16 ] ; + +#line 23 "app/online/vl_ol_0012.pgc" + char h_bizdate [ 9 ] ; + +#line 24 "app/online/vl_ol_0012.pgc" + long h_daily_limit ; + +#line 25 "app/online/vl_ol_0012.pgc" + long h_used_amt ; +/* exec sql end declare section */ +#line 26 "app/online/vl_ol_0012.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0012] 정합성 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0012] KEY 누락"); + char merch[16], bizdate[16]; + long amount = 0, projected; + + tx_log(TX_LOG_INFO, "[VL_OL_0012] 한도 정합 검증 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0012] 필수 필드 누락(MERCHID/BIZDATE)"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1200"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (!date_is_valid(bizdate) || amount < 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0012] 입력값 오류 date=%s amt=%ld", bizdate, amount); + tx_buf_sets(ctx->out, "ERRCODE", "VL1201"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch, merch, sizeof(h_merch) - 1); h_merch[sizeof(h_merch) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + h_daily_limit = 0; + h_used_amt = 0; + + /* 1) 가맹점 일한도 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select daily_limit from vl_limit where merch_id = $1 ", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_daily_limit),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 59 "app/online/vl_ol_0012.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0012] 한도 미설정 merch=%s", merch); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1202"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_limit"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1209"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 2) 당일 누적금액 합산 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( SUM ( amount ) , 0 ) from vl_ledger where merch_id = $1 and txn_date = $2 ", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_used_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 81 "app/online/vl_ol_0012.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT SUM vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1219"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 한도 판정 */ + projected = h_used_amt + amount; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "DAILYLIMIT", h_daily_limit); + tx_buf_setlong(ctx->out, "USEDAMT", h_used_amt); + tx_buf_setlong(ctx->out, "REQAMT", amount); + tx_buf_setlong(ctx->out, "PROJECTED", projected); + + if (projected > h_daily_limit) { + tx_log(TX_LOG_WARN, "[VL_OL_0012] 한도 초과 merch=%s proj=%ld limit=%ld", + merch, projected, h_daily_limit); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1203"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0012] 한도 이내 merch=%s proj=%ld limit=%ld", + merch, projected, h_daily_limit); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0012.pgc b/app/online/vl_ol_0012.pgc index 992de83..658ef18 100644 --- a/app/online/vl_ol_0012.pgc +++ b/app/online/vl_ol_0012.pgc @@ -1,14 +1,16 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0012.pgc - 정합성 온라인 서비스 (VL_OL_0012) [tier=easy] + * vl_ol_0012.pgc - 가맹점 한도 정합 검증 서비스 (VL_OL_0012) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점 일한도(vl_limit) 를 조회하고, 당일 원장(vl_ledger) 누적금액을 합산한 + * 뒤 신규 요청금액을 더해 한도 초과 여부를 판정한다. 초과 시 불일치(VL12xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,32 +18,97 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0012, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16]; + char h_bizdate[9]; + long h_daily_limit; + long h_used_amt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0012] 정합성 단건조회 서비스 진입"); + char merch[16], bizdate[16]; + long amount = 0, projected; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0012] KEY 누락"); + tx_log(TX_LOG_INFO, "[VL_OL_0012] 한도 정합 검증 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || + tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0012] 필수 필드 누락(MERCHID/BIZDATE)"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1200"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + tx_buf_getlong(ctx->in, "AMOUNT", &amount); + if (!date_is_valid(bizdate) || amount < 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0012] 입력값 오류 date=%s amt=%ld", bizdate, amount); + tx_buf_sets(ctx->out, "ERRCODE", "VL1201"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0012] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_merch, merch, sizeof(h_merch) - 1); h_merch[sizeof(h_merch) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + h_daily_limit = 0; + h_used_amt = 0; + + /* 1) 가맹점 일한도 조회 */ + EXEC SQL + SELECT daily_limit + INTO :h_daily_limit + FROM vl_limit + WHERE merch_id = :h_merch; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0012] 한도 미설정 merch=%s", merch); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1202"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_limit"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1209"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 2) 당일 누적금액 합산 */ + EXEC SQL + SELECT COALESCE(SUM(amount), 0) + INTO :h_used_amt + FROM vl_ledger + WHERE merch_id = :h_merch + AND txn_date = :h_bizdate; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT SUM vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1219"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 한도 판정 */ + projected = h_used_amt + amount; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0012] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "DAILYLIMIT", h_daily_limit); + tx_buf_setlong(ctx->out, "USEDAMT", h_used_amt); + tx_buf_setlong(ctx->out, "REQAMT", amount); + tx_buf_setlong(ctx->out, "PROJECTED", projected); + + if (projected > h_daily_limit) { + tx_log(TX_LOG_WARN, "[VL_OL_0012] 한도 초과 merch=%s proj=%ld limit=%ld", + merch, projected, h_daily_limit); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1203"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0012] 한도 이내 merch=%s proj=%ld limit=%ld", + merch, projected, h_daily_limit); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0013.c b/app/online/vl_ol_0013.c index 0c263bf..f904b3b 100644 --- a/app/online/vl_ol_0013.c +++ b/app/online/vl_ol_0013.c @@ -8,15 +8,18 @@ #line 1 "app/online/vl_ol_0013.pgc" /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0013.pgc - 정합성 온라인 서비스 (VL_OL_0013) [tier=medium] + * vl_ol_0013.pgc - 수수료 정합 검증 서비스 (VL_OL_0013) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장(vl_rec_select) 의 거래금액/수수료를 확보하고, 가맹점 수수료율 테이블 + * (vl_fee_rate, 만분율 bps) 을 조회하여 기대수수료(금액*요율/10000) 를 계산한다. + * 원장 수수료와 기대수수료가 다르면 불일치(VL13xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,60 +92,99 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0013.pgc" +#line 18 "app/online/vl_ol_0013.pgc" TX_SERVICE(VL_OL_0013, ctx) { + /* exec sql begin declare section */ + + + +#line 23 "app/online/vl_ol_0013.pgc" + char h_merch [ 16 ] ; + +#line 24 "app/online/vl_ol_0013.pgc" + long h_fee_rate ; +/* exec sql end declare section */ +#line 25 "app/online/vl_ol_0013.pgc" + + vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + long expected_fee, fee_diff; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0013] 정합성 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0013] 수수료 정합 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0013] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0013] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0013] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0013] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1300"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 1) 원장 금액/수수료 확보 */ memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = vl_rec_insert(&rec); + rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0013] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[VL_OL_0013] 원장 조회 실패 key=%s rc=%d", key, rc); + tx_buf_sets(ctx->out, "ERRCODE", "VL1301"); tx_return(ctx, rc, ctx->out); return; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); + /* 2) 가맹점 수수료율(bps) 조회 */ + strncpy(h_merch, rec.merch_id, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + h_fee_rate = 0; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select fee_rate_bps from vl_fee_rate where merch_id = $1 ", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_fee_rate),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 60 "app/online/vl_ol_0013.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0013] 수수료율 미설정 merch=%s", rec.merch_id); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1302"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_fee_rate"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1309"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 기대수수료 계산 및 판정 (만분율) */ + expected_fee = (rec.amount * h_fee_rate) / 10000; + fee_diff = rec.fee - expected_fee; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0013] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_setlong(ctx->out, "FEERATE", h_fee_rate); + tx_buf_setlong(ctx->out, "LEDGERFEE", rec.fee); + tx_buf_setlong(ctx->out, "EXPECTEDFEE", expected_fee); + tx_buf_setlong(ctx->out, "FEEDIFF", fee_diff); + + if (fee_diff != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0013] 수수료 불일치 key=%s ledger=%ld expected=%ld", + key, rec.fee, expected_fee); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1303"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0013] 수수료 정합 key=%s fee=%ld", key, rec.fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0013.pgc b/app/online/vl_ol_0013.pgc index 48f7b83..666e300 100644 --- a/app/online/vl_ol_0013.pgc +++ b/app/online/vl_ol_0013.pgc @@ -1,14 +1,17 @@ /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0013.pgc - 정합성 온라인 서비스 (VL_OL_0013) [tier=medium] + * vl_ol_0013.pgc - 수수료 정합 검증 서비스 (VL_OL_0013) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장(vl_rec_select) 의 거래금액/수수료를 확보하고, 가맹점 수수료율 테이블 + * (vl_fee_rate, 만분율 bps) 을 조회하여 기대수수료(금액*요율/10000) 를 계산한다. + * 원장 수수료와 기대수수료가 다르면 불일치(VL13xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,55 +19,84 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0013, ctx) { + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16]; + long h_fee_rate; + EXEC SQL END DECLARE SECTION; + vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + long expected_fee, fee_diff; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0013] 정합성 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0013] 수수료 정합 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0013] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0013] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0013] 영업일 오류 date=%s", bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0013] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1300"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 1) 원장 금액/수수료 확보 */ memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = vl_rec_insert(&rec); + rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0013] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[VL_OL_0013] 원장 조회 실패 key=%s rc=%d", key, rc); + tx_buf_sets(ctx->out, "ERRCODE", "VL1301"); tx_return(ctx, rc, ctx->out); return; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); + /* 2) 가맹점 수수료율(bps) 조회 */ + strncpy(h_merch, rec.merch_id, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + h_fee_rate = 0; + + EXEC SQL + SELECT fee_rate_bps + INTO :h_fee_rate + FROM vl_fee_rate + WHERE merch_id = :h_merch; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0013] 수수료율 미설정 merch=%s", rec.merch_id); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1302"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_fee_rate"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1309"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 기대수수료 계산 및 판정 (만분율) */ + expected_fee = (rec.amount * h_fee_rate) / 10000; + fee_diff = rec.fee - expected_fee; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0013] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); + tx_buf_setlong(ctx->out, "FEERATE", h_fee_rate); + tx_buf_setlong(ctx->out, "LEDGERFEE", rec.fee); + tx_buf_setlong(ctx->out, "EXPECTEDFEE", expected_fee); + tx_buf_setlong(ctx->out, "FEEDIFF", fee_diff); + + if (fee_diff != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0013] 수수료 불일치 key=%s ledger=%ld expected=%ld", + key, rec.fee, expected_fee); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1303"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0013] 수수료 정합 key=%s fee=%ld", key, rec.fee); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0014.c b/app/online/vl_ol_0014.c index e18a9d9..7b6dc73 100644 --- a/app/online/vl_ol_0014.c +++ b/app/online/vl_ol_0014.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0014.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0014.pgc - 정합성 온라인 서비스 (VL_OL_0014) [tier=easy] + * vl_ol_0014.pgc - 정산 정합 검증 서비스 (VL_OL_0014) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산원장(vl_settle) 의 정산금액이 매입집계(vl_purchase_sum) 의 (매입액 - 수수료) + * 와 일치하는지 대사한다. 정산금액이 계산된 순지급액과 다르면 불일치(VL14xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,37 +91,119 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0014.pgc" +#line 17 "app/online/vl_ol_0014.pgc" TX_SERVICE(VL_OL_0014, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 22 "app/online/vl_ol_0014.pgc" + char h_settle_key [ 16 ] ; + +#line 23 "app/online/vl_ol_0014.pgc" + long h_settle_amt ; + +#line 24 "app/online/vl_ol_0014.pgc" + long h_purchase_amt ; + +#line 25 "app/online/vl_ol_0014.pgc" + long h_fee_amt ; +/* exec sql end declare section */ +#line 26 "app/online/vl_ol_0014.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0014] 정합성 단건조회 서비스 진입"); + + char key[16]; + long net_amt, diff; + + tx_log(TX_LOG_INFO, "[VL_OL_0014] 정산 정합 검증 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[VL_OL_0014] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1400"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_settle_key, key, sizeof(h_settle_key) - 1); + h_settle_key[sizeof(h_settle_key) - 1] = '\0'; + h_settle_amt = 0; h_purchase_amt = 0; h_fee_amt = 0; + + /* 1) 정산금액 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select settle_amount from vl_settle where settle_key = $1 ", + ECPGt_char,(h_settle_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settle_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 49 "app/online/vl_ol_0014.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0014] 정산원장 없음 key=%s", key); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1401"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_settle"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1409"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 2) 매입집계(매입액/수수료) 조회 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( purchase_amount , 0 ) , coalesce ( fee_amount , 0 ) from vl_purchase_sum where settle_key = $1 ", + ECPGt_char,(h_settle_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_purchase_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fee_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 70 "app/online/vl_ol_0014.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0014] 매입집계 없음 key=%s", key); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1402"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_purchase_sum"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1419"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 순지급액 대사 */ + net_amt = h_purchase_amt - h_fee_amt; + diff = h_settle_amt - net_amt; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "SETTLEAMT", h_settle_amt); + tx_buf_setlong(ctx->out, "PURCHASEAMT", h_purchase_amt); + tx_buf_setlong(ctx->out, "FEEAMT", h_fee_amt); + tx_buf_setlong(ctx->out, "NETAMT", net_amt); + tx_buf_setlong(ctx->out, "DIFF", diff); + + if (diff != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0014] 정산 불일치 key=%s settle=%ld net=%ld", + key, h_settle_amt, net_amt); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1403"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0014] 정산 정합 key=%s settle=%ld", key, h_settle_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0014.pgc b/app/online/vl_ol_0014.pgc index 5288f97..a86cfbd 100644 --- a/app/online/vl_ol_0014.pgc +++ b/app/online/vl_ol_0014.pgc @@ -1,14 +1,16 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0014.pgc - 정합성 온라인 서비스 (VL_OL_0014) [tier=easy] + * vl_ol_0014.pgc - 정산 정합 검증 서비스 (VL_OL_0014) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 정산원장(vl_settle) 의 정산금액이 매입집계(vl_purchase_sum) 의 (매입액 - 수수료) + * 와 일치하는지 대사한다. 정산금액이 계산된 순지급액과 다르면 불일치(VL14xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,32 +18,94 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0014, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_settle_key[16]; + long h_settle_amt; + long h_purchase_amt; + long h_fee_amt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0014] 정합성 단건조회 서비스 진입"); + char key[16]; + long net_amt, diff; + + tx_log(TX_LOG_INFO, "[VL_OL_0014] 정산 정합 검증 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[VL_OL_0014] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1400"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0014] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_settle_key, key, sizeof(h_settle_key) - 1); + h_settle_key[sizeof(h_settle_key) - 1] = '\0'; + h_settle_amt = 0; h_purchase_amt = 0; h_fee_amt = 0; + + /* 1) 정산금액 조회 */ + EXEC SQL + SELECT settle_amount + INTO :h_settle_amt + FROM vl_settle + WHERE settle_key = :h_settle_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0014] 정산원장 없음 key=%s", key); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1401"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_settle"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1409"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 2) 매입집계(매입액/수수료) 조회 */ + EXEC SQL + SELECT COALESCE(purchase_amount, 0), COALESCE(fee_amount, 0) + INTO :h_purchase_amt, :h_fee_amt + FROM vl_purchase_sum + WHERE settle_key = :h_settle_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0014] 매입집계 없음 key=%s", key); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1402"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_purchase_sum"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1419"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 순지급액 대사 */ + net_amt = h_purchase_amt - h_fee_amt; + diff = h_settle_amt - net_amt; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0014] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "SETTLEAMT", h_settle_amt); + tx_buf_setlong(ctx->out, "PURCHASEAMT", h_purchase_amt); + tx_buf_setlong(ctx->out, "FEEAMT", h_fee_amt); + tx_buf_setlong(ctx->out, "NETAMT", net_amt); + tx_buf_setlong(ctx->out, "DIFF", diff); + + if (diff != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0014] 정산 불일치 key=%s settle=%ld net=%ld", + key, h_settle_amt, net_amt); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1403"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0014] 정산 정합 key=%s settle=%ld", key, h_settle_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0015.c b/app/online/vl_ol_0015.c index 58294fc..37c4f78 100644 --- a/app/online/vl_ol_0015.c +++ b/app/online/vl_ol_0015.c @@ -8,15 +8,18 @@ #line 1 "app/online/vl_ol_0015.pgc" /* @tier hard @module vl @transform validation-online */ /* - * vl_ol_0015.pgc - 정합성 온라인 서비스 (VL_OL_0015) [tier=hard] + * vl_ol_0015.pgc - 원장-집계 정합 검증 서비스 (VL_OL_0015) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 단건(vl_rec_select) 이 일자별 집계테이블(vl_daily_agg) 에 정상 반영되었는지 + * 대사한다. 집계행의 반영여부(applied_yn) 와 집계금액에 해당 건이 포함되었는지를 + * 확인하여, 미반영이거나 집계금액이 원장금액 미만이면 불일치(VL15xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,88 +92,126 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0015.pgc" +#line 18 "app/online/vl_ol_0015.pgc" TX_SERVICE(VL_OL_0015, ctx) { + /* exec sql begin declare section */ + + + + + + +#line 23 "app/online/vl_ol_0015.pgc" + char h_merch [ 16 ] ; + +#line 24 "app/online/vl_ol_0015.pgc" + char h_bizdate [ 9 ] ; + +#line 25 "app/online/vl_ol_0015.pgc" + char h_applied_yn [ 2 ] ; + +#line 26 "app/online/vl_ol_0015.pgc" + long h_agg_amount ; + +#line 27 "app/online/vl_ol_0015.pgc" + long h_agg_cnt ; +/* exec sql end declare section */ +#line 28 "app/online/vl_ol_0015.pgc" + + vl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; + char key[16]; int rc; - TXBUF *sub_in; - TXBUF *sub_out; - tx_log(TX_LOG_INFO, "[VL_OL_0015] 정합성 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0015] 원장-집계 정합 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0015] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0015] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0015] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1500"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - + /* 1) 원장 단건 확보 */ memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = vl_rec_insert(&rec); + rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0015] 원장반영 실패 rc=%d", rc); - tx_abort(); + tx_log(TX_LOG_WARN, "[VL_OL_0015] 원장 조회 실패 key=%s rc=%d", key, rc); + tx_buf_sets(ctx->out, "ERRCODE", "VL1501"); tx_return(ctx, rc, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + /* 2) 집계행 조회 (가맹점 + 영업일) */ + strncpy(h_merch, rec.merch_id, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + strncpy(h_bizdate, rec.biz_date, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + memset(h_applied_yn, 0, sizeof(h_applied_yn)); + h_agg_amount = 0; + h_agg_cnt = 0; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select applied_yn , agg_amount , agg_count from vl_daily_agg where biz_date = $1 and merch_id = $2 ", + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_applied_yn),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_agg_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_agg_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 67 "app/online/vl_ol_0015.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0015] 집계행 없음 merch=%s date=%s", + rec.merch_id, rec.biz_date); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1502"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_daily_agg"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1509"); + tx_return(ctx, TX_EDB, ctx->out); + return; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("VL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[VL_OL_0015] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (vl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - + /* 3) 반영여부 판정 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[VL_OL_0015] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_sets(ctx->out, "BIZDATE", rec.biz_date); + tx_buf_setlong(ctx->out, "LEDGERAMT", rec.amount); + tx_buf_sets(ctx->out, "APPLIEDYN", h_applied_yn); + tx_buf_setlong(ctx->out, "AGGAMT", h_agg_amount); + tx_buf_setlong(ctx->out, "AGGCNT", h_agg_cnt); + + if (memcmp(h_applied_yn, "Y", 1) != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0015] 집계 미반영 key=%s applied=%s", + key, h_applied_yn); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1503"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + if (h_agg_amount < rec.amount || h_agg_cnt < 1) { + tx_log(TX_LOG_WARN, "[VL_OL_0015] 집계금액 부족 key=%s agg=%ld ledger=%ld", + key, h_agg_amount, rec.amount); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1504"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0015] 원장-집계 정합 key=%s agg=%ld", key, h_agg_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0015.pgc b/app/online/vl_ol_0015.pgc index 5d94ada..f30713c 100644 --- a/app/online/vl_ol_0015.pgc +++ b/app/online/vl_ol_0015.pgc @@ -1,14 +1,17 @@ /* @tier hard @module vl @transform validation-online */ /* - * vl_ol_0015.pgc - 정합성 온라인 서비스 (VL_OL_0015) [tier=hard] + * vl_ol_0015.pgc - 원장-집계 정합 검증 서비스 (VL_OL_0015) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장 단건(vl_rec_select) 이 일자별 집계테이블(vl_daily_agg) 에 정상 반영되었는지 + * 대사한다. 집계행의 반영여부(applied_yn) 와 집계금액에 해당 건이 포함되었는지를 + * 확인하여, 미반영이거나 집계금액이 원장금액 미만이면 불일치(VL15xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,83 +19,97 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0015, ctx) { + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16]; + char h_bizdate[9]; + char h_applied_yn[2]; + long h_agg_amount; + long h_agg_cnt; + EXEC SQL END DECLARE SECTION; + vl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; + char key[16]; int rc; - TXBUF *sub_in; - TXBUF *sub_out; - tx_log(TX_LOG_INFO, "[VL_OL_0015] 정합성 트랜잭션 오케스트레이션 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0015] 원장-집계 정합 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0015] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0015] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0015] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1500"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); - return; - } - + /* 1) 원장 단건 확보 */ memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; - - rc = vl_rec_insert(&rec); + rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0015] 원장반영 실패 rc=%d", rc); - tx_abort(); + tx_log(TX_LOG_WARN, "[VL_OL_0015] 원장 조회 실패 key=%s rc=%d", key, rc); + tx_buf_sets(ctx->out, "ERRCODE", "VL1501"); tx_return(ctx, rc, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + /* 2) 집계행 조회 (가맹점 + 영업일) */ + strncpy(h_merch, rec.merch_id, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + strncpy(h_bizdate, rec.biz_date, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + memset(h_applied_yn, 0, sizeof(h_applied_yn)); + h_agg_amount = 0; + h_agg_cnt = 0; + + EXEC SQL + SELECT applied_yn, agg_amount, agg_count + INTO :h_applied_yn, :h_agg_amount, :h_agg_cnt + FROM vl_daily_agg + WHERE biz_date = :h_bizdate + AND merch_id = :h_merch; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0015] 집계행 없음 merch=%s date=%s", + rec.merch_id, rec.biz_date); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1502"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_daily_agg"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1509"); + tx_return(ctx, TX_EDB, ctx->out); + return; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("VL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[VL_OL_0015] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (vl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - + /* 3) 반영여부 판정 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[VL_OL_0015] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); + tx_buf_sets(ctx->out, "BIZDATE", rec.biz_date); + tx_buf_setlong(ctx->out, "LEDGERAMT", rec.amount); + tx_buf_sets(ctx->out, "APPLIEDYN", h_applied_yn); + tx_buf_setlong(ctx->out, "AGGAMT", h_agg_amount); + tx_buf_setlong(ctx->out, "AGGCNT", h_agg_cnt); + + if (memcmp(h_applied_yn, "Y", 1) != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0015] 집계 미반영 key=%s applied=%s", + key, h_applied_yn); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1503"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + if (h_agg_amount < rec.amount || h_agg_cnt < 1) { + tx_log(TX_LOG_WARN, "[VL_OL_0015] 집계금액 부족 key=%s agg=%ld ledger=%ld", + key, h_agg_amount, rec.amount); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1504"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0015] 원장-집계 정합 key=%s agg=%ld", key, h_agg_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0016.c b/app/online/vl_ol_0016.c index 835480e..862731a 100644 --- a/app/online/vl_ol_0016.c +++ b/app/online/vl_ol_0016.c @@ -8,15 +8,18 @@ #line 1 "app/online/vl_ol_0016.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0016.pgc - 정합성 온라인 서비스 (VL_OL_0016) [tier=easy] + * vl_ol_0016.pgc - 전문 포맷 검증 서비스 (VL_OL_0016) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 RAW 전문을 고정길이 레이아웃(acq_msg_t) 으로 언팩하여 길이/필수필드 + * (msg_validate) 를 검증하고, 전문유형코드(msg_type) 를 전문유형 마스터 + * (vl_msg_type) 에서 조회하여 등록/사용 여부를 확인한다. 실패 시 불일치(VL16xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,37 +92,111 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0016.pgc" +#line 18 "app/online/vl_ol_0016.pgc" TX_SERVICE(VL_OL_0016, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 23 "app/online/vl_ol_0016.pgc" + char h_msg_type [ 5 ] ; + +#line 24 "app/online/vl_ol_0016.pgc" + char h_use_yn [ 2 ] ; + +#line 25 "app/online/vl_ol_0016.pgc" + char h_type_name [ 41 ] ; +/* exec sql end declare section */ +#line 26 "app/online/vl_ol_0016.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0016] 정합성 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0016] KEY 누락"); + acq_msg_t msg; + char raw[128]; + int rawlen; + + tx_log(TX_LOG_INFO, "[VL_OL_0016] 전문 포맷 검증 서비스 진입"); + + if (tx_buf_get(ctx->in, "RAW", raw, sizeof(raw)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0016] RAW 전문 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1600"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0016] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 길이 검증 */ + rawlen = (int)strlen(raw); + if (rawlen < MSG_ACQ_LEN) { + tx_log(TX_LOG_WARN, "[VL_OL_0016] 전문 길이 부족 len=%d need=%d", + rawlen, MSG_ACQ_LEN); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1601"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 2) 언팩 + 필수필드 검증 */ + memset(&msg, 0, sizeof(msg)); + if (msg_unpack(&msg, raw, rawlen) != 0 || msg_validate(&msg) != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0016] 전문 검증 실패(msg_validate)"); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1602"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 3) 전문유형코드 유효성 조회 */ + memset(h_msg_type, 0, sizeof(h_msg_type)); + memcpy(h_msg_type, msg.msg_type, FLD_MSG_TYPE_LEN); + h_msg_type[FLD_MSG_TYPE_LEN] = '\0'; + memset(h_use_yn, 0, sizeof(h_use_yn)); + memset(h_type_name, 0, sizeof(h_type_name)); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select use_yn , type_name from vl_msg_type where msg_type = $1 ", + ECPGt_char,(h_msg_type),(long)5,(long)1,(5)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_use_yn),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_type_name),(long)41,(long)1,(41)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 73 "app/online/vl_ol_0016.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0016] 미등록 전문유형 type=%s", h_msg_type); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1603"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_msg_type"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1609"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0016] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MSGTYPE", h_msg_type); + tx_buf_sets(ctx->out, "TYPENAME", h_type_name); + tx_buf_sets(ctx->out, "USEYN", h_use_yn); + tx_buf_setlong(ctx->out, "RAWLEN", (long)rawlen); + + if (memcmp(h_use_yn, "Y", 1) != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0016] 비활성 전문유형 type=%s use=%s", + h_msg_type, h_use_yn); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1604"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0016] 전문 포맷 정상 type=%s name=%s", + h_msg_type, h_type_name); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0016.pgc b/app/online/vl_ol_0016.pgc index 5ff1e72..02c2efa 100644 --- a/app/online/vl_ol_0016.pgc +++ b/app/online/vl_ol_0016.pgc @@ -1,14 +1,17 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0016.pgc - 정합성 온라인 서비스 (VL_OL_0016) [tier=easy] + * vl_ol_0016.pgc - 전문 포맷 검증 서비스 (VL_OL_0016) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 RAW 전문을 고정길이 레이아웃(acq_msg_t) 으로 언팩하여 길이/필수필드 + * (msg_validate) 를 검증하고, 전문유형코드(msg_type) 를 전문유형 마스터 + * (vl_msg_type) 에서 조회하여 등록/사용 여부를 확인한다. 실패 시 불일치(VL16xx). */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,32 +19,91 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0016, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_msg_type[5]; + char h_use_yn[2]; + char h_type_name[41]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0016] 정합성 단건조회 서비스 진입"); + acq_msg_t msg; + char raw[128]; + int rawlen; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0016] KEY 누락"); + tx_log(TX_LOG_INFO, "[VL_OL_0016] 전문 포맷 검증 서비스 진입"); + + if (tx_buf_get(ctx->in, "RAW", raw, sizeof(raw)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0016] RAW 전문 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1600"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0016] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 1) 길이 검증 */ + rawlen = (int)strlen(raw); + if (rawlen < MSG_ACQ_LEN) { + tx_log(TX_LOG_WARN, "[VL_OL_0016] 전문 길이 부족 len=%d need=%d", + rawlen, MSG_ACQ_LEN); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1601"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 2) 언팩 + 필수필드 검증 */ + memset(&msg, 0, sizeof(msg)); + if (msg_unpack(&msg, raw, rawlen) != 0 || msg_validate(&msg) != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0016] 전문 검증 실패(msg_validate)"); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1602"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + /* 3) 전문유형코드 유효성 조회 */ + memset(h_msg_type, 0, sizeof(h_msg_type)); + memcpy(h_msg_type, msg.msg_type, FLD_MSG_TYPE_LEN); + h_msg_type[FLD_MSG_TYPE_LEN] = '\0'; + memset(h_use_yn, 0, sizeof(h_use_yn)); + memset(h_type_name, 0, sizeof(h_type_name)); + + EXEC SQL + SELECT use_yn, type_name + INTO :h_use_yn, :h_type_name + FROM vl_msg_type + WHERE msg_type = :h_msg_type; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0016] 미등록 전문유형 type=%s", h_msg_type); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1603"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_msg_type"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1609"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0016] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MSGTYPE", h_msg_type); + tx_buf_sets(ctx->out, "TYPENAME", h_type_name); + tx_buf_sets(ctx->out, "USEYN", h_use_yn); + tx_buf_setlong(ctx->out, "RAWLEN", (long)rawlen); + + if (memcmp(h_use_yn, "Y", 1) != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0016] 비활성 전문유형 type=%s use=%s", + h_msg_type, h_use_yn); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1604"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0016] 전문 포맷 정상 type=%s name=%s", + h_msg_type, h_type_name); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0017.c b/app/online/vl_ol_0017.c index a41c3ad..90d33fa 100644 --- a/app/online/vl_ol_0017.c +++ b/app/online/vl_ol_0017.c @@ -8,15 +8,18 @@ #line 1 "app/online/vl_ol_0017.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0017.pgc - 정합성 온라인 서비스 (VL_OL_0017) [tier=easy] + * vl_ol_0017.pgc - 참조무결성 검증 서비스 (VL_OL_0017) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장(vl_rec_select) 의 가맹점ID 가 가맹점 마스터(mm_merchant) 에, 입력 카드사코드 + * 가 발급사 마스터(vl_issuer) 에 각각 존재하는지 두 건의 참조를 검증한다. 어느 한쪽 + * 이라도 부모행이 없으면 참조무결성 위반(VL17xx) 으로 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,37 +92,121 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0017.pgc" +#line 18 "app/online/vl_ol_0017.pgc" TX_SERVICE(VL_OL_0017, ctx) { + /* exec sql begin declare section */ + + + + + +#line 23 "app/online/vl_ol_0017.pgc" + char h_merch [ 16 ] ; + +#line 24 "app/online/vl_ol_0017.pgc" + char h_issuer [ 8 ] ; + +#line 25 "app/online/vl_ol_0017.pgc" + long h_merch_cnt ; + +#line 26 "app/online/vl_ol_0017.pgc" + long h_issuer_cnt ; +/* exec sql end declare section */ +#line 27 "app/online/vl_ol_0017.pgc" + + vl_rec_t rec; - char key[16]; + char key[16], issuer[8]; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0017] 정합성 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0017] 참조무결성 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0017] KEY 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "ISSUER", issuer, sizeof(issuer)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0017] 필수 필드 누락(KEY/ISSUER)"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1700"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 1) 원장에서 가맹점ID 확보 */ memset(&rec, 0, sizeof(rec)); rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0017] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[VL_OL_0017] 원장 조회 실패 key=%s rc=%d", key, rc); + tx_buf_sets(ctx->out, "ERRCODE", "VL1701"); tx_return(ctx, rc, ctx->out); return; } + strncpy(h_merch, rec.merch_id, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + strncpy(h_issuer, issuer, sizeof(h_issuer) - 1); + h_issuer[sizeof(h_issuer) - 1] = '\0'; + h_merch_cnt = 0; + h_issuer_cnt = 0; + + /* 2) 가맹점 참조 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from mm_merchant where merch_id = $1 ", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_merch_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 65 "app/online/vl_ol_0017.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT COUNT mm_merchant"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1709"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 발급사(카드사코드) 참조 확인 */ + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from vl_issuer where issuer_code = $1 ", + ECPGt_char,(h_issuer),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_issuer_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 79 "app/online/vl_ol_0017.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT COUNT vl_issuer"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1719"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 4) 무결성 판정 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "KEY", key); tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0017] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "ISSUER", issuer); + tx_buf_setlong(ctx->out, "MERCHREF", h_merch_cnt); + tx_buf_setlong(ctx->out, "ISSUERREF", h_issuer_cnt); + + if (h_merch_cnt < 1) { + tx_log(TX_LOG_WARN, "[VL_OL_0017] 가맹점 참조 위반 merch=%s", rec.merch_id); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1703"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + if (h_issuer_cnt < 1) { + tx_log(TX_LOG_WARN, "[VL_OL_0017] 발급사 참조 위반 issuer=%s", issuer); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1704"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0017] 참조무결성 정상 merch=%s issuer=%s", + rec.merch_id, issuer); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0017.pgc b/app/online/vl_ol_0017.pgc index c74b906..c8301e7 100644 --- a/app/online/vl_ol_0017.pgc +++ b/app/online/vl_ol_0017.pgc @@ -1,14 +1,17 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0017.pgc - 정합성 온라인 서비스 (VL_OL_0017) [tier=easy] + * vl_ol_0017.pgc - 참조무결성 검증 서비스 (VL_OL_0017) * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 원장(vl_rec_select) 의 가맹점ID 가 가맹점 마스터(mm_merchant) 에, 입력 카드사코드 + * 가 발급사 마스터(vl_issuer) 에 각각 존재하는지 두 건의 참조를 검증한다. 어느 한쪽 + * 이라도 부모행이 없으면 참조무결성 위반(VL17xx) 으로 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,32 +19,98 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0017, ctx) { + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16]; + char h_issuer[8]; + long h_merch_cnt; + long h_issuer_cnt; + EXEC SQL END DECLARE SECTION; + vl_rec_t rec; - char key[16]; + char key[16], issuer[8]; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0017] 정합성 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0017] 참조무결성 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0017] KEY 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "ISSUER", issuer, sizeof(issuer)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0017] 필수 필드 누락(KEY/ISSUER)"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1700"); tx_return(ctx, TX_EINVAL, ctx->out); return; } + /* 1) 원장에서 가맹점ID 확보 */ memset(&rec, 0, sizeof(rec)); rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0017] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[VL_OL_0017] 원장 조회 실패 key=%s rc=%d", key, rc); + tx_buf_sets(ctx->out, "ERRCODE", "VL1701"); tx_return(ctx, rc, ctx->out); return; } + strncpy(h_merch, rec.merch_id, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + strncpy(h_issuer, issuer, sizeof(h_issuer) - 1); + h_issuer[sizeof(h_issuer) - 1] = '\0'; + h_merch_cnt = 0; + h_issuer_cnt = 0; + + /* 2) 가맹점 참조 확인 */ + EXEC SQL + SELECT COUNT(*) + INTO :h_merch_cnt + FROM mm_merchant + WHERE merch_id = :h_merch; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT COUNT mm_merchant"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1709"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 발급사(카드사코드) 참조 확인 */ + EXEC SQL + SELECT COUNT(*) + INTO :h_issuer_cnt + FROM vl_issuer + WHERE issuer_code = :h_issuer; + + if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { + TX_DBIO_LOG_ERR("SELECT COUNT vl_issuer"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1719"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 4) 무결성 판정 */ tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); + tx_buf_sets(ctx->out, "KEY", key); tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0017] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "ISSUER", issuer); + tx_buf_setlong(ctx->out, "MERCHREF", h_merch_cnt); + tx_buf_setlong(ctx->out, "ISSUERREF", h_issuer_cnt); + + if (h_merch_cnt < 1) { + tx_log(TX_LOG_WARN, "[VL_OL_0017] 가맹점 참조 위반 merch=%s", rec.merch_id); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1703"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + if (h_issuer_cnt < 1) { + tx_log(TX_LOG_WARN, "[VL_OL_0017] 발급사 참조 위반 issuer=%s", issuer); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1704"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0017] 참조무결성 정상 merch=%s issuer=%s", + rec.merch_id, issuer); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0018.c b/app/online/vl_ol_0018.c index c51a817..8e71c7c 100644 --- a/app/online/vl_ol_0018.c +++ b/app/online/vl_ol_0018.c @@ -8,15 +8,18 @@ #line 1 "app/online/vl_ol_0018.pgc" /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0018.pgc - 정합성 온라인 서비스 (VL_OL_0018) [tier=medium] + * vl_ol_0018.pgc - 중복승인 검출 서비스 (VL_OL_0018) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 동일 승인번호(approval_no) 가 원장(vl_ledger) 에 2건 이상 존재하는지 + * 집계하여 중복승인 여부를 판정한다. 중복이면 표준 에러코드(VL18xx) 를 + * 응답에 실어 반환한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,60 +92,91 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0018.pgc" +#line 18 "app/online/vl_ol_0018.pgc" TX_SERVICE(VL_OL_0018, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + +#line 23 "app/online/vl_ol_0018.pgc" + char h_appr [ 10 ] ; + +#line 24 "app/online/vl_ol_0018.pgc" + char h_bizdate [ 9 ] ; + +#line 25 "app/online/vl_ol_0018.pgc" + long h_dupcnt ; +/* exec sql end declare section */ +#line 26 "app/online/vl_ol_0018.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0018] 정합성 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0018] 필수 필드 누락"); + char appr[10]; + char bizdate[16]; + + tx_log(TX_LOG_INFO, "[VL_OL_0018] 중복승인 검출 서비스 진입"); + + if (tx_buf_get(ctx->in, "APPRNO", appr, sizeof(appr)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0018] 승인번호(APPRNO) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1800"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0018] 금액 범위 오류 amount=%ld", amount); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0018] 영업일(BIZDATE) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1801"); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0018] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[VL_OL_0018] 영업일 형식 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "VL1802"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_appr, appr, sizeof(h_appr) - 1); + h_appr[sizeof(h_appr) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0018] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + h_dupcnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from vl_ledger where approval_no = $1 and biz_date = $2 ", + ECPGt_char,(h_appr),(long)10,(long)1,(10)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_dupcnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 63 "app/online/vl_ol_0018.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT COUNT vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1809"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0018] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "APPRNO", appr); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "DUPCNT", h_dupcnt); + + if (h_dupcnt >= 2) { + tx_log(TX_LOG_WARN, "[VL_OL_0018] 중복승인 검출 appr=%s cnt=%ld", + appr, h_dupcnt); + tx_buf_sets(ctx->out, "RESULT", "DUPLICATE"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1803"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "UNIQUE"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0018] 중복없음 appr=%s cnt=%ld", appr, h_dupcnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0018.pgc b/app/online/vl_ol_0018.pgc index 1f87d94..5b20cb6 100644 --- a/app/online/vl_ol_0018.pgc +++ b/app/online/vl_ol_0018.pgc @@ -1,14 +1,17 @@ /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0018.pgc - 정합성 온라인 서비스 (VL_OL_0018) [tier=medium] + * vl_ol_0018.pgc - 중복승인 검출 서비스 (VL_OL_0018) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 동일 승인번호(approval_no) 가 원장(vl_ledger) 에 2건 이상 존재하는지 + * 집계하여 중복승인 여부를 판정한다. 중복이면 표준 에러코드(VL18xx) 를 + * 응답에 실어 반환한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,55 +19,72 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0018, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_appr[10]; + char h_bizdate[9]; + long h_dupcnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0018] 정합성 등록 서비스 진입"); + char appr[10]; + char bizdate[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0018] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[VL_OL_0018] 중복승인 검출 서비스 진입"); + + if (tx_buf_get(ctx->in, "APPRNO", appr, sizeof(appr)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0018] 승인번호(APPRNO) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1800"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0018] 금액 범위 오류 amount=%ld", amount); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0018] 영업일(BIZDATE) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1801"); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0018] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[VL_OL_0018] 영업일 형식 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "VL1802"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_appr, appr, sizeof(h_appr) - 1); + h_appr[sizeof(h_appr) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0018] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + h_dupcnt = 0; + EXEC SQL + SELECT COUNT(*) + INTO :h_dupcnt + FROM vl_ledger + WHERE approval_no = :h_appr + AND biz_date = :h_bizdate; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT COUNT vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1809"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0018] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "APPRNO", appr); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "DUPCNT", h_dupcnt); + + if (h_dupcnt >= 2) { + tx_log(TX_LOG_WARN, "[VL_OL_0018] 중복승인 검출 appr=%s cnt=%ld", + appr, h_dupcnt); + tx_buf_sets(ctx->out, "RESULT", "DUPLICATE"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1803"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "UNIQUE"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0018] 중복없음 appr=%s cnt=%ld", appr, h_dupcnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0019.c b/app/online/vl_ol_0019.c index 58efe20..2b0df37 100644 --- a/app/online/vl_ol_0019.c +++ b/app/online/vl_ol_0019.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0019.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0019.pgc - 정합성 온라인 서비스 (VL_OL_0019) [tier=easy] + * vl_ol_0019.pgc - 이상금액 탐지 서비스 (VL_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점의 과거 평균거래금액(AVG vl_ledger) 을 산출하여, 입력금액이 + * 임계배수(기본 5배) 를 초과하면 이상거래로 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,37 +91,109 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0019.pgc" +#line 17 "app/online/vl_ol_0019.pgc" +#define VL0019_DEFAULT_MULT 5 + TX_SERVICE(VL_OL_0019, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 24 "app/online/vl_ol_0019.pgc" + char h_merch [ 16 ] ; + +#line 25 "app/online/vl_ol_0019.pgc" + long h_avg_amt ; + +#line 26 "app/online/vl_ol_0019.pgc" + long h_sample ; +/* exec sql end declare section */ +#line 27 "app/online/vl_ol_0019.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0019] 정합성 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0019] KEY 누락"); + char merch[16]; + long amount = 0; + long mult = 0; + long threshold; + + tx_log(TX_LOG_INFO, "[VL_OL_0019] 이상금액 탐지 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0019] 가맹점ID(MERCHID) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1900"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_getlong(ctx->in, "AMOUNT", &amount) != TX_OK || + !amount_is_valid(amount)) { + tx_log(TX_LOG_WARN, "[VL_OL_0019] 금액 범위 오류 amount=%ld", amount); + tx_buf_sets(ctx->out, "ERRCODE", "VL1901"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 임계배수: 요청에 오면 사용, 없거나 비정상이면 기본값 */ + if (tx_buf_getlong(ctx->in, "MULT", &mult) != TX_OK || mult <= 0) { + mult = VL0019_DEFAULT_MULT; + } + + strncpy(h_merch, merch, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + + h_avg_amt = 0; + h_sample = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( AVG ( amount ) , 0 ) , count ( * ) from vl_ledger where merch_id = $1 and status in ( 'M' , 'S' )", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_avg_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_sample),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 65 "app/online/vl_ol_0019.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT AVG vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1909"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_setlong(ctx->out, "AVGAMT", h_avg_amt); + tx_buf_setlong(ctx->out, "SAMPLE", h_sample); + tx_buf_setlong(ctx->out, "MULT", mult); + + /* 표본이 없으면 판단 보류(정상 처리) */ + if (h_sample == 0 || h_avg_amt <= 0) { + tx_log(TX_LOG_INFO, "[VL_OL_0019] 표본부족 merch=%s 판단보류", merch); + tx_buf_sets(ctx->out, "RESULT", "NODATA"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + threshold = h_avg_amt * mult; + tx_buf_setlong(ctx->out, "THRESHOLD", threshold); + + if (amount > threshold) { + tx_log(TX_LOG_WARN, "[VL_OL_0019] 이상금액 탐지 merch=%s amt=%ld thr=%ld", + merch, amount, threshold); + tx_buf_sets(ctx->out, "RESULT", "ANOMALY"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1902"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "NORMAL"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0019] 정상 merch=%s amt=%ld avg=%ld", + merch, amount, h_avg_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0019.pgc b/app/online/vl_ol_0019.pgc index e23beb3..4c0bb13 100644 --- a/app/online/vl_ol_0019.pgc +++ b/app/online/vl_ol_0019.pgc @@ -1,47 +1,107 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0019.pgc - 정합성 온라인 서비스 (VL_OL_0019) [tier=easy] + * vl_ol_0019.pgc - 이상금액 탐지 서비스 (VL_OL_0019) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 가맹점의 과거 평균거래금액(AVG vl_ledger) 을 산출하여, 입력금액이 + * 임계배수(기본 5배) 를 초과하면 이상거래로 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" EXEC SQL INCLUDE sqlca; +#define VL0019_DEFAULT_MULT 5 + TX_SERVICE(VL_OL_0019, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16]; + long h_avg_amt; + long h_sample; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0019] 정합성 단건조회 서비스 진입"); + char merch[16]; + long amount = 0; + long mult = 0; + long threshold; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0019] KEY 누락"); + tx_log(TX_LOG_INFO, "[VL_OL_0019] 이상금액 탐지 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0019] 가맹점ID(MERCHID) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1900"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_getlong(ctx->in, "AMOUNT", &amount) != TX_OK || + !amount_is_valid(amount)) { + tx_log(TX_LOG_WARN, "[VL_OL_0019] 금액 범위 오류 amount=%ld", amount); + tx_buf_sets(ctx->out, "ERRCODE", "VL1901"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0019] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + /* 임계배수: 요청에 오면 사용, 없거나 비정상이면 기본값 */ + if (tx_buf_getlong(ctx->in, "MULT", &mult) != TX_OK || mult <= 0) { + mult = VL0019_DEFAULT_MULT; + } + + strncpy(h_merch, merch, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; + + h_avg_amt = 0; + h_sample = 0; + EXEC SQL + SELECT COALESCE(AVG(amount), 0), COUNT(*) + INTO :h_avg_amt, :h_sample + FROM vl_ledger + WHERE merch_id = :h_merch + AND status IN ('M', 'S'); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT AVG vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1909"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0019] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "AMOUNT", amount); + tx_buf_setlong(ctx->out, "AVGAMT", h_avg_amt); + tx_buf_setlong(ctx->out, "SAMPLE", h_sample); + tx_buf_setlong(ctx->out, "MULT", mult); + + /* 표본이 없으면 판단 보류(정상 처리) */ + if (h_sample == 0 || h_avg_amt <= 0) { + tx_log(TX_LOG_INFO, "[VL_OL_0019] 표본부족 merch=%s 판단보류", merch); + tx_buf_sets(ctx->out, "RESULT", "NODATA"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + threshold = h_avg_amt * mult; + tx_buf_setlong(ctx->out, "THRESHOLD", threshold); + + if (amount > threshold) { + tx_log(TX_LOG_WARN, "[VL_OL_0019] 이상금액 탐지 merch=%s amt=%ld thr=%ld", + merch, amount, threshold); + tx_buf_sets(ctx->out, "RESULT", "ANOMALY"); + tx_buf_sets(ctx->out, "ERRCODE", "VL1902"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "NORMAL"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0019] 정상 merch=%s amt=%ld avg=%ld", + merch, amount, h_avg_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0020.c b/app/online/vl_ol_0020.c index b35cf36..bafa135 100644 --- a/app/online/vl_ol_0020.c +++ b/app/online/vl_ol_0020.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0020.pgc" /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0020.pgc - 정합성 온라인 서비스 (VL_OL_0020) [tier=medium] + * vl_ol_0020.pgc - 정합성 상태조회 서비스 (VL_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 영업일의 상태별(R:접수 / M:완료 / U:불일치 / S:정산) 건수를 + * 각각 집계하여 응답에 실어 반환한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,60 +91,94 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0020.pgc" +#line 17 "app/online/vl_ol_0020.pgc" TX_SERVICE(VL_OL_0020, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + +#line 22 "app/online/vl_ol_0020.pgc" + char h_bizdate [ 9 ] ; + +#line 23 "app/online/vl_ol_0020.pgc" + char h_status [ 2 ] ; + +#line 24 "app/online/vl_ol_0020.pgc" + long h_cnt ; +/* exec sql end declare section */ +#line 25 "app/online/vl_ol_0020.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0020] 정합성 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0020] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char bizdate[16]; + long cnt_r = 0, cnt_m = 0, cnt_u = 0, cnt_s = 0, total; + int i; + const char *st_list[4] = { VL_ST_RECV, VL_ST_DONE, VL_ST_FAIL, VL_ST_SETTLED }; + long *cnt_slot[4]; - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0020] 금액 범위 오류 amount=%ld", amount); + tx_log(TX_LOG_INFO, "[VL_OL_0020] 정합성 상태조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0020] 영업일(BIZDATE) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2000"); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0020] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[VL_OL_0020] 영업일 형식 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "VL2001"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0020] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + cnt_slot[0] = &cnt_r; + cnt_slot[1] = &cnt_m; + cnt_slot[2] = &cnt_u; + cnt_slot[3] = &cnt_s; + + for (i = 0; i < 4; i++) { + strncpy(h_status, st_list[i], sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + h_cnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from vl_ledger where biz_date = $1 and status = $2 ", + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_status),(long)2,(long)1,(2)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 66 "app/online/vl_ol_0020.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT COUNT status vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2009"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + *cnt_slot[i] = h_cnt; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); + total = cnt_r + cnt_m + cnt_u + cnt_s; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0020] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNTRECV", cnt_r); + tx_buf_setlong(ctx->out, "CNTDONE", cnt_m); + tx_buf_setlong(ctx->out, "CNTFAIL", cnt_u); + tx_buf_setlong(ctx->out, "CNTSETTLED", cnt_s); + tx_buf_setlong(ctx->out, "CNTTOTAL", total); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, + "[VL_OL_0020] 상태집계 date=%s R=%ld M=%ld U=%ld S=%ld 계=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s, total); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0020.pgc b/app/online/vl_ol_0020.pgc index b7ff39b..771f288 100644 --- a/app/online/vl_ol_0020.pgc +++ b/app/online/vl_ol_0020.pgc @@ -1,14 +1,16 @@ /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0020.pgc - 정합성 온라인 서비스 (VL_OL_0020) [tier=medium] + * vl_ol_0020.pgc - 정합성 상태조회 서비스 (VL_OL_0020) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 영업일의 상태별(R:접수 / M:완료 / U:불일치 / S:정산) 건수를 + * 각각 집계하여 응답에 실어 반환한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,55 +18,75 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0020, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[9]; + char h_status[2]; + long h_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0020] 정합성 등록 서비스 진입"); + char bizdate[16]; + long cnt_r = 0, cnt_m = 0, cnt_u = 0, cnt_s = 0, total; + int i; + const char *st_list[4] = { VL_ST_RECV, VL_ST_DONE, VL_ST_FAIL, VL_ST_SETTLED }; + long *cnt_slot[4]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0020] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[VL_OL_0020] 정합성 상태조회 서비스 진입"); - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0020] 금액 범위 오류 amount=%ld", amount); + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0020] 영업일(BIZDATE) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2000"); tx_return(ctx, TX_EINVAL, ctx->out); return; } if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0020] 영업일 오류 date=%s", bizdate); + tx_log(TX_LOG_WARN, "[VL_OL_0020] 영업일 형식 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "VL2001"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0020] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); - return; + cnt_slot[0] = &cnt_r; + cnt_slot[1] = &cnt_m; + cnt_slot[2] = &cnt_u; + cnt_slot[3] = &cnt_s; + + for (i = 0; i < 4; i++) { + strncpy(h_status, st_list[i], sizeof(h_status) - 1); + h_status[sizeof(h_status) - 1] = '\0'; + + h_cnt = 0; + EXEC SQL + SELECT COUNT(*) + INTO :h_cnt + FROM vl_ledger + WHERE biz_date = :h_bizdate + AND status = :h_status; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT COUNT status vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2009"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + *cnt_slot[i] = h_cnt; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); + total = cnt_r + cnt_m + cnt_u + cnt_s; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0020] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "CNTRECV", cnt_r); + tx_buf_setlong(ctx->out, "CNTDONE", cnt_m); + tx_buf_setlong(ctx->out, "CNTFAIL", cnt_u); + tx_buf_setlong(ctx->out, "CNTSETTLED", cnt_s); + tx_buf_setlong(ctx->out, "CNTTOTAL", total); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, + "[VL_OL_0020] 상태집계 date=%s R=%ld M=%ld U=%ld S=%ld 계=%ld", + bizdate, cnt_r, cnt_m, cnt_u, cnt_s, total); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0021.c b/app/online/vl_ol_0021.c index c627ffa..0d951fb 100644 --- a/app/online/vl_ol_0021.c +++ b/app/online/vl_ol_0021.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0021.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0021.pgc - 정합성 온라인 서비스 (VL_OL_0021) [tier=easy] + * vl_ol_0021.pgc - 매입-정산 대사 서비스 (VL_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 영업일 기준 매입합계(SUM vl_purchase) 와 정산합계(SUM vl_settle) 를 + * 조회하여 차액을 산출하고 정합/불일치를 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,37 +91,101 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0021.pgc" +#line 17 "app/online/vl_ol_0021.pgc" TX_SERVICE(VL_OL_0021, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + +#line 22 "app/online/vl_ol_0021.pgc" + char h_bizdate [ 9 ] ; + +#line 23 "app/online/vl_ol_0021.pgc" + long h_purch_sum ; + +#line 24 "app/online/vl_ol_0021.pgc" + long h_settle_sum ; +/* exec sql end declare section */ +#line 25 "app/online/vl_ol_0021.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0021] 정합성 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0021] KEY 누락"); + char bizdate[16]; + long diff; + + tx_log(TX_LOG_INFO, "[VL_OL_0021] 매입-정산 대사 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0021] 영업일(BIZDATE) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2100"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[VL_OL_0021] 영업일 형식 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "VL2101"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + + h_purch_sum = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( SUM ( amount ) , 0 ) from vl_purchase where biz_date = $1 ", + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_purch_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 53 "app/online/vl_ol_0021.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT SUM vl_purchase"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2108"); + tx_return(ctx, TX_EDB, ctx->out); return; } + h_settle_sum = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( SUM ( settle_amt ) , 0 ) from vl_settle where biz_date = $1 ", + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settle_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 67 "app/online/vl_ol_0021.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT SUM vl_settle"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2109"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + diff = h_purch_sum - h_settle_sum; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "PURCHSUM", h_purch_sum); + tx_buf_setlong(ctx->out, "SETTLESUM", h_settle_sum); + tx_buf_setlong(ctx->out, "DIFF", diff); + + if (diff != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0021] 매입-정산 불일치 date=%s diff=%ld", + bizdate, diff); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2102"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0021] 매입-정산 정합 date=%s sum=%ld", + bizdate, h_purch_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0021.pgc b/app/online/vl_ol_0021.pgc index 6fdff25..6d6b658 100644 --- a/app/online/vl_ol_0021.pgc +++ b/app/online/vl_ol_0021.pgc @@ -1,14 +1,16 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0021.pgc - 정합성 온라인 서비스 (VL_OL_0021) [tier=easy] + * vl_ol_0021.pgc - 매입-정산 대사 서비스 (VL_OL_0021) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 영업일 기준 매입합계(SUM vl_purchase) 와 정산합계(SUM vl_settle) 를 + * 조회하여 차액을 산출하고 정합/불일치를 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,32 +18,81 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0021, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[9]; + long h_purch_sum; + long h_settle_sum; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0021] 정합성 단건조회 서비스 진입"); + char bizdate[16]; + long diff; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0021] KEY 누락"); + tx_log(TX_LOG_INFO, "[VL_OL_0021] 매입-정산 대사 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0021] 영업일(BIZDATE) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2100"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[VL_OL_0021] 영업일 형식 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "VL2101"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0021] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + + h_purch_sum = 0; + EXEC SQL + SELECT COALESCE(SUM(amount), 0) + INTO :h_purch_sum + FROM vl_purchase + WHERE biz_date = :h_bizdate; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT SUM vl_purchase"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2108"); + tx_return(ctx, TX_EDB, ctx->out); return; } + h_settle_sum = 0; + EXEC SQL + SELECT COALESCE(SUM(settle_amt), 0) + INTO :h_settle_sum + FROM vl_settle + WHERE biz_date = :h_bizdate; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT SUM vl_settle"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2109"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + diff = h_purch_sum - h_settle_sum; + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0021] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "PURCHSUM", h_purch_sum); + tx_buf_setlong(ctx->out, "SETTLESUM", h_settle_sum); + tx_buf_setlong(ctx->out, "DIFF", diff); + + if (diff != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0021] 매입-정산 불일치 date=%s diff=%ld", + bizdate, diff); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2102"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0021] 매입-정산 정합 date=%s sum=%ld", + bizdate, h_purch_sum); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0022.c b/app/online/vl_ol_0022.c index 66b8255..8cd62ae 100644 --- a/app/online/vl_ol_0022.c +++ b/app/online/vl_ol_0022.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0022.pgc" /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0022.pgc - 정합성 온라인 서비스 (VL_OL_0022) [tier=medium] + * vl_ol_0022.pgc - 가맹점별 정합성 집계 서비스 (VL_OL_0022) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 가맹점ID 에 대해 완료(status='M') / 불일치(status='U') 의 건수와 + * 금액합계를 각각 집계하여 응답한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,60 +91,117 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0022.pgc" +#line 17 "app/online/vl_ol_0022.pgc" TX_SERVICE(VL_OL_0022, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + /* exec sql begin declare section */ + + + + + + +#line 22 "app/online/vl_ol_0022.pgc" + char h_merch [ 16 ] ; + +#line 23 "app/online/vl_ol_0022.pgc" + long h_done_cnt ; + +#line 24 "app/online/vl_ol_0022.pgc" + long h_done_amt ; + +#line 25 "app/online/vl_ol_0022.pgc" + long h_fail_cnt ; + +#line 26 "app/online/vl_ol_0022.pgc" + long h_fail_amt ; +/* exec sql end declare section */ +#line 27 "app/online/vl_ol_0022.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0022] 정합성 등록 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0022] 필수 필드 누락"); + char merch[16]; + long total_cnt, total_amt; + + tx_log(TX_LOG_INFO, "[VL_OL_0022] 가맹점별 정합성 집계 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0022] 가맹점ID(MERCHID) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2200"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0022] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0022] 영업일 오류 date=%s", bizdate); + if (strlen(merch) == 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0022] 가맹점ID 공백"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2201"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_merch, merch, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0022] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + h_done_cnt = 0; + h_done_amt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( SUM ( amount ) , 0 ) from vl_ledger where merch_id = $1 and status = 'M'", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_done_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_done_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 57 "app/online/vl_ol_0022.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT 완료집계 vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2208"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); + h_fail_cnt = 0; + h_fail_amt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( SUM ( amount ) , 0 ) from vl_ledger where merch_id = $1 and status = 'U'", + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_fail_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fail_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 73 "app/online/vl_ol_0022.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT 불일치집계 vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2209"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + total_cnt = h_done_cnt + h_fail_cnt; + total_amt = h_done_amt + h_fail_amt; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0022] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "DONECNT", h_done_cnt); + tx_buf_setlong(ctx->out, "DONEAMT", h_done_amt); + tx_buf_setlong(ctx->out, "FAILCNT", h_fail_cnt); + tx_buf_setlong(ctx->out, "FAILAMT", h_fail_amt); + tx_buf_setlong(ctx->out, "TOTALCNT", total_cnt); + tx_buf_setlong(ctx->out, "TOTALAMT", total_amt); + + /* 불일치가 하나라도 있으면 주의 플래그 */ + if (h_fail_cnt > 0) { + tx_buf_sets(ctx->out, "RESULT", "HASFAIL"); + } else { + tx_buf_sets(ctx->out, "RESULT", "CLEAN"); + } + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, + "[VL_OL_0022] 집계 merch=%s 완료=%ld/%ld 불일치=%ld/%ld", + merch, h_done_cnt, h_done_amt, h_fail_cnt, h_fail_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0022.pgc b/app/online/vl_ol_0022.pgc index ab09259..e196059 100644 --- a/app/online/vl_ol_0022.pgc +++ b/app/online/vl_ol_0022.pgc @@ -1,14 +1,16 @@ /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0022.pgc - 정합성 온라인 서비스 (VL_OL_0022) [tier=medium] + * vl_ol_0022.pgc - 가맹점별 정합성 집계 서비스 (VL_OL_0022) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 가맹점ID 에 대해 완료(status='M') / 불일치(status='U') 의 건수와 + * 금액합계를 각각 집계하여 응답한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,55 +18,89 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0022, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_merch[16]; + long h_done_cnt; + long h_done_amt; + long h_fail_cnt; + long h_fail_amt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0022] 정합성 등록 서비스 진입"); + char merch[16]; + long total_cnt, total_amt; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0022] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[VL_OL_0022] 가맹점별 정합성 집계 서비스 진입"); + + if (tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0022] 가맹점ID(MERCHID) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2200"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0022] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0022] 영업일 오류 date=%s", bizdate); + if (strlen(merch) == 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0022] 가맹점ID 공백"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2201"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ + strncpy(h_merch, merch, sizeof(h_merch) - 1); + h_merch[sizeof(h_merch) - 1] = '\0'; - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0022] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + h_done_cnt = 0; + h_done_amt = 0; + EXEC SQL + SELECT COUNT(*), COALESCE(SUM(amount), 0) + INTO :h_done_cnt, :h_done_amt + FROM vl_ledger + WHERE merch_id = :h_merch + AND status = 'M'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT 완료집계 vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2208"); + tx_return(ctx, TX_EDB, ctx->out); return; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); + h_fail_cnt = 0; + h_fail_amt = 0; + EXEC SQL + SELECT COUNT(*), COALESCE(SUM(amount), 0) + INTO :h_fail_cnt, :h_fail_amt + FROM vl_ledger + WHERE merch_id = :h_merch + AND status = 'U'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT 불일치집계 vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2209"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + total_cnt = h_done_cnt + h_fail_cnt; + total_amt = h_done_amt + h_fail_amt; tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0022] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "MERCHID", merch); + tx_buf_setlong(ctx->out, "DONECNT", h_done_cnt); + tx_buf_setlong(ctx->out, "DONEAMT", h_done_amt); + tx_buf_setlong(ctx->out, "FAILCNT", h_fail_cnt); + tx_buf_setlong(ctx->out, "FAILAMT", h_fail_amt); + tx_buf_setlong(ctx->out, "TOTALCNT", total_cnt); + tx_buf_setlong(ctx->out, "TOTALAMT", total_amt); + + /* 불일치가 하나라도 있으면 주의 플래그 */ + if (h_fail_cnt > 0) { + tx_buf_sets(ctx->out, "RESULT", "HASFAIL"); + } else { + tx_buf_sets(ctx->out, "RESULT", "CLEAN"); + } + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, + "[VL_OL_0022] 집계 merch=%s 완료=%ld/%ld 불일치=%ld/%ld", + merch, h_done_cnt, h_done_amt, h_fail_cnt, h_fail_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0023.c b/app/online/vl_ol_0023.c index f9898d5..294612f 100644 --- a/app/online/vl_ol_0023.c +++ b/app/online/vl_ol_0023.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0023.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0023.pgc - 정합성 온라인 서비스 (VL_OL_0023) [tier=easy] + * vl_ol_0023.pgc - 카드사별 불일치 집계 서비스 (VL_OL_0023) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 카드사코드(issuer_cd) 의 불일치(status='U') 건수와 금액합계를 + * 원장에서 집계하여 응답한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,37 +91,92 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0023.pgc" +#line 17 "app/online/vl_ol_0023.pgc" TX_SERVICE(VL_OL_0023, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 22 "app/online/vl_ol_0023.pgc" + char h_issuer [ 4 ] ; + +#line 23 "app/online/vl_ol_0023.pgc" + char h_bizdate [ 9 ] ; + +#line 24 "app/online/vl_ol_0023.pgc" + long h_fail_cnt ; + +#line 25 "app/online/vl_ol_0023.pgc" + long h_fail_amt ; +/* exec sql end declare section */ +#line 26 "app/online/vl_ol_0023.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0023] 정합성 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0023] KEY 누락"); + char issuer[8]; + char bizdate[16]; + + tx_log(TX_LOG_INFO, "[VL_OL_0023] 카드사별 불일치 집계 서비스 진입"); + + if (tx_buf_get(ctx->in, "ISSUER", issuer, sizeof(issuer)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0023] 카드사코드(ISSUER) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2300"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (strlen(issuer) == 0 || strlen(issuer) > 3) { + tx_log(TX_LOG_WARN, "[VL_OL_0023] 카드사코드 길이 오류 issuer=%s", issuer); + tx_buf_sets(ctx->out, "ERRCODE", "VL2301"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[VL_OL_0023] 영업일 오류"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2302"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_issuer, issuer, sizeof(h_issuer) - 1); + h_issuer[sizeof(h_issuer) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + + h_fail_cnt = 0; + h_fail_amt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( SUM ( amount ) , 0 ) from vl_ledger where issuer_cd = $1 and biz_date = $2 and status = 'U'", + ECPGt_char,(h_issuer),(long)4,(long)1,(4)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_fail_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fail_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 66 "app/online/vl_ol_0023.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT 카드사불일치 vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2309"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "ISSUER", issuer); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "FAILCNT", h_fail_cnt); + tx_buf_setlong(ctx->out, "FAILAMT", h_fail_amt); + tx_buf_sets(ctx->out, "RESULT", (h_fail_cnt > 0) ? "HASFAIL" : "CLEAN"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, "[VL_OL_0023] 카드사=%s date=%s 불일치=%ld 금액=%ld", + issuer, bizdate, h_fail_cnt, h_fail_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0023.pgc b/app/online/vl_ol_0023.pgc index 7b95fe4..00c3aa4 100644 --- a/app/online/vl_ol_0023.pgc +++ b/app/online/vl_ol_0023.pgc @@ -1,14 +1,16 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0023.pgc - 정합성 온라인 서비스 (VL_OL_0023) [tier=easy] + * vl_ol_0023.pgc - 카드사별 불일치 집계 서비스 (VL_OL_0023) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 카드사코드(issuer_cd) 의 불일치(status='U') 건수와 금액합계를 + * 원장에서 집계하여 응답한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,32 +18,69 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0023, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_issuer[4]; + char h_bizdate[9]; + long h_fail_cnt; + long h_fail_amt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0023] 정합성 단건조회 서비스 진입"); + char issuer[8]; + char bizdate[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0023] KEY 누락"); + tx_log(TX_LOG_INFO, "[VL_OL_0023] 카드사별 불일치 집계 서비스 진입"); + + if (tx_buf_get(ctx->in, "ISSUER", issuer, sizeof(issuer)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0023] 카드사코드(ISSUER) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2300"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (strlen(issuer) == 0 || strlen(issuer) > 3) { + tx_log(TX_LOG_WARN, "[VL_OL_0023] 카드사코드 길이 오류 issuer=%s", issuer); + tx_buf_sets(ctx->out, "ERRCODE", "VL2301"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK || + !date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[VL_OL_0023] 영업일 오류"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2302"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0023] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_issuer, issuer, sizeof(h_issuer) - 1); + h_issuer[sizeof(h_issuer) - 1] = '\0'; + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + + h_fail_cnt = 0; + h_fail_amt = 0; + EXEC SQL + SELECT COUNT(*), COALESCE(SUM(amount), 0) + INTO :h_fail_cnt, :h_fail_amt + FROM vl_ledger + WHERE issuer_cd = :h_issuer + AND biz_date = :h_bizdate + AND status = 'U'; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT 카드사불일치 vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2309"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0023] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "ISSUER", issuer); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "FAILCNT", h_fail_cnt); + tx_buf_setlong(ctx->out, "FAILAMT", h_fail_amt); + tx_buf_sets(ctx->out, "RESULT", (h_fail_cnt > 0) ? "HASFAIL" : "CLEAN"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, "[VL_OL_0023] 카드사=%s date=%s 불일치=%ld 금액=%ld", + issuer, bizdate, h_fail_cnt, h_fail_amt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0024.c b/app/online/vl_ol_0024.c index 2c91470..7c37a1b 100644 --- a/app/online/vl_ol_0024.c +++ b/app/online/vl_ol_0024.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0024.pgc" /* @tier hard @module vl @transform validation-online */ /* - * vl_ol_0024.pgc - 정합성 온라인 서비스 (VL_OL_0024) [tier=hard] + * vl_ol_0024.pgc - 승인번호 정합 서비스 (VL_OL_0024) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 승인번호의 형식(9자리 숫자) 을 검증한 뒤, 승인원장(vl_approval) + * 에 실제 등록되어 있는지 조회하여 정합 여부를 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,88 +91,117 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0024.pgc" +#line 17 "app/online/vl_ol_0024.pgc" +#define VL0024_APPR_LEN 9 + +/* 9자리 전부 숫자인지 검사 (형식검증) */ +static int vl0024_is_appr_format(const char *s) +{ + int i; + if (s == NULL) { + return 0; + } + if ((int)strlen(s) != VL0024_APPR_LEN) { + return 0; + } + for (i = 0; i < VL0024_APPR_LEN; i++) { + if (s[i] < '0' || s[i] > '9') { + return 0; + } + } + return 1; +} + TX_SERVICE(VL_OL_0024, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + +#line 42 "app/online/vl_ol_0024.pgc" + char h_appr [ 10 ] ; + +#line 43 "app/online/vl_ol_0024.pgc" + char h_merch [ 16 ] ; + +#line 44 "app/online/vl_ol_0024.pgc" + long h_amount ; + +#line 45 "app/online/vl_ol_0024.pgc" + char h_appr_date [ 9 ] ; +/* exec sql end declare section */ +#line 46 "app/online/vl_ol_0024.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0024] 정합성 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0024] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + char appr[16]; - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0024] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + tx_log(TX_LOG_INFO, "[VL_OL_0024] 승인번호 정합 서비스 진입"); + + if (tx_buf_get(ctx->in, "APPRNO", appr, sizeof(appr)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0024] 승인번호(APPRNO) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2400"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + if (!vl0024_is_appr_format(appr)) { + tx_log(TX_LOG_WARN, "[VL_OL_0024] 승인번호 형식 오류 appr=%s", appr); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "APPRNO", appr); + tx_buf_sets(ctx->out, "RESULT", "BADFORMAT"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2401"); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + strncpy(h_appr, appr, sizeof(h_appr) - 1); + h_appr[sizeof(h_appr) - 1] = '\0'; - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0024] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + memset(h_merch, 0, sizeof(h_merch)); + memset(h_appr_date, 0, sizeof(h_appr_date)); + h_amount = 0; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select merch_id , appr_amount , appr_date from vl_approval where approval_no = $1 ", + ECPGt_char,(h_appr),(long)10,(long)1,(10)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_amount),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_appr_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 80 "app/online/vl_ol_0024.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0024] 승인원장 미등록 appr=%s", appr); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "APPRNO", appr); + tx_buf_sets(ctx->out, "RESULT", "NOTFOUND"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2402"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_approval"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2409"); + tx_return(ctx, TX_EDB, ctx->out); + return; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("VL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[VL_OL_0024] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (vl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[VL_OL_0024] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "APPRNO", appr); + tx_buf_sets(ctx->out, "MERCHID", h_merch); + tx_buf_setlong(ctx->out, "APPRAMT", h_amount); + tx_buf_sets(ctx->out, "APPRDATE", h_appr_date); + tx_buf_sets(ctx->out, "RESULT", "VALID"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, "[VL_OL_0024] 승인번호 정합 appr=%s merch=%s amt=%ld", + appr, h_merch, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0024.pgc b/app/online/vl_ol_0024.pgc index 5b3059f..882fb91 100644 --- a/app/online/vl_ol_0024.pgc +++ b/app/online/vl_ol_0024.pgc @@ -1,98 +1,109 @@ /* @tier hard @module vl @transform validation-online */ /* - * vl_ol_0024.pgc - 정합성 온라인 서비스 (VL_OL_0024) [tier=hard] + * vl_ol_0024.pgc - 승인번호 정합 서비스 (VL_OL_0024) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 승인번호의 형식(9자리 숫자) 을 검증한 뒤, 승인원장(vl_approval) + * 에 실제 등록되어 있는지 조회하여 정합 여부를 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" EXEC SQL INCLUDE sqlca; +#define VL0024_APPR_LEN 9 + +/* 9자리 전부 숫자인지 검사 (형식검증) */ +static int vl0024_is_appr_format(const char *s) +{ + int i; + if (s == NULL) { + return 0; + } + if ((int)strlen(s) != VL0024_APPR_LEN) { + return 0; + } + for (i = 0; i < VL0024_APPR_LEN; i++) { + if (s[i] < '0' || s[i] > '9') { + return 0; + } + } + return 1; +} + TX_SERVICE(VL_OL_0024, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_appr[10]; + char h_merch[16]; + long h_amount; + char h_appr_date[9]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0024] 정합성 트랜잭션 오케스트레이션 진입"); + char appr[16]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0024] 필수 필드 누락"); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); + tx_log(TX_LOG_INFO, "[VL_OL_0024] 승인번호 정합 서비스 진입"); - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0024] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (tx_buf_get(ctx->in, "APPRNO", appr, sizeof(appr)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0024] 승인번호(APPRNO) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2400"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + if (!vl0024_is_appr_format(appr)) { + tx_log(TX_LOG_WARN, "[VL_OL_0024] 승인번호 형식 오류 appr=%s", appr); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "APPRNO", appr); + tx_buf_sets(ctx->out, "RESULT", "BADFORMAT"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2401"); + tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + strncpy(h_appr, appr, sizeof(h_appr) - 1); + h_appr[sizeof(h_appr) - 1] = '\0'; - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0024] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + memset(h_merch, 0, sizeof(h_merch)); + memset(h_appr_date, 0, sizeof(h_appr_date)); + h_amount = 0; + + EXEC SQL + SELECT merch_id, appr_amount, appr_date + INTO :h_merch, :h_amount, :h_appr_date + FROM vl_approval + WHERE approval_no = :h_appr; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0024] 승인원장 미등록 appr=%s", appr); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "APPRNO", appr); + tx_buf_sets(ctx->out, "RESULT", "NOTFOUND"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2402"); + tx_return(ctx, TX_ENOENT, ctx->out); return; } - - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_approval"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2409"); + tx_return(ctx, TX_EDB, ctx->out); + return; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("VL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[VL_OL_0024] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (vl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[VL_OL_0024] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "APPRNO", appr); + tx_buf_sets(ctx->out, "MERCHID", h_merch); + tx_buf_setlong(ctx->out, "APPRAMT", h_amount); + tx_buf_sets(ctx->out, "APPRDATE", h_appr_date); + tx_buf_sets(ctx->out, "RESULT", "VALID"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, "[VL_OL_0024] 승인번호 정합 appr=%s merch=%s amt=%ld", + appr, h_merch, h_amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0025.c b/app/online/vl_ol_0025.c index 9b7b072..c2804e5 100644 --- a/app/online/vl_ol_0025.c +++ b/app/online/vl_ol_0025.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0025.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0025.pgc - 정합성 온라인 서비스 (VL_OL_0025) [tier=easy] + * vl_ol_0025.pgc - 거래일자 범위 검증 서비스 (VL_OL_0025) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 시작일/종료일의 유효성과 순서를 검증한 뒤, 대상 원장건의 거래일이 + * 지정 범위 내에 있는지 조회하여 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,37 +91,119 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0025.pgc" +#line 17 "app/online/vl_ol_0025.pgc" TX_SERVICE(VL_OL_0025, ctx) { - vl_rec_t rec; + /* exec sql begin declare section */ + + + + + + +#line 22 "app/online/vl_ol_0025.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/vl_ol_0025.pgc" + char h_from [ 9 ] ; + +#line 24 "app/online/vl_ol_0025.pgc" + char h_to [ 9 ] ; + +#line 25 "app/online/vl_ol_0025.pgc" + long h_inrange ; + +#line 26 "app/online/vl_ol_0025.pgc" + char h_txn_date [ 9 ] ; +/* exec sql end declare section */ +#line 27 "app/online/vl_ol_0025.pgc" + + char key[16]; - int rc; + char from_dt[16]; + char to_dt[16]; - tx_log(TX_LOG_INFO, "[VL_OL_0025] 정합성 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0025] 거래일자 범위 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0025] KEY 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "FROMDT", from_dt, sizeof(from_dt)) != TX_OK || + tx_buf_get(ctx->in, "TODT", to_dt, sizeof(to_dt)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0025] 필수 필드(KEY/FROMDT/TODT) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2500"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0025] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(from_dt) || !date_is_valid(to_dt)) { + tx_log(TX_LOG_WARN, "[VL_OL_0025] 일자 형식 오류 from=%s to=%s", + from_dt, to_dt); + tx_buf_sets(ctx->out, "ERRCODE", "VL2501"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (strcmp(from_dt, to_dt) > 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0025] 범위 역전 from=%s to=%s", + from_dt, to_dt); + tx_buf_sets(ctx->out, "ERRCODE", "VL2502"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_from, from_dt, sizeof(h_from) - 1); + h_from[sizeof(h_from) - 1] = '\0'; + strncpy(h_to, to_dt, sizeof(h_to) - 1); + h_to[sizeof(h_to) - 1] = '\0'; + + memset(h_txn_date, 0, sizeof(h_txn_date)); + h_inrange = 0; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select txn_date , case when txn_date between $1 and $2 then 1 else 0 end from vl_ledger where txn_key = $3 ", + ECPGt_char,(h_from),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_to),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_txn_date),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_inrange),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 74 "app/online/vl_ol_0025.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0025] 원장 없음 key=%s", key); + tx_buf_sets(ctx->out, "ERRCODE", "VL2503"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_ledger 범위검증"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2509"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0025] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "TXNDATE", h_txn_date); + tx_buf_sets(ctx->out, "FROMDT", from_dt); + tx_buf_sets(ctx->out, "TODT", to_dt); + + if (h_inrange != 1) { + tx_log(TX_LOG_WARN, "[VL_OL_0025] 범위 밖 key=%s txn=%s", key, h_txn_date); + tx_buf_sets(ctx->out, "RESULT", "OUTOFRANGE"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2504"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "INRANGE"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0025] 범위내 key=%s txn=%s", key, h_txn_date); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0025.pgc b/app/online/vl_ol_0025.pgc index 1c7808c..b2ed239 100644 --- a/app/online/vl_ol_0025.pgc +++ b/app/online/vl_ol_0025.pgc @@ -1,14 +1,16 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0025.pgc - 정합성 온라인 서비스 (VL_OL_0025) [tier=easy] + * vl_ol_0025.pgc - 거래일자 범위 검증 서비스 (VL_OL_0025) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 시작일/종료일의 유효성과 순서를 검증한 뒤, 대상 원장건의 거래일이 + * 지정 범위 내에 있는지 조회하여 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,32 +18,90 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0025, ctx) { - vl_rec_t rec; + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_from[9]; + char h_to[9]; + long h_inrange; + char h_txn_date[9]; + EXEC SQL END DECLARE SECTION; + char key[16]; - int rc; + char from_dt[16]; + char to_dt[16]; - tx_log(TX_LOG_INFO, "[VL_OL_0025] 정합성 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0025] 거래일자 범위 검증 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0025] KEY 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "FROMDT", from_dt, sizeof(from_dt)) != TX_OK || + tx_buf_get(ctx->in, "TODT", to_dt, sizeof(to_dt)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0025] 필수 필드(KEY/FROMDT/TODT) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2500"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0025] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + if (!date_is_valid(from_dt) || !date_is_valid(to_dt)) { + tx_log(TX_LOG_WARN, "[VL_OL_0025] 일자 형식 오류 from=%s to=%s", + from_dt, to_dt); + tx_buf_sets(ctx->out, "ERRCODE", "VL2501"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (strcmp(from_dt, to_dt) > 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0025] 범위 역전 from=%s to=%s", + from_dt, to_dt); + tx_buf_sets(ctx->out, "ERRCODE", "VL2502"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_from, from_dt, sizeof(h_from) - 1); + h_from[sizeof(h_from) - 1] = '\0'; + strncpy(h_to, to_dt, sizeof(h_to) - 1); + h_to[sizeof(h_to) - 1] = '\0'; + + memset(h_txn_date, 0, sizeof(h_txn_date)); + h_inrange = 0; + + EXEC SQL + SELECT txn_date, + CASE WHEN txn_date BETWEEN :h_from AND :h_to THEN 1 ELSE 0 END + INTO :h_txn_date, :h_inrange + FROM vl_ledger + WHERE txn_key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0025] 원장 없음 key=%s", key); + tx_buf_sets(ctx->out, "ERRCODE", "VL2503"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_ledger 범위검증"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2509"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0025] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "TXNDATE", h_txn_date); + tx_buf_sets(ctx->out, "FROMDT", from_dt); + tx_buf_sets(ctx->out, "TODT", to_dt); + + if (h_inrange != 1) { + tx_log(TX_LOG_WARN, "[VL_OL_0025] 범위 밖 key=%s txn=%s", key, h_txn_date); + tx_buf_sets(ctx->out, "RESULT", "OUTOFRANGE"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2504"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "INRANGE"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0025] 범위내 key=%s txn=%s", key, h_txn_date); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0026.c b/app/online/vl_ol_0026.c index 661d0d4..ddf9dda 100644 --- a/app/online/vl_ol_0026.c +++ b/app/online/vl_ol_0026.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0026.pgc" /* @tier hard @module vl @transform validation-online */ /* - * vl_ol_0026.pgc - 정합성 온라인 서비스 (VL_OL_0026) [tier=hard] + * vl_ol_0026.pgc - 금액합계 검증 서비스 (VL_OL_0026) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 영업일 원장합계(SUM vl_ledger) 와 일집계 테이블(vl_daily_agg.total_amt) + * 의 합계를 대사하여 차액 및 건수 불일치를 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,88 +91,137 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0026.pgc" +#line 17 "app/online/vl_ol_0026.pgc" TX_SERVICE(VL_OL_0026, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + /* exec sql begin declare section */ + + + + + + +#line 22 "app/online/vl_ol_0026.pgc" + char h_bizdate [ 9 ] ; + +#line 23 "app/online/vl_ol_0026.pgc" + long h_ledger_sum ; + +#line 24 "app/online/vl_ol_0026.pgc" + long h_ledger_cnt ; + +#line 25 "app/online/vl_ol_0026.pgc" + long h_agg_amt ; + +#line 26 "app/online/vl_ol_0026.pgc" + long h_agg_cnt ; +/* exec sql end declare section */ +#line 27 "app/online/vl_ol_0026.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0026] 정합성 트랜잭션 오케스트레이션 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0026] 필수 필드 누락"); + char bizdate[16]; + long diff_amt, diff_cnt; + int mismatch = 0; + + tx_log(TX_LOG_INFO, "[VL_OL_0026] 금액합계 검증 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0026] 영업일(BIZDATE) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2600"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0026] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[VL_OL_0026] 영업일 형식 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "VL2601"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + + /* 원장 상세 합계 */ + h_ledger_sum = 0; + h_ledger_cnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( SUM ( amount ) , 0 ) , count ( * ) from vl_ledger where biz_date = $1 ", + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_ledger_sum),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_ledger_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 58 "app/online/vl_ol_0026.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT SUM vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2608"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 일집계 테이블 값 */ + h_agg_amt = 0; + h_agg_cnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select total_amt , total_cnt from vl_daily_agg where biz_date = $1 ", + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_agg_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_agg_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 74 "app/online/vl_ol_0026.pgc" - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0026] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0026] 일집계 미생성 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "LEDGERSUM", h_ledger_sum); + tx_buf_setlong(ctx->out, "LEDGERCNT", h_ledger_cnt); + tx_buf_sets(ctx->out, "RESULT", "NOAGG"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2602"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_daily_agg"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2609"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + diff_amt = h_ledger_sum - h_agg_amt; + diff_cnt = h_ledger_cnt - h_agg_cnt; + if (diff_amt != 0 || diff_cnt != 0) { + mismatch = 1; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("VL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[VL_OL_0026] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (vl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[VL_OL_0026] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "LEDGERSUM", h_ledger_sum); + tx_buf_setlong(ctx->out, "LEDGERCNT", h_ledger_cnt); + tx_buf_setlong(ctx->out, "AGGAMT", h_agg_amt); + tx_buf_setlong(ctx->out, "AGGCNT", h_agg_cnt); + tx_buf_setlong(ctx->out, "DIFFAMT", diff_amt); + tx_buf_setlong(ctx->out, "DIFFCNT", diff_cnt); + + if (mismatch) { + tx_log(TX_LOG_WARN, + "[VL_OL_0026] 합계 불일치 date=%s diffAmt=%ld diffCnt=%ld", + bizdate, diff_amt, diff_cnt); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2603"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0026] 합계 정합 date=%s sum=%ld cnt=%ld", + bizdate, h_ledger_sum, h_ledger_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0026.pgc b/app/online/vl_ol_0026.pgc index e9eb220..514c9a0 100644 --- a/app/online/vl_ol_0026.pgc +++ b/app/online/vl_ol_0026.pgc @@ -1,14 +1,16 @@ /* @tier hard @module vl @transform validation-online */ /* - * vl_ol_0026.pgc - 정합성 온라인 서비스 (VL_OL_0026) [tier=hard] + * vl_ol_0026.pgc - 금액합계 검증 서비스 (VL_OL_0026) [tier=hard] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 영업일 원장합계(SUM vl_ledger) 와 일집계 테이블(vl_daily_agg.total_amt) + * 의 합계를 대사하여 차액 및 건수 불일치를 판정한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,83 +18,107 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0026, ctx) { - vl_rec_t rec; - char key[16], merch[16], bizdate[16], nextbiz[9]; - long amount = 0, daysum = 0; - int rc; - TXBUF *sub_in; - TXBUF *sub_out; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[9]; + long h_ledger_sum; + long h_ledger_cnt; + long h_agg_amt; + long h_agg_cnt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0026] 정합성 트랜잭션 오케스트레이션 진입"); + char bizdate[16]; + long diff_amt, diff_cnt; + int mismatch = 0; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0026] 필수 필드 누락"); + tx_log(TX_LOG_INFO, "[VL_OL_0026] 금액합계 검증 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0026] 영업일(BIZDATE) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2600"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - if (!amount_is_valid(amount) || !date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0026] 입력 검증 실패 amount=%ld date=%s", - amount, bizdate); + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[VL_OL_0026] 영업일 형식 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "VL2601"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - /* 트랜잭션 시작 → 원장 반영 → 후속 서비스 연계 → 커밋 */ - if (tx_begin() != TX_OK) { - tx_return(ctx, TX_FAIL, ctx->out); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + + /* 원장 상세 합계 */ + h_ledger_sum = 0; + h_ledger_cnt = 0; + EXEC SQL + SELECT COALESCE(SUM(amount), 0), COUNT(*) + INTO :h_ledger_sum, :h_ledger_cnt + FROM vl_ledger + WHERE biz_date = :h_bizdate; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT SUM vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2608"); + tx_return(ctx, TX_EDB, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; + /* 일집계 테이블 값 */ + h_agg_amt = 0; + h_agg_cnt = 0; + EXEC SQL + SELECT total_amt, total_cnt + INTO :h_agg_amt, :h_agg_cnt + FROM vl_daily_agg + WHERE biz_date = :h_bizdate; - rc = vl_rec_insert(&rec); - if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0026] 원장반영 실패 rc=%d", rc); - tx_abort(); - tx_return(ctx, rc, ctx->out); + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0026] 일집계 미생성 date=%s", bizdate); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "LEDGERSUM", h_ledger_sum); + tx_buf_setlong(ctx->out, "LEDGERCNT", h_ledger_cnt); + tx_buf_sets(ctx->out, "RESULT", "NOAGG"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2602"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_daily_agg"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2609"); + tx_return(ctx, TX_EDB, ctx->out); return; } - /* 정산 영업일 = 거래일의 다음 영업일 */ - if (date_next_business(bizdate, nextbiz) != 0) { - memcpy(nextbiz, bizdate, 8); - nextbiz[8] = '\0'; + diff_amt = h_ledger_sum - h_agg_amt; + diff_cnt = h_ledger_cnt - h_agg_cnt; + if (diff_amt != 0 || diff_cnt != 0) { + mismatch = 1; } - /* 후속 정산집계 서비스 연계 (레지스트리 문자열 디스패치) */ - sub_in = tx_buf_alloc(); - sub_out = tx_buf_alloc(); - if (sub_in && sub_out) { - tx_buf_sets(sub_in, "MERCHID", merch); - tx_buf_sets(sub_in, "BIZDATE", nextbiz); - tx_buf_setlong(sub_in, "AMOUNT", amount); - rc = tx_call("VL_POST", sub_in, sub_out); - if (rc != TX_OK) - tx_log(TX_LOG_WARN, "[VL_OL_0026] 후속연계 경고 rc=%d", rc); - } - tx_buf_free(sub_in); - tx_buf_free(sub_out); - - /* 교차 검증: 당일 합계 재계산 */ - if (vl_rec_sum(bizdate, &daysum) != TX_OK) - daysum = 0; - - tx_commit(); - tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); - tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYSUM", daysum); - tx_log(TX_LOG_INFO, "[VL_OL_0026] 완료 key=%s 당일합계=%ld", key, daysum); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "LEDGERSUM", h_ledger_sum); + tx_buf_setlong(ctx->out, "LEDGERCNT", h_ledger_cnt); + tx_buf_setlong(ctx->out, "AGGAMT", h_agg_amt); + tx_buf_setlong(ctx->out, "AGGCNT", h_agg_cnt); + tx_buf_setlong(ctx->out, "DIFFAMT", diff_amt); + tx_buf_setlong(ctx->out, "DIFFCNT", diff_cnt); + + if (mismatch) { + tx_log(TX_LOG_WARN, + "[VL_OL_0026] 합계 불일치 date=%s diffAmt=%ld diffCnt=%ld", + bizdate, diff_amt, diff_cnt); + tx_buf_sets(ctx->out, "RESULT", "MISMATCH"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2603"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "RESULT", "MATCH"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0026] 합계 정합 date=%s sum=%ld cnt=%ld", + bizdate, h_ledger_sum, h_ledger_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0027.c b/app/online/vl_ol_0027.c index e2ee163..aee5197 100644 --- a/app/online/vl_ol_0027.c +++ b/app/online/vl_ol_0027.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0027.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0027.pgc - 정합성 온라인 서비스 (VL_OL_0027) [tier=easy] + * vl_ol_0027.pgc - 정합성 재검증 서비스 (VL_OL_0027) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 불일치(status='U') 단건을 대상으로 원천(vl_recheck_src) 의 금액/가맹점을 + * 다시 대조하여, 정합이면 상태를 완료(M) 로 전이한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,19 +91,37 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0027.pgc" +#line 17 "app/online/vl_ol_0027.pgc" TX_SERVICE(VL_OL_0027, ctx) { + /* exec sql begin declare section */ + + + + +#line 22 "app/online/vl_ol_0027.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/vl_ol_0027.pgc" + long h_src_amt ; + +#line 24 "app/online/vl_ol_0027.pgc" + char h_src_merch [ 16 ] ; +/* exec sql end declare section */ +#line 25 "app/online/vl_ol_0027.pgc" + + vl_rec_t rec; char key[16]; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0027] 정합성 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0027] 정합성 재검증 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[VL_OL_0027] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2700"); tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -109,17 +129,86 @@ TX_SERVICE(VL_OL_0027, ctx) memset(&rec, 0, sizeof(rec)); rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0027] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[VL_OL_0027] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); + tx_buf_sets(ctx->out, "ERRCODE", "VL2701"); tx_return(ctx, rc, ctx->out); return; } + /* 재검증 대상은 불일치(U) 건에 한한다 */ + if (strcmp(rec.status, VL_ST_FAIL) != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0027] 대상아님 key=%s status=%s", + key, rec.status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", rec.status); + tx_buf_sets(ctx->out, "RESULT", "SKIP"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2702"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + memset(h_src_merch, 0, sizeof(h_src_merch)); + h_src_amt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select src_amount , src_merch_id from vl_recheck_src where txn_key = $1 ", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_src_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_src_merch),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 72 "app/online/vl_ol_0027.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0027] 재검증 원천 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "RESULT", "NOSOURCE"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2703"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_recheck_src"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2709"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0027] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "LEDGERAMT", rec.amount); + tx_buf_setlong(ctx->out, "SRCAMT", h_src_amt); + + /* 금액·가맹점 모두 일치해야 재검증 통과 */ + if (rec.amount != h_src_amt || strcmp(rec.merch_id, h_src_merch) != 0) { + tx_log(TX_LOG_WARN, + "[VL_OL_0027] 재검증 여전 불일치 key=%s led=%ld src=%ld", + key, rec.amount, h_src_amt); + tx_buf_sets(ctx->out, "RESULT", "STILLFAIL"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2704"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + rc = vl_rec_update_status(key, VL_ST_DONE); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0027] 상태전이 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); + tx_buf_sets(ctx->out, "ERRCODE", "VL2705"); + tx_return(ctx, rc, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "STATUS", VL_ST_DONE); + tx_buf_sets(ctx->out, "RESULT", "RESOLVED"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0027] 재검증 정합→완료 key=%s amt=%ld", + key, rec.amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0027.pgc b/app/online/vl_ol_0027.pgc index e53e952..110d89d 100644 --- a/app/online/vl_ol_0027.pgc +++ b/app/online/vl_ol_0027.pgc @@ -1,14 +1,16 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0027.pgc - 정합성 온라인 서비스 (VL_OL_0027) [tier=easy] + * vl_ol_0027.pgc - 정합성 재검증 서비스 (VL_OL_0027) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 불일치(status='U') 단건을 대상으로 원천(vl_recheck_src) 의 금액/가맹점을 + * 다시 대조하여, 정합이면 상태를 완료(M) 로 전이한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,14 +18,21 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0027, ctx) { + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + long h_src_amt; + char h_src_merch[16]; + EXEC SQL END DECLARE SECTION; + vl_rec_t rec; char key[16]; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0027] 정합성 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0027] 정합성 재검증 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { tx_log(TX_LOG_ERROR, "[VL_OL_0027] KEY 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2700"); tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -31,17 +40,82 @@ TX_SERVICE(VL_OL_0027, ctx) memset(&rec, 0, sizeof(rec)); rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0027] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[VL_OL_0027] 원장 조회 실패 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); + tx_buf_sets(ctx->out, "ERRCODE", "VL2701"); tx_return(ctx, rc, ctx->out); return; } + /* 재검증 대상은 불일치(U) 건에 한한다 */ + if (strcmp(rec.status, VL_ST_FAIL) != 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0027] 대상아님 key=%s status=%s", + key, rec.status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", rec.status); + tx_buf_sets(ctx->out, "RESULT", "SKIP"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2702"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + + memset(h_src_merch, 0, sizeof(h_src_merch)); + h_src_amt = 0; + EXEC SQL + SELECT src_amount, src_merch_id + INTO :h_src_amt, :h_src_merch + FROM vl_recheck_src + WHERE txn_key = :h_key; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_WARN, "[VL_OL_0027] 재검증 원천 없음 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "RESULT", "NOSOURCE"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2703"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_recheck_src"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2709"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0027] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_setlong(ctx->out, "LEDGERAMT", rec.amount); + tx_buf_setlong(ctx->out, "SRCAMT", h_src_amt); + + /* 금액·가맹점 모두 일치해야 재검증 통과 */ + if (rec.amount != h_src_amt || strcmp(rec.merch_id, h_src_merch) != 0) { + tx_log(TX_LOG_WARN, + "[VL_OL_0027] 재검증 여전 불일치 key=%s led=%ld src=%ld", + key, rec.amount, h_src_amt); + tx_buf_sets(ctx->out, "RESULT", "STILLFAIL"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2704"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + rc = vl_rec_update_status(key, VL_ST_DONE); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0027] 상태전이 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); + tx_buf_sets(ctx->out, "ERRCODE", "VL2705"); + tx_return(ctx, rc, ctx->out); + return; + } + + tx_buf_sets(ctx->out, "STATUS", VL_ST_DONE); + tx_buf_sets(ctx->out, "RESULT", "RESOLVED"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + tx_log(TX_LOG_INFO, "[VL_OL_0027] 재검증 정합→완료 key=%s amt=%ld", + key, rec.amount); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0028.c b/app/online/vl_ol_0028.c index ab7b876..91059b0 100644 --- a/app/online/vl_ol_0028.c +++ b/app/online/vl_ol_0028.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0028.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0028.pgc - 정합성 온라인 서비스 (VL_OL_0028) [tier=easy] + * vl_ol_0028.pgc - 오류내역 조회 서비스 (VL_OL_0028) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 에러코드로 오류로그(vl_error_log) 의 최근 1건(발생일시/사유/처리키) + * 을 조회하여 응답한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,37 +91,93 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0028.pgc" +#line 17 "app/online/vl_ol_0028.pgc" TX_SERVICE(VL_OL_0028, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + +#line 22 "app/online/vl_ol_0028.pgc" + char h_errcode [ 8 ] ; + +#line 23 "app/online/vl_ol_0028.pgc" + char h_occur_dt [ 15 ] ; + +#line 24 "app/online/vl_ol_0028.pgc" + char h_reason [ 128 ] ; + +#line 25 "app/online/vl_ol_0028.pgc" + char h_ref_key [ 16 ] ; +/* exec sql end declare section */ +#line 26 "app/online/vl_ol_0028.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0028] 정합성 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0028] KEY 누락"); + char errcode[8]; + + tx_log(TX_LOG_INFO, "[VL_OL_0028] 오류내역 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "ERRCODE", errcode, sizeof(errcode)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0028] 조회 에러코드(ERRCODE) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2800"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (strlen(errcode) == 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0028] 에러코드 공백"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2801"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_errcode, errcode, sizeof(h_errcode) - 1); + h_errcode[sizeof(h_errcode) - 1] = '\0'; + + memset(h_occur_dt, 0, sizeof(h_occur_dt)); + memset(h_reason, 0, sizeof(h_reason)); + memset(h_ref_key, 0, sizeof(h_ref_key)); + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select occur_dt , reason , ref_key from vl_error_log where err_code = $1 order by occur_dt desc fetch first 1 row only", + ECPGt_char,(h_errcode),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_char,(h_occur_dt),(long)15,(long)1,(15)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_reason),(long)128,(long)1,(128)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_ref_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 58 "app/online/vl_ol_0028.pgc" + + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_INFO, "[VL_OL_0028] 오류내역 없음 code=%s", errcode); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "QUERYCODE", errcode); + tx_buf_sets(ctx->out, "RESULT", "NONE"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2802"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_error_log"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2809"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "QUERYCODE", errcode); + tx_buf_sets(ctx->out, "OCCURDT", h_occur_dt); + tx_buf_sets(ctx->out, "REASON", h_reason); + tx_buf_sets(ctx->out, "REFKEY", h_ref_key); + tx_buf_sets(ctx->out, "RESULT", "FOUND"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, "[VL_OL_0028] 오류내역 조회 code=%s dt=%s key=%s", + errcode, h_occur_dt, h_ref_key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0028.pgc b/app/online/vl_ol_0028.pgc index 47db6e8..dfccf78 100644 --- a/app/online/vl_ol_0028.pgc +++ b/app/online/vl_ol_0028.pgc @@ -1,14 +1,16 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0028.pgc - 정합성 온라인 서비스 (VL_OL_0028) [tier=easy] + * vl_ol_0028.pgc - 오류내역 조회 서비스 (VL_OL_0028) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 에러코드로 오류로그(vl_error_log) 의 최근 1건(발생일시/사유/처리키) + * 을 조회하여 응답한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,32 +18,70 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0028, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_errcode[8]; + char h_occur_dt[15]; + char h_reason[128]; + char h_ref_key[16]; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0028] 정합성 단건조회 서비스 진입"); + char errcode[8]; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0028] KEY 누락"); + tx_log(TX_LOG_INFO, "[VL_OL_0028] 오류내역 조회 서비스 진입"); + + if (tx_buf_get(ctx->in, "ERRCODE", errcode, sizeof(errcode)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0028] 조회 에러코드(ERRCODE) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2800"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (strlen(errcode) == 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0028] 에러코드 공백"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2801"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0028] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_errcode, errcode, sizeof(h_errcode) - 1); + h_errcode[sizeof(h_errcode) - 1] = '\0'; + + memset(h_occur_dt, 0, sizeof(h_occur_dt)); + memset(h_reason, 0, sizeof(h_reason)); + memset(h_ref_key, 0, sizeof(h_ref_key)); + + EXEC SQL + SELECT occur_dt, reason, ref_key + INTO :h_occur_dt, :h_reason, :h_ref_key + FROM vl_error_log + WHERE err_code = :h_errcode + ORDER BY occur_dt DESC + FETCH FIRST 1 ROW ONLY; + + if (sqlca.sqlcode == TX_SQL_NOTFOUND) { + tx_log(TX_LOG_INFO, "[VL_OL_0028] 오류내역 없음 code=%s", errcode); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "QUERYCODE", errcode); + tx_buf_sets(ctx->out, "RESULT", "NONE"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2802"); + tx_return(ctx, TX_ENOENT, ctx->out); + return; + } + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("SELECT vl_error_log"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2809"); + tx_return(ctx, TX_EDB, ctx->out); return; } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0028] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "QUERYCODE", errcode); + tx_buf_sets(ctx->out, "OCCURDT", h_occur_dt); + tx_buf_sets(ctx->out, "REASON", h_reason); + tx_buf_sets(ctx->out, "REFKEY", h_ref_key); + tx_buf_sets(ctx->out, "RESULT", "FOUND"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, "[VL_OL_0028] 오류내역 조회 code=%s dt=%s key=%s", + errcode, h_occur_dt, h_ref_key); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0029.c b/app/online/vl_ol_0029.c index 213a404..1f52070 100644 --- a/app/online/vl_ol_0029.c +++ b/app/online/vl_ol_0029.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0029.pgc" /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0029.pgc - 정합성 온라인 서비스 (VL_OL_0029) [tier=medium] + * vl_ol_0029.pgc - 불일치 사유 등록 서비스 (VL_OL_0029) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리키 + 사유코드 + 사유텍스트를 불일치사유(vl_mismatch_reason) 이력에 + * INSERT 하고, 해당 원장건의 상태를 불일치(U) 로 전이한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,60 +91,116 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0029.pgc" +#line 17 "app/online/vl_ol_0029.pgc" TX_SERVICE(VL_OL_0029, ctx) { + /* exec sql begin declare section */ + + + + +#line 22 "app/online/vl_ol_0029.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/vl_ol_0029.pgc" + char h_rcode [ 8 ] ; + +#line 24 "app/online/vl_ol_0029.pgc" + char h_reason [ 128 ] ; +/* exec sql end declare section */ +#line 25 "app/online/vl_ol_0029.pgc" + + vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + char rcode[8]; + char reason[128]; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0029] 정합성 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0029] 불일치 사유 등록 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0029] 필수 필드 누락"); + tx_buf_get(ctx->in, "RCODE", rcode, sizeof(rcode)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0029] 필수 필드(KEY/RCODE) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2900"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0029] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0029] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) { + strncpy(reason, "사유미기재", sizeof(reason) - 1); + reason[sizeof(reason) - 1] = '\0'; } + /* 원장 존재 확인 */ memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = vl_rec_insert(&rec); + rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0029] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[VL_OL_0029] 원장 없음 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); + tx_buf_sets(ctx->out, "ERRCODE", "VL2901"); tx_return(ctx, rc, ctx->out); return; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0029] 트랜잭션 시작 실패"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2902"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_rcode, rcode, sizeof(h_rcode) - 1); + h_rcode[sizeof(h_rcode) - 1] = '\0'; + strncpy(h_reason, reason, sizeof(h_reason) - 1); + h_reason[sizeof(h_reason) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into vl_mismatch_reason ( txn_key , reason_code , reason_txt , reg_dt ) values ( $1 , $2 , $3 , TO_CHAR ( SYSTIMESTAMP , 'YYYYMMDDHH24MISS' ) )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_rcode),(long)8,(long)1,(8)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_reason),(long)128,(long)1,(128)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 75 "app/online/vl_ol_0029.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT vl_mismatch_reason"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL2909"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + rc = vl_rec_update_status(key, VL_ST_FAIL); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0029] 상태전이(U) 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL2903"); + tx_return(ctx, rc, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0029] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL2904"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0029] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "RCODE", rcode); + tx_buf_sets(ctx->out, "STATUS", VL_ST_FAIL); + tx_buf_sets(ctx->out, "RESULT", "REGISTERED"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, "[VL_OL_0029] 사유등록 완료 key=%s rcode=%s", key, rcode); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0029.pgc b/app/online/vl_ol_0029.pgc index 9e89703..1c98a4e 100644 --- a/app/online/vl_ol_0029.pgc +++ b/app/online/vl_ol_0029.pgc @@ -1,14 +1,16 @@ /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0029.pgc - 정합성 온라인 서비스 (VL_OL_0029) [tier=medium] + * vl_ol_0029.pgc - 불일치 사유 등록 서비스 (VL_OL_0029) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 처리키 + 사유코드 + 사유텍스트를 불일치사유(vl_mismatch_reason) 이력에 + * INSERT 하고, 해당 원장건의 상태를 불일치(U) 로 전이한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,55 +18,95 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0029, ctx) { + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_rcode[8]; + char h_reason[128]; + EXEC SQL END DECLARE SECTION; + vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + char rcode[8]; + char reason[128]; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0029] 정합성 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0029] 불일치 사유 등록 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0029] 필수 필드 누락"); + tx_buf_get(ctx->in, "RCODE", rcode, sizeof(rcode)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0029] 필수 필드(KEY/RCODE) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2900"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0029] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0029] 영업일 오류 date=%s", bizdate); - tx_return(ctx, TX_EINVAL, ctx->out); - return; + if (tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) { + strncpy(reason, "사유미기재", sizeof(reason) - 1); + reason[sizeof(reason) - 1] = '\0'; } + /* 원장 존재 확인 */ memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = vl_rec_insert(&rec); + rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0029] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[VL_OL_0029] 원장 없음 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); + tx_buf_sets(ctx->out, "ERRCODE", "VL2901"); tx_return(ctx, rc, ctx->out); return; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0029] 트랜잭션 시작 실패"); + tx_buf_sets(ctx->out, "ERRCODE", "VL2902"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_rcode, rcode, sizeof(h_rcode) - 1); + h_rcode[sizeof(h_rcode) - 1] = '\0'; + strncpy(h_reason, reason, sizeof(h_reason) - 1); + h_reason[sizeof(h_reason) - 1] = '\0'; + + EXEC SQL + INSERT INTO vl_mismatch_reason + (txn_key, reason_code, reason_txt, reg_dt) + VALUES (:h_key, :h_rcode, :h_reason, TO_CHAR(SYSTIMESTAMP, 'YYYYMMDDHH24MISS')); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT vl_mismatch_reason"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL2909"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + rc = vl_rec_update_status(key, VL_ST_FAIL); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0029] 상태전이(U) 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL2903"); + tx_return(ctx, rc, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0029] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL2904"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0029] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "RCODE", rcode); + tx_buf_sets(ctx->out, "STATUS", VL_ST_FAIL); + tx_buf_sets(ctx->out, "RESULT", "REGISTERED"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, "[VL_OL_0029] 사유등록 완료 key=%s rcode=%s", key, rcode); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0030.c b/app/online/vl_ol_0030.c index 954b189..a8cb4a7 100644 --- a/app/online/vl_ol_0030.c +++ b/app/online/vl_ol_0030.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0030.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0030.pgc - 정합성 온라인 서비스 (VL_OL_0030) [tier=easy] + * vl_ol_0030.pgc - 정합성 승인처리 서비스 (VL_OL_0030) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 검토완료된 원장건을 완료(M) 상태로 전이하고, 승인이력(vl_approval_hist) + * 에 승인자/승인일시를 INSERT 한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,19 +91,35 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0030.pgc" +#line 17 "app/online/vl_ol_0030.pgc" TX_SERVICE(VL_OL_0030, ctx) { + /* exec sql begin declare section */ + + + +#line 22 "app/online/vl_ol_0030.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/vl_ol_0030.pgc" + char h_approver [ 16 ] ; +/* exec sql end declare section */ +#line 24 "app/online/vl_ol_0030.pgc" + + vl_rec_t rec; char key[16]; + char approver[16]; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0030] 정합성 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0030] 정합성 승인처리 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0030] KEY 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "APPROVER", approver, sizeof(approver)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0030] 필수 필드(KEY/APPROVER) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3000"); tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -109,17 +127,81 @@ TX_SERVICE(VL_OL_0030, ctx) memset(&rec, 0, sizeof(rec)); rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0030] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[VL_OL_0030] 원장 없음 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); + tx_buf_sets(ctx->out, "ERRCODE", "VL3001"); tx_return(ctx, rc, ctx->out); return; } + /* 이미 완료/정산된 건은 승인 대상이 아니다 */ + if (strcmp(rec.status, VL_ST_DONE) == 0 || + strcmp(rec.status, VL_ST_SETTLED) == 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0030] 승인불가 상태 key=%s status=%s", + key, rec.status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", rec.status); + tx_buf_sets(ctx->out, "RESULT", "ALREADY"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3002"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0030] 트랜잭션 시작 실패"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3003"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + rc = vl_rec_update_status(key, VL_ST_DONE); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0030] 상태전이(M) 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL3004"); + tx_return(ctx, rc, ctx->out); + return; + } + + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_approver, approver, sizeof(h_approver) - 1); + h_approver[sizeof(h_approver) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into vl_approval_hist ( txn_key , approver_id , appr_dt , prev_status ) values ( $1 , $2 , TO_CHAR ( SYSTIMESTAMP , 'YYYYMMDDHH24MISS' ) , 'R' )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_approver),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 91 "app/online/vl_ol_0030.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT vl_approval_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL3009"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0030] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL3005"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "APPROVER", approver); + tx_buf_sets(ctx->out, "STATUS", VL_ST_DONE); + tx_buf_sets(ctx->out, "RESULT", "APPROVED"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, "[VL_OL_0030] 승인처리 완료 key=%s approver=%s", + key, approver); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0030.pgc b/app/online/vl_ol_0030.pgc index d0d07f7..a36aab8 100644 --- a/app/online/vl_ol_0030.pgc +++ b/app/online/vl_ol_0030.pgc @@ -1,14 +1,16 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0030.pgc - 정합성 온라인 서비스 (VL_OL_0030) [tier=easy] + * vl_ol_0030.pgc - 정합성 승인처리 서비스 (VL_OL_0030) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 검토완료된 원장건을 완료(M) 상태로 전이하고, 승인이력(vl_approval_hist) + * 에 승인자/승인일시를 INSERT 한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,14 +18,22 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0030, ctx) { + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_approver[16]; + EXEC SQL END DECLARE SECTION; + vl_rec_t rec; char key[16]; + char approver[16]; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0030] 정합성 단건조회 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0030] 정합성 승인처리 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0030] KEY 누락"); + if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || + tx_buf_get(ctx->in, "APPROVER", approver, sizeof(approver)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0030] 필수 필드(KEY/APPROVER) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3000"); tx_return(ctx, TX_EINVAL, ctx->out); return; } @@ -31,17 +41,79 @@ TX_SERVICE(VL_OL_0030, ctx) memset(&rec, 0, sizeof(rec)); rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0030] 조회 실패 key=%s rc=%d(%s)", + tx_log(TX_LOG_WARN, "[VL_OL_0030] 원장 없음 key=%s rc=%d(%s)", key, rc, tx_strerror(rc)); + tx_buf_sets(ctx->out, "ERRCODE", "VL3001"); tx_return(ctx, rc, ctx->out); return; } + /* 이미 완료/정산된 건은 승인 대상이 아니다 */ + if (strcmp(rec.status, VL_ST_DONE) == 0 || + strcmp(rec.status, VL_ST_SETTLED) == 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0030] 승인불가 상태 key=%s status=%s", + key, rec.status); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", rec.status); + tx_buf_sets(ctx->out, "RESULT", "ALREADY"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3002"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0030] 트랜잭션 시작 실패"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3003"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + rc = vl_rec_update_status(key, VL_ST_DONE); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0030] 상태전이(M) 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL3004"); + tx_return(ctx, rc, ctx->out); + return; + } + + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_approver, approver, sizeof(h_approver) - 1); + h_approver[sizeof(h_approver) - 1] = '\0'; + + EXEC SQL + INSERT INTO vl_approval_hist + (txn_key, approver_id, appr_dt, prev_status) + VALUES (:h_key, :h_approver, + TO_CHAR(SYSTIMESTAMP, 'YYYYMMDDHH24MISS'), 'R'); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT vl_approval_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL3009"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0030] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL3005"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0030] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "APPROVER", approver); + tx_buf_sets(ctx->out, "STATUS", VL_ST_DONE); + tx_buf_sets(ctx->out, "RESULT", "APPROVED"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, "[VL_OL_0030] 승인처리 완료 key=%s approver=%s", + key, approver); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0031.c b/app/online/vl_ol_0031.c index db6cdd3..3e743b6 100644 --- a/app/online/vl_ol_0031.c +++ b/app/online/vl_ol_0031.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0031.pgc" /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0031.pgc - 정합성 온라인 서비스 (VL_OL_0031) [tier=medium] + * vl_ol_0031.pgc - 정합성 반려처리 서비스 (VL_OL_0031) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 검토건을 불일치(U) 상태로 전이하고, 반려이력(vl_reject_hist) 에 + * 반려자/반려사유/반려일시를 INSERT 한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,60 +91,131 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0031.pgc" +#line 17 "app/online/vl_ol_0031.pgc" TX_SERVICE(VL_OL_0031, ctx) { + /* exec sql begin declare section */ + + + + +#line 22 "app/online/vl_ol_0031.pgc" + char h_key [ 16 ] ; + +#line 23 "app/online/vl_ol_0031.pgc" + char h_rejector [ 16 ] ; + +#line 24 "app/online/vl_ol_0031.pgc" + char h_reason [ 128 ] ; +/* exec sql end declare section */ +#line 25 "app/online/vl_ol_0031.pgc" + + vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + char rejector[16]; + char reason[128]; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0031] 정합성 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0031] 정합성 반려처리 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0031] 필수 필드 누락"); + tx_buf_get(ctx->in, "REJECTOR", rejector, sizeof(rejector)) != TX_OK || + tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0031] 필수 필드(KEY/REJECTOR/REASON) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3100"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0031] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0031] 영업일 오류 date=%s", bizdate); + if (strlen(reason) == 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0031] 반려사유 공백 key=%s", key); + tx_buf_sets(ctx->out, "ERRCODE", "VL3101"); tx_return(ctx, TX_EINVAL, ctx->out); return; } memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = vl_rec_insert(&rec); + rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0031] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[VL_OL_0031] 원장 없음 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); + tx_buf_sets(ctx->out, "ERRCODE", "VL3102"); tx_return(ctx, rc, ctx->out); return; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); + /* 정산완료 건은 반려 불가 */ + if (strcmp(rec.status, VL_ST_SETTLED) == 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0031] 정산완료 반려불가 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", rec.status); + tx_buf_sets(ctx->out, "RESULT", "LOCKED"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3103"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0031] 트랜잭션 시작 실패"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3104"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + rc = vl_rec_update_status(key, VL_ST_FAIL); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0031] 상태전이(U) 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL3105"); + tx_return(ctx, rc, ctx->out); + return; + } + + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_rejector, rejector, sizeof(h_rejector) - 1); + h_rejector[sizeof(h_rejector) - 1] = '\0'; + strncpy(h_reason, reason, sizeof(h_reason) - 1); + h_reason[sizeof(h_reason) - 1] = '\0'; + + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "insert into vl_reject_hist ( txn_key , rejector_id , reject_reason , reject_dt ) values ( $1 , $2 , $3 , TO_CHAR ( SYSTIMESTAMP , 'YYYYMMDDHH24MISS' ) )", + ECPGt_char,(h_key),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_rejector),(long)16,(long)1,(16)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_char,(h_reason),(long)128,(long)1,(128)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);} +#line 100 "app/online/vl_ol_0031.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT vl_reject_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL3109"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0031] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL3106"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0031] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "REJECTOR", rejector); + tx_buf_sets(ctx->out, "STATUS", VL_ST_FAIL); + tx_buf_sets(ctx->out, "RESULT", "REJECTED"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, "[VL_OL_0031] 반려처리 완료 key=%s rejector=%s", + key, rejector); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0031.pgc b/app/online/vl_ol_0031.pgc index 2e1481b..0a1e48d 100644 --- a/app/online/vl_ol_0031.pgc +++ b/app/online/vl_ol_0031.pgc @@ -1,14 +1,16 @@ /* @tier medium @module vl @transform validation-online */ /* - * vl_ol_0031.pgc - 정합성 온라인 서비스 (VL_OL_0031) [tier=medium] + * vl_ol_0031.pgc - 정합성 반려처리 서비스 (VL_OL_0031) [tier=medium] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 검토건을 불일치(U) 상태로 전이하고, 반려이력(vl_reject_hist) 에 + * 반려자/반려사유/반려일시를 INSERT 한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,55 +18,111 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0031, ctx) { + EXEC SQL BEGIN DECLARE SECTION; + char h_key[16]; + char h_rejector[16]; + char h_reason[128]; + EXEC SQL END DECLARE SECTION; + vl_rec_t rec; - char key[16], merch[16], bizdate[16]; - long amount = 0, cnt; + char key[16]; + char rejector[16]; + char reason[128]; int rc; - tx_log(TX_LOG_INFO, "[VL_OL_0031] 정합성 등록 서비스 진입"); + tx_log(TX_LOG_INFO, "[VL_OL_0031] 정합성 반려처리 서비스 진입"); if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK || - tx_buf_get(ctx->in, "MERCHID", merch, sizeof(merch)) != TX_OK || - tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0031] 필수 필드 누락"); + tx_buf_get(ctx->in, "REJECTOR", rejector, sizeof(rejector)) != TX_OK || + tx_buf_get(ctx->in, "REASON", reason, sizeof(reason)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0031] 필수 필드(KEY/REJECTOR/REASON) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3100"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - tx_buf_getlong(ctx->in, "AMOUNT", &amount); - - /* 업무검증: 금액 범위 + 영업일 형식 */ - if (!amount_is_valid(amount)) { - tx_log(TX_LOG_WARN, "[VL_OL_0031] 금액 범위 오류 amount=%ld", amount); - tx_return(ctx, TX_EINVAL, ctx->out); - return; - } - if (!date_is_valid(bizdate)) { - tx_log(TX_LOG_WARN, "[VL_OL_0031] 영업일 오류 date=%s", bizdate); + if (strlen(reason) == 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0031] 반려사유 공백 key=%s", key); + tx_buf_sets(ctx->out, "ERRCODE", "VL3101"); tx_return(ctx, TX_EINVAL, ctx->out); return; } memset(&rec, 0, sizeof(rec)); - strncpy(rec.key, key, sizeof(rec.key) - 1); - strncpy(rec.merch_id, merch, sizeof(rec.merch_id) - 1); - strncpy(rec.biz_date, bizdate, sizeof(rec.biz_date) - 1); - strncpy(rec.status, VL_ST_RECV, sizeof(rec.status) - 1); - rec.amount = amount; - rec.fee = amount / 100; /* 수수료 1% 가정 */ - - rc = vl_rec_insert(&rec); + rc = vl_rec_select(key, &rec); if (rc != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0031] 등록 실패 rc=%d(%s)", rc, tx_strerror(rc)); + tx_log(TX_LOG_WARN, "[VL_OL_0031] 원장 없음 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); + tx_buf_sets(ctx->out, "ERRCODE", "VL3102"); tx_return(ctx, rc, ctx->out); return; } - cnt = vl_rec_count(bizdate, VL_ST_RECV); + /* 정산완료 건은 반려 불가 */ + if (strcmp(rec.status, VL_ST_SETTLED) == 0) { + tx_log(TX_LOG_WARN, "[VL_OL_0031] 정산완료 반려불가 key=%s", key); + tx_buf_reset(ctx->out); + tx_buf_sets(ctx->out, "KEY", key); + tx_buf_sets(ctx->out, "STATUS", rec.status); + tx_buf_sets(ctx->out, "RESULT", "LOCKED"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3103"); + tx_return(ctx, TX_OK, ctx->out); + return; + } + + if (tx_begin() != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0031] 트랜잭션 시작 실패"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3104"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + rc = vl_rec_update_status(key, VL_ST_FAIL); + if (rc != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0031] 상태전이(U) 실패 key=%s rc=%d(%s)", + key, rc, tx_strerror(rc)); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL3105"); + tx_return(ctx, rc, ctx->out); + return; + } + + strncpy(h_key, key, sizeof(h_key) - 1); + h_key[sizeof(h_key) - 1] = '\0'; + strncpy(h_rejector, rejector, sizeof(h_rejector) - 1); + h_rejector[sizeof(h_rejector) - 1] = '\0'; + strncpy(h_reason, reason, sizeof(h_reason) - 1); + h_reason[sizeof(h_reason) - 1] = '\0'; + + EXEC SQL + INSERT INTO vl_reject_hist + (txn_key, rejector_id, reject_reason, reject_dt) + VALUES (:h_key, :h_rejector, :h_reason, + TO_CHAR(SYSTIMESTAMP, 'YYYYMMDDHH24MISS')); + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("INSERT vl_reject_hist"); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL3109"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + if (tx_commit() != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0031] 커밋 실패 key=%s", key); + tx_abort(); + tx_buf_sets(ctx->out, "ERRCODE", "VL3106"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "RESPCODE", RESP_OK); tx_buf_sets(ctx->out, "KEY", key); - tx_buf_setlong(ctx->out, "DAYCNT", (cnt < 0) ? 0 : cnt); - tx_log(TX_LOG_INFO, "[VL_OL_0031] 등록완료 key=%s 당일건수=%ld", key, cnt); + tx_buf_sets(ctx->out, "REJECTOR", rejector); + tx_buf_sets(ctx->out, "STATUS", VL_ST_FAIL); + tx_buf_sets(ctx->out, "RESULT", "REJECTED"); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, "[VL_OL_0031] 반려처리 완료 key=%s rejector=%s", + key, rejector); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0032.c b/app/online/vl_ol_0032.c index ae29e61..3bbe2a5 100644 --- a/app/online/vl_ol_0032.c +++ b/app/online/vl_ol_0032.c @@ -8,15 +8,17 @@ #line 1 "app/online/vl_ol_0032.pgc" /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0032.pgc - 정합성 온라인 서비스 (VL_OL_0032) [tier=easy] + * vl_ol_0032.pgc - 정합성 리포트 요약 서비스 (VL_OL_0032) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 영업일의 대상/완료/불일치/정산 건수와 금액합계를 여러 집계 SQL 로 + * 모아 하나의 리포트 응답으로 구성한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -89,37 +91,146 @@ struct sqlca_t *ECPGget_sqlca(void); #endif -#line 15 "app/online/vl_ol_0032.pgc" +#line 17 "app/online/vl_ol_0032.pgc" TX_SERVICE(VL_OL_0032, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + /* exec sql begin declare section */ + + + + + + + + +#line 22 "app/online/vl_ol_0032.pgc" + char h_bizdate [ 9 ] ; + +#line 23 "app/online/vl_ol_0032.pgc" + long h_total_cnt ; + +#line 24 "app/online/vl_ol_0032.pgc" + long h_total_amt ; + +#line 25 "app/online/vl_ol_0032.pgc" + long h_done_cnt ; + +#line 26 "app/online/vl_ol_0032.pgc" + long h_fail_cnt ; + +#line 27 "app/online/vl_ol_0032.pgc" + long h_settle_cnt ; + +#line 28 "app/online/vl_ol_0032.pgc" + long h_settle_amt ; +/* exec sql end declare section */ +#line 29 "app/online/vl_ol_0032.pgc" - tx_log(TX_LOG_INFO, "[VL_OL_0032] 정합성 단건조회 서비스 진입"); - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0032] KEY 누락"); + char bizdate[16]; + char amt_disp[32]; + long fail_rate; + + tx_log(TX_LOG_INFO, "[VL_OL_0032] 정합성 리포트 요약 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0032] 영업일(BIZDATE) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3200"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[VL_OL_0032] 영업일 형식 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "VL3201"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + + /* 1) 대상 전체 건수/금액 */ + h_total_cnt = 0; + h_total_amt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( SUM ( amount ) , 0 ) from vl_ledger where biz_date = $1 ", + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_total_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_total_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 60 "app/online/vl_ol_0032.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("리포트 대상집계 vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3208"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 2) 완료/불일치 건수 */ + h_done_cnt = 0; + h_fail_cnt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select coalesce ( SUM ( case when status = 'M' then 1 else 0 end ) , 0 ) , coalesce ( SUM ( case when status = 'U' then 1 else 0 end ) , 0 ) from vl_ledger where biz_date = $1 ", + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_done_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_fail_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 78 "app/online/vl_ol_0032.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("리포트 상태집계 vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3209"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 정산 건수/금액 */ + h_settle_cnt = 0; + h_settle_amt = 0; + { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) , coalesce ( SUM ( settle_amt ) , 0 ) from vl_settle where biz_date = $1 ", + ECPGt_char,(h_bizdate),(long)9,(long)1,(9)*sizeof(char), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, + ECPGt_long,&(h_settle_cnt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, + ECPGt_long,&(h_settle_amt),(long)1,(long)1,sizeof(long), + ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} +#line 94 "app/online/vl_ol_0032.pgc" + + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("리포트 정산집계 vl_settle"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3210"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 불일치율(%) = 불일치 / 대상 * 100 */ + fail_rate = (h_total_cnt > 0) ? (h_fail_cnt * 100 / h_total_cnt) : 0; + + memset(amt_disp, 0, sizeof(amt_disp)); + amount_format_won(h_total_amt, amt_disp, sizeof(amt_disp)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "TOTALCNT", h_total_cnt); + tx_buf_setlong(ctx->out, "TOTALAMT", h_total_amt); + tx_buf_sets(ctx->out, "TOTALAMTDISP", amt_disp); + tx_buf_setlong(ctx->out, "DONECNT", h_done_cnt); + tx_buf_setlong(ctx->out, "FAILCNT", h_fail_cnt); + tx_buf_setlong(ctx->out, "SETTLECNT", h_settle_cnt); + tx_buf_setlong(ctx->out, "SETTLEAMT", h_settle_amt); + tx_buf_setlong(ctx->out, "FAILRATE", fail_rate); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, + "[VL_OL_0032] 리포트 date=%s 대상=%ld 완료=%ld 불일치=%ld 정산=%ld", + bizdate, h_total_cnt, h_done_cnt, h_fail_cnt, h_settle_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/app/online/vl_ol_0032.pgc b/app/online/vl_ol_0032.pgc index b35016a..9408cd3 100644 --- a/app/online/vl_ol_0032.pgc +++ b/app/online/vl_ol_0032.pgc @@ -1,14 +1,16 @@ /* @tier easy @module vl @transform validation-online */ /* - * vl_ol_0032.pgc - 정합성 온라인 서비스 (VL_OL_0032) [tier=easy] + * vl_ol_0032.pgc - 정합성 리포트 요약 서비스 (VL_OL_0032) [tier=easy] * - * TxCore TX_SERVICE 엔트리. 요청 TXBUF 를 검증하고 vl 표준 DBIO 를 - * 통해 처리한 뒤 응답 TXBUF 를 구성한다. (ATMI void SVC(TPSVCINFO*) 대응) + * 입력 영업일의 대상/완료/불일치/정산 건수와 금액합계를 여러 집계 SQL 로 + * 모아 하나의 리포트 응답으로 구성한다. */ #include +#include #include #include "txcore.h" +#include "txcore_dbio.h" #include "acq_util.h" #include "vl_core.h" @@ -16,32 +18,108 @@ EXEC SQL INCLUDE sqlca; TX_SERVICE(VL_OL_0032, ctx) { - vl_rec_t rec; - char key[16]; - int rc; + EXEC SQL BEGIN DECLARE SECTION; + char h_bizdate[9]; + long h_total_cnt; + long h_total_amt; + long h_done_cnt; + long h_fail_cnt; + long h_settle_cnt; + long h_settle_amt; + EXEC SQL END DECLARE SECTION; - tx_log(TX_LOG_INFO, "[VL_OL_0032] 정합성 단건조회 서비스 진입"); + char bizdate[16]; + char amt_disp[32]; + long fail_rate; - if (tx_buf_get(ctx->in, "KEY", key, sizeof(key)) != TX_OK) { - tx_log(TX_LOG_ERROR, "[VL_OL_0032] KEY 누락"); + tx_log(TX_LOG_INFO, "[VL_OL_0032] 정합성 리포트 요약 서비스 진입"); + + if (tx_buf_get(ctx->in, "BIZDATE", bizdate, sizeof(bizdate)) != TX_OK) { + tx_log(TX_LOG_ERROR, "[VL_OL_0032] 영업일(BIZDATE) 누락"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3200"); + tx_return(ctx, TX_EINVAL, ctx->out); + return; + } + if (!date_is_valid(bizdate)) { + tx_log(TX_LOG_WARN, "[VL_OL_0032] 영업일 형식 오류 date=%s", bizdate); + tx_buf_sets(ctx->out, "ERRCODE", "VL3201"); tx_return(ctx, TX_EINVAL, ctx->out); return; } - memset(&rec, 0, sizeof(rec)); - rc = vl_rec_select(key, &rec); - if (rc != TX_OK) { - tx_log(TX_LOG_WARN, "[VL_OL_0032] 조회 실패 key=%s rc=%d(%s)", - key, rc, tx_strerror(rc)); - tx_return(ctx, rc, ctx->out); + strncpy(h_bizdate, bizdate, sizeof(h_bizdate) - 1); + h_bizdate[sizeof(h_bizdate) - 1] = '\0'; + + /* 1) 대상 전체 건수/금액 */ + h_total_cnt = 0; + h_total_amt = 0; + EXEC SQL + SELECT COUNT(*), COALESCE(SUM(amount), 0) + INTO :h_total_cnt, :h_total_amt + FROM vl_ledger + WHERE biz_date = :h_bizdate; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("리포트 대상집계 vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3208"); + tx_return(ctx, TX_EDB, ctx->out); return; } + /* 2) 완료/불일치 건수 */ + h_done_cnt = 0; + h_fail_cnt = 0; + EXEC SQL + SELECT + COALESCE(SUM(CASE WHEN status = 'M' THEN 1 ELSE 0 END), 0), + COALESCE(SUM(CASE WHEN status = 'U' THEN 1 ELSE 0 END), 0) + INTO :h_done_cnt, :h_fail_cnt + FROM vl_ledger + WHERE biz_date = :h_bizdate; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("리포트 상태집계 vl_ledger"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3209"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 3) 정산 건수/금액 */ + h_settle_cnt = 0; + h_settle_amt = 0; + EXEC SQL + SELECT COUNT(*), COALESCE(SUM(settle_amt), 0) + INTO :h_settle_cnt, :h_settle_amt + FROM vl_settle + WHERE biz_date = :h_bizdate; + + if (sqlca.sqlcode != TX_SQL_OK) { + TX_DBIO_LOG_ERR("리포트 정산집계 vl_settle"); + tx_buf_sets(ctx->out, "ERRCODE", "VL3210"); + tx_return(ctx, TX_EDB, ctx->out); + return; + } + + /* 불일치율(%) = 불일치 / 대상 * 100 */ + fail_rate = (h_total_cnt > 0) ? (h_fail_cnt * 100 / h_total_cnt) : 0; + + memset(amt_disp, 0, sizeof(amt_disp)); + amount_format_won(h_total_amt, amt_disp, sizeof(amt_disp)); + tx_buf_reset(ctx->out); - tx_buf_sets(ctx->out, "KEY", rec.key); - tx_buf_sets(ctx->out, "MERCHID", rec.merch_id); - tx_buf_setlong(ctx->out, "AMOUNT", rec.amount); - tx_buf_sets(ctx->out, "STATUS", rec.status); - tx_log(TX_LOG_INFO, "[VL_OL_0032] 조회완료 key=%s amount=%ld", key, rec.amount); + tx_buf_sets(ctx->out, "BIZDATE", bizdate); + tx_buf_setlong(ctx->out, "TOTALCNT", h_total_cnt); + tx_buf_setlong(ctx->out, "TOTALAMT", h_total_amt); + tx_buf_sets(ctx->out, "TOTALAMTDISP", amt_disp); + tx_buf_setlong(ctx->out, "DONECNT", h_done_cnt); + tx_buf_setlong(ctx->out, "FAILCNT", h_fail_cnt); + tx_buf_setlong(ctx->out, "SETTLECNT", h_settle_cnt); + tx_buf_setlong(ctx->out, "SETTLEAMT", h_settle_amt); + tx_buf_setlong(ctx->out, "FAILRATE", fail_rate); + tx_buf_sets(ctx->out, "ERRCODE", RESP_OK); + + tx_log(TX_LOG_INFO, + "[VL_OL_0032] 리포트 date=%s 대상=%ld 완료=%ld 불일치=%ld 정산=%ld", + bizdate, h_total_cnt, h_done_cnt, h_fail_cnt, h_settle_cnt); tx_return(ctx, TX_OK, ctx->out); } diff --git a/inventory.json b/inventory.json index 9650aa8..ed9edb2 100644 --- a/inventory.json +++ b/inventory.json @@ -52,7 +52,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/ac_bt_0001.pgc", - "loc": 138, + "loc": 149, "sqlCount": 7 }, { @@ -61,7 +61,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/ac_bt_0002.pgc", - "loc": 138, + "loc": 150, "sqlCount": 7 }, { @@ -70,7 +70,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/ac_bt_0003.pgc", - "loc": 138, + "loc": 141, "sqlCount": 7 }, { @@ -79,7 +79,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/ac_bt_0004.pgc", - "loc": 138, + "loc": 153, "sqlCount": 7 }, { @@ -88,7 +88,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/ac_bt_0005.pgc", - "loc": 138, + "loc": 147, "sqlCount": 7 }, { @@ -97,7 +97,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/ac_bt_0006.pgc", - "loc": 138, + "loc": 141, "sqlCount": 7 }, { @@ -106,7 +106,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/ac_bt_0007.pgc", - "loc": 138, + "loc": 160, "sqlCount": 7 }, { @@ -115,7 +115,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/ac_bt_0008.pgc", - "loc": 138, + "loc": 148, "sqlCount": 7 }, { @@ -124,7 +124,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/ac_bt_0009.pgc", - "loc": 138, + "loc": 169, "sqlCount": 7 }, { @@ -133,7 +133,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/ac_bt_0010.pgc", - "loc": 138, + "loc": 179, "sqlCount": 7 }, { @@ -142,7 +142,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/ac_bt_0011.pgc", - "loc": 138, + "loc": 157, "sqlCount": 7 }, { @@ -151,7 +151,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/ac_bt_0012.pgc", - "loc": 138, + "loc": 143, "sqlCount": 7 }, { @@ -160,7 +160,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/ac_bt_0013.pgc", - "loc": 138, + "loc": 148, "sqlCount": 7 }, { @@ -169,7 +169,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/ac_bt_0014.pgc", - "loc": 138, + "loc": 158, "sqlCount": 7 }, { @@ -178,7 +178,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/ac_bt_0015.pgc", - "loc": 138, + "loc": 157, "sqlCount": 7 }, { @@ -187,7 +187,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/ac_bt_0016.pgc", - "loc": 138, + "loc": 148, "sqlCount": 7 }, { @@ -196,7 +196,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/ac_bt_0017.pgc", - "loc": 138, + "loc": 180, "sqlCount": 7 }, { @@ -214,7 +214,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/au_bt_0002.pgc", - "loc": 138, + "loc": 89, "sqlCount": 7 }, { @@ -223,7 +223,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/au_bt_0003.pgc", - "loc": 138, + "loc": 75, "sqlCount": 7 }, { @@ -232,7 +232,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/au_bt_0004.pgc", - "loc": 138, + "loc": 83, "sqlCount": 7 }, { @@ -241,7 +241,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/au_bt_0005.pgc", - "loc": 138, + "loc": 84, "sqlCount": 7 }, { @@ -250,7 +250,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/au_bt_0006.pgc", - "loc": 138, + "loc": 83, "sqlCount": 7 }, { @@ -259,7 +259,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/au_bt_0007.pgc", - "loc": 138, + "loc": 82, "sqlCount": 7 }, { @@ -268,7 +268,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/au_bt_0008.pgc", - "loc": 138, + "loc": 72, "sqlCount": 7 }, { @@ -277,7 +277,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/au_bt_0009.pgc", - "loc": 138, + "loc": 86, "sqlCount": 7 }, { @@ -286,7 +286,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/au_bt_0010.pgc", - "loc": 138, + "loc": 80, "sqlCount": 7 }, { @@ -295,7 +295,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/au_bt_0011.pgc", - "loc": 138, + "loc": 81, "sqlCount": 7 }, { @@ -304,7 +304,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/au_bt_0012.pgc", - "loc": 138, + "loc": 80, "sqlCount": 7 }, { @@ -313,7 +313,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/au_bt_0013.pgc", - "loc": 138, + "loc": 95, "sqlCount": 7 }, { @@ -322,7 +322,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/au_bt_0014.pgc", - "loc": 138, + "loc": 77, "sqlCount": 7 }, { @@ -331,7 +331,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/au_bt_0015.pgc", - "loc": 138, + "loc": 77, "sqlCount": 7 }, { @@ -340,7 +340,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/au_bt_0016.pgc", - "loc": 138, + "loc": 84, "sqlCount": 7 }, { @@ -349,7 +349,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/au_bt_0017.pgc", - "loc": 138, + "loc": 75, "sqlCount": 7 }, { @@ -367,7 +367,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cl_bt_0002.pgc", - "loc": 138, + "loc": 145, "sqlCount": 7 }, { @@ -376,7 +376,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/cl_bt_0003.pgc", - "loc": 138, + "loc": 145, "sqlCount": 7 }, { @@ -385,7 +385,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cl_bt_0004.pgc", - "loc": 138, + "loc": 126, "sqlCount": 7 }, { @@ -394,7 +394,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/cl_bt_0005.pgc", - "loc": 138, + "loc": 128, "sqlCount": 7 }, { @@ -403,7 +403,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cl_bt_0006.pgc", - "loc": 138, + "loc": 134, "sqlCount": 7 }, { @@ -412,7 +412,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/cl_bt_0007.pgc", - "loc": 138, + "loc": 140, "sqlCount": 7 }, { @@ -421,7 +421,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cl_bt_0008.pgc", - "loc": 138, + "loc": 152, "sqlCount": 7 }, { @@ -430,7 +430,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/cl_bt_0009.pgc", - "loc": 138, + "loc": 157, "sqlCount": 7 }, { @@ -439,7 +439,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cl_bt_0010.pgc", - "loc": 138, + "loc": 136, "sqlCount": 7 }, { @@ -448,7 +448,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cl_bt_0011.pgc", - "loc": 138, + "loc": 128, "sqlCount": 7 }, { @@ -457,7 +457,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/cl_bt_0012.pgc", - "loc": 138, + "loc": 154, "sqlCount": 7 }, { @@ -466,7 +466,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cl_bt_0013.pgc", - "loc": 138, + "loc": 130, "sqlCount": 7 }, { @@ -475,7 +475,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/cl_bt_0014.pgc", - "loc": 138, + "loc": 149, "sqlCount": 7 }, { @@ -484,7 +484,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cl_bt_0015.pgc", - "loc": 138, + "loc": 129, "sqlCount": 7 }, { @@ -493,7 +493,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/cl_bt_0016.pgc", - "loc": 138, + "loc": 176, "sqlCount": 7 }, { @@ -502,7 +502,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cm_bt_0001.pgc", - "loc": 138, + "loc": 140, "sqlCount": 7 }, { @@ -511,7 +511,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cm_bt_0002.pgc", - "loc": 138, + "loc": 135, "sqlCount": 7 }, { @@ -520,7 +520,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/cm_bt_0003.pgc", - "loc": 138, + "loc": 144, "sqlCount": 7 }, { @@ -529,7 +529,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cm_bt_0004.pgc", - "loc": 138, + "loc": 141, "sqlCount": 7 }, { @@ -538,7 +538,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/cm_bt_0005.pgc", - "loc": 138, + "loc": 149, "sqlCount": 7 }, { @@ -547,7 +547,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cm_bt_0006.pgc", - "loc": 138, + "loc": 157, "sqlCount": 7 }, { @@ -556,7 +556,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/cm_bt_0007.pgc", - "loc": 138, + "loc": 123, "sqlCount": 7 }, { @@ -565,7 +565,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cm_bt_0008.pgc", - "loc": 138, + "loc": 161, "sqlCount": 7 }, { @@ -574,7 +574,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/cm_bt_0009.pgc", - "loc": 138, + "loc": 135, "sqlCount": 7 }, { @@ -583,7 +583,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cm_bt_0010.pgc", - "loc": 138, + "loc": 137, "sqlCount": 7 }, { @@ -592,7 +592,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/cm_bt_0011.pgc", - "loc": 138, + "loc": 159, "sqlCount": 7 }, { @@ -601,7 +601,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cm_bt_0012.pgc", - "loc": 138, + "loc": 122, "sqlCount": 7 }, { @@ -610,7 +610,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cm_bt_0013.pgc", - "loc": 138, + "loc": 117, "sqlCount": 7 }, { @@ -619,7 +619,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/cm_bt_0014.pgc", - "loc": 138, + "loc": 155, "sqlCount": 7 }, { @@ -628,7 +628,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/cm_bt_0015.pgc", - "loc": 138, + "loc": 103, "sqlCount": 7 }, { @@ -637,7 +637,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/cm_bt_0016.pgc", - "loc": 138, + "loc": 149, "sqlCount": 7 }, { @@ -655,7 +655,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/lg_bt_0002.pgc", - "loc": 138, + "loc": 137, "sqlCount": 7 }, { @@ -664,7 +664,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/lg_bt_0003.pgc", - "loc": 138, + "loc": 115, "sqlCount": 7 }, { @@ -673,7 +673,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/lg_bt_0004.pgc", - "loc": 138, + "loc": 141, "sqlCount": 7 }, { @@ -682,7 +682,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/lg_bt_0005.pgc", - "loc": 138, + "loc": 126, "sqlCount": 7 }, { @@ -691,7 +691,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/lg_bt_0006.pgc", - "loc": 138, + "loc": 147, "sqlCount": 7 }, { @@ -700,7 +700,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/lg_bt_0007.pgc", - "loc": 138, + "loc": 125, "sqlCount": 7 }, { @@ -709,7 +709,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/lg_bt_0008.pgc", - "loc": 138, + "loc": 120, "sqlCount": 7 }, { @@ -718,7 +718,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/lg_bt_0009.pgc", - "loc": 138, + "loc": 137, "sqlCount": 7 }, { @@ -727,7 +727,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/lg_bt_0010.pgc", - "loc": 138, + "loc": 135, "sqlCount": 7 }, { @@ -736,7 +736,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/lg_bt_0011.pgc", - "loc": 138, + "loc": 126, "sqlCount": 7 }, { @@ -745,7 +745,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/lg_bt_0012.pgc", - "loc": 138, + "loc": 125, "sqlCount": 7 }, { @@ -754,7 +754,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/lg_bt_0013.pgc", - "loc": 138, + "loc": 156, "sqlCount": 7 }, { @@ -763,7 +763,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/lg_bt_0014.pgc", - "loc": 138, + "loc": 147, "sqlCount": 7 }, { @@ -772,7 +772,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/lg_bt_0015.pgc", - "loc": 138, + "loc": 129, "sqlCount": 7 }, { @@ -781,7 +781,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/lg_bt_0016.pgc", - "loc": 138, + "loc": 134, "sqlCount": 7 }, { @@ -790,7 +790,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mg_bt_0001.pgc", - "loc": 138, + "loc": 151, "sqlCount": 7 }, { @@ -799,7 +799,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/mg_bt_0002.pgc", - "loc": 138, + "loc": 149, "sqlCount": 7 }, { @@ -808,7 +808,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mg_bt_0003.pgc", - "loc": 138, + "loc": 142, "sqlCount": 7 }, { @@ -817,7 +817,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/mg_bt_0004.pgc", - "loc": 138, + "loc": 140, "sqlCount": 7 }, { @@ -826,7 +826,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mg_bt_0005.pgc", - "loc": 138, + "loc": 111, "sqlCount": 7 }, { @@ -835,7 +835,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/mg_bt_0006.pgc", - "loc": 138, + "loc": 146, "sqlCount": 7 }, { @@ -853,7 +853,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/mg_bt_0008.pgc", - "loc": 138, + "loc": 139, "sqlCount": 7 }, { @@ -862,7 +862,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mg_bt_0009.pgc", - "loc": 138, + "loc": 149, "sqlCount": 7 }, { @@ -871,7 +871,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/mg_bt_0010.pgc", - "loc": 138, + "loc": 123, "sqlCount": 7 }, { @@ -880,7 +880,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mg_bt_0011.pgc", - "loc": 138, + "loc": 143, "sqlCount": 7 }, { @@ -889,7 +889,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mg_bt_0012.pgc", - "loc": 138, + "loc": 124, "sqlCount": 7 }, { @@ -898,7 +898,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/mg_bt_0013.pgc", - "loc": 138, + "loc": 128, "sqlCount": 7 }, { @@ -907,7 +907,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mg_bt_0014.pgc", - "loc": 138, + "loc": 129, "sqlCount": 7 }, { @@ -916,7 +916,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/mg_bt_0015.pgc", - "loc": 138, + "loc": 141, "sqlCount": 7 }, { @@ -925,7 +925,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mg_bt_0016.pgc", - "loc": 138, + "loc": 125, "sqlCount": 7 }, { @@ -934,7 +934,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/mg_bt_0017.pgc", - "loc": 138, + "loc": 134, "sqlCount": 7 }, { @@ -952,7 +952,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/mm_bt_0002.pgc", - "loc": 138, + "loc": 162, "sqlCount": 7 }, { @@ -961,7 +961,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mm_bt_0003.pgc", - "loc": 138, + "loc": 137, "sqlCount": 7 }, { @@ -970,7 +970,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mm_bt_0004.pgc", - "loc": 138, + "loc": 144, "sqlCount": 7 }, { @@ -979,7 +979,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/mm_bt_0005.pgc", - "loc": 138, + "loc": 163, "sqlCount": 7 }, { @@ -988,7 +988,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mm_bt_0006.pgc", - "loc": 138, + "loc": 136, "sqlCount": 7 }, { @@ -997,7 +997,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/mm_bt_0007.pgc", - "loc": 138, + "loc": 162, "sqlCount": 7 }, { @@ -1006,7 +1006,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mm_bt_0008.pgc", - "loc": 138, + "loc": 152, "sqlCount": 7 }, { @@ -1024,7 +1024,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mm_bt_0010.pgc", - "loc": 138, + "loc": 154, "sqlCount": 7 }, { @@ -1033,7 +1033,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/mm_bt_0011.pgc", - "loc": 138, + "loc": 161, "sqlCount": 7 }, { @@ -1042,7 +1042,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mm_bt_0012.pgc", - "loc": 138, + "loc": 121, "sqlCount": 7 }, { @@ -1051,7 +1051,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mm_bt_0013.pgc", - "loc": 138, + "loc": 157, "sqlCount": 7 }, { @@ -1060,7 +1060,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/mm_bt_0014.pgc", - "loc": 138, + "loc": 142, "sqlCount": 7 }, { @@ -1069,7 +1069,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/mm_bt_0015.pgc", - "loc": 138, + "loc": 147, "sqlCount": 7 }, { @@ -1078,7 +1078,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/mm_bt_0016.pgc", - "loc": 138, + "loc": 142, "sqlCount": 7 }, { @@ -1096,7 +1096,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/py_bt_0002.pgc", - "loc": 138, + "loc": 112, "sqlCount": 7 }, { @@ -1105,7 +1105,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/py_bt_0003.pgc", - "loc": 138, + "loc": 97, "sqlCount": 7 }, { @@ -1114,7 +1114,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/py_bt_0004.pgc", - "loc": 138, + "loc": 76, "sqlCount": 7 }, { @@ -1123,7 +1123,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/py_bt_0005.pgc", - "loc": 138, + "loc": 81, "sqlCount": 7 }, { @@ -1132,7 +1132,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/py_bt_0006.pgc", - "loc": 138, + "loc": 75, "sqlCount": 7 }, { @@ -1141,7 +1141,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/py_bt_0007.pgc", - "loc": 138, + "loc": 80, "sqlCount": 7 }, { @@ -1150,7 +1150,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/py_bt_0008.pgc", - "loc": 138, + "loc": 88, "sqlCount": 7 }, { @@ -1159,7 +1159,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/py_bt_0009.pgc", - "loc": 138, + "loc": 75, "sqlCount": 7 }, { @@ -1168,7 +1168,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/py_bt_0010.pgc", - "loc": 138, + "loc": 96, "sqlCount": 7 }, { @@ -1177,7 +1177,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/py_bt_0011.pgc", - "loc": 138, + "loc": 74, "sqlCount": 7 }, { @@ -1186,7 +1186,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/py_bt_0012.pgc", - "loc": 138, + "loc": 74, "sqlCount": 7 }, { @@ -1195,7 +1195,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/py_bt_0013.pgc", - "loc": 138, + "loc": 86, "sqlCount": 7 }, { @@ -1204,7 +1204,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/py_bt_0014.pgc", - "loc": 138, + "loc": 74, "sqlCount": 7 }, { @@ -1213,7 +1213,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/py_bt_0015.pgc", - "loc": 138, + "loc": 84, "sqlCount": 7 }, { @@ -1222,7 +1222,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/py_bt_0016.pgc", - "loc": 138, + "loc": 96, "sqlCount": 7 }, { @@ -1240,7 +1240,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/rc_bt_0002.pgc", - "loc": 138, + "loc": 149, "sqlCount": 7 }, { @@ -1249,7 +1249,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/rc_bt_0003.pgc", - "loc": 138, + "loc": 148, "sqlCount": 7 }, { @@ -1258,7 +1258,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/rc_bt_0004.pgc", - "loc": 138, + "loc": 145, "sqlCount": 7 }, { @@ -1267,7 +1267,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/rc_bt_0005.pgc", - "loc": 138, + "loc": 156, "sqlCount": 7 }, { @@ -1276,7 +1276,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/rc_bt_0006.pgc", - "loc": 138, + "loc": 128, "sqlCount": 7 }, { @@ -1285,7 +1285,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/rc_bt_0007.pgc", - "loc": 138, + "loc": 141, "sqlCount": 7 }, { @@ -1294,7 +1294,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/rc_bt_0008.pgc", - "loc": 138, + "loc": 133, "sqlCount": 7 }, { @@ -1303,7 +1303,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/rc_bt_0009.pgc", - "loc": 138, + "loc": 183, "sqlCount": 7 }, { @@ -1312,7 +1312,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/rc_bt_0010.pgc", - "loc": 138, + "loc": 156, "sqlCount": 7 }, { @@ -1321,7 +1321,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/rc_bt_0011.pgc", - "loc": 138, + "loc": 153, "sqlCount": 7 }, { @@ -1330,7 +1330,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/rc_bt_0012.pgc", - "loc": 138, + "loc": 149, "sqlCount": 7 }, { @@ -1339,7 +1339,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/rc_bt_0013.pgc", - "loc": 138, + "loc": 155, "sqlCount": 7 }, { @@ -1348,7 +1348,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/rc_bt_0014.pgc", - "loc": 138, + "loc": 139, "sqlCount": 7 }, { @@ -1357,7 +1357,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/rc_bt_0015.pgc", - "loc": 138, + "loc": 171, "sqlCount": 7 }, { @@ -1366,7 +1366,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/rc_bt_0016.pgc", - "loc": 138, + "loc": 155, "sqlCount": 7 }, { @@ -1375,7 +1375,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/rc_bt_0017.pgc", - "loc": 138, + "loc": 153, "sqlCount": 7 }, { @@ -1393,7 +1393,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/st_bt_0002.pgc", - "loc": 138, + "loc": 103, "sqlCount": 7 }, { @@ -1402,7 +1402,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/st_bt_0003.pgc", - "loc": 138, + "loc": 127, "sqlCount": 7 }, { @@ -1411,7 +1411,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/st_bt_0004.pgc", - "loc": 138, + "loc": 126, "sqlCount": 7 }, { @@ -1420,7 +1420,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/st_bt_0005.pgc", - "loc": 138, + "loc": 114, "sqlCount": 7 }, { @@ -1429,7 +1429,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/st_bt_0006.pgc", - "loc": 138, + "loc": 96, "sqlCount": 7 }, { @@ -1438,7 +1438,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/st_bt_0007.pgc", - "loc": 138, + "loc": 115, "sqlCount": 7 }, { @@ -1447,7 +1447,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/st_bt_0008.pgc", - "loc": 138, + "loc": 113, "sqlCount": 7 }, { @@ -1456,7 +1456,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/st_bt_0009.pgc", - "loc": 138, + "loc": 125, "sqlCount": 7 }, { @@ -1465,7 +1465,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/st_bt_0010.pgc", - "loc": 138, + "loc": 107, "sqlCount": 7 }, { @@ -1474,7 +1474,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/st_bt_0011.pgc", - "loc": 138, + "loc": 114, "sqlCount": 7 }, { @@ -1483,7 +1483,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/st_bt_0012.pgc", - "loc": 138, + "loc": 117, "sqlCount": 7 }, { @@ -1492,7 +1492,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/st_bt_0013.pgc", - "loc": 138, + "loc": 130, "sqlCount": 7 }, { @@ -1501,7 +1501,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/st_bt_0014.pgc", - "loc": 138, + "loc": 116, "sqlCount": 7 }, { @@ -1510,7 +1510,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/st_bt_0015.pgc", - "loc": 138, + "loc": 112, "sqlCount": 7 }, { @@ -1519,7 +1519,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/st_bt_0016.pgc", - "loc": 138, + "loc": 119, "sqlCount": 7 }, { @@ -1537,7 +1537,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/vl_bt_0002.pgc", - "loc": 138, + "loc": 142, "sqlCount": 7 }, { @@ -1546,7 +1546,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/vl_bt_0003.pgc", - "loc": 138, + "loc": 158, "sqlCount": 7 }, { @@ -1555,7 +1555,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/vl_bt_0004.pgc", - "loc": 138, + "loc": 143, "sqlCount": 7 }, { @@ -1564,7 +1564,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/vl_bt_0005.pgc", - "loc": 138, + "loc": 149, "sqlCount": 7 }, { @@ -1573,7 +1573,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/vl_bt_0006.pgc", - "loc": 138, + "loc": 156, "sqlCount": 7 }, { @@ -1582,7 +1582,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/vl_bt_0007.pgc", - "loc": 138, + "loc": 158, "sqlCount": 7 }, { @@ -1591,7 +1591,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/vl_bt_0008.pgc", - "loc": 138, + "loc": 153, "sqlCount": 7 }, { @@ -1600,7 +1600,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/vl_bt_0009.pgc", - "loc": 138, + "loc": 154, "sqlCount": 7 }, { @@ -1609,7 +1609,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/vl_bt_0010.pgc", - "loc": 138, + "loc": 152, "sqlCount": 7 }, { @@ -1618,7 +1618,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/vl_bt_0011.pgc", - "loc": 138, + "loc": 164, "sqlCount": 7 }, { @@ -1627,7 +1627,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/vl_bt_0012.pgc", - "loc": 138, + "loc": 178, "sqlCount": 7 }, { @@ -1636,7 +1636,7 @@ "archetype": "batch", "tier": "medium", "path": "app/batch/vl_bt_0013.pgc", - "loc": 138, + "loc": 160, "sqlCount": 7 }, { @@ -1645,7 +1645,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/vl_bt_0014.pgc", - "loc": 138, + "loc": 124, "sqlCount": 7 }, { @@ -1654,7 +1654,7 @@ "archetype": "batch", "tier": "hard", "path": "app/batch/vl_bt_0015.pgc", - "loc": 138, + "loc": 156, "sqlCount": 7 }, { @@ -1663,7 +1663,7 @@ "archetype": "batch", "tier": "easy", "path": "app/batch/vl_bt_0016.pgc", - "loc": 138, + "loc": 141, "sqlCount": 7 }, { @@ -5452,7 +5452,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0001.pgc", - "loc": 47, + "loc": 122, "sqlCount": 1 }, { @@ -5461,7 +5461,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/ac_ol_0002.pgc", - "loc": 70, + "loc": 141, "sqlCount": 1 }, { @@ -5470,7 +5470,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0003.pgc", - "loc": 47, + "loc": 143, "sqlCount": 1 }, { @@ -5479,7 +5479,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/ac_ol_0004.pgc", - "loc": 70, + "loc": 126, "sqlCount": 1 }, { @@ -5488,7 +5488,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0005.pgc", - "loc": 47, + "loc": 120, "sqlCount": 1 }, { @@ -5497,7 +5497,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/ac_ol_0006.pgc", - "loc": 98, + "loc": 150, "sqlCount": 1 }, { @@ -5506,7 +5506,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0007.pgc", - "loc": 47, + "loc": 129, "sqlCount": 1 }, { @@ -5515,7 +5515,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/ac_ol_0008.pgc", - "loc": 98, + "loc": 132, "sqlCount": 1 }, { @@ -5524,7 +5524,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0009.pgc", - "loc": 47, + "loc": 109, "sqlCount": 1 }, { @@ -5533,7 +5533,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0010.pgc", - "loc": 47, + "loc": 111, "sqlCount": 1 }, { @@ -5542,7 +5542,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/ac_ol_0011.pgc", - "loc": 70, + "loc": 125, "sqlCount": 1 }, { @@ -5551,7 +5551,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0012.pgc", - "loc": 47, + "loc": 108, "sqlCount": 1 }, { @@ -5560,7 +5560,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/ac_ol_0013.pgc", - "loc": 70, + "loc": 112, "sqlCount": 1 }, { @@ -5569,7 +5569,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0014.pgc", - "loc": 47, + "loc": 82, "sqlCount": 1 }, { @@ -5578,7 +5578,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/ac_ol_0015.pgc", - "loc": 70, + "loc": 100, "sqlCount": 1 }, { @@ -5587,7 +5587,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0016.pgc", - "loc": 47, + "loc": 90, "sqlCount": 1 }, { @@ -5596,7 +5596,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/ac_ol_0017.pgc", - "loc": 98, + "loc": 160, "sqlCount": 1 }, { @@ -5605,7 +5605,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0018.pgc", - "loc": 47, + "loc": 129, "sqlCount": 1 }, { @@ -5614,7 +5614,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0019.pgc", - "loc": 47, + "loc": 121, "sqlCount": 1 }, { @@ -5623,7 +5623,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/ac_ol_0020.pgc", - "loc": 70, + "loc": 131, "sqlCount": 1 }, { @@ -5632,7 +5632,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0021.pgc", - "loc": 47, + "loc": 122, "sqlCount": 1 }, { @@ -5641,7 +5641,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/ac_ol_0022.pgc", - "loc": 70, + "loc": 122, "sqlCount": 1 }, { @@ -5650,7 +5650,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0023.pgc", - "loc": 47, + "loc": 121, "sqlCount": 1 }, { @@ -5659,7 +5659,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/ac_ol_0024.pgc", - "loc": 70, + "loc": 120, "sqlCount": 1 }, { @@ -5668,7 +5668,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0025.pgc", - "loc": 47, + "loc": 126, "sqlCount": 1 }, { @@ -5677,7 +5677,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/ac_ol_0026.pgc", - "loc": 98, + "loc": 149, "sqlCount": 1 }, { @@ -5686,7 +5686,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0027.pgc", - "loc": 47, + "loc": 122, "sqlCount": 1 }, { @@ -5695,7 +5695,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/ac_ol_0028.pgc", - "loc": 98, + "loc": 153, "sqlCount": 1 }, { @@ -5704,7 +5704,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0029.pgc", - "loc": 47, + "loc": 123, "sqlCount": 1 }, { @@ -5713,7 +5713,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0030.pgc", - "loc": 47, + "loc": 149, "sqlCount": 1 }, { @@ -5722,7 +5722,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/ac_ol_0031.pgc", - "loc": 70, + "loc": 128, "sqlCount": 1 }, { @@ -5731,7 +5731,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/ac_ol_0032.pgc", - "loc": 47, + "loc": 130, "sqlCount": 1 }, { @@ -5749,7 +5749,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/au_ol_0002.pgc", - "loc": 70, + "loc": 77, "sqlCount": 1 }, { @@ -5758,7 +5758,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0003.pgc", - "loc": 47, + "loc": 65, "sqlCount": 1 }, { @@ -5767,7 +5767,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/au_ol_0004.pgc", - "loc": 70, + "loc": 75, "sqlCount": 1 }, { @@ -5776,7 +5776,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0005.pgc", - "loc": 47, + "loc": 66, "sqlCount": 1 }, { @@ -5785,7 +5785,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/au_ol_0006.pgc", - "loc": 70, + "loc": 64, "sqlCount": 1 }, { @@ -5794,7 +5794,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0007.pgc", - "loc": 47, + "loc": 71, "sqlCount": 1 }, { @@ -5803,7 +5803,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/au_ol_0008.pgc", - "loc": 98, + "loc": 64, "sqlCount": 1 }, { @@ -5812,7 +5812,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0009.pgc", - "loc": 47, + "loc": 61, "sqlCount": 1 }, { @@ -5821,7 +5821,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0010.pgc", - "loc": 47, + "loc": 80, "sqlCount": 1 }, { @@ -5830,7 +5830,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/au_ol_0011.pgc", - "loc": 70, + "loc": 68, "sqlCount": 1 }, { @@ -5839,7 +5839,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0012.pgc", - "loc": 47, + "loc": 70, "sqlCount": 1 }, { @@ -5848,7 +5848,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/au_ol_0013.pgc", - "loc": 70, + "loc": 77, "sqlCount": 1 }, { @@ -5857,7 +5857,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0014.pgc", - "loc": 47, + "loc": 71, "sqlCount": 1 }, { @@ -5866,7 +5866,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/au_ol_0015.pgc", - "loc": 70, + "loc": 65, "sqlCount": 1 }, { @@ -5875,7 +5875,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0016.pgc", - "loc": 47, + "loc": 65, "sqlCount": 1 }, { @@ -5884,7 +5884,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/au_ol_0017.pgc", - "loc": 98, + "loc": 59, "sqlCount": 1 }, { @@ -5893,7 +5893,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0018.pgc", - "loc": 47, + "loc": 57, "sqlCount": 1 }, { @@ -5902,7 +5902,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/au_ol_0019.pgc", - "loc": 98, + "loc": 64, "sqlCount": 1 }, { @@ -5911,7 +5911,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0020.pgc", - "loc": 47, + "loc": 46, "sqlCount": 1 }, { @@ -5920,7 +5920,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0021.pgc", - "loc": 47, + "loc": 55, "sqlCount": 1 }, { @@ -5929,7 +5929,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/au_ol_0022.pgc", - "loc": 70, + "loc": 65, "sqlCount": 1 }, { @@ -5938,7 +5938,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0023.pgc", - "loc": 47, + "loc": 71, "sqlCount": 1 }, { @@ -5947,7 +5947,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/au_ol_0024.pgc", - "loc": 70, + "loc": 60, "sqlCount": 1 }, { @@ -5956,7 +5956,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0025.pgc", - "loc": 47, + "loc": 61, "sqlCount": 1 }, { @@ -5965,7 +5965,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/au_ol_0026.pgc", - "loc": 70, + "loc": 55, "sqlCount": 1 }, { @@ -5974,7 +5974,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0027.pgc", - "loc": 47, + "loc": 57, "sqlCount": 1 }, { @@ -5983,7 +5983,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/au_ol_0028.pgc", - "loc": 98, + "loc": 57, "sqlCount": 1 }, { @@ -5992,7 +5992,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0029.pgc", - "loc": 47, + "loc": 55, "sqlCount": 1 }, { @@ -6001,7 +6001,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0030.pgc", - "loc": 47, + "loc": 65, "sqlCount": 1 }, { @@ -6010,7 +6010,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/au_ol_0031.pgc", - "loc": 70, + "loc": 64, "sqlCount": 1 }, { @@ -6019,7 +6019,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/au_ol_0032.pgc", - "loc": 47, + "loc": 55, "sqlCount": 1 }, { @@ -6037,7 +6037,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0002.pgc", - "loc": 47, + "loc": 129, "sqlCount": 1 }, { @@ -6046,7 +6046,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cl_ol_0003.pgc", - "loc": 70, + "loc": 124, "sqlCount": 1 }, { @@ -6055,7 +6055,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0004.pgc", - "loc": 47, + "loc": 122, "sqlCount": 1 }, { @@ -6064,7 +6064,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cl_ol_0005.pgc", - "loc": 70, + "loc": 130, "sqlCount": 1 }, { @@ -6073,7 +6073,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0006.pgc", - "loc": 47, + "loc": 127, "sqlCount": 1 }, { @@ -6082,7 +6082,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cl_ol_0007.pgc", - "loc": 70, + "loc": 123, "sqlCount": 1 }, { @@ -6091,7 +6091,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0008.pgc", - "loc": 47, + "loc": 129, "sqlCount": 1 }, { @@ -6100,7 +6100,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/cl_ol_0009.pgc", - "loc": 98, + "loc": 121, "sqlCount": 1 }, { @@ -6109,7 +6109,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0010.pgc", - "loc": 47, + "loc": 122, "sqlCount": 1 }, { @@ -6118,7 +6118,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0011.pgc", - "loc": 47, + "loc": 120, "sqlCount": 1 }, { @@ -6127,7 +6127,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cl_ol_0012.pgc", - "loc": 70, + "loc": 137, "sqlCount": 1 }, { @@ -6136,7 +6136,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0013.pgc", - "loc": 47, + "loc": 148, "sqlCount": 1 }, { @@ -6145,7 +6145,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cl_ol_0014.pgc", - "loc": 70, + "loc": 143, "sqlCount": 1 }, { @@ -6154,7 +6154,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0015.pgc", - "loc": 47, + "loc": 121, "sqlCount": 1 }, { @@ -6163,7 +6163,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cl_ol_0016.pgc", - "loc": 70, + "loc": 144, "sqlCount": 1 }, { @@ -6172,7 +6172,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0017.pgc", - "loc": 47, + "loc": 122, "sqlCount": 1 }, { @@ -6181,7 +6181,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/cl_ol_0018.pgc", - "loc": 98, + "loc": 133, "sqlCount": 1 }, { @@ -6190,7 +6190,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0019.pgc", - "loc": 47, + "loc": 127, "sqlCount": 1 }, { @@ -6199,7 +6199,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/cl_ol_0020.pgc", - "loc": 98, + "loc": 158, "sqlCount": 1 }, { @@ -6208,7 +6208,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0021.pgc", - "loc": 47, + "loc": 137, "sqlCount": 1 }, { @@ -6217,7 +6217,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0022.pgc", - "loc": 47, + "loc": 123, "sqlCount": 1 }, { @@ -6226,7 +6226,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cl_ol_0023.pgc", - "loc": 70, + "loc": 122, "sqlCount": 1 }, { @@ -6235,7 +6235,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0024.pgc", - "loc": 47, + "loc": 123, "sqlCount": 1 }, { @@ -6244,7 +6244,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cl_ol_0025.pgc", - "loc": 70, + "loc": 124, "sqlCount": 1 }, { @@ -6253,7 +6253,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0026.pgc", - "loc": 47, + "loc": 123, "sqlCount": 1 }, { @@ -6262,7 +6262,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cl_ol_0027.pgc", - "loc": 70, + "loc": 126, "sqlCount": 1 }, { @@ -6271,7 +6271,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0028.pgc", - "loc": 47, + "loc": 132, "sqlCount": 1 }, { @@ -6280,7 +6280,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/cl_ol_0029.pgc", - "loc": 98, + "loc": 132, "sqlCount": 1 }, { @@ -6289,7 +6289,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0030.pgc", - "loc": 47, + "loc": 123, "sqlCount": 1 }, { @@ -6298,7 +6298,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cl_ol_0031.pgc", - "loc": 47, + "loc": 122, "sqlCount": 1 }, { @@ -6307,7 +6307,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0001.pgc", - "loc": 47, + "loc": 76, "sqlCount": 1 }, { @@ -6316,7 +6316,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0002.pgc", - "loc": 47, + "loc": 74, "sqlCount": 1 }, { @@ -6325,7 +6325,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cm_ol_0003.pgc", - "loc": 70, + "loc": 62, "sqlCount": 1 }, { @@ -6334,7 +6334,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0004.pgc", - "loc": 47, + "loc": 90, "sqlCount": 1 }, { @@ -6343,7 +6343,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cm_ol_0005.pgc", - "loc": 70, + "loc": 88, "sqlCount": 1 }, { @@ -6352,7 +6352,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0006.pgc", - "loc": 47, + "loc": 114, "sqlCount": 1 }, { @@ -6361,7 +6361,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cm_ol_0007.pgc", - "loc": 70, + "loc": 119, "sqlCount": 1 }, { @@ -6370,7 +6370,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0008.pgc", - "loc": 47, + "loc": 84, "sqlCount": 1 }, { @@ -6379,7 +6379,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/cm_ol_0009.pgc", - "loc": 98, + "loc": 120, "sqlCount": 1 }, { @@ -6388,7 +6388,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0010.pgc", - "loc": 47, + "loc": 108, "sqlCount": 1 }, { @@ -6397,7 +6397,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/cm_ol_0011.pgc", - "loc": 98, + "loc": 82, "sqlCount": 1 }, { @@ -6406,7 +6406,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0012.pgc", - "loc": 47, + "loc": 78, "sqlCount": 1 }, { @@ -6415,7 +6415,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0013.pgc", - "loc": 47, + "loc": 75, "sqlCount": 1 }, { @@ -6424,7 +6424,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cm_ol_0014.pgc", - "loc": 70, + "loc": 65, "sqlCount": 1 }, { @@ -6433,7 +6433,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0015.pgc", - "loc": 47, + "loc": 71, "sqlCount": 1 }, { @@ -6442,7 +6442,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cm_ol_0016.pgc", - "loc": 70, + "loc": 73, "sqlCount": 1 }, { @@ -6451,7 +6451,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0017.pgc", - "loc": 47, + "loc": 104, "sqlCount": 1 }, { @@ -6460,7 +6460,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cm_ol_0018.pgc", - "loc": 70, + "loc": 96, "sqlCount": 1 }, { @@ -6469,7 +6469,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0019.pgc", - "loc": 47, + "loc": 63, "sqlCount": 1 }, { @@ -6478,7 +6478,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/cm_ol_0020.pgc", - "loc": 98, + "loc": 101, "sqlCount": 1 }, { @@ -6487,7 +6487,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0021.pgc", - "loc": 47, + "loc": 102, "sqlCount": 1 }, { @@ -6496,7 +6496,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0022.pgc", - "loc": 47, + "loc": 72, "sqlCount": 1 }, { @@ -6505,7 +6505,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cm_ol_0023.pgc", - "loc": 70, + "loc": 71, "sqlCount": 1 }, { @@ -6514,7 +6514,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0024.pgc", - "loc": 47, + "loc": 81, "sqlCount": 1 }, { @@ -6523,7 +6523,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cm_ol_0025.pgc", - "loc": 70, + "loc": 74, "sqlCount": 1 }, { @@ -6532,7 +6532,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0026.pgc", - "loc": 47, + "loc": 116, "sqlCount": 1 }, { @@ -6541,7 +6541,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/cm_ol_0027.pgc", - "loc": 70, + "loc": 92, "sqlCount": 1 }, { @@ -6550,7 +6550,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0028.pgc", - "loc": 47, + "loc": 69, "sqlCount": 1 }, { @@ -6559,7 +6559,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/cm_ol_0029.pgc", - "loc": 98, + "loc": 79, "sqlCount": 1 }, { @@ -6568,7 +6568,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/cm_ol_0030.pgc", - "loc": 47, + "loc": 84, "sqlCount": 1 }, { @@ -6577,7 +6577,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/cm_ol_0031.pgc", - "loc": 98, + "loc": 71, "sqlCount": 1 }, { @@ -6604,7 +6604,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0003.pgc", - "loc": 47, + "loc": 127, "sqlCount": 1 }, { @@ -6613,7 +6613,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0004.pgc", - "loc": 47, + "loc": 129, "sqlCount": 1 }, { @@ -6622,7 +6622,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/lg_ol_0005.pgc", - "loc": 70, + "loc": 143, "sqlCount": 1 }, { @@ -6631,7 +6631,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0006.pgc", - "loc": 47, + "loc": 134, "sqlCount": 1 }, { @@ -6640,7 +6640,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/lg_ol_0007.pgc", - "loc": 70, + "loc": 152, "sqlCount": 1 }, { @@ -6649,7 +6649,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0008.pgc", - "loc": 47, + "loc": 152, "sqlCount": 1 }, { @@ -6658,7 +6658,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/lg_ol_0009.pgc", - "loc": 70, + "loc": 120, "sqlCount": 1 }, { @@ -6667,7 +6667,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0010.pgc", - "loc": 47, + "loc": 128, "sqlCount": 1 }, { @@ -6676,7 +6676,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/lg_ol_0011.pgc", - "loc": 98, + "loc": 152, "sqlCount": 1 }, { @@ -6685,7 +6685,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0012.pgc", - "loc": 47, + "loc": 120, "sqlCount": 1 }, { @@ -6694,7 +6694,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/lg_ol_0013.pgc", - "loc": 98, + "loc": 120, "sqlCount": 1 }, { @@ -6703,7 +6703,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0014.pgc", - "loc": 47, + "loc": 121, "sqlCount": 1 }, { @@ -6712,7 +6712,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0015.pgc", - "loc": 47, + "loc": 133, "sqlCount": 1 }, { @@ -6721,7 +6721,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/lg_ol_0016.pgc", - "loc": 70, + "loc": 158, "sqlCount": 1 }, { @@ -6730,7 +6730,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0017.pgc", - "loc": 47, + "loc": 121, "sqlCount": 1 }, { @@ -6739,7 +6739,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/lg_ol_0018.pgc", - "loc": 70, + "loc": 139, "sqlCount": 1 }, { @@ -6748,7 +6748,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0019.pgc", - "loc": 47, + "loc": 133, "sqlCount": 1 }, { @@ -6757,7 +6757,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/lg_ol_0020.pgc", - "loc": 70, + "loc": 125, "sqlCount": 1 }, { @@ -6766,7 +6766,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0021.pgc", - "loc": 47, + "loc": 125, "sqlCount": 1 }, { @@ -6775,7 +6775,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/lg_ol_0022.pgc", - "loc": 98, + "loc": 123, "sqlCount": 1 }, { @@ -6784,7 +6784,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0023.pgc", - "loc": 47, + "loc": 122, "sqlCount": 1 }, { @@ -6793,7 +6793,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0024.pgc", - "loc": 47, + "loc": 133, "sqlCount": 1 }, { @@ -6802,7 +6802,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/lg_ol_0025.pgc", - "loc": 70, + "loc": 131, "sqlCount": 1 }, { @@ -6811,7 +6811,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0026.pgc", - "loc": 47, + "loc": 124, "sqlCount": 1 }, { @@ -6820,7 +6820,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/lg_ol_0027.pgc", - "loc": 70, + "loc": 120, "sqlCount": 1 }, { @@ -6829,7 +6829,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0028.pgc", - "loc": 47, + "loc": 132, "sqlCount": 1 }, { @@ -6838,7 +6838,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/lg_ol_0029.pgc", - "loc": 70, + "loc": 121, "sqlCount": 1 }, { @@ -6847,7 +6847,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0030.pgc", - "loc": 47, + "loc": 136, "sqlCount": 1 }, { @@ -6856,7 +6856,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/lg_ol_0031.pgc", - "loc": 98, + "loc": 129, "sqlCount": 1 }, { @@ -6865,7 +6865,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/lg_ol_0032.pgc", - "loc": 47, + "loc": 129, "sqlCount": 1 }, { @@ -6874,7 +6874,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0001.pgc", - "loc": 47, + "loc": 85, "sqlCount": 1 }, { @@ -6883,7 +6883,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mg_ol_0002.pgc", - "loc": 70, + "loc": 98, "sqlCount": 1 }, { @@ -6892,7 +6892,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0003.pgc", - "loc": 47, + "loc": 113, "sqlCount": 1 }, { @@ -6901,7 +6901,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mg_ol_0004.pgc", - "loc": 70, + "loc": 81, "sqlCount": 1 }, { @@ -6910,7 +6910,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0005.pgc", - "loc": 47, + "loc": 82, "sqlCount": 1 }, { @@ -6919,7 +6919,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mg_ol_0006.pgc", - "loc": 70, + "loc": 88, "sqlCount": 1 }, { @@ -6928,7 +6928,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0007.pgc", - "loc": 47, + "loc": 79, "sqlCount": 1 }, { @@ -6937,7 +6937,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/mg_ol_0008.pgc", - "loc": 98, + "loc": 90, "sqlCount": 1 }, { @@ -6946,7 +6946,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0009.pgc", - "loc": 47, + "loc": 102, "sqlCount": 1 }, { @@ -6955,7 +6955,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/mg_ol_0010.pgc", - "loc": 98, + "loc": 93, "sqlCount": 1 }, { @@ -6964,7 +6964,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0011.pgc", - "loc": 47, + "loc": 72, "sqlCount": 1 }, { @@ -6973,7 +6973,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0012.pgc", - "loc": 47, + "loc": 109, "sqlCount": 1 }, { @@ -6982,7 +6982,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mg_ol_0013.pgc", - "loc": 70, + "loc": 128, "sqlCount": 1 }, { @@ -6991,7 +6991,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0014.pgc", - "loc": 47, + "loc": 117, "sqlCount": 1 }, { @@ -7000,7 +7000,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mg_ol_0015.pgc", - "loc": 70, + "loc": 102, "sqlCount": 1 }, { @@ -7009,7 +7009,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0016.pgc", - "loc": 47, + "loc": 77, "sqlCount": 1 }, { @@ -7018,7 +7018,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mg_ol_0017.pgc", - "loc": 70, + "loc": 109, "sqlCount": 1 }, { @@ -7027,7 +7027,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0018.pgc", - "loc": 47, + "loc": 92, "sqlCount": 1 }, { @@ -7036,7 +7036,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/mg_ol_0019.pgc", - "loc": 98, + "loc": 123, "sqlCount": 1 }, { @@ -7045,7 +7045,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0020.pgc", - "loc": 47, + "loc": 104, "sqlCount": 1 }, { @@ -7054,7 +7054,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0021.pgc", - "loc": 47, + "loc": 72, "sqlCount": 1 }, { @@ -7063,7 +7063,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mg_ol_0022.pgc", - "loc": 70, + "loc": 103, "sqlCount": 1 }, { @@ -7072,7 +7072,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0023.pgc", - "loc": 47, + "loc": 113, "sqlCount": 1 }, { @@ -7081,7 +7081,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mg_ol_0024.pgc", - "loc": 70, + "loc": 97, "sqlCount": 1 }, { @@ -7090,7 +7090,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0025.pgc", - "loc": 47, + "loc": 109, "sqlCount": 1 }, { @@ -7099,7 +7099,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mg_ol_0026.pgc", - "loc": 70, + "loc": 82, "sqlCount": 1 }, { @@ -7108,7 +7108,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0027.pgc", - "loc": 47, + "loc": 79, "sqlCount": 1 }, { @@ -7117,7 +7117,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/mg_ol_0028.pgc", - "loc": 98, + "loc": 101, "sqlCount": 1 }, { @@ -7126,7 +7126,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0029.pgc", - "loc": 47, + "loc": 65, "sqlCount": 1 }, { @@ -7135,7 +7135,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/mg_ol_0030.pgc", - "loc": 98, + "loc": 66, "sqlCount": 1 }, { @@ -7144,7 +7144,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0031.pgc", - "loc": 47, + "loc": 75, "sqlCount": 1 }, { @@ -7153,7 +7153,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mg_ol_0032.pgc", - "loc": 47, + "loc": 117, "sqlCount": 1 }, { @@ -7171,7 +7171,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/mm_ol_0002.pgc", - "loc": 98, + "loc": 108, "sqlCount": 1 }, { @@ -7180,7 +7180,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0003.pgc", - "loc": 47, + "loc": 114, "sqlCount": 1 }, { @@ -7189,7 +7189,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0004.pgc", - "loc": 47, + "loc": 126, "sqlCount": 1 }, { @@ -7198,7 +7198,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mm_ol_0005.pgc", - "loc": 70, + "loc": 90, "sqlCount": 1 }, { @@ -7207,7 +7207,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0006.pgc", - "loc": 47, + "loc": 129, "sqlCount": 1 }, { @@ -7216,7 +7216,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mm_ol_0007.pgc", - "loc": 70, + "loc": 84, "sqlCount": 1 }, { @@ -7225,7 +7225,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0008.pgc", - "loc": 47, + "loc": 126, "sqlCount": 1 }, { @@ -7234,7 +7234,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mm_ol_0009.pgc", - "loc": 70, + "loc": 90, "sqlCount": 1 }, { @@ -7243,7 +7243,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0010.pgc", - "loc": 47, + "loc": 137, "sqlCount": 1 }, { @@ -7252,7 +7252,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/mm_ol_0011.pgc", - "loc": 98, + "loc": 87, "sqlCount": 1 }, { @@ -7261,7 +7261,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0012.pgc", - "loc": 47, + "loc": 116, "sqlCount": 1 }, { @@ -7270,7 +7270,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0013.pgc", - "loc": 47, + "loc": 81, "sqlCount": 1 }, { @@ -7279,7 +7279,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mm_ol_0014.pgc", - "loc": 70, + "loc": 127, "sqlCount": 1 }, { @@ -7288,7 +7288,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0015.pgc", - "loc": 47, + "loc": 72, "sqlCount": 1 }, { @@ -7297,7 +7297,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mm_ol_0016.pgc", - "loc": 70, + "loc": 146, "sqlCount": 1 }, { @@ -7306,7 +7306,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0017.pgc", - "loc": 47, + "loc": 97, "sqlCount": 1 }, { @@ -7315,7 +7315,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mm_ol_0018.pgc", - "loc": 70, + "loc": 95, "sqlCount": 1 }, { @@ -7324,7 +7324,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0019.pgc", - "loc": 47, + "loc": 143, "sqlCount": 1 }, { @@ -7333,7 +7333,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/mm_ol_0020.pgc", - "loc": 98, + "loc": 151, "sqlCount": 1 }, { @@ -7342,7 +7342,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0021.pgc", - "loc": 47, + "loc": 84, "sqlCount": 1 }, { @@ -7351,7 +7351,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/mm_ol_0022.pgc", - "loc": 98, + "loc": 119, "sqlCount": 1 }, { @@ -7360,7 +7360,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0023.pgc", - "loc": 47, + "loc": 83, "sqlCount": 1 }, { @@ -7369,7 +7369,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0024.pgc", - "loc": 47, + "loc": 114, "sqlCount": 1 }, { @@ -7378,7 +7378,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mm_ol_0025.pgc", - "loc": 70, + "loc": 102, "sqlCount": 1 }, { @@ -7387,7 +7387,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0026.pgc", - "loc": 47, + "loc": 91, "sqlCount": 1 }, { @@ -7396,7 +7396,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mm_ol_0027.pgc", - "loc": 70, + "loc": 143, "sqlCount": 1 }, { @@ -7405,7 +7405,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0028.pgc", - "loc": 47, + "loc": 126, "sqlCount": 1 }, { @@ -7414,7 +7414,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/mm_ol_0029.pgc", - "loc": 70, + "loc": 142, "sqlCount": 1 }, { @@ -7423,7 +7423,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0030.pgc", - "loc": 47, + "loc": 82, "sqlCount": 1 }, { @@ -7432,7 +7432,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/mm_ol_0031.pgc", - "loc": 98, + "loc": 105, "sqlCount": 1 }, { @@ -7441,7 +7441,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/mm_ol_0032.pgc", - "loc": 47, + "loc": 84, "sqlCount": 1 }, { @@ -7459,7 +7459,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/py_ol_0002.pgc", - "loc": 98, + "loc": 76, "sqlCount": 1 }, { @@ -7468,7 +7468,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0003.pgc", - "loc": 47, + "loc": 56, "sqlCount": 1 }, { @@ -7477,7 +7477,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/py_ol_0004.pgc", - "loc": 98, + "loc": 56, "sqlCount": 1 }, { @@ -7486,7 +7486,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0005.pgc", - "loc": 47, + "loc": 53, "sqlCount": 1 }, { @@ -7495,7 +7495,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0006.pgc", - "loc": 47, + "loc": 55, "sqlCount": 1 }, { @@ -7504,7 +7504,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/py_ol_0007.pgc", - "loc": 70, + "loc": 58, "sqlCount": 1 }, { @@ -7513,7 +7513,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0008.pgc", - "loc": 47, + "loc": 68, "sqlCount": 1 }, { @@ -7522,7 +7522,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/py_ol_0009.pgc", - "loc": 70, + "loc": 61, "sqlCount": 1 }, { @@ -7531,7 +7531,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0010.pgc", - "loc": 47, + "loc": 56, "sqlCount": 1 }, { @@ -7540,7 +7540,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/py_ol_0011.pgc", - "loc": 70, + "loc": 53, "sqlCount": 1 }, { @@ -7549,7 +7549,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0012.pgc", - "loc": 47, + "loc": 49, "sqlCount": 1 }, { @@ -7558,7 +7558,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/py_ol_0013.pgc", - "loc": 98, + "loc": 55, "sqlCount": 1 }, { @@ -7567,7 +7567,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0014.pgc", - "loc": 47, + "loc": 67, "sqlCount": 1 }, { @@ -7576,7 +7576,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0015.pgc", - "loc": 47, + "loc": 63, "sqlCount": 1 }, { @@ -7585,7 +7585,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/py_ol_0016.pgc", - "loc": 70, + "loc": 61, "sqlCount": 1 }, { @@ -7594,7 +7594,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0017.pgc", - "loc": 47, + "loc": 61, "sqlCount": 1 }, { @@ -7603,7 +7603,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/py_ol_0018.pgc", - "loc": 70, + "loc": 54, "sqlCount": 1 }, { @@ -7612,7 +7612,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0019.pgc", - "loc": 47, + "loc": 52, "sqlCount": 1 }, { @@ -7621,7 +7621,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/py_ol_0020.pgc", - "loc": 70, + "loc": 56, "sqlCount": 1 }, { @@ -7630,7 +7630,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0021.pgc", - "loc": 47, + "loc": 55, "sqlCount": 1 }, { @@ -7639,7 +7639,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/py_ol_0022.pgc", - "loc": 98, + "loc": 53, "sqlCount": 1 }, { @@ -7648,7 +7648,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0023.pgc", - "loc": 47, + "loc": 53, "sqlCount": 1 }, { @@ -7657,7 +7657,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/py_ol_0024.pgc", - "loc": 98, + "loc": 47, "sqlCount": 1 }, { @@ -7666,7 +7666,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0025.pgc", - "loc": 47, + "loc": 58, "sqlCount": 1 }, { @@ -7675,7 +7675,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0026.pgc", - "loc": 47, + "loc": 61, "sqlCount": 1 }, { @@ -7684,7 +7684,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/py_ol_0027.pgc", - "loc": 70, + "loc": 49, "sqlCount": 1 }, { @@ -7693,7 +7693,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0028.pgc", - "loc": 47, + "loc": 45, "sqlCount": 1 }, { @@ -7702,7 +7702,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/py_ol_0029.pgc", - "loc": 70, + "loc": 42, "sqlCount": 1 }, { @@ -7711,7 +7711,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0030.pgc", - "loc": 47, + "loc": 61, "sqlCount": 1 }, { @@ -7720,7 +7720,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/py_ol_0031.pgc", - "loc": 70, + "loc": 63, "sqlCount": 1 }, { @@ -7729,7 +7729,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/py_ol_0032.pgc", - "loc": 47, + "loc": 57, "sqlCount": 1 }, { @@ -7747,7 +7747,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/rc_ol_0002.pgc", - "loc": 70, + "loc": 76, "sqlCount": 1 }, { @@ -7756,7 +7756,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0003.pgc", - "loc": 47, + "loc": 120, "sqlCount": 1 }, { @@ -7765,7 +7765,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/rc_ol_0004.pgc", - "loc": 70, + "loc": 125, "sqlCount": 1 }, { @@ -7774,7 +7774,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0005.pgc", - "loc": 47, + "loc": 120, "sqlCount": 1 }, { @@ -7783,7 +7783,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/rc_ol_0006.pgc", - "loc": 98, + "loc": 123, "sqlCount": 1 }, { @@ -7792,7 +7792,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0007.pgc", - "loc": 47, + "loc": 122, "sqlCount": 1 }, { @@ -7801,7 +7801,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0008.pgc", - "loc": 47, + "loc": 89, "sqlCount": 1 }, { @@ -7810,7 +7810,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/rc_ol_0009.pgc", - "loc": 70, + "loc": 95, "sqlCount": 1 }, { @@ -7819,7 +7819,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0010.pgc", - "loc": 47, + "loc": 77, "sqlCount": 1 }, { @@ -7828,7 +7828,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/rc_ol_0011.pgc", - "loc": 70, + "loc": 118, "sqlCount": 1 }, { @@ -7837,7 +7837,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0012.pgc", - "loc": 47, + "loc": 100, "sqlCount": 1 }, { @@ -7846,7 +7846,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/rc_ol_0013.pgc", - "loc": 70, + "loc": 122, "sqlCount": 1 }, { @@ -7855,7 +7855,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0014.pgc", - "loc": 47, + "loc": 124, "sqlCount": 1 }, { @@ -7864,7 +7864,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/rc_ol_0015.pgc", - "loc": 98, + "loc": 120, "sqlCount": 1 }, { @@ -7873,7 +7873,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0016.pgc", - "loc": 47, + "loc": 124, "sqlCount": 1 }, { @@ -7882,7 +7882,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/rc_ol_0017.pgc", - "loc": 98, + "loc": 124, "sqlCount": 1 }, { @@ -7891,7 +7891,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0018.pgc", - "loc": 47, + "loc": 138, "sqlCount": 1 }, { @@ -7900,7 +7900,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0019.pgc", - "loc": 47, + "loc": 122, "sqlCount": 1 }, { @@ -7909,7 +7909,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/rc_ol_0020.pgc", - "loc": 70, + "loc": 122, "sqlCount": 1 }, { @@ -7918,7 +7918,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0021.pgc", - "loc": 47, + "loc": 120, "sqlCount": 1 }, { @@ -7927,7 +7927,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/rc_ol_0022.pgc", - "loc": 70, + "loc": 124, "sqlCount": 1 }, { @@ -7936,7 +7936,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0023.pgc", - "loc": 47, + "loc": 117, "sqlCount": 1 }, { @@ -7945,7 +7945,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/rc_ol_0024.pgc", - "loc": 70, + "loc": 80, "sqlCount": 1 }, { @@ -7954,7 +7954,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0025.pgc", - "loc": 47, + "loc": 96, "sqlCount": 1 }, { @@ -7963,7 +7963,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/rc_ol_0026.pgc", - "loc": 98, + "loc": 139, "sqlCount": 1 }, { @@ -7972,7 +7972,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0027.pgc", - "loc": 47, + "loc": 120, "sqlCount": 1 }, { @@ -7981,7 +7981,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0028.pgc", - "loc": 47, + "loc": 127, "sqlCount": 1 }, { @@ -7990,7 +7990,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/rc_ol_0029.pgc", - "loc": 70, + "loc": 124, "sqlCount": 1 }, { @@ -7999,7 +7999,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0030.pgc", - "loc": 47, + "loc": 120, "sqlCount": 1 }, { @@ -8008,7 +8008,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/rc_ol_0031.pgc", - "loc": 70, + "loc": 120, "sqlCount": 1 }, { @@ -8017,7 +8017,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/rc_ol_0032.pgc", - "loc": 47, + "loc": 120, "sqlCount": 1 }, { @@ -8035,7 +8035,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/st_ol_0002.pgc", - "loc": 70, + "loc": 69, "sqlCount": 1 }, { @@ -8044,7 +8044,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0003.pgc", - "loc": 47, + "loc": 61, "sqlCount": 1 }, { @@ -8053,7 +8053,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/st_ol_0004.pgc", - "loc": 98, + "loc": 55, "sqlCount": 1 }, { @@ -8062,7 +8062,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0005.pgc", - "loc": 47, + "loc": 67, "sqlCount": 1 }, { @@ -8071,7 +8071,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0006.pgc", - "loc": 47, + "loc": 56, "sqlCount": 1 }, { @@ -8080,7 +8080,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/st_ol_0007.pgc", - "loc": 70, + "loc": 57, "sqlCount": 1 }, { @@ -8089,7 +8089,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0008.pgc", - "loc": 47, + "loc": 60, "sqlCount": 1 }, { @@ -8098,7 +8098,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/st_ol_0009.pgc", - "loc": 70, + "loc": 56, "sqlCount": 1 }, { @@ -8107,7 +8107,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0010.pgc", - "loc": 47, + "loc": 66, "sqlCount": 1 }, { @@ -8116,7 +8116,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/st_ol_0011.pgc", - "loc": 70, + "loc": 62, "sqlCount": 1 }, { @@ -8125,7 +8125,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0012.pgc", - "loc": 47, + "loc": 64, "sqlCount": 1 }, { @@ -8134,7 +8134,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/st_ol_0013.pgc", - "loc": 98, + "loc": 54, "sqlCount": 1 }, { @@ -8143,7 +8143,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0014.pgc", - "loc": 47, + "loc": 54, "sqlCount": 1 }, { @@ -8152,7 +8152,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/st_ol_0015.pgc", - "loc": 98, + "loc": 51, "sqlCount": 1 }, { @@ -8161,7 +8161,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0016.pgc", - "loc": 47, + "loc": 60, "sqlCount": 1 }, { @@ -8170,7 +8170,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0017.pgc", - "loc": 47, + "loc": 65, "sqlCount": 1 }, { @@ -8179,7 +8179,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/st_ol_0018.pgc", - "loc": 70, + "loc": 52, "sqlCount": 1 }, { @@ -8188,7 +8188,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0019.pgc", - "loc": 47, + "loc": 65, "sqlCount": 1 }, { @@ -8197,7 +8197,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/st_ol_0020.pgc", - "loc": 70, + "loc": 63, "sqlCount": 1 }, { @@ -8206,7 +8206,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0021.pgc", - "loc": 47, + "loc": 59, "sqlCount": 1 }, { @@ -8215,7 +8215,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/st_ol_0022.pgc", - "loc": 70, + "loc": 56, "sqlCount": 1 }, { @@ -8224,7 +8224,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0023.pgc", - "loc": 47, + "loc": 55, "sqlCount": 1 }, { @@ -8233,7 +8233,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/st_ol_0024.pgc", - "loc": 98, + "loc": 62, "sqlCount": 1 }, { @@ -8242,7 +8242,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0025.pgc", - "loc": 47, + "loc": 61, "sqlCount": 1 }, { @@ -8251,7 +8251,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0026.pgc", - "loc": 47, + "loc": 62, "sqlCount": 1 }, { @@ -8260,7 +8260,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/st_ol_0027.pgc", - "loc": 70, + "loc": 64, "sqlCount": 1 }, { @@ -8269,7 +8269,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0028.pgc", - "loc": 47, + "loc": 65, "sqlCount": 1 }, { @@ -8278,7 +8278,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/st_ol_0029.pgc", - "loc": 70, + "loc": 67, "sqlCount": 1 }, { @@ -8287,7 +8287,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0030.pgc", - "loc": 47, + "loc": 65, "sqlCount": 1 }, { @@ -8296,7 +8296,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/st_ol_0031.pgc", - "loc": 70, + "loc": 60, "sqlCount": 1 }, { @@ -8305,7 +8305,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/st_ol_0032.pgc", - "loc": 47, + "loc": 60, "sqlCount": 1 }, { @@ -8323,7 +8323,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/vl_ol_0002.pgc", - "loc": 70, + "loc": 113, "sqlCount": 1 }, { @@ -8332,7 +8332,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0003.pgc", - "loc": 47, + "loc": 93, "sqlCount": 1 }, { @@ -8341,7 +8341,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/vl_ol_0004.pgc", - "loc": 98, + "loc": 94, "sqlCount": 1 }, { @@ -8350,7 +8350,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0005.pgc", - "loc": 47, + "loc": 107, "sqlCount": 1 }, { @@ -8359,7 +8359,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/vl_ol_0006.pgc", - "loc": 98, + "loc": 109, "sqlCount": 1 }, { @@ -8368,7 +8368,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0007.pgc", - "loc": 47, + "loc": 102, "sqlCount": 1 }, { @@ -8377,7 +8377,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0008.pgc", - "loc": 47, + "loc": 99, "sqlCount": 1 }, { @@ -8386,7 +8386,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/vl_ol_0009.pgc", - "loc": 70, + "loc": 127, "sqlCount": 1 }, { @@ -8395,7 +8395,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0010.pgc", - "loc": 47, + "loc": 88, "sqlCount": 1 }, { @@ -8404,7 +8404,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/vl_ol_0011.pgc", - "loc": 70, + "loc": 107, "sqlCount": 1 }, { @@ -8413,7 +8413,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0012.pgc", - "loc": 47, + "loc": 114, "sqlCount": 1 }, { @@ -8422,7 +8422,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/vl_ol_0013.pgc", - "loc": 70, + "loc": 102, "sqlCount": 1 }, { @@ -8431,7 +8431,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0014.pgc", - "loc": 47, + "loc": 111, "sqlCount": 1 }, { @@ -8440,7 +8440,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/vl_ol_0015.pgc", - "loc": 98, + "loc": 115, "sqlCount": 1 }, { @@ -8449,7 +8449,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0016.pgc", - "loc": 47, + "loc": 109, "sqlCount": 1 }, { @@ -8458,7 +8458,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0017.pgc", - "loc": 47, + "loc": 116, "sqlCount": 1 }, { @@ -8467,7 +8467,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/vl_ol_0018.pgc", - "loc": 70, + "loc": 90, "sqlCount": 1 }, { @@ -8476,7 +8476,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0019.pgc", - "loc": 47, + "loc": 107, "sqlCount": 1 }, { @@ -8485,7 +8485,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/vl_ol_0020.pgc", - "loc": 70, + "loc": 92, "sqlCount": 1 }, { @@ -8494,7 +8494,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0021.pgc", - "loc": 47, + "loc": 98, "sqlCount": 1 }, { @@ -8503,7 +8503,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/vl_ol_0022.pgc", - "loc": 70, + "loc": 106, "sqlCount": 1 }, { @@ -8512,7 +8512,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0023.pgc", - "loc": 47, + "loc": 86, "sqlCount": 1 }, { @@ -8521,7 +8521,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/vl_ol_0024.pgc", - "loc": 98, + "loc": 109, "sqlCount": 1 }, { @@ -8530,7 +8530,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0025.pgc", - "loc": 47, + "loc": 107, "sqlCount": 1 }, { @@ -8539,7 +8539,7 @@ "archetype": "online", "tier": "hard", "path": "app/online/vl_ol_0026.pgc", - "loc": 98, + "loc": 124, "sqlCount": 1 }, { @@ -8548,7 +8548,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0027.pgc", - "loc": 47, + "loc": 121, "sqlCount": 1 }, { @@ -8557,7 +8557,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0028.pgc", - "loc": 47, + "loc": 87, "sqlCount": 1 }, { @@ -8566,7 +8566,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/vl_ol_0029.pgc", - "loc": 70, + "loc": 112, "sqlCount": 1 }, { @@ -8575,7 +8575,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0030.pgc", - "loc": 47, + "loc": 119, "sqlCount": 1 }, { @@ -8584,7 +8584,7 @@ "archetype": "online", "tier": "medium", "path": "app/online/vl_ol_0031.pgc", - "loc": 70, + "loc": 128, "sqlCount": 1 }, { @@ -8593,7 +8593,7 @@ "archetype": "online", "tier": "easy", "path": "app/online/vl_ol_0032.pgc", - "loc": 47, + "loc": 125, "sqlCount": 1 } ]