Files
saman/문서/architecture_low.md
T
2026-07-20 11:39:23 +09:00

33 KiB

(주)삼안 ERP 및 그룹웨어 시스템 (Satis) 초상세 로우레벨 분석 명세서

본 문서는 (주)삼안의 **ERP 및 그룹웨어 시스템 (saman-statis / Satis)**의 실제 소스 코드를 직접 분석한
초상세 로우레벨(Ultra-Detailed Low-Level) 구현 명세서입니다.
OracleClass.php 전체 메소드 명세, 전자결재 알고리즘, 문서번호 채번 방식, 인코딩 파이프라인, 결재 서명 렌더링,
시스템 글로벌 변수, DB 데이터 모델, 보안 핸들링 등을 실제 소스 코드 기반으로 완전히 명세합니다.


1. 모듈별 컨트롤러 & 뷰 맵핑 딕셔너리 (Module Controller-View Map)

1.1 회계 관리 모듈 (Account) — 30개 컨트롤러

컨트롤러 (Satis/sys/controller/Account/) 대응 View (Smarty/templates/Satis/Account/) 주요 기능
AccountSlipTemp_Controller.php AccountSlipTemp.tpl 임시전표 작성/조회/수정/결재 상신
AccountSlipConfirmed_Controller.php AccountSlipConfirmed.tpl 확정전표 조회 및 결재 상태 확인
AccountSlipAutoManager_Controller.php AccountSlipAutoManager.tpl 더존 iCube ERP 자동전표 전송
AccountBalanceStatement_Controller.php AccountBalanceStatement.tpl 합계잔액시산표 생성
AccountFinancialStatement_Controller.php AccountFinancialStatement.tpl 손익계산서(P/L) / 대차대조표(B/S)
AccountGisungCalculation_Controller.php AccountGisungCalculation.tpl 프로젝트 기성 청구금액 산출
AccountJointOfficeBudgetManager_Controller.php AccountJointOfficeBudget.tpl 공동도급(JV) 예산/지분율 원가 관리
AccountFixAsset_Controller.php AccountFixAsset.tpl 고정자산 감가상각비/대장 관리
AccountCorporateTax_Controller.php AccountCorporateTax.tpl 법인세 계산 및 세무 조정
AccountWithHoldingTax_Controller.php AccountWithHoldingTax.tpl 원천세 신고 / 원천징수 영수증
AccountVAT_Controller.php AccountVAT.tpl 부가가치세 신고 데이터 집계
AccountCostCalculation_Controller.php AccountCostCalculation.tpl 원가 계산 및 분석
AccountCostManagement_Controller.php AccountCostManagement.tpl 원가 관리 현황
AccountFundPlan_Controller.php AccountFundPlan.tpl 자금 계획 관리
AccountDepositsManager_Controller.php AccountDepositsManager.tpl 예금/차입금 관리
AccountLoanManager_Controller.php AccountLoanManager.tpl 대출 관리
AccountSecurities_Controller.php AccountSecurities.tpl 유가증권 관리
AccountEntertainmentExpenses_Controller.php AccountEntertainmentExpenses.tpl 접대비 관리
AccountCarManager_Controller.php AccountCarManager.tpl 차량 운행/유지비 관리
AccountDailyWorkTask_Controller.php AccountDailyWorkTask.tpl 일일 업무 실적 처리
AccountSummary_Controller.php AccountSummary.tpl 회계 요약 현황
AccountSummaryReport_Controller.php AccountSummaryReport.tpl 회계 종합 보고서
AccountDeadlineManager_Controller.php AccountDeadlineManager.tpl 결산 기한 관리
AccountMonthSupplementary_Controller.php AccountMonthSupplementary.tpl 월별 추가 분개 관리
AccountBillCheckManager_Controller.php AccountBillCheckManager.tpl 어음/수표 관리
AccountFinaCode_Controller.php AccountFinaCode.tpl 재무 코드 관리
AccountFundCode_Controller.php AccountFundCode.tpl 자금 코드 관리
AccountAssetCode_Controller.php AccountAssetCode.tpl 자산 코드 관리
FinalClosing_Controller.php FinalClosing.tpl 결산 확정 처리
AccountGisungCalculation_Controller.php AccountGisungCalculation.tpl 기성 계산 처리

1.2 인사/급여 관리 모듈 (Person) — 23개 컨트롤러

