first commit 2

This commit is contained in:
hmw1001
2026-06-11 18:47:38 +09:00
parent c768729ce6
commit 6f534e33a6
11095 changed files with 1595758 additions and 0 deletions
@@ -0,0 +1,108 @@
-- 이 파일은 'order_form' 화면의 모든 UI 요소를 DB에 등록합니다.
ALTER TABLE `estimate_item` ADD `window_main_type` VARCHAR(255) NULL DEFAULT NULL AFTER `product`;
-- 1. 주소 및 집 정보
INSERT INTO `g5_ui_manager` (`screen_code`, `group_code`, `resource_code`, `resource_type`, `resource_desc`) VALUES
('order_form', 'address', 'address_label', 'LABEL', '주소 입력 섹션의 메인 라벨'),
('order_form', 'address', 'postcode_button', 'LABEL', '우편번호 찾기 버튼 텍스트'),
('order_form', 'house_info', 'house_type_label', 'LABEL', '집의 유형 라벨'),
('order_form', 'house_info', 'house_type_data', 'DATA', '집의 유형 선택 옵션'),
('order_form', 'house_info', 'house_size_label', 'LABEL', '평형 라벨');
-- 2. 공통 옵션
INSERT INTO `g5_ui_manager` (`screen_code`, `group_code`, `resource_code`, `resource_type`, `resource_desc`) VALUES
('order_form', 'common_options', 'material_label', 'LABEL', '창 재질 라벨'),
('order_form', 'common_options', 'material_data', 'DATA', '창 재질 선택 옵션'),
('order_form', 'common_options', 'color_label', 'LABEL', '창호 색상 라벨'),
('order_form', 'common_options', 'color_data', 'DATA', '창호 색상 선택 옵션'),
('order_form', 'common_options', 'glass_color_label', 'LABEL', '유리 사양(색상) 라벨'),
('order_form', 'common_options', 'glass_color_data', 'DATA', '유리 사양(색상) 선택 옵션'),
('order_form', 'common_options', 'glass_thick_label', 'LABEL', '유리 두께 라벨'),
('order_form', 'common_options', 'glass_thick_data', 'DATA', '유리 두께 선택 옵션'),
('order_form', 'common_options', 'construct_label', 'LABEL', '시공 여부 라벨'),
('order_form', 'common_options', 'construct_data', 'DATA', '시공 여부 선택 옵션');
-- 3. 액션 툴바 및 동적 폼
INSERT INTO `g5_ui_manager` (`screen_code`, `group_code`, `resource_code`, `resource_type`, `resource_desc`) VALUES
('order_form', 'action_toolbar', 'section_label', 'LABEL', '액션 툴바의 메인 라벨(9. 창 추가 및 관리)'),
('order_form', 'action_toolbar', 'add_window_button', 'LABEL', '창 추가하기 버튼'),
('order_form', 'action_toolbar', 'help_label', 'LABEL', '액션 툴바의 도움말 라벨'),
('order_form', 'action_toolbar', 'guide_button', 'LABEL', '창호선택가이드 버튼'),
('order_form', 'action_toolbar', 'tutorial_button', 'LABEL', '실측튜토리얼 버튼'),
('order_form', 'dynamic_window', 'window_type_data', 'DATA', '동적 창 폼의 창 종류 선택 옵션'),
('order_form', 'dynamic_window_template', 'window_label', 'LABEL', '동적 폼의 메인 라벨(창 종류 선택)'),
('order_form', 'dynamic_window_template', 'size_label', 'LABEL', '동적 폼의 규격 라벨'),
('order_form', 'dynamic_window_template', 'replace_label', 'LABEL', '동적 폼의 교체위치 라벨'),
('order_form', 'dynamic_window_template', 'replace_option_outer', 'LABEL', '동적 폼의 교체위치 옵션(외부창)'),
('order_form', 'dynamic_window_template', 'replace_option_inner', 'LABEL', '동적 폼의 교체위치 옵션(내창)'),
('order_form', 'dynamic_window_template', 'remarks_label', 'LABEL', '동적 폼의 비고 라벨'),
('order_form', 'dynamic_window_template', 'screen_notice', 'LABEL', '동적 폼의 방충망 관련 안내 문구');
-- 4. 하단 안내 문구
INSERT INTO `g5_ui_manager` (`screen_code`, `group_code`, `resource_code`, `resource_type`, `resource_desc`) VALUES
('order_form', 'footer_notice', 'main_notice', 'LABEL', '폼 하단의 파란색 박스 안내 문구');
-- 5. 플레이스홀더 (입력창 안내 문구)
INSERT INTO `g5_ui_manager` (`screen_code`, `group_code`, `resource_code`, `resource_type`, `resource_desc`) VALUES
('order_form', 'placeholders', 'zipcode', 'LABEL', '우편번호 입력창'),
('order_form', 'placeholders', 'address_main', 'LABEL', '기본주소 입력창'),
('order_form', 'placeholders', 'address_detail', 'LABEL', '상세주소 입력창'),
('order_form', 'placeholders', 'address_extra', 'LABEL', '참고항목 입력창'),
('order_form', 'placeholders', 'house_size', 'LABEL', '평형 입력창'),
('order_form', 'placeholders', 'size_width', 'LABEL', '동적 폼의 가로 사이즈 입력창'),
('order_form', 'placeholders', 'size_height', 'LABEL', '동적 폼의 세로 사이즈 입력창'),
('order_form', 'placeholders', 'remarks', 'LABEL', '동적 폼의 비고 입력창');
-- 이 파일은 'order_form' 화면에 새로운 UI 요소를 추가로 등록합니다.
-- 동적 창 템플릿에 '창호 형태'와 '창호 비율' 옵션 추가
INSERT INTO `g5_ui_manager` (`screen_code`, `group_code`, `resource_code`, `resource_type`, `resource_desc`) VALUES
('order_form', 'dynamic_window_template', 'window_form_label', 'LABEL', '동적 폼의 창호형태 라벨'),
('order_form', 'dynamic_window_template', 'window_form_data', 'DATA', '동적 폼의 창호형태 선택 옵션'),
('order_form', 'dynamic_window_template', 'window_ratio_label', 'LABEL', '동적 폼의 창비율 라벨'),
('order_form', 'dynamic_window_template', 'window_ratio_data', 'DATA', '동적 폼의 창비율 선택 옵션 (이미지 포함)');
-- 화장실/주방창, 터닝도어 전용 '유리' 옵션 추가
INSERT INTO `g5_ui_manager` (`screen_code`, `group_code`, `resource_code`, `resource_type`, `resource_desc`) VALUES
('order_form', 'dynamic_window_template', 'special_glass_label', 'LABEL', '특수창 전용 유리 라벨'),
('order_form', 'dynamic_window_template', 'special_glass_data', 'DATA', '특수창 전용 유리 선택 옵션');
-- 이 파일은 'order_form' 화면의 기존 리소스에 툴팁 설명을 추가합니다.
-- 6. 시정장치
INSERT INTO `g5_ui_manager` (`screen_code`, `group_code`, `resource_code`, `resource_type`, `resource_desc`) VALUES
('order_form', 'common_options', 'handle_label', 'LABEL', '시정장치 라벨'),
('order_form', 'common_options', 'handle_data', 'DATA', '시정장치 선택 옵션');
-- 사용자 지정 색상 입력창 플레이스홀더
INSERT INTO `g5_ui_manager` (`screen_code`, `group_code`, `resource_code`, `resource_type`, `resource_desc`) VALUES
('order_form', 'placeholders', 'custom_color', 'LABEL', '사용자 지정 색상 입력창');
-- 이 파일은 'order_form' 화면에 새로운 UI 요소를 추가로 등록합니다.
-- 동적 창 템플릿에 '창호 형태'와 '창호 비율' 옵션 추가
INSERT INTO `g5_ui_manager` (`screen_code`, `group_code`, `resource_code`, `resource_type`, `resource_desc`) VALUES
('order_form', 'dynamic_window_template', 'window_form_label', 'LABEL', '동적 폼의 창호형태 라벨'),
('order_form', 'dynamic_window_template', 'window_form_data', 'DATA', '동적 폼의 창호형태 선택 옵션'),
('order_form', 'dynamic_window_template', 'window_ratio_label', 'LABEL', '동적 폼의 창비율 라벨'),
('order_form', 'dynamic_window_template', 'window_ratio_data', 'DATA', '동적 폼의 창비율 선택 옵션 (이미지 포함)');
-- 화장실/주방창, 터닝도어 전용 '유리' 옵션 추가
INSERT INTO `g5_ui_manager` (`screen_code`, `group_code`, `resource_code`, `resource_type`, `resource_desc`) VALUES
('order_form', 'dynamic_window_template', 'special_glass_label', 'LABEL', '특수창 전용 유리 라벨'),
('order_form', 'dynamic_window_template', 'special_glass_data', 'DATA', '특수창 전용 유리 선택 옵션');
-- 동적 창 템플릿의 '창 종류 선택' 라벨에 툴팁 추가
UPDATE g5_common_lang
SET cl_description = '우리집과 비교해서 비슷한 곳을 선택하시면 됩니다.'
WHERE target_table = 'g5_ui_manager'
AND target_id = (SELECT um_id FROM g5_ui_manager WHERE screen_code = 'order_form' AND resource_code = 'window_label');
-- 동적 창 템플릿의 '기존창 규격' 라벨에 툴팁 추가
UPDATE g5_common_lang
SET cl_description = '창문 사이즈를 mm단위로 입력해주세요.'
WHERE target_table = 'g5_ui_manager'
AND target_id = (SELECT um_id FROM g5_ui_manager WHERE screen_code = 'order_form' AND resource_code = 'size_label');
-- 개별 설정 안내 툴팁 리소스 등록
INSERT INTO `g5_ui_manager` (`screen_code`, `group_code`, `resource_code`, `resource_type`, `resource_desc`)
VALUES ('order_form', 'dynamic_window_template', 'individual_setting_tooltip', 'LABEL', '동적 폼의 개별 설정 안내 툴팁')
ON DUPLICATE KEY UPDATE resource_desc = '동적 폼의 개별 설정 안내 툴팁';
INSERT INTO `g5_common_lang` (`target_table`, `target_id`, `lang_code`, `cl_name`, `cl_description`)
VALUES ('g5_ui_manager', LAST_INSERT_ID(), 'ko', '', '개별 설정시 \'\'를 참고 하세요')
ON DUPLICATE KEY UPDATE cl_description = '개별 설정시 \'\'를 참고 하세요';
@@ -0,0 +1,91 @@
<?php
include_once('../../../../../common.php'); // 💡 [수정] 그누보드 전체 환경을 로드하도록 수정
header('Content-Type: application/json');
// 1. 입력값 검증
$wr_id = isset($_POST['wr_id']) ? (int)$_POST['wr_id'] : 0;
// 💡 [수정] bo_table 값 받기
$bo_table = isset($_POST['bo_table']) ? preg_replace('/[^a-z0-9_]/i', '', $_POST['bo_table']) : '';
if (!$wr_id || !$bo_table) {
echo json_encode(['success' => false, 'message' => '잘못된 접근입니다. (파라미터 부족)']);
exit;
}
// 2. 권한 검증 (본인 또는 관리자만 삭제 가능)
// 💡 [수정] bo_table을 이용해 테이블명 생성
$write_table = $g5['write_prefix'] . $bo_table;
// 게시판 존재 여부 확인 (테이블 존재 여부 체크는 생략 가능하지만, 안전을 위해 bo_table 검증)
$board = get_board_db($bo_table);
if (!$board) {
echo json_encode(['success' => false, 'message' => '존재하지 않는 게시판입니다.']);
exit;
}
// 게시글 조회
$write = get_write($write_table, $wr_id);
if (!$write['wr_id']) {
echo json_encode(['success' => false, 'message' => '존재하지 않는 글입니다.']);
exit;
}
if (!$is_admin && $member['mb_id'] !== $write['mb_id']) {
echo json_encode(['success' => false, 'message' => '삭제할 권한이 없습니다.']);
exit;
}
// 3. DB 트랜잭션 시작
sql_query("START TRANSACTION");
try {
// 4. 삭제할 estimate 정보 조회
$estimate = sql_fetch("SELECT id FROM estimate WHERE wr_id = '{$wr_id}'");
if ($estimate) {
$estimate_id = $estimate['id'];
// 4-1. estimate_item 삭제
$sql_delete_items = "DELETE FROM estimate_item WHERE estimate_id = '{$estimate_id}'";
sql_query($sql_delete_items);
// 4-2. estimate_bidding 삭제 (이 글이 제안한 입찰 정보)
$sql_delete_bidding = "DELETE FROM estimate_bidding WHERE wr_id = '{$wr_id}'";
sql_query($sql_delete_bidding);
// 4-3. estimate (마스터) 삭제
$sql_delete_estimate = "DELETE FROM estimate WHERE id = '{$estimate_id}'";
sql_query($sql_delete_estimate);
}
// 5. 게시글 삭제
// 5-1. 댓글 삭제 (이 글에 달린 대댓글)
$sql_delete_comments = "DELETE FROM {$write_table} WHERE wr_parent = '{$wr_id}' AND wr_is_comment = 1";
sql_query($sql_delete_comments);
// 5-2. 게시글 본문 삭제
$sql_delete_write = "DELETE FROM {$write_table} WHERE wr_id = '{$wr_id}'";
sql_query($sql_delete_write);
// 5-3. 게시판 글 수 업데이트
// 답변글도 게시판 글 수에 포함되므로 1 감소시킴
$sql_update_board_count = "UPDATE g5_board SET bo_count_write = bo_count_write - 1 WHERE bo_table = '{$bo_table}'";
sql_query($sql_update_board_count);
// 5-4. 최신글 캐시 삭제
delete_cache_latest($bo_table);
// 6. 트랜잭션 커밋
sql_query("COMMIT");
echo json_encode(['success' => true, 'message' => '견적 제안이 성공적으로 삭제되었습니다.']);
} catch (Exception $e) {
// 7. 오류 발생 시 롤백
sql_query("ROLLBACK");
echo json_encode(['success' => false, 'message' => 'DB 처리 중 오류가 발생했습니다: ' . $e->getMessage()]);
}
exit;
?>
@@ -0,0 +1,235 @@
<?php
header("Content-Type: application/vnd.ms-excel");
header("Content-Disposition: attachment; filename=견적서_".date('Ymd_His').".xls");
header("Content-Description: PHP5 Generated Data");
header("Content-charset=utf-8");
include_once('./_common.php');
// 1. 견적서 기본 정보 가져오기
$wr_id = isset($_GET['wr_id']) ? intval($_GET['wr_id']) : 0;
if (!$wr_id) die('잘못된 접근입니다.');
// 🔧 estimate 테이블에서 데이터 가져오기
$sql = "SELECT * FROM estimate WHERE wr_id = '{$wr_id}' AND is_deleted = 0";
$row = sql_fetch($sql);
if (!$row) die('존재하지 않는 견적서입니다.');
// 🔧 게시글 정보도 가져오기
$write_table = $g5['write_prefix'] . 'order'; // bo_table이 order라고 가정
$write_data = sql_fetch("SELECT * FROM {$write_table} WHERE wr_id = '{$wr_id}'");
if (!$write_data) die('게시글 정보를 찾을 수 없습니다.');
// 2. 견적 항목 가져오기
$id = $row['id'];
$sql2 = "SELECT * FROM estimate_item WHERE estimate_id = '{$id}' AND is_deleted = 0 ORDER BY no ASC";
$result2 = sql_query($sql2);
$seller_company = "(주)하이플랫폼";
$seller_address = "주소 : 서울시 동작구 사당로 20바길 19, 401";
$seller_ceo = "대표 : 권 재 원";
$seller_phone = "TEL : 010-8000-6456";
// 헤더 정의 - order2.php와 동일한 순서
$headers = array("No", "집형태", "위치", "품명", "규격", "창재질", "색상", "유리두께", "유리색상", "유리", "창비율", "창호형태", "교체위치", "시정장치", "시공여부", "수량", "단가", "금액");
?>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
td,th {mso-number-format:'\@'; font-family:'맑은 고딕',Malgun Gothic;}
.title {font-size:2em; font-weight:900; text-align:center;}
.underbar { border-bottom:1.5pt solid #222; }
</style>
<table border="0" style="width:100%;margin-bottom:24px;">
<tr>
<td class="title" colspan="8" style="text-align:center;font-size:2em;font-weight:900;">견적 사양서</td>
</tr>
<tr>
<td></td>
</tr>
<tr>
<td style="width:40%;vertical-align:top;">
<table border="0" style="width:100%;">
<tr style="border-bottom:1px solid #222;">
<td colspan="2" style="width:100px;">업체명 :</td>
<td colspan="4" style="width:400px;">
<?=htmlspecialchars($row['company_name'] ?: '')?> 귀하
</td>
</tr>
<tr style="border-bottom:1px solid #222;">
<td colspan="2">현장명 :</td>
<td colspan="4" >
<?=htmlspecialchars($row['site_name'])?>
</td>
</tr>
<tr style="border-bottom:1px solid #222;">
<td colspan="2">견적일자 :</td>
<td colspan="4">
<?=htmlspecialchars($row['estimate_date'])?>
</td>
</tr>
</table>
</td>
<td style="width:20%" colspan="3"></td>
<td style="width:40%;vertical-align:top;">
<table border="0" style="width:100%;">
<tr><td colspan=5 style="font-weight:bold;"><?=$seller_company?></td></tr>
<tr><td colspan=5><?=$seller_address?></td></tr>
<tr><td colspan=5><?=$seller_ceo?> <img src="https://jmhc2021.cafe24.com/program/sign.png" style="height:30px;margin-bottom:-10px;"></td></tr>
<tr><td colspan=5><?=$seller_phone?></td></tr>
</table>
</td>
</tr>
<tr>
<td></td>
</tr>
</table>
<div>하기와 같이 견적합니다</div>
<table border="1" cellpadding="4" cellspacing="0">
<thead>
<tr>
<?php foreach($headers as $h) echo "<th style='background:#eee'>{$h}</th>"; ?>
</tr>
</thead>
<tbody>
<?php
$sum = 0;
$row_index = 1;
// order2.php와 일치하는 데이터 매핑
while($item = sql_fetch_array($result2)) {
echo "<tr>";
// No - 순서대로 번호 부여
echo "<td>{$row_index}</td>";
// 집형태 - 게시글의 wr_2에서 가져옴 또는 item의 house_type
$house_type = !empty($item['house_type']) ? $item['house_type'] : $write_data['wr_2'];
echo "<td>".htmlspecialchars($house_type ?: '')."</td>";
// 위치 (location 또는 windowsposion)
$location = !empty($item['location']) ? $item['location'] : (!empty($item['windowsposion']) ? $item['windowsposion'] : '');
echo "<td>".htmlspecialchars($location)."</td>";
// 품명
echo "<td>".htmlspecialchars($item['product'])."</td>";
// 규격 (폭 x 높이)
$width = !empty($item['spec_width']) ? $item['spec_width'] : (!empty($item['width']) ? $item['width'] : '');
$height = !empty($item['spec_height']) ? $item['spec_height'] : (!empty($item['height']) ? $item['height'] : '');
echo "<td style='width:100px'>".htmlspecialchars($width)." × ".htmlspecialchars($height)."</td>";
// 창재질 - 게시글의 wr_4에서 가져옴 또는 기본값
$window_material = !empty($item['window_material']) ? $item['window_material'] : $write_data['wr_4'];
echo "<td>".htmlspecialchars($window_material ?: 'PVC(폴리염화비닐)')."</td>";
// 색상
echo "<td style='width:50px'>".htmlspecialchars($item['color'])."</td>";
// 유리두께
echo "<td style='width:50px'>".htmlspecialchars($item['glass_thickness'])."</td>";
// 유리색상
echo "<td>".htmlspecialchars($item['glass_color'])."</td>";
// 유리 (복층유리/단층유리/삼중유리)
$glass = !empty($item['glass_type']) ? $item['glass_type'] : (!empty($item['glass']) ? $item['glass'] : '');
echo "<td>".htmlspecialchars($glass)."</td>";
// 창비율
echo "<td>".htmlspecialchars($item['windowRatio'])."</td>";
// 창호형태
echo "<td>".htmlspecialchars($item['windowType'])."</td>";
// 교체위치
echo "<td style='width:50px'>".htmlspecialchars($item['replacePart'])."</td>";
// 시정장치 (손잡이)
echo "<td>".htmlspecialchars($item['handle'])."</td>";
// 시공여부
echo "<td style='width:50px'>".htmlspecialchars($item['install'])."</td>";
// 수량
echo "<td style='width:30px'>".htmlspecialchars($item['qty'])."</td>";
// 단가
echo "<td style='text-align:right'>".number_format((int)$item['price'])."</td>";
// 금액
echo "<td style='text-align:right;font-weight:bold'>".number_format((int)$item['amount'])."</td>";
$sum += (int)$item['amount'];
$row_index++;
echo "</tr>";
}
?>
</tbody>
<tfoot>
<tr>
<td colspan="<?=count($headers)-1?>" style="text-align:center;font-weight:bold;background:#ccc">합계</td>
<td style='text-align:right;font-weight:bold;background:#ccc;color:#d9534f;font-size:1.1em;'><?=number_format($sum)?> 원</td>
</tr>
<tr>
<td colspan="<?=count($headers)?>" style="text-align:left;padding:15px;">
<strong>비고</strong><br>
** 시공비 견적은 창호전문상담가의 방문으로 가능합니다.<br>
** 본 견적서는 <?=date('Y년 m월 d일')?> 기준으로 작성되었습니다.<br>
** 견적 유효기간: 견적일로부터 30일<br>
** 시장 상황에 따라 가격이 변동될 수 있습니다.
</td>
</tr>
</tfoot>
</table>
<!-- 추가 정보 테이블 -->
<table border="1" cellpadding="8" cellspacing="0" style="margin-top:30px;width:100%;">
<tr style="background:#f8f9fa;">
<th colspan="4" style="text-align:center;font-size:1.2em;font-weight:bold;">견적 요청 정보</th>
</tr>
<tr>
<th style="width:15%;background:#e9ecef;">고객명</th>
<td style="width:35%;"><?=htmlspecialchars($write_data['wr_name'] ?: '')?></td>
<th style="width:15%;background:#e9ecef;">연락처</th>
<td style="width:35%;">***-****-****</td>
</tr>
<tr>
<th style="background:#e9ecef;">주소</th>
<td colspan="3">
<?php
// 개인정보 보호를 위한 주소 마스킹
$address = ($write_data['wr_6'] ?: '') . ' ' . ($write_data['wr_7'] ?: '');
if (strlen(trim($address)) > 10) {
echo substr($address, 0, 10) . '***';
} else {
echo '***';
}
?>
</td>
</tr>
<tr>
<th style="background:#e9ecef;">요청사항</th>
<td colspan="3">
<?php
$content = strip_tags($write_data['wr_content'] ?: '');
// 개인정보 필터링
$content = preg_replace('/([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/', '***@***.***', $content);
$content = preg_replace('/(\d{2,3})-?(\d{3,4})-?(\d{4})/', '***-****-****', $content);
echo htmlspecialchars(mb_substr($content, 0, 200)) . (mb_strlen($content) > 200 ? '...' : '');
?>
</td>
</tr>
</table>
<div style="margin-top:30px;padding:15px;border:2px solid #007bff;background:#f8f9ff;">
<h4 style="color:#007bff;margin:0 0 10px 0;">📋 견적처 정보</h4>
<p style="margin:0;line-height:1.6;">
<strong><?=$seller_company?></strong><br>
<?=$seller_address?><br>
<?=$seller_ceo?><br>
<?=$seller_phone?><br>
<strong>견적일자:</strong> <?=htmlspecialchars($row['estimate_date'])?><br>
<strong>견적번호:</strong> EST-<?=str_pad($wr_id, 6, '0', STR_PAD_LEFT)?>
</p>
</div>
@@ -0,0 +1,137 @@
<?php
ob_clean(); // Clear any accidental output buffering before JSON response
include_once('../../../common.php');
include_once(G5_ADMIN_PATH . '/order_manage/classes/EstimateManager.class.php');
// 1. 입력 값 검증 및 파라미터 받기
$target_wr_id = isset($_POST['wr_id']) ? (int)$_POST['wr_id'] : 0;
$new_status = isset($_POST['wr_1']) ? trim($_POST['wr_1']) : ''; // wr_1 is the new status for the post
$bo_table = isset($_POST['bo_table']) ? trim($_POST['bo_table']) : '';
if (!$target_wr_id || !$new_status || !$bo_table) {
die(json_encode(['status' => 'error', 'message' => '필수 정보가 누락되었습니다.']));
}
$estimateManager = new EstimateManager();
$result = false;
$message = '처리 실패';
// 2. 대상 게시물 정보 조회 (g5_write_ 테이블)
global $g5; // $g5 변수를 전역으로 선언
$write_table = $g5['write_prefix'] . $bo_table;
$target_post = sql_fetch("SELECT * FROM `{$write_table}` WHERE wr_id = '{$target_wr_id}'");
if (!$target_post) {
die(json_encode(['status' => 'error', 'message' => '존재하지 않는 게시물입니다.']));
}
// 3. 사용자 역할 정의
$current_user_level = $member['mb_level'] ?? 0;
$is_admin = ($current_user_level >= 8); // 8:직원, 9:부관리자, 10:관리자
$is_agent = false; // 💡 [추가] 대리점 기능 비활성화
try {
// Determine if it's an original post or a reply
$is_reply_post = (!empty($target_post['wr_reply'])); // Check wr_reply for threaded replies
if ($is_reply_post) {
// Action on a reply post (e.g., '견적채택')
$origin_wr_id = $target_post['wr_parent']; // wr_parent is the wr_id of the original post
$origin_wr_num = $target_post['wr_num']; // wr_num is the wr_num
$origin_estimate = sql_fetch("SELECT * FROM `estimate` WHERE wr_id = '{$origin_wr_id}' AND is_deleted = 0");
if (!$origin_estimate) {
die(json_encode(['status' => 'error', 'message' => '원본 견적서를 찾을 수 없습니다.']));
}
$is_origin_owner = ($member['mb_id'] && $member['mb_id'] === $origin_estimate['created_by']);
if ($new_status === '견적채택') {
if (!$is_admin && !$is_origin_owner) {
die(json_encode(['status' => 'error', 'message' => '견적 채택 권한이 없습니다.']));
}
// 💡 [수정] 대리점 관련 로직은 실행되지 않도록 막거나, 필요 없으면 삭제
if ($is_agent) { // 대리점 기능 비활성화로 인해 이 블록은 실행되지 않음
die(json_encode(['status' => 'error', 'message' => '대리점은 견적 채택을 할 수 없습니다.']));
}
// Start transaction
sql_query("START TRANSACTION");
// 1. Update selected reply's wr_1 to '견적채택'
$result = sql_query("UPDATE `{$write_table}` SET wr_1 = '견적채택' WHERE wr_id = '{$target_wr_id}'");
if (!$result) throw new Exception("선택된 답변 상태 업데이트 실패.");
// 2. Update other replies for the same origin post to '견적취소'
$result = sql_query("UPDATE `{$write_table}` SET wr_1 = '견적취소' WHERE wr_num = '{$origin_wr_num}' AND wr_id != '{$target_wr_id}'");
if (!$result) throw new Exception("다른 답변 상태 업데이트 실패.");
// 3. Update original estimate status to '입금예정'
$result = $estimateManager->updateEstimateStatus($origin_estimate['id'], '입금예정');
if (!$result) throw new Exception("원본 견적 상태 업데이트 실패.");
// 4. Update estimate_bidding table
// Find the estimate_bidding entry for the selected reply
$selected_bidding = sql_fetch("SELECT id FROM `estimate_bidding` WHERE wr_id = '{$target_wr_id}' AND estimate_id = '{$origin_estimate['id']}'");
if ($selected_bidding) {
$result = sql_query("UPDATE `estimate_bidding` SET status = 'selected' WHERE id = '{$selected_bidding['id']}'");
if (!$result) throw new Exception("선택된 입찰 상태 업데이트 실패.");
}
// Update other bids for the same estimate to 'unselected'
$result = sql_query("UPDATE `estimate_bidding` SET status = 'unselected' WHERE estimate_id = '{$origin_estimate['id']}' AND wr_id != '{$target_wr_id}'");
if (!$result) throw new Exception("다른 입찰 상태 업데이트 실패.");
sql_query("COMMIT");
$message = '견적이 성공적으로 채택되었습니다.';
} else {
// Admin can change reply status (e.g., revert from '견적채택' for testing/correction)
if ($is_admin) {
$result = sql_query("UPDATE `{$write_table}` SET wr_1 = '{$new_status}' WHERE wr_id = '{$target_wr_id}'");
if (!$result) throw new Exception("답변 상태 변경 실패.");
$message = '답변 상태가 성공적으로 변경되었습니다.';
} else {
$message = '답변 상태 변경 권한이 없습니다.';
}
}
} else {
// Action on an original post (e.g., admin changing status)
$origin_estimate = sql_fetch("SELECT * FROM `estimate` WHERE wr_id = '{$target_wr_id}' AND is_deleted = 0");
if (!$origin_estimate) {
die(json_encode(['status' => 'error', 'message' => '원본 견적서를 찾을 수 없습니다.']));
}
$is_origin_owner = ($member['mb_id'] && $member['mb_id'] === $origin_estimate['created_by']);
// Admin can change status, including reverting from '입금예정'
if ($is_admin) {
$result = $estimateManager->updateEstimateStatus($origin_estimate['id'], $new_status);
$message = $result ? '원본 견적 상태가 성공적으로 변경되었습니다.' : '원본 견적 상태 변경에 실패했습니다.';
}
// Customer can change status from '견적신청중' to '작성완료' (if applicable, though list.php handles this via link)
elseif ($is_origin_owner && $new_status === '작성완료' && $origin_estimate['status'] === '견적신청중') {
$result = $estimateManager->updateEstimateStatus($origin_estimate['id'], $new_status);
$message = $result ? '원본 견적 상태가 성공적으로 변경되었습니다.' : '원본 견적 상태 변경에 실패했습니다.';
}
else {
$message = '원본 견적 상태 변경 권한이 없습니다.';
}
}
} catch (Exception $e) {
sql_query("ROLLBACK"); // Rollback on error
$message = '오류 발생: ' . $e->getMessage();
error_log("estimate_select.php 오류: " . $e->getMessage());
}
// 4. 결과 반환
if ($result) {
echo json_encode(['status' => 'success', 'message' => $message]);
} else {
echo json_encode(['status' => 'error', 'message' => $message]);
}
exit;
@@ -0,0 +1,45 @@
<?php
include_once('./_common.php');
// JSON 응답 헤더 설정
header('Content-Type: application/json');
// POST 요청 확인
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(['success' => false, 'message' => '잘못된 요청입니다.']);
exit;
}
// ReservationManager 클래스 로드
if (!file_exists(G5_ADMIN_PATH . '/consultant_manage/classes/ReservationManager.class.php')) {
echo json_encode(['success' => false, 'message' => '예약 관리 시스템을 찾을 수 없습니다.']);
exit;
}
require_once(G5_ADMIN_PATH . '/consultant_manage/classes/ReservationManager.class.php');
require_once(G5_ADMIN_PATH . '/consultant_manage/functions.php'); // 헬퍼 함수 로드
try {
$reservationManager = new ReservationManager();
// 데이터 준비
$data = [
'customer_name' => $member['mb_name'] ?? '비회원', // 로그인한 경우 회원 이름 사용
'customer_phone' => $_POST['consultation_phone'],
'customer_email' => $member['mb_email'] ?? '',
'reservation_date' => substr($_POST['consultation_date'], 0, 10),
'reservation_time' => substr($_POST['consultation_date'], 11, 5),
'request_memo' => $_POST['consultation_content'],
'wr_id' => 0, // 게시글 ID가 있다면 연동 가능
'consultation_type' => 'expert_visit', // 전문가 방문 상담
'resource_id' => !empty($_POST['consultation_resource']) ? (int)$_POST['consultation_resource'] : null // 💡 [추가] 리소스 ID 저장
];
// 예약 생성
$result = $reservationManager->createReservation($data);
echo json_encode($result);
} catch (Exception $e) {
echo json_encode(['success' => false, 'message' => '서버 오류: ' . $e->getMessage()]);
}
@@ -0,0 +1,19 @@
<div class="col-lg-12" style="background:#fff;border:1px solid #ddd;">
<font color="#00f"><i class="xi-touch"></i> <strong>창호 선택 가이드</strong></font>
<div class="col-lg-12 col-sm-12">
<div class="form-group">
<span class="tip-mark" data-tip="아파트평면도명칭">※아파트평면도 명칭</span>
<span class="tip-mark" data-tip="아파트확장형명칭">※아파트확장형 명칭</span>
<span class="tip-mark" data-tip="창호형태">※창호형태</span>
<span class="tip-mark" data-tip="창비율">※창비율</span>
<span class="tip-mark" data-tip="방충망">※방충망</span>
<span class="tip-mark" data-tip="창호색상">※창호색상</span>
<span class="tip-mark" data-tip="교체위치">※교체위치</span>
<span class="tip-mark" data-tip="유리사양">※유리사양</span>
<span class="tip-mark" data-tip="유리두께">※유리두께</span>
<span class="tip-mark" data-tip="시정장치">※시정장치</span>
<!-- ID를 guide-tip-desc로 지정하여 order.php에서 제어합니다. -->
<div id="guide-tip-desc" class="tip-desc-box"><br><center> 글씨를 클릭하면 설명글과 이미지를 볼수 있어요</center><br></div>
</div>
</div>
</div>
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8" ?>
<dwsync>
<file name="btn_close.gif" server="happyjung.com:5001/www/" local="131979082007552031" remote="131980119000000000" Dst="0" />
<file name="bull_1.gif" server="happyjung.com:5001/www/" local="131979082006962424" remote="131980119000000000" Dst="0" />
<file name="bull_10.gif" server="happyjung.com:5001/www/" local="131979082006772542" remote="131980119000000000" Dst="0" />
<file name="bull_11.gif" server="happyjung.com:5001/www/" local="131979082006232877" remote="131980119000000000" Dst="0" />
<file name="bull_12.gif" server="happyjung.com:5001/www/" local="131979082005803143" remote="131980119000000000" Dst="0" />
<file name="bull_13.gif" server="happyjung.com:5001/www/" local="131979082005253594" remote="131980119000000000" Dst="0" />
<file name="bull_14.gif" server="happyjung.com:5001/www/" local="131979082004743798" remote="131980119000000000" Dst="0" />
<file name="bull_15.gif" server="happyjung.com:5001/www/" local="131979082003824369" remote="131980119000000000" Dst="0" />
<file name="bull_16.gif" server="happyjung.com:5001/www/" local="131979082003684453" remote="131980119000000000" Dst="0" />
<file name="bull_17.gif" server="happyjung.com:5001/www/" local="131979082003164776" remote="131980119000000000" Dst="0" />
<file name="bull_18.gif" server="happyjung.com:5001/www/" local="131979082002515176" remote="131980119000000000" Dst="0" />
<file name="bull_19.gif" server="happyjung.com:5001/www/" local="131979082002055462" remote="131980119000000000" Dst="0" />
<file name="bull_2.gif" server="happyjung.com:5001/www/" local="131979082001565747" remote="131980119000000000" Dst="0" />
<file name="bull_20.gif" server="happyjung.com:5001/www/" local="131979082001026095" remote="131980119000000000" Dst="0" />
<file name="bull_21.gif" server="happyjung.com:5001/www/" local="131979082000586371" remote="131980119000000000" Dst="0" />
<file name="bull_22.gif" server="happyjung.com:5001/www/" local="131979082000106668" remote="131980119000000000" Dst="0" />
<file name="bull_23.gif" server="happyjung.com:5001/www/" local="131979081999616945" remote="131980119000000000" Dst="0" />
<file name="bull_24.gif" server="happyjung.com:5001/www/" local="131979081998767473" remote="131980119000000000" Dst="0" />
<file name="bull_25.gif" server="happyjung.com:5001/www/" local="131979081998677552" remote="131980119000000000" Dst="0" />
<file name="bull_26.gif" server="happyjung.com:5001/www/" local="131979081998177863" remote="131980119000000000" Dst="0" />
<file name="bull_27.gif" server="happyjung.com:5001/www/" local="131979081997628202" remote="131980119000000000" Dst="0" />
<file name="bull_28.gif" server="happyjung.com:5001/www/" local="131979081997108523" remote="131980119000000000" Dst="0" />
<file name="bull_29.gif" server="happyjung.com:5001/www/" local="131979081996478914" remote="131980119000000000" Dst="0" />
<file name="bull_3.gif" server="happyjung.com:5001/www/" local="131979081996059173" remote="131980119000000000" Dst="0" />
<file name="bull_30-1.gif" server="happyjung.com:5001/www/" local="131979081995559497" remote="131980119000000000" Dst="0" />
<file name="bull_30.gif" server="happyjung.com:5001/www/" local="131979081995109763" remote="131980119000000000" Dst="0" />
<file name="bull_4.gif" server="happyjung.com:5001/www/" local="131979081994650045" remote="131980119000000000" Dst="0" />
<file name="bull_5.gif" server="happyjung.com:5001/www/" local="131979081994130368" remote="131980119000000000" Dst="0" />
<file name="bull_6.gif" server="happyjung.com:5001/www/" local="131979081993300882" remote="131980119000000000" Dst="0" />
<file name="bull_7.gif" server="happyjung.com:5001/www/" local="131979081993250910" remote="131980119000000000" Dst="0" />
<file name="bull_8.gif" server="happyjung.com:5001/www/" local="131979081992281511" remote="131980119000000000" Dst="0" />
<file name="bull_9.gif" server="happyjung.com:5001/www/" local="131979081992191568" remote="131980119000000000" Dst="0" />
<file name="bull_a.gif" server="happyjung.com:5001/www/" local="131979081991262143" remote="131980119000000000" Dst="0" />
<file name="cal.gif" server="happyjung.com:5001/www/" local="131979081991202152" remote="131980119000000000" Dst="0" />
<file name="icon.gif" server="happyjung.com:5001/www/" local="131979081990362720" remote="131980119000000000" Dst="0" />
<file name="icon_file.gif" server="happyjung.com:5001/www/" local="131979081990302736" remote="131980119000000000" Dst="0" />
<file name="icon_hot.gif" server="happyjung.com:5001/www/" local="131979081989483215" remote="131980119000000000" Dst="0" />
<file name="icon_img.gif" server="happyjung.com:5001/www/" local="131979081989433276" remote="131980119000000000" Dst="0" />
<file name="icon_link.gif" server="happyjung.com:5001/www/" local="131979081988553808" remote="131980119000000000" Dst="0" />
<file name="icon_mobile.gif" server="happyjung.com:5001/www/" local="131979081988503855" remote="131980119000000000" Dst="0" />
<file name="icon_movie.gif" server="happyjung.com:5001/www/" local="131979081987634370" remote="131980119000000000" Dst="0" />
<file name="icon_new.gif" server="happyjung.com:5001/www/" local="131979081987584419" remote="131980119000000000" Dst="0" />
<file name="icon_reply.gif" server="happyjung.com:5001/www/" local="131979081986765031" remote="131980119000000000" Dst="0" />
<file name="icon_secret.gif" server="happyjung.com:5001/www/" local="131979081986715074" remote="131980119000000000" Dst="0" />
<file name="icon_sound.gif" server="happyjung.com:5001/www/" local="131979081985855489" remote="131980119000000000" Dst="0" />
<file name="m_next.gif" server="happyjung.com:5001/www/" local="131979081985805519" remote="131980119000000000" Dst="0" />
<file name="m_prev.gif" server="happyjung.com:5001/www/" local="131979081985035998" remote="131980119000000000" Dst="0" />
<file name="point.gif" server="happyjung.com:5001/www/" local="131979081984896083" remote="131980119000000000" Dst="0" />
<file name="y_next.gif" server="happyjung.com:5001/www/" local="131979081984436368" remote="131980119000000000" Dst="0" />
<file name="y_prev.gif" server="happyjung.com:5001/www/" local="131979081983207101" remote="131980119000000000" Dst="0" />
</dwsync>
Binary file not shown.

After

Width:  |  Height:  |  Size: 874 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 874 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 908 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 909 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Some files were not shown because too many files have changed in this diff Show More