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 |
공백은  로 대체 |
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)
처리 흐름:
PersonPaymentCalculation: 직원의 당월 급여 총액 및 4대보험 회사 부담금 계산ProjectMenHour: 해당 월 직원의 프로젝트별 투입 M/M 입력- Oracle 저장 프로시저: 비율 계산 → 각 프로젝트 원가 테이블 자동 배부
AccountGisungCalculation: 프로젝트 기성 청구 시 누적 원가 반영
10. 로우레벨 개발 시 주의사항
Caution
SQL Injection 위험:
approval_function.php및function.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.php→DefaultInformation.php순서로 초기화가 완료되어야 합니다. 순서가 바뀌면$_SESSION['satis_user_id']가 null이 되어 개발자 권한 판별이 실패합니다.