컨트롤러 (Satis/sys/controller/Person/) 대응 View (Smarty/templates/Satis/Person/) 주요 기능
PersonInfoManager_Controller.php PersonInfoManager.tpl 인사기록 카드, 신상정보, 이력 관리
PersonAppointment_Controller.php PersonAppointment.tpl 발령 처리 (입사/퇴사/이동/승진/전보)
PersonInsaLicense_Controller.php PersonInsaLicense.tpl 엔지니어링 기술자 자격증 관리
PersonCode_Controller.php PersonCode.tpl 인사 코드 마스터 관리
PersonTime_Controller.php PersonTime.tpl 근태 관리 (연차/야근/출퇴근)
PersonPayBase_Controller.php PersonPayBase.tpl 급여 기준 정보 설정
PersonPayBasic_Controller.php PersonPayBasic.tpl 기본급/수당 항목 설정
PersonPaymentCalculation_Controller.php PersonPaymentCalculation.tpl 월 급여/상여 계산 엔진
PersonPaymentReport_Controller.php PersonPaymentReport.tpl 급여명세서 / 부서별 지급 총괄
PersonSlipPaymentManager_Controller.php PersonSlipPaymentManager.tpl 급여 전표 연동 처리
PersonRetirementPay_Controller.php PersonRetirementPay.tpl 퇴직금 계산 및 충당금 정산
PersonYearEndTax_Controller.php PersonYearEndTax.tpl 연말정산 (yeta_data 연동)
PersonInsaReport_Controller.php PersonInsaReport.tpl 인사 현황 보고서
PersonInsaStatusReport_Controller.php PersonInsaStatusReport.tpl 인사 상태 보고서
PersonOrganiz_Controller.php PersonOrganiz.tpl 조직도 관리
Duzon_link_controller.php Duzon_link.tpl 더존 인사/급여 ERP 데이터 싱크
auth_pay_controller.php auth_pay.tpl 급여 지급 권한 처리
emp_check_controller.php emp_check.tpl 재직 여부 확인

1.3 프로젝트/현장 공정 모듈 (Project) — 15개 컨트롤러

컨트롤러 (Satis/sys/controller/Project/) 대응 View (Smarty/templates/Satis/Project/) 주요 기능
Project_Controller.php Project.tpl 프로젝트 마스터 등록/관리
ProjectOrderContract_Controller.php ProjectOrderContract.tpl 수주 계약, 변경 계약, 공동도급 지분율
ProjectOrderGisung_Controller.php ProjectOrderGisung.tpl 기성 청구 / 수금 / 미수금
ProjectMenHour_Controller.php ProjectMenHour.tpl M/M 공수 집계 및 원가 배부
ProjectCostStatus_Controller.php ProjectCostStatus.tpl 투입 원가(인건비/직접비/경비) 현황
ProjectCostTotal_Controller.php ProjectCostTotal.tpl 원가 합계 현황
ProjectPrePayment_Controller.php ProjectPrePayment.tpl 선급금 신청/수금/정산
ProjectStatus_Controller.php ProjectStatus.tpl 프로젝트 진행 현황
ProjectBaseCode_Controller.php ProjectBaseCode.tpl 프로젝트 기본 코드 관리
ProjectPartner_Controller.php ProjectPartner.tpl 협력업체 관리
DeptAssessment_Controller.php DeptAssessment.tpl 부서 평가 관리
TargetManagement_Controller.php TargetManagement.tpl 수주 목표 관리
PrinterCopy_Controller.php PrinterCopy.tpl 인쇄 출력 처리

2. 핵심 클래스 전체 메소드 명세 (OracleClass.php — 1,392라인)

Satis/sys/util/OracleClass.php는 시스템 전체 Oracle DB 연동의 핵심 클래스입니다.
실제 소스 분석 기반의 전체 메소드 분류표입니다.

2.1 DB 커넥션 및 기본 쿼리 메소드

메소드명 설명
OracleClass($smarty) 생성자. OCI 커넥션 생성. oci_connect('satis', 'SATIS11707808', 'samandb')
db_close_oracle() 커넥션 명시적 해제. oci_close()
Query($i_azsql) SQL 파싱 및 실행 후 Statement 핸들 반환.
fetch_array($stmt) oci_fetch_array($stmt, OCI_BOTH) — 연관+인덱스 배열 동시 반환
ExcuteQuery($i_azsql) DML 단순 실행 (반환값 없음)
LoadSql($i_azsql) 레거시 ociparse/ociexecute/ocifetchinto API 기반 단일 컬럼 조회
LoadConfigData($i_azsql) 설정 데이터를 name=value 포맷으로 직접 echo 출력
__destruct() PHP 소멸자: Statement 자동 해제 + 커넥션 자동 close

2.2 Stored Procedure (저장 프로시저) 조회 메소드

