Files
sam-kd/0readme/estimate/output_head_developer_guide.md
hskwon aca1767eb9 초기 커밋: 5130 레거시 시스템
- URL 하드코딩 → .env APP_URL 기반 동적 URL로 변경
- DB 연결 하드코딩 → .env 기반으로 변경
- MySQL strict mode DATE 오류 수정
2025-12-10 20:14:31 +09:00

566 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# output_head.php 개발자 가이드
## 📋 개요
`output_head.php`는 방화셔터 견적 시스템의 거래명세표 출력 페이지 헤더를 담당하는 핵심 PHP 컴포넌트입니다. 이 파일은 거래명세표 화면의 상단 부분을 구성하며, 견적 정보 조회, 헤더 출력, 할인율 적용, 금액 계산 테이블 등을 포함합니다. 특히 인정제품과 비인정제품의 구분, 할인율 적용, VAT 포함 계산 등 거래명세표 특화 기능을 제공합니다.
## 🏗️ 파일 구조
### 📁 파일 위치
```
/estimate/common/output_head.php
```
### 📊 파일 정보
- **파일 크기**: 10.2KB (263 lines)
- **주요 언어**: PHP + HTML + CSS
- **의존성**: Bootstrap, jQuery, MySQL/PDO
- **주요 기능**: 거래명세표 헤더, 할인율 적용, 금액 계산
## 🔧 핵심 기능
### 1. **헤더 및 의존성 로드**
```php
<?php include $_SERVER['DOCUMENT_ROOT'] . '/load_header.php';
// 수주내역에서 거래명세표 표시
$selectWork = '거래명세표';
?>
<title> <?=$title_message?> </title>
<link rel="stylesheet" href="css/style.css?v=<?=time()?>">
</head>
<body>
```
### 2. **견적 정보 조회**
```php
$num = isset($_REQUEST['num']) ? $_REQUEST['num'] : '';
$option = isset($_REQUEST['option']) ? $_REQUEST['option'] : 'option'; // 기본 option 견적서와 산출서의 다른점을 표현하는 것
require_once($_SERVER['DOCUMENT_ROOT'] . "/lib/mydb.php");
require_once($_SERVER['DOCUMENT_ROOT'] . "/estimate/fetch_unitprice.php");
$pdo = db_connect();
try {
$sql = "select * from {$DB}.{$tablename} where num = ? ";
$stmh = $pdo->prepare($sql);
$stmh->bindValue(1, $num, PDO::PARAM_STR);
$stmh->execute();
$count = $stmh->rowCount();
if ($count < 1) {
print "검색결과가 없습니다.<br>";
} else {
$row = $stmh->fetch(PDO::FETCH_ASSOC);
include $_SERVER['DOCUMENT_ROOT'] . "/output/_row.php";
// output_extra 불러오기
$sql_extra = "SELECT * FROM {$DB}.output_extra WHERE parent_num = ?";
$stmh_extra = $pdo->prepare($sql_extra);
$stmh_extra->bindValue(1, $num, PDO::PARAM_STR);
$stmh_extra->execute();
$row_extra = $stmh_extra->fetch(PDO::FETCH_ASSOC);
if ($row_extra) {
include $_SERVER['DOCUMENT_ROOT'] . "/output/_row_extra.php";
}
}
} catch (PDOException $Exception) {
print "오류: " . $Exception->getMessage();
}
```
### 3. **견적 데이터 디코딩**
```php
// JSON 문자열을 PHP 배열로 디코딩합니다.
if($major_category == '스크린')
$decodedEstimateList = json_decode($estimateList, true);
if($major_category == '철재')
$decodedEstimateList = json_decode($estimateSlatList, true);
// 디코딩된 데이터가 배열인지 확인합니다.
if (!is_array($decodedEstimateList)) {
echo "데이터가 정상적이지 않습니다. 확인바랍니다.";
exit;
}
echo '<script>';
echo 'var dataList = ' . json_encode($detailJson ?? []) . ';';
echo '</script>';
```
## 📋 폼 구조
### 🎯 **숨겨진 입력 필드**
```html
<form id="board_form" name="board_form" method="post" enctype="multipart/form-data">
<input type="hidden" id="mode" name="mode" value="<?= isset($mode) ? $mode : '' ?>">
<input type="hidden" id="num" name="num" value="<?= isset($num) ? $num : '' ?>">
<input type="hidden" id="user_name" name="user_name" value="<?= isset($user_name) ? $user_name : '' ?>">
<input type="hidden" id="update_log" name="update_log" value="<?= isset($update_log) ? $update_log : null ?>">
<input type="hidden" id="tablename" name="tablename" value="<?= isset($tablename) ? $tablename : '' ?>">
<input type="hidden" id="header" name="header" value="<?= isset($header) ? $header : '' ?>">
<input type="hidden" id="detailJson" name="detailJson">
<input type="hidden" id="estimateSurang" name="estimateSurang" value="<?= isset($estimateSurang) ? $estimateSurang : '' ?>">
<input type="hidden" id="estimateTotal" name="estimateTotal" value="<?= isset($estimateTotal) ? $estimateTotal : '' ?>">
<input type="hidden" id="ET_unapproved" name="ET_unapproved" value="<?= isset($ET_unapproved) ? $ET_unapproved : '' ?>">
<input type="hidden" id="ET_total" name="ET_total" value="<?= isset($ET_total) ? $ET_total : '' ?>">
<input type="hidden" id="option" name="option" value="<?= isset($option) ? $option : '' ?>">
```
## 🎨 UI 구성 요소
### 📊 **헤더 네비게이션**
```html
<div class="container mt-2">
<div class="d-flex align-items-center justify-content-end mt-3 m-1 mb-4">
<span class="badge bg-secondary me-5 fs-5"> ( <?=$major_category?> 거래명세표) </span>
<input type="checkbox" id="showlistCheckbox" <?php echo ($option == 'option') ? '' : ''; ?>>
<label for="showlistCheckbox" class="me-3">소요자재 </label>
<input type="checkbox" id="showEstimateCheckbox" <?php echo ($option == 'option') ? '' : ''; ?>>
<label for="showEstimateCheckbox" class="me-3">산출내역서</label>
<input type="checkbox" id="showVendorCheckbox" <?php echo ($option == 'option') ? 'checked' : ''; ?>>
<label for="showVendorCheckbox" class="me-3">업체발송용</label>
<button type="button" class="btn btn-danger btn-sm me-1 ms-1 initialBtn">
<i class="bi bi-arrow-clockwise"></i> 재계산
</button>
<button type="button" class="btn btn-dark btn-sm ms-1 me-1 saveBtn">
<i class="bi bi-floppy"></i> 산출내역 저장
</button>
<button type="button" class="btn btn-dark btn-sm ms-1 me-1" onclick="generatePDF()">
PDF 저장
</button>
<button type="button" class="btn btn-dark btn-sm me-1" onclick="sendmail();">
<i class="bi bi-envelope-arrow-up"></i> 전송
</button>
<button type="button" class="btn btn-secondary btn-sm ms-5" onclick="self.close();">
&times; 닫기
</button>&nbsp;
</div>
</div>
```
### ⚠️ **알림 메시지**
```html
<div class="row">
<div class="col-5">
<div class="d-flex align-items-center justify-content-center mt-2 mb-0">
<div class="alert alert-primary mb-2" role="alert">
검사비는 제주도를 제외하고, 5만원으로 수정, <br> '산출내역 저장'버튼을 누른 후 저장해야 금액이 확정됩니다.
</div>
</div>
</div>
<div class="col-7">
<div class="d-flex align-items-center justify-content-end mt-0">
<table class="table table-bordered mt-0 text-end">
<thead>
<tr>
<th colspan="6" class="text-end">
<div class="d-flex align-items-center justify-content-end">
<span class="mx-5">할인율 적용</span> &nbsp;&nbsp;
<input type="text" id="EstimateDiscountRate" name="EstimateDiscountRate" class="form-control text-end w100px"
value="<?= isset($EstimateDiscountRate) ? $EstimateDiscountRate : '' ?>">
&nbsp; %
</div>
</th>
</tr>
<tr>
<th colspan="6" class="text-end">
<span class="text-danger me-5">💬 수정금액이 있으면 자동금액보다 수정금액이 우선됨.</span>
<span class="text-danger">(VAT포함)</span>
</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
```
### 💰 **금액 계산 테이블**
```html
<table class="table table-bordered mt-0 text-end">
<thead class="table-secondary">
<tr>
<th class="text-center">자동 견적금액</th>
<th class="text-center">수정 견적금액</th>
<th class="text-center">견적 차액</th>
<th class="text-center">견적확정액</th>
<th class="text-center">할인금액</th>
<th class="text-center">최종 결정금액</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="text" id="EstimateFirstSum" name="EstimateFirstSum" class="form-control text-end" readonly
value="<?= isset($EstimateFirstSum) && $EstimateFirstSum != 0 ? number_format($EstimateFirstSum) : $EstimateFirstSum ?>">
</td>
<td>
<input type="text" id="EstimateUpdatetSum" name="EstimateUpdatetSum" class="form-control text-end text-primary" readonly
value="<?= isset($EstimateUpdatetSum) && $EstimateUpdatetSum != 0 ? number_format($EstimateUpdatetSum) : $EstimateUpdatetSum ?>">
</td>
<td>
<input type="text" id="EstimateDiffer" name="EstimateDiffer" class="form-control text-end text-danger" readonly
value="<?= isset($EstimateDiffer) && $EstimateDiffer != 0 ? number_format($EstimateDiffer) : $EstimateDiffer ?>">
</td>
<td>
<input type="text" id="EstimateFixAmount" name="EstimateFixAmount" class="form-control text-end text-dark" readonly
value="<?= isset($EstimateFixAmount) && $EstimateFixAmount != 0 ? number_format($EstimateFixAmount) : $EstimateFixAmount ?>">
</td>
<td>
<input type="text" id="EstimateDiscount" name="EstimateDiscount" class="form-control text-end text-primary" readonly
value="<?= isset($EstimateDiscount) && $EstimateDiscount != 0 ? number_format($EstimateDiscount) : $EstimateDiscount ?>">
</td>
<td>
<input type="text" id="EstimateFinalSum" name="EstimateFinalSum" class="form-control text-end fw-bold text-success" readonly
value="<?= isset($EstimateFinalSum) && $EstimateFinalSum != 0 ? number_format($EstimateFinalSum) : $EstimateFinalSum ?>">
</td>
</tr>
</tbody>
</table>
```
## 📄 거래명세표 헤더
### 🏢 **거래명세표 기본 정보**
```html
<div class="container mt-1">
<div class="d-flex align-items-center justify-content-center ">
<table class="table table-sm" style="border-collapse: collapse;">
<tbody>
<tr>
<td class="text-center align-middle fw-bold fs-4" style="border-top:none; border-bottom:none;" >
<?php
if($option!=='option')
echo '<span class="text-dark"> ' . $title_message . ' </span> </td>';
else
echo '<span class="text-dark"> ' . $title_message_sub . ' </span> </td>';
?>
</tr>
</tbody>
</table>
</div>
</div>
```
### 👥 **업체 정보 테이블**
```html
<div class="d-flex align-items-center justify-content-center ">
<table class="table" style="border-collapse: collapse;">
<tbody>
<tr>
<td class="text-center fw-bold yellowBold " style="width:10%;">업체명</td>
<td class="text-center yellowBold " style="width:40%;"> <?=$secondord?> (귀하) </td>
<td rowspan="5" class="text-center align-middle fw-bold" style="width:5%; border-top:none; border-bottom:none;" >공 급 자</td>
<td class="text-center fw-bold lightgray " > 상호 </td>
<td class="text-center fw-bold" colspan="3" >㈜ 경동기업 </td>
</tr>
<tr>
<td class="text-center fw-bold">제품명</td>
<td class="text-center" > <?=$subTitle?> </td>
<td class="text-center fw-bold lightgray " style="width:10%;" >등록번호</td>
<td class="text-center" style="width:10%;"> 139-87-00333 </td>
<td class="text-center fw-bold lightgray " >대표자</td>
<td class="text-center fw-bold">
<div class="d-flex align-items-center justify-content-center ">
이 경 호 &nbsp;
</div>
</td>
</tr>
<tr>
<td class="text-center fw-bold">현장명</td>
<td class="text-center fw-bold" > <?=$outworkplace?></td>
<td class="text-center fw-bold lightgray " > 사업장주소 </td>
<td colspan="3" class="text-center"> 경기도 김포시 통진읍 옹정로 45-22</td>
</tr>
<tr>
<td class="text-center fw-bold">담당자</td>
<td class="text-center"><?=$secondordman?></td>
<td class="text-center fw-bold lightgray " > 업 태 </td>
<td class="text-center" > 제조업 </td>
<td class="text-center fw-bold lightgray " >종목</td>
<td class="text-center" > 방화셔터, 금속창호 </td>
</tr>
<tr>
<td class="text-center fw-bold">연락처</td>
<td class="text-center"><?=$secondordmantel?></td>
<td class="text-center fw-bold lightgray " > TEL. </td>
<td class="text-center" > 031-983-5130</td>
<td class="text-center fw-bold lightgray " > FAX </td>
<td class="text-center" > 02-6911-6315 </td>
</tr>
</tbody>
</table>
</div>
```
### 💵 **합계 금액 표시 (VAT 포함)**
```html
<div class="d-flex align-items-center justify-content-center ">
<table class="table" style="border-collapse: collapse;">
<tbody>
<tr>
<td colspan="3" class="align-middle text-end fs-6 fw-bold" style="width:250px;">
( ₩ <span id="totalsum"> </span> )
<span class="text-danger"> (VAT 포함) </span>
</td>
</tr>
</tbody>
</table>
</div>
```
## 📊 데이터 변수
### 🎯 **주요 PHP 변수**
- `$num`: 견적 번호
- `$option`: 견적서/산출서 구분 옵션 (기본값: 'option')
- `$major_category`: 주요 카테고리 (스크린/철재)
- `$title_message`: 제목 메시지
- `$title_message_sub`: 서브 제목 메시지
- `$secondord`: 발주처명
- `$subTitle`: 제품명
- `$outworkplace`: 현장명
- `$secondordman`: 담당자명
- `$secondordmantel`: 연락처
### 💰 **금액 관련 변수**
- `$EstimateFirstSum`: 자동 견적금액
- `$EstimateUpdatetSum`: 수정 견적금액
- `$EstimateDiffer`: 견적 차액
- `$EstimateFixAmount`: 견적확정액
- `$EstimateDiscount`: 할인금액
- `$EstimateFinalSum`: 최종 결정금액
- `$EstimateDiscountRate`: 할인율
- `$estimateSurang`: 견적 수량
- `$estimateTotal`: 인정제품 금액
- `$ET_unapproved`: 비인정제품 금액
- `$ET_total`: 총 금액
### 📋 **데이터베이스 관련 변수**
- `$estimateList`: 스크린 견적 리스트 (JSON)
- `$estimateSlatList`: 철재 견적 리스트 (JSON)
- `$detailJson`: 상세 견적 데이터 (JSON)
- `$shutterboxMsg`: 셔터박스 오류 메시지
## 🔧 개발자 사용법
### 📝 **기본 사용법**
```php
// 파일 포함
include_once('/estimate/common/output_head.php');
// 견적 번호 설정
$_REQUEST['num'] = '견적번호';
// 옵션 설정 (거래명세표 모드)
$_REQUEST['option'] = 'option';
```
### 🎯 **데이터 조회**
```php
// 견적 정보 조회
$sql = "SELECT * FROM {$DB}.{$tablename} WHERE num = ?";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(1, $num, PDO::PARAM_STR);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
// 추가 정보 조회
$sql_extra = "SELECT * FROM {$DB}.output_extra WHERE parent_num = ?";
$stmt_extra = $pdo->prepare($sql_extra);
$stmt_extra->bindValue(1, $num, PDO::PARAM_STR);
$stmt_extra->execute();
$row_extra = $stmt_extra->fetch(PDO::FETCH_ASSOC);
```
### 📊 **JSON 데이터 처리**
```php
// 스크린/철재 구분하여 데이터 디코딩
if($major_category == '스크린') {
$decodedEstimateList = json_decode($estimateList, true);
} else if($major_category == '철재') {
$decodedEstimateList = json_decode($estimateSlatList, true);
}
// JavaScript 변수로 전달
echo '<script>';
echo 'var dataList = ' . json_encode($detailJson ?? []) . ';';
echo '</script>';
```
## 🚨 주의사항
### ⚠️ **필수 의존성**
- PHP 7.4+
- MySQL/PDO
- Bootstrap 5.x
- jQuery 3.x
### 🔒 **보안 고려사항**
- SQL 인젝션 방지를 위한 prepared statements 사용
- 입력값 검증 및 이스케이프 처리
- 세션 기반 인증 확인
### 📱 **성능 최적화**
- 데이터베이스 쿼리 최적화
- JSON 데이터 크기 제한
- 불필요한 데이터베이스 호출 최소화
## 🐛 디버깅 가이드
### 🔍 **일반적인 문제 해결**
#### 1. 견적 정보가 표시되지 않는 경우
```php
// 데이터베이스 연결 확인
if (!$pdo) {
echo "데이터베이스 연결 실패";
exit;
}
// 쿼리 결과 확인
$stmt = $pdo->prepare($sql);
$stmt->bindValue(1, $num, PDO::PARAM_STR);
$stmt->execute();
$count = $stmt->rowCount();
echo "조회된 행 수: " . $count;
if ($count > 0) {
$row = $stmt->fetch(PDO::FETCH_ASSOC);
var_dump($row); // 결과 확인
}
```
#### 2. JSON 데이터 오류
```php
// JSON 디코딩 오류 확인
if($major_category == '스크린') {
$decodedEstimateList = json_decode($estimateList, true);
} else if($major_category == '철재') {
$decodedEstimateList = json_decode($estimateSlatList, true);
}
if (json_last_error() !== JSON_ERROR_NONE) {
echo "JSON 디코딩 오류: " . json_last_error_msg();
echo "원본 데이터: " . ($major_category == '스크린' ? $estimateList : $estimateSlatList);
}
```
#### 3. 추가 정보 조회 오류
```php
// output_extra 테이블 조회 확인
$sql_extra = "SELECT * FROM {$DB}.output_extra WHERE parent_num = ?";
$stmt_extra = $pdo->prepare($sql_extra);
$stmt_extra->bindValue(1, $num, PDO::PARAM_STR);
$stmt_extra->execute();
$row_extra = $stmt_extra->fetch(PDO::FETCH_ASSOC);
if ($row_extra) {
echo "추가 정보 존재: ";
var_dump($row_extra);
} else {
echo "추가 정보 없음";
}
```
#### 4. 변수가 정의되지 않은 경우
```php
// 변수 존재 여부 확인
if (!isset($title_message)) {
echo "title_message 변수가 정의되지 않았습니다.";
}
// 기본값 설정
$title_message = $title_message ?? '기본 제목';
$major_category = $major_category ?? '스크린';
$option = $option ?? 'option';
```
## 📚 관련 파일
### 🔗 **의존성 파일**
- `load_header.php`: 헤더 로드
- `mydb.php`: 데이터베이스 연결
- `fetch_unitprice.php`: 단가 조회 함수
- `_row.php`: 행 데이터 처리
- `_row_extra.php`: 추가 행 데이터 처리
### 🔗 **연관 파일**
- `lastJS.php`: 거래명세표 JavaScript
- `compare_lastJS.php`: 견적 비교 JavaScript
- `estimate_compare_head.php`: 견적 비교 헤더
- `common_screen.php`: 스크린 테이블 생성
- `common_slat.php`: 철재 테이블 생성
### 🔗 **CSS 클래스**
- `yellowBold`: 노란색 굵은 글씨
- `lightgray`: 연한 회색 배경
- `table-secondary`: 테이블 헤더 배경
- `w100px`: 100px 너비
### 🔗 **JavaScript 변수**
- `dataList`: 견적 데이터 배열
- `EstimateFirstSum`: 자동 견적금액
- `EstimateUpdatetSum`: 수정 견적금액
- `EstimateDiffer`: 견적 차액
- `EstimateFixAmount`: 견적확정액
- `EstimateDiscount`: 할인금액
- `EstimateFinalSum`: 최종 결정금액
- `EstimateDiscountRate`: 할인율
## 🎯 향후 개선 방향
### 🔄 **코드 리팩토링**
- 클래스 기반 구조로 변경
- 설정 파일 분리
- 의존성 주입 패턴 도입
### 🎨 **UI/UX 개선**
- 반응형 디자인 개선
- 다크 모드 지원
- 접근성 향상
### ⚡ **성능 최적화**
- 데이터베이스 쿼리 최적화
- 캐싱 시스템 도입
- 이미지 최적화
### 🔧 **기능 확장**
- 다국어 지원
- 템플릿 시스템 도입
- 실시간 업데이트
## 📊 거래명세표 구조
### 🎯 **거래명세표 구성 요소**
1. **헤더 정보**: 제목, 업체 정보
2. **금액 계산**: 자동/수정/차액/확정/할인/최종 금액
3. **할인율 적용**: 사용자 입력 할인율
4. **VAT 포함**: 부가세 포함 금액 표시
### 📈 **데이터 흐름**
1. **견적 번호 입력** → 데이터베이스 조회
2. **견적 정보 로드** → 변수에 할당
3. **추가 정보 조회** → output_extra 테이블
4. **JSON 데이터 디코딩** → JavaScript 변수로 전달
5. **헤더 출력** → HTML 렌더링
### 🔄 **금액 계산 프로세스**
1. **자동 견적금액** → 시스템 자동 계산
2. **수정 견적금액** → 사용자 수정
3. **견적 차액** → 수정금액 - 자동금액
4. **견적확정액** → 우선순위에 따른 확정
5. **할인금액** → 확정액 × 할인율
6. **최종 결정금액** → 확정액 - 할인금액
### 💰 **VAT 포함 계산**
- 거래명세표는 VAT 포함 금액으로 표시
- 견적서와 달리 부가세가 포함된 최종 금액
- 할인율 적용 후 VAT 계산
---
**📅 문서 버전**: 1.0
**👨‍💻 작성자**: 개발팀
**📝 최종 수정일**: 2024-12-24
**🔗 관련 문서**: [견적 시스템 전체 가이드](./README.md), [common_addrowJS 개발자 가이드](./common_addrowJS_developer_guide.md), [common_screen 개발자 가이드](./common_screen_developer_guide.md), [common_slat 개발자 가이드](./common_slat_developer_guide.md), [compare_lastJS 개발자 가이드](./compare_lastJS_developer_guide.md), [compare_price_edit_table 개발자 가이드](./compare_price_edit_table_developer_guide.md), [estimate_compare_head 개발자 가이드](./estimate_compare_head_developer_guide.md), [lastJS 개발자 가이드](./lastJS_developer_guide.md)