38 KiB
(주)삼안 ERP 시스템 (Satis) 초상세 로우레벨 분석 명세서
본 문서는 (주)삼안의 **ERP 시스템 (Satis)**의 실제 소스 코드를 직접 분석한
초상세 로우레벨(Ultra-Detailed Low-Level) 구현 명세서입니다.
OracleClass.php 전체 메소드 명세, 비즈니스 로직(Model / Satis/sys/model/*) 20개 모듈 구조, 전자결재 알고리즘,
문서번호 채번 방식, 인코딩 파이프라인, 결재 서명 렌더링, 시스템 글로벌 변수, DB 데이터 모델, 보안 핸들링 등을 명세합니다.
1. 모듈별 컨트롤러, 모델 & 뷰 맵핑 딕셔너리 (Controller - Model - 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 |
인쇄 출력 처리 |
1.4 비즈니스 로직 (Model 계층: Satis/sys/model/*) 20개 모듈 명세
Model 계층은 MVC 아키텍처 중 비즈니스 데이터 처리 및 DB 저장 프로시저(SP) 파라미터 구성, 결과 데이터 가공을 담당합니다. 모든 Logic 클래스는 생성자에서 $this->oracle = new OracleClass($smarty)를 초기화하여 데이터베이스에 접근합니다.
A. 모듈별 대표 Logic 클래스 목록
모듈 디렉토리 (Satis/sys/model/) |
포함 Logic 파일 수 | 주요 Logic 클래스명 (*_logic.php) |
핵심 역할 및 기능 |
|---|---|---|---|
| Account | 41개 | AccountSlipTemp_logic.php, AccountSlipConfirmed_logic.php, AccountGisungCalculation_logic.php, FinalClosing_logic.php |
임시/확정 전표 처리, 기성 청구금액 계산, 원가 배부, 결산 처리 |
| Person | 36개 | PersonInfoManager_logic.php, PersonPaymentCalculation_logic.php, PersonYearEndTax_logic.php, PersonAppointment_logic.php |
인사기록, 급여/상여 산출 엔진, 연말정산(yeta_data), 인사 발령 |
| Project | 16개 | Project_logic.php, ProjectOrderContract_logic.php, ProjectMenHour_logic.php, ProjectOrderGisung_logic.php |
프로젝트 마스터, 수주/변경 계약, M/M 공수 투입 집계, 기성 수금 |
| Document | 1개 | Document_logic.php |
전자결재 서식 생성, 문서 양식 데이터 파싱 |
| Sign | 6개 | SignLogic.php, SignDocumentLogic.php |
전자서명 문서 생성, 결재선 저장, 결재 상태 검증 및 서명 렌더링 |
| Sale | 9개 | SaleBid_logic.php, SaleContractManager_logic.php, SalePerformanceManager_logic.php |
영업 입찰, 수주 계약, 수금 및 수주 실적 관리 |
| Purchase | 4개 | PurchaseClaim_logic.php, PurchaseOrder_logic.php, PurchaseExactCalculation_logic.php |
구매 신청, 발주 처리, 대금 정산 |
| Travel | 1개 | Travel_logic.php |
국내/해외 출장 신청 및 여비 정산 처리 |
| Asset | 3개 | AssetInfo_logic.php, AssetPurchase_logic.php, AssetBasicCode_logic.php |
고정자산 등록, 자산 구매, 자산 코드 관리 |
| Book | 1개 | Book_logic.php |
도서 목록 등록 및 대여/반납 처리 |
| Association | 1개 | Association_logic.php |
엔지니어링 협회 회원 관리 및 수수료 정산 |
| Management | 4개 | CommonCode_logic.php, MenuAuth_logic.php, Notify_logic.php |
시스템 공통 코드, 사용자 메뉴 권한, 알림 팝업 |
| Supervisor | 2개 | Supervisor_logic.php |
시스템 전체 공정코드, 부서코드, 권한 마스터 관리 |
| Common | 17개 | sms_logic.php, PdfLogic.php, popup_logic.php, work_logic.php |
SMS 발송 연동, PDF 출력 변환, 공통 팝업 데이터 공급 |
| Jqgrid | 1개 | Jqgrid_logic.php |
jqGrid 요청에 대응하는 JSON 데이터 바인딩 로직 |
| Fileupload | 8개 | Fileupload_logic.php, AddCode01_upload_insa_image.php |
첨부파일 업로드/다운로드, 인사 사진 업로드 및 삭제 |
| Help | 1개 | Help_logic.php |
시스템 사용 도움말 및 Q&A 로직 |
| Report | 1개 | Report_logic.php |
경영진 보고서 및 엑셀 보고서 데이터 가공 |
| Sample | 1개 | Sample01_logic.php |
표준 개발용 샘플 로직 |
| TestLogic | 1개 | TestLogic.php |
개발자 테스트 전용 로직 |
B. Model (Logic) 클래스의 공통 설계 구조 패턴
-
초기화 및 의존성 주입:
class AccountSlipTempLogic { var $oracle; var $smarty; function AccountSlipTempLogic($smarty) { $this->smarty = $smarty; $this->oracle = new OracleClass($smarty); // DB 객체 인스턴스화 } } -
Controller 파라미터 바인딩 및 프로시저 호출:
- Controller의
$ActionMode요청에 따라 Logic 클래스 내 동명의 메소드가 실행됩니다. - 입력 파라미터를 sanitize 및 인코딩 변환(
HangleEncodeUTF8_EUCKR) 후 Oracle 저장 프로시저 호출 구문(BEGIN usp_...; END;)을 완성합니다. LoadProcedure_new()또는executeSelect()를 호출하여 결과를 Smarty 뷰로 전달합니다.
- Controller의
-
파일 업로드 및 이미지 관리 로직 (
Fileupload_logic.php&AddCode01_...):- 인사기록카드 사원 사진 업로드 시
AddCode01_upload_insa_image.php를 통해 이미지를 서브 디렉터리(erpphoto/)로 이송하고 파일명을 사번(MemberNo.jpg)으로 표준화합니다. - 파일 업로드 시 확장자 및 용량 제한 검증을 수행하고, Oracle DB에 첨부파일 메타데이터를 저장합니다.
- 인사기록카드 사원 사진 업로드 시
2. 핵심 클래스 전체 메소드 명세 (OracleClass.php — 1,392라인)
Satis/sys/util/OracleClass.php는 시스템 전체 Oracle DB 연동의 핵심 클래스입니다.
실제 소스 분석 기반의 전체 메소드 분류표입니다.
2.1 DB 커넥션 및 기본 쿼리 메소드
| 메소드명 | 설명 |
|---|---|
OracleClass($smarty) |
생성자. OCI 커넥션 생성. oci_connect('satis', '************', '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이 되어 개발자 권한 판별이 실패합니다.