메소드명 인자 반환 방식 특이사항
LoadProcedure($sql, $title, $short_array) SQL, Smarty 변수명 $query_data 배열 + Smarty assign 기본 프로시저 호출 메소드
LoadProcedure_old($sql, $title, $short_array) SQL, Smarty 변수명 $query_data 배열 $_short 축약형 컬럼 지원 (구버전)
LoadProcedure_new($sql, $title, $short_array, $return_type) SQL, 변수명, 반환 타입 array / json / jsonchart / print_r / Smarty 출력 타입 지정 가능
LoadProcedure_new2($sql, $title, $short_array, $return_type) SQL, 변수명, 반환 타입 단일 Row 전용 단건 조회용 최적화
LoadProcedure_empty($sql, $title, $load_type, $return_type) SQL, 변수명, 로드타입 $query_data 배열 w2ui 모드 시 recid 자동 부여
LoadProcedure_broken_cp949($sql, $title, $load_type, $return_type) SQL, 변수명, 로드타입 $query_data 배열 CP949 인코딩 깨짐 데이터 처리용
LoadProcedureAjax($sql) SQL print_r(urldecode(json_encode($data))) Ajax 요청 전용 JSON 직접 출력
LoadProcedureApi($sql, $output_type) SQL, 출력 타입 array / json / jsonchart API/외부 호출 전용
Get_ProcedureData($Param01~05) 최대 5개 파라미터 array usp_get_procedure_data 프로시저 공통 호출 래퍼
executeSelect($sql) SQL 구조화된 결과 객체 (success, rows, columns, error) 최신 트랜잭션 안전 조회 함수 (2025-07-25 추가)

2.3 DML 실행 메소드 (INSERT / UPDATE / DELETE)

메소드명 설명 에러 반환 방식
ProcedureExcuteQuery($sql) 프로시저 DML 실행, 에러 시 echo echo '-----'.$err['message']
ProcedureExcuteQuery_new($sql, $encode_type, $return_type) 인코딩 선택(utf8/euckr) 후 DML 실행 echo 출력
ProcedureExcuteQuery_boolean($sql) DML 실행 후 true/false 반환 boolean
ProcedureExcuteQuery_boolean2($sql) DML 실행 후 result/error 구조체 반환 배열 구조체
ProcedureExcuteQuery_ErrorCheck($sql, $memberId, $date, $returnYN, $encode_type, $output_msg) 프로시저 실행 후 Common_Confirm_Error 프로시저로 이중 에러 검증 echo 또는 return
ProcedureExcuteQuery_Return($sql, $ReturnContent) DML 후 RESULT_CODE / RESULT_MSG / ALL 선택 반환 문자열 또는 배열
ProcedureExcuteQuery_ExcReturn($sql, $ReturnContent) 최신 try-catch 기반 DML. Oracle 에러코드 한국어 메시지 매핑 JSON 또는 한국어 에러 문자열
executeDml($sql) 트랜잭션 안전 DML 실행 (2025-07-25 신규 추가) 구조화된 결과 객체
ProcedureExcuteQuery_ErrorPrint($sql, $encode_type, $return_type) 에러 발생 시 콘솔 출력 echo

2.4 Oracle 에러코드 → 한국어 메시지 매핑 테이블

ProcedureExcuteQuery_ExcReturn() 내부의 $ErrorKind_Array로 Oracle ORA- 에러 코드를 사용자 친화적 한국어 메시지로 변환합니다.

$ErrorKind_Array = [
    "-1"     => "존재하는 내용입니다. 확인 후 입력해 주세요.",         // ORA-00001: DUP_VAL_ON_INDEX
    "-1400"  => "빈값을 입력 할 수 없습니다.",                       // NOT NULL 위반
    "-1722"  => "숫자자리에 문자를 입력할 수 없습니다.",               // INVALID_NUMBER
    "-1407"  => "빈값을 입력 할 수 없습니다.",                       // NOT NULL 위반 (다른 컬럼)
    "-1438"  => "숫자값이 너무 커서 입력할 수 없습니다.",              // 숫자 정밀도 초과
    "-12899" => "글자가 너무 길어 입력할 수 없습니다.",               // VARCHAR2 길이 초과
    "-6550"  => "필수항목이 입력되지 않았습니다.",                    // PL/SQL 컴파일 에러
    "-6511"  => "(CURSOR_ALREADY_OPEN) : 커서가 이미 OPEN 상태",
    "-6502"  => "(VALUE_ERROR) : 수치 또는 값 오류",
    "-1476"  => "(ZERO_DIVIDE) : 0으로 나눌때",
    "-1017"  => "(LOGIN_DENIED) : 잘못된 사용자 이름이나 비밀번호로 로그인 시도",
    "-1422"  => "(TOO_MANY_ROWS) : SELECT INTO 결과가 두 건 이상",
];

2.5 문자 인코딩 변환 메소드 (CP949 ↔ UTF-8 파이프라인)

Oracle DB는 CP949 (EUC-KR) 인코딩을 사용하고, 웹 화면은 UTF-8로 구성되어 있어 양방향 변환이 필수입니다.

