/* Processed by ecpg (15.18 (Debian 15.18-0+deb12u1)) */ /* These include files are added by the preprocessor */ #include #include #include /* End of automatic include section */ #line 1 "app/online/rc_ol_0005.pgc" /* @tier medium @module rc @transform reconciliation-online */ /* * rc_ol_0005.pgc - 승인-매입 금액불일치 검출 (단건, read-only) (RC_OL_0005) * * 요청 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 #include "txcore.h" #include "txcore_dbio.h" #include "acq_util.h" #include "rc_core.h" #line 1 "/usr/include/postgresql/sqlca.h" #ifndef POSTGRES_SQLCA_H #define POSTGRES_SQLCA_H #ifndef PGDLLIMPORT #if defined(WIN32) || defined(__CYGWIN__) #define PGDLLIMPORT __declspec (dllimport) #else #define PGDLLIMPORT #endif /* __CYGWIN__ */ #endif /* PGDLLIMPORT */ #define SQLERRMC_LEN 150 #ifdef __cplusplus extern "C" { #endif struct sqlca_t { char sqlcaid[8]; long sqlabc; long sqlcode; struct { int sqlerrml; char sqlerrmc[SQLERRMC_LEN]; } sqlerrm; char sqlerrp[8]; long sqlerrd[6]; /* Element 0: empty */ /* 1: OID of processed tuple if applicable */ /* 2: number of rows processed */ /* after an INSERT, UPDATE or */ /* DELETE statement */ /* 3: empty */ /* 4: empty */ /* 5: empty */ char sqlwarn[8]; /* Element 0: set to 'W' if at least one other is 'W' */ /* 1: if 'W' at least one character string */ /* value was truncated when it was */ /* stored into a host variable. */ /* * 2: if 'W' a (hopefully) non-fatal notice occurred */ /* 3: empty */ /* 4: empty */ /* 5: empty */ /* 6: empty */ /* 7: empty */ char sqlstate[5]; }; struct sqlca_t *ECPGget_sqlca(void); #ifndef POSTGRES_ECPG_INTERNAL #define sqlca (*ECPGget_sqlca()) #endif #ifdef __cplusplus } #endif #endif #line 36 "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] 승인-매입 금액불일치 검출 서비스 진입"); /* --- 요청 필드 추출 --- */ 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)", 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_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); /* 참고: 동일 영업일의 불일치('U') 미해소 건수를 임베디드 SQL 로 조회 (read-only) */ { /* exec sql begin declare section */ #line 120 "app/online/rc_ol_0005.pgc" char h_d5_bd [ 9 ] ; #line 121 "app/online/rc_ol_0005.pgc" long h_d5_peer ; /* exec sql end declare section */ #line 122 "app/online/rc_ol_0005.pgc" strncpy(h_d5_bd, rec.biz_date, sizeof(h_d5_bd) - 1); h_d5_bd[sizeof(h_d5_bd) - 1] = '\0'; h_d5_peer = 0; { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "select count ( * ) from rc_ledger where biz_date = $1 and status = 'U'", ECPGt_char,(h_d5_bd),(long)9,(long)1,(9)*sizeof(char), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_long,&(h_d5_peer),(long)1,(long)1,sizeof(long), ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EORT);} #line 132 "app/online/rc_ol_0005.pgc" if (sqlca.sqlcode != TX_SQL_OK && sqlca.sqlcode != TX_SQL_NOTFOUND) { TX_DBIO_LOG_ERR("SELECT rc_ledger unmatched peers"); h_d5_peer = 0; } tx_buf_setlong(ctx->out, "PEERUNMATCH", h_d5_peer); } 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); }