graph LR
    DB["Oracle DB (CP949/EUC-KR)"]
    PHP["PHP Application (UTF-8)"]
    Browser["Web Browser (UTF-8)"]

    DB -->|HangleEncode: ICONV cp949→UTF-8| PHP
    PHP -->|HangleEncodeUTF8_EUCKR: ICONV UTF-8→cp949| DB
    PHP -->|HangleEncodeAjax: cp949→UTF-8 Ajax 전용| Browser
메소드명 변환 방향 비고
HangleEncode($item) CP949 → UTF-8 공백은 &nbsp로 대체
HangleEncode_empty($item) CP949 → UTF-8 공백 대체 없음
HangleEncodeAjax($item) CP949 → UTF-8 Ajax 응답 전용
HangleEncodeUTF8_EUCKR($item) UTF-8 → CP949 SQL 삽입 시 한글 보호
bear3StrCut($str, $len, $tail) 멀티바이트 문자열 정규식 자르기 (preg_match)
resizeString($str, $size, $addStr) mb_strlen/mb_substr 기반 UTF-8 자르기

2.6 로그 기록 메소드

function OracleLogWrite($msg, $menu) {
    // 로그 파일 경로: Satis/sys/log/YYYY-MM-DD_{메소드명}.txt
    $cfile = "../../log/" . date("Y-m-d") . "_" . $menu . ".txt";

    // 기존 파일 내용 읽기 → 새 내용 추가 → 파일 쓰기
    if (file_exists($cfile)) {
        $fd = fopen($cfile, 'r');
        $con = fread($fd, filesize($cfile));
        fclose($fd);
    }
    $fp = fopen($cfile, 'w');
    $cond = $con . date("Y-m-d H:i:s") . "  " . $msg . "\n";
    fwrite($fp, $cond);
    fclose($fp);
}

로그 파일 명명 규칙: YYYY-MM-DD_{호출된메소드명}.txt
예: 2026-07-20_LoadProcedure.txt, 2026-07-20_ExcuteQuery_new.txt

2.7 최신 트랜잭션 안전 메소드 (2025-07-25 추가, 김병철)

function executeSelect($sql) {
    try {
        set_error_handler(array($this, 'custom_error_handler'));
        $stmt = oci_parse($this->connection, $this->HangleEncodeUTF8_EUCKR($sql));

        // Ref Cursor 포함 여부 자동 감지
        if (strpos($sql, ":entries") !== false) {
            $entries = oci_new_cursor($this->connection);
            oci_bind_by_name($stmt, ":entries", $entries, -1, OCI_B_CURSOR);
            oci_execute($stmt, OCI_DEFAULT);
            oci_execute($entries);
            $fetchResult = $this->fetchResults($entries, $result);
        } else {
            oci_execute($stmt, OCI_DEFAULT);
            $fetchResult = $this->fetchResults($stmt, $result);
        }
        $result["rows"] = $fetchResult[0];
        $result["columns"] = $fetchResult[1];

    } catch (Exception $e) {
        oci_rollback($this->connection);  // 에러 시 자동 롤백
        $this->makeErrorResult($result, $stmt, $e->getMessage());

        // Oracle 에러코드 20000 이상 = 사용자 정의 예외 → DB 에러로그 삽입
        if ($result["error"]["datas"]["oci_error_data"]["code"] >= 20000) {
            $this->insertSmErrorLog($_SESSION["satis_user_id"], $errorMessage);
        }
    }
    restore_error_handler();
    return $result;
}

3. 전자결재 엔진 알고리즘 (Satis/inc/approval_function.php — 1,224라인)

3.1 전자결재 상태 머신 (State Machine)

stateDiagram-v2
    [*] --> CREATE : 기안 문서 작성 (DOC_STATUS_CREATE)

    CREATE --> TEMP   : 임시저장 (TEMP-00001)
    CREATE --> APPROVE: 결재 상신 (PROCESS_APPROVE=1)
    TEMP   --> APPROVE: 상신 실행

    state APPROVE {
        [*] --> DEPT   : 부서내결재 (부서내 / PROCESS_CODE)
        DEPT  --> RECV : 부서접수 (RECEIVE)
        RECV  --> SANCTION: 결의부서내 (SANCTION_CODE)
        SANCTION --> FINISH_LOOP: 처리부서내 (SANCTION_CODE2)
    }

    APPROVE --> BACK     : 이전 결재자 반송 (PROCESS_BACK="BACK")
    APPROVE --> RETURN   : 부결/반송 (PROCESS_RETURN="RETURN")
    APPROVE --> FINISH   : 최종 승인 (PROCESS_FINISH="FINISH")
    APPROVE --> DECISION : 전결 (PROCESS_DECISION="FINISH-DECISION")

    BACK   --> APPROVE : 재결재
    RETURN --> CREATE  : 재기안
    FINISH   --> [*]
    DECISION --> [*]

3.2 전체 상태 상수 정의 (approval_var.php)

/* 결재 진행 상태 코드 */
$PROCESS_APPROVE    = 1;               // 결재진행중
$PROCESS_TEMPORARY  = "TEMP-";        // 임시저장 (형식: TEMP-00001 또는 TEMP-00001-T02211)
$PROCESS_ACCEPT     = "ACCEPT";       // 가결
$PROCESS_REJECTION  = "REJECT";       // 부결 (기능삭제됨)
$PROCESS_RETURN     = "RETURN";       // 반송 (재기안 가능)
$PROCESS_BACK       = "BACK";         // 반송 - 이전 결재자에게 재전달
$PROCESS_FINISH     = "FINISH";       // 최종 결재완료
$PROCESS_DECISION   = "FINISH-DECISION"; // 전결 처리
$PROCESS_RECEIVE    = "RECEIVE";      // 부서접수 대기
$PROCESS_CANCEL     = "CANCEL";       // 기안취소
$PROCESS_DELETE     = "DELETE";       // 기안 삭제

/* 문서 편집 상태 코드 */
$DOC_STATUS_CREATE  = "CREATE";   // 새 기안 문서 작성
$DOC_STATUS_EDIT    = "EDIT";     // 기안 문서 편집
$DOC_STATUS_VIEW    = "VIEWER";   // 기안 문서 보기 (편집불가)
$DOC_STATUS_APPROVE = "APPROVE";  // 결재함 문서 결재하기
$DOC_STATUS_ACCEPT  = "ACCEPT";   // 부서 문서 접수하기

/* 결재 라인 위치 코드 */
$PROCESS_CODE    = "부서내";           // 부서내 결재 진행 중
$TEMPORARY_CODE  = "부서내임시저장";   // 임시저장 코드
$SANCTION_CODE   = "결의부서내";       // 결의부서 결재 진행 중
$SANCTION_CODE2  = "처리부서내";       // 처리부서 결재 진행 중
$STEP_NO         = 9;                  // 최대 결재 단계 수

3.3 문서 번호 채번 알고리즘 (Serial Number Generation)

결재 상신 시 문서번호가 자동으로 채번되는 두 가지 방식이 있습니다.

A. 임시저장 문서번호 (TempSerialNo / TempSerialNo2)

// 형식: "TEMP-00001" 또는 "TEMP-00001-T02211"
function TempSerialNo() {
    // SanctionDoc_tbl에서 TEMP-로 시작하는 마지막 번호 조회
    $res04 = mysql_query("SELECT * FROM SanctionDoc_tbl WHERE DocSN LIKE 'TEMP-%' ORDER BY DocSN DESC LIMIT 0,1", $db);
    if (mysql_num_rows($res04) > 0) {
        $aa = mysql_result($res04, 0, "DocSN");
        $bb = split("TEMP-", $aa);
        return "TEMP-" . sprintf("%05d", ($bb[1] + 1)); // 5자리 zero-padding
    } else {
        return "TEMP-" . sprintf("%05d", 1); // 최초: TEMP-00001
    }
}

// TempSerialNo2: 사번 suffix 포함 형식 (TEMP-00001-T02211)
// split("-", "TEMP-00021-T02211") → $bb[1] = "00021"

B. 결재 상신 문서번호 (NewSerialNo / NewSerialNo2)

// 형식: "2026-00001" 또는 "2026-00001-T02211"
function NewSerialNo() {
    $NewKey = date('Y-'); // "2026-"
    $res04 = mysql_query("SELECT * FROM SanctionDoc_tbl WHERE DocSN LIKE '$NewKey%' ORDER BY DocSN DESC LIMIT 0,1", $db);
    if (mysql_num_rows($res04) > 0) {
        $aa = mysql_result($res04, 0, "DocSN");
        $bb = split($NewKey, $aa); // "2026-00005" → $bb[1] = "00005"
        return $NewKey . sprintf("%05d", ($bb[1] + 1)); // "2026-00006"
    } else {
        return $NewKey . sprintf("%05d", 1); // 최초: "2026-00001"
    }
}

3.4 결재선 순회 알고리즘 (NextSanctionState)

결재 진행 시 현재 결재 위치를 다음 결재자로 이동시키는 핵심 알고리즘입니다.

function NextSanctionState($Tmp_Sanction, $Tmp_SanctionState) {
    // $Tmp_Sanction = "A001:B002:RECEIVE:C003:D004:FINISH" (결재선 배열, ':' 구분)
    // $Tmp_SanctionState = "1:부서내:A001:2026-07-20" (현재 단계:코드:사번:날짜)
    
    $TmpState = split(":", $Tmp_SanctionState);
    $Start = $TmpState[0]; // 현재 결재 인덱스

    // RECEIVE 위치(index) 먼저 탐색
    for ($i = $Start; $i < count($TmpArr); $i++) {
        if ($TmpArr[$i] == $PROCESS_RECEIVE) {
            $RECEIVE_INDEX = $i; break;
        }
    }

    // 다음 결재자 상태 결정
    for ($i = $Start; $i < count($TmpArr); $i++) {
        switch ($TmpArr[$i]) {
            case $PROCESS_RECEIVE:  // 부서접수로 넘김
                $Res_SanctionState = ($i+1).":".$PROCESS_RECEIVE.":".$TmpState[2].":".date('Y-m-d');
                break;
            case $PROCESS_FINISH:   // 결재 완료
                $Res_SanctionState = ($i+1).":".$PROCESS_FINISH.":".$TmpState[2].":".date('Y-m-d');
                break;
            default:                // 다음 결재자에게 넘김
                // RECEIVE 이후면 처리부서내(SANCTION_CODE2), 이전이면 결의부서내(SANCTION_CODE)
                $SANCTION_CODE_ = ($i > $RECEIVE_INDEX) ? $SANCTION_CODE2 : $SANCTION_CODE;
                $Res_SanctionState = ($i+1).":".$SANCTION_CODE_.":".$TmpArr[$i].":".date('Y-m-d');
                break;
        }
        break; // 첫 번째 유효 결재자만 처리
    }
    return $Res_SanctionState;
}

3.5 결재 서명/인장 렌더링 알고리즘 (FindSanctionState / FindSanctionState2)

결재 문서 화면에서 결재자 서명 이미지(인장) 또는 텍스트를 표시하는 렌더링 로직입니다.

function FindSanctionState2($TmpSN, $sName, $sGroup) {
    // SanctionState_tbl에서 해당 결재자의 결재 이력 조회
    $azSQL = "SELECT * FROM SanctionState_tbl WHERE DocSN='$TmpSN' AND MemberNo LIKE '%$sName%'";
    
    $SanctionState = mysql_result($res06, 0, "SanctionState");
    $src_photo = "../../../erpphoto/$MemberNo.jpg"; // 결재자 서명 사진 경로

    // 대리결재 감지
    if ($sGroup == "결의부서내") {
        if (strpos(mysql_result($res06, 0, "MemberNo"), "대결") !== false) {
            $Member_Name .= "-대결"; // 대리결재 표시
        }
    }

    if ($SanctionState == "ACCEPT" || $SanctionState == "FINISH-DECISION") {
        if (file_exists($src_photo)) {
            // 서명 이미지 존재 시: <img src='erpphoto/A001.jpg' height=30>
            return "<img src='$src_photo' height=30><br>$Member_Name<br>($SanctionDate)";
        } else {
            // 서명 없음 시: 빨간색 텍스트("승인" 또는 "전결")
            $ok_str = ($SanctionState == "ACCEPT") ? "승인" : "전결";
            return "<font color=red>$ok_str</font><br><br>$Member_Name<br>($SanctionDate)";
        }
    } else {
        return "<br><br>$Member_Name<br>(미결)"; // 아직 결재 안 됨
    }
}

3.6 공통 코드 룩업 함수 목록 (approval_function.php)

전자결재 처리 중 MySQL intranet_tbl / member_tbl / SystemConfig_Tbl을 조회하는 헬퍼 함수 목록입니다.

함수명 조회 테이블 반환값
MemberNo2Ip($mCode) intranet_tbl 사번으로 IP 주소 반환 (끝자리 0/1이면 공백)
MemberNo2BossIP($Tmp_Sanction, $Step) intranet_tbl 결재선 특정 스텝의 결재자 IP
MemberNo2Name($mCode) member_tbl 사번 → 한글 성명
MemberNo2Rank($mCode) member_tbl, SystemConfig_Tbl 사번 → 직급명
MemberNo2GroupCode($mCode) member_tbl 사번 → 부서코드 (2자리 zero-pad)
MemberNo2GroupName($mCode) member_tbl, SystemConfig_Tbl 사번 → 부서명
MemberNo2SiteName($mCode) systemconfig_tbl 사번 → 현장명 (TeamLeader 기준)
DocCode2Name($DocCode) systemconfig_tbl 결재 서식 코드 → 서식명
ProjectCode2Name($mCode) project_tbl 프로젝트 코드 → 약칭
ProjectCode2Bridge($mCode, $no) project_cs_summary_tbl 교량 프로젝트 약칭
Code2Name($mCode, $dbSysKey, $Kind) SystemConfig_Tbl 일반 코드 → 명칭 (Kind=0) 또는 설명 (Kind=1)
Group2Manager($mCode) SystemConfig_Tbl 부서코드 → 부서장 사번 (Note 컬럼)
ProcessingState($TmpSN, $tmpState, $mCode) SanctionDoc_tbl 현재 결재 상태를 한국어 명칭으로 변환
FindSanctionDate($TmpSN, $TmpMemberNo) SanctionState_tbl 특정 결재자의 결재일자 반환
TempState($mCode) 임시저장 상태 문자열 생성 (0:부서내임시저장:A001:YYYY-MM-DD)

4. 시스템 글로벌 변수 및 초기화 파일 (DefaultInformation.php)

모든 컨트롤러에서 include "../../util/DefaultInformation.php"로 로드되는
시스템 기본 정보 세션 초기화 파일입니다.

/* 화면 요청 정보 */
$smarty->assign('auth_page_id',    $_REQUEST['target_id']);
$smarty->assign('auth_page_devYN2', 'Y');
$smarty->assign('satis_user_id',    $_SESSION['satis_user_id']);

/* 개발자 화이트리스트 (devYN='Y' 권한 부여) */
$devUserList = [
    "203155" => "한정규",
    "216070" => "정명준",
    "209171" => "신지호",
    "216011" => "장계석",
    "221064" => "김병철",
    "223158" => "김진선",
];

/* 로그인 사용자가 개발자 목록에 있으면 devYN='Y' 설정 */
if (array_key_exists($_SESSION['satis_user_id'], $devUserList)) {
    $smarty->assign('auth_page_devYN', 'Y');
}

/* 회사 기본 정보 */
$site_code = "SAMAN";           // 회사 영문 식별 코드
$site_name = "삼안";
$Corporation_name = "(주)삼안";
$company_registration_number = "211-81-25425"; // 사업자등록번호
$smarty->assign('site_code', $site_code);
$smarty->assign('site_name', $site_name);
$smarty->assign('Corporation_name', $Corporation_name);
$smarty->assign('company_registration_number', $company_registration_number);

5. 공통 헬퍼 함수 라이브러리 (Satis/inc/function.php — 1,093라인)

함수명 설명
str_cutting($str, $len) 멀티바이트 문자열 자르기 (한글 2바이트 처리, 생략 시 ... 추가)
substr1($str, $len) str_cutting의 Trailing 없는 단순 잘라내기 버전
ConfigName($category, $code) config_tbl 코드 → 명칭 반환
ConfigName2($category, $code) config_tbl 코드 → 설명(desc) 반환
Code2Name($mCode, $dbSysKey, $Kind) SystemConfig_Tbl 일반 코드 → 명칭 반환
change_code($pcode) 연도 코드 XX를 실제 연도(2자리)로 치환

6. 데이터베이스 스키마 상세 (MySQL 인트라넷 DB — hallaerp)

approval_function.php의 실제 SQL 쿼리 분석으로 도출한 인트라넷 MySQL DB 테이블 구조입니다.

테이블명 주요 컬럼 역할
member_tbl MemberNo, KorName, RankCode, GroupCode, WorkPosition 사원 기본 정보 (WorkPosition='9' = 퇴직자)
intranet_tbl MemberNo, UseIP 사원 네트워크 IP 정보
SanctionDoc_tbl DocSN, DocCode, PG_Date, DocTitle, DocStatus 전자결재 문서 마스터
SanctionState_tbl DocSN, MemberNo, SanctionOrder, SanctionState, SanctionDate 결재자별 결재 이력
SystemConfig_Tbl SysKey, Code, Name, Description, Note 시스템 공통 코드 사전 (부서코드, 직급코드, 결재서식 등)
project_tbl ProjectCode, ProjectNickname 프로젝트 약칭
project_cs_summary_tbl ProjectCode, no, Item 교량/구조물 프로젝트 세부 항목
config_tbl category, code, name, desc 화면별 코드 설정

7. 예외 처리 & 보안 핸들링 상세 (AuthClass.php — 591라인)

7.1 CSRF 토큰 생성 알고리즘

function Auth_TokenGenerater($assign_boolean = false) {
    // 62자 문자풀에서 10자리 난수 토큰 생성
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $randomString = '';
    for ($i = 0; $i < 10; $i++) {
        $randomString .= $characters[rand(0, 61)];
    }
    if ($assign_boolean) {
        $_SESSION['Token'] = $randomString;    // 서버 세션에 저장
        $this->smarty->assign('Token', $randomString); // 뷰로 전달 (히든 필드 렌더링)
    } else {
        return $randomString;
    }
}

7.2 토큰 검증 흐름

sequenceDiagram
    autonumber
    actor User
    participant Controller
    participant AuthClass
    participant Session

    User->>Controller: 폼 제출 (Token 파라미터 포함)
    Controller->>AuthClass: Auth_TokenChecker($Token, $page_move=true)
    AuthClass->>Session: $_SESSION['Token'] 조회
    alt 토큰 일치
        AuthClass->>Controller: Smarty assign('Token', ...) 후 정상 처리
    else 토큰 불일치
        AuthClass->>Session: session_destroy() 세션 파기
        AuthClass->>User: auth_move_page.tpl 출력 (로그인 페이지 이동)
        AuthClass->>Controller: exit() 강제 종료
    end

7.3 세션 변수 목록

세션 키 설명
$_SESSION['satis_user_id'] 로그인 사원번호 (예: "221064")
$_SESSION['satis_user_name'] 로그인 사원 성명
$_SESSION['satis_user_deptcode'] 사원 부서코드
$_SESSION['satis_user_deptname'] 사원 부서명
$_SESSION['Token'] CSRF Anti-Forgery Token (10자리 난수)
$_SESSION['Title'] 현재 화면 제목
$_SESSION['Auth'] 권한 레벨
$_SESSION['Type'] 사용자 유형

8. 핵심 소스 파일 사전 (Source File Dictionary — 전체)

파일 경로 용량 / 라인 핵심 역할
Satis/sys/util/OracleClass.php 46KB / 1,392라인 Oracle OCI 커넥션, Ref Cursor 조회/DML, 인코딩 변환
Satis/sys/util/AuthClass.php 18KB / 591라인 세션 토큰 생성/검증, CSRF 방어
Satis/sys/util/CommonCodeList.php 294KB 시스템 공통 코드 PHP 배열 인메모리 캐시
Satis/inc/approval_function.php 35KB / 1,224라인 전자결재 상태 처리, 결재선 순회, 서명 렌더링
Satis/inc/function.php 30KB / 1,093라인 문자열 처리, 코드룩업, 공통 헬퍼 함수
Satis/inc/function_intranet.php 33KB 인트라넷 전용 헬퍼 함수
Satis/inc/approval_var.php 1.6KB / 29라인 전자결재 상태 상수 정의
Satis/inc/dbcon.inc 0.4KB 인트라넷 MySQL 커넥션 (erp.samaneng.com/hallaerp)
Satis/inc/saman_icube_dbcon.inc 0.5KB 더존 iCube MS-SQL 커넥션 (192.168.13.50:5539/DZICUBE)
Satis/sys/util/DefaultInformation.php 1.3KB 시스템 글로벌 변수, 개발자 권한, 회사 정보 초기화
SmartyConfig.php 0.3KB Smarty 엔진 경로 설정
Satis/sys/util/PrintAgent.php 1KB 인쇄 에이전트 유틸리티
Satis/sys/util/ReportViewExcel.php 2.8KB Excel 보고서 출력 처리

9. M/M (Man-Month) 투입 공수 및 원가 배부 공식

엔지니어링 업계 특성상 프로젝트별 인건비 원가 배부는 투입 M/M 비율을 기준으로 계산됩니다.

\text{Project}_{i} \text{ 원가 배부액} = \text{직원 월 급여} \times \left( \frac{\text{Project}_{i} \text{ 투입 M/M}}{\displaystyle\sum_{j=1}^{n} \text{Project}_{j} \text{ 투입 M/M}} \right)

처리 흐름:

  1. PersonPaymentCalculation: 직원의 당월 급여 총액 및 4대보험 회사 부담금 계산
  2. ProjectMenHour: 해당 월 직원의 프로젝트별 투입 M/M 입력
  3. Oracle 저장 프로시저: 비율 계산 → 각 프로젝트 원가 테이블 자동 배부
  4. AccountGisungCalculation: 프로젝트 기성 청구 시 누적 원가 반영

10. 로우레벨 개발 시 주의사항

Caution

SQL Injection 위험: approval_function.phpfunction.php의 MySQL 함수들은 대부분 $mCode 값을 필터링 없이 SQL에 직접 삽입합니다. 수정 시 반드시 mysql_real_escape_string() 또는 PDO Prepared Statement로 교체해야 합니다.

Warning

레거시 드라이버: mysql_connect(), mssql_connect() 등 PHP 7.x에서 완전히 제거된 드라이버를 사용합니다. PHP 5.6 이하 환경에서만 실행 가능합니다.

Warning

하드코딩된 개발자 화이트리스트: DefaultInformation.php에 개발자 사번이 하드코딩되어 있습니다. 인사 이동 시 소스 직접 수정이 필요합니다.

Note

Oracle 트랜잭션: ProcedureExcuteQuery()OCI_COMMIT_ON_SUCCESS(자동 커밋) 모드로 동작합니다. 여러 DML을 하나의 트랜잭션으로 묶으려면 OCI_DEFAULT 모드의 executeDml() / executeSelect()를 사용해야 합니다.

Note

Session 의존성: 모든 컨트롤러 로직은 session_start()GetUserInfo.phpDefaultInformation.php 순서로 초기화가 완료되어야 합니다. 순서가 바뀌면 $_SESSION['satis_user_id']가 null이 되어 개발자 권한 판별이 실패합니다.