Files
wace_plm/WebContent/WEB-INF/view/salesmgmt/salesMgmt/transactionStatementForm.jsp
2025-11-13 17:52:46 +09:00

907 lines
22 KiB
Plaintext

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.pms.common.utils.*"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ page import="java.util.*" %>
<%@include file= "/init.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>거래명세서</title>
<style>
@media print {
.no-print { display: none !important; }
@page { margin: 5mm; }
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "맑은 고딕", "Malgun Gothic", sans-serif;
font-size: 11pt;
line-height: 1.4;
padding: 10px;
background-color: #f0f0f0;
}
.container {
width: 210mm;
height: 297mm;
margin: 0 auto;
padding: 0;
border: 2px solid #000;
background-color: white;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
overflow: hidden;
}
/* 제목 */
.header {
text-align: center;
font-size: 24pt;
font-weight: bold;
padding: 15px 0;
border-bottom: 2px solid #000;
letter-spacing: 8px;
}
/* 상단 정보 테이블 */
.info-table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
.info-table td {
border: 1px solid #000;
padding: 6px 10px;
font-size: 9pt;
vertical-align: middle;
}
.row-1 td, .row-2 td, .row-3 td, .row-4 td, .row-5 td {
height: 30px;
}
.date-cell {
text-align: left;
width: 30%;
padding: 8px 12px;
}
.date-label {
font-weight: bold;
margin-right: 6px;
font-size: 9pt;
}
.reg-label-cell {
background-color: #e8e8e8;
font-weight: bold;
text-align: center;
width: 10%;
font-size: 9pt;
}
.reg-value-cell {
font-size: 13pt;
font-weight: bold;
text-align: center;
}
.stamp-cell {
width: 15%;
text-align: center;
padding: 5px;
vertical-align: middle;
}
.stamp-box {
width: 70px;
height: 140px;
border: 1px solid #666;
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 9pt;
color: #333;
line-height: 1.4;
}
.date-label-cell {
background-color: #e8e8e8;
font-weight: bold;
text-align: center;
width: 8%;
font-size: 9pt;
padding: 6px;
}
.date-value-cell {
text-align: left;
padding: 6px 10px;
font-size: 9pt;
}
.receiver-cell {
text-align: center;
padding: 8px 12px;
width: 30%;
vertical-align: middle;
}
.receiver-name {
font-size: 13pt;
font-weight: bold;
display: inline;
margin-right: 8px;
}
.receiver-suffix {
font-size: 12pt;
font-weight: bold;
display: inline;
}
.supplier-label-cell {
background-color: #e8e8e8;
font-weight: bold;
text-align: center;
width: 10px;
line-height: 1.4;
font-size: 7pt;
padding: 0px;
vertical-align: middle;
letter-spacing: 0;
position: relative;
}
.supplier-field-label {
background-color: #e8e8e8;
font-weight: bold;
text-align: center;
width: 10%;
font-size: 9pt;
padding: 4px 2px;
word-break: keep-all;
}
.supplier-field-label-small {
background-color: #e8e8e8;
font-weight: bold;
text-align: center;
width: 7%;
font-size: 9pt;
padding: 4px 2px;
word-break: keep-all;
}
.supplier-field-value {
text-align: center;
font-size: 9pt;
padding: 4px 6px;
word-break: break-word;
overflow: visible;
white-space: normal;
}
.supplier-field-value-small {
text-align: center;
font-size: 9pt;
padding: 4px 6px;
word-break: break-word;
overflow: visible;
white-space: normal;
}
.supplier-row-1, .supplier-row-1-right, .supplier-row-2, .supplier-row-3, .supplier-row-4 {
text-align: left;
font-size: 9pt;
padding: 6px 10px;
}
.supplier-inline {
display: block;
}
.supplier-label-inline {
font-weight: bold;
margin-right: 8px;
margin-left: 15px;
padding-left: 15px;
border-left: 1px solid #666;
font-size: 8pt;
}
.supply-text-cell {
text-align: left;
font-size: 9pt;
padding: 8px 12px;
width: 30%;
}
/* 합계 금액 */
.amount-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 15px;
border-bottom: 2px solid #000;
background-color: #f9f9f9;
height: 40px;
}
.amount-label {
font-size: 10pt;
flex: 1;
}
.amount-text {
font-size: 13pt;
font-weight: bold;
flex: 2;
text-align: right;
}
.amount-won-symbol {
font-size: 16pt;
font-weight: bold;
flex: 0.5;
text-align: center;
}
.amount-number {
font-size: 15pt;
font-weight: bold;
flex: 2;
text-align: right;
}
/* 품목 테이블 */
.items-table {
width: 100%;
border-collapse: collapse;
}
.items-table th,
.items-table td {
border: 1px solid #000;
padding: 5px 8px;
font-size: 9pt;
text-align: center;
}
.items-table th {
background-color: #e8e8e8;
font-weight: bold;
height: 32px;
}
.items-table td {
height: 38px;
}
.items-table td[contenteditable="true"] {
background-color: #fffef0;
cursor: text;
}
.text-left {
text-align: left !important;
padding-left: 10px !important;
}
.text-right {
text-align: right !important;
padding-right: 10px !important;
}
.total-row {
background-color: #e8e8e8;
font-weight: bold;
}
/* 비고 */
.note-section {
border-top: 1px solid #000;
padding: 10px 15px;
min-height: 90px;
}
.note-title {
font-weight: bold;
font-size: 9pt;
margin-bottom: 6px;
}
.note-content {
padding: 3px;
min-height: 60px;
font-size: 9pt;
line-height: 1.5;
}
.note-content[contenteditable="true"] {
background-color: #fffef0;
cursor: text;
}
/* 하단 */
.footer-table {
width: 100%;
border-collapse: collapse;
margin-top: 40px;
margin-bottom: 0;
}
.footer-table td {
border: 1px solid #000;
padding: 8px 12px;
width: 50%;
vertical-align: top;
height: 60px;
}
.footer-label {
font-weight: bold;
font-size: 10pt;
margin-bottom: 25px;
}
.footer-sign {
text-align: right;
font-size: 10pt;
}
/* 버튼 */
.button-area {
text-align: center;
margin: 15px 0;
}
.btn {
padding: 10px 30px;
margin: 0 5px;
font-size: 14px;
cursor: pointer;
border: none;
border-radius: 3px;
}
.btn-print {
background-color: #2196F3;
color: white;
}
.btn-close {
background-color: #f44336;
color: white;
}
</style>
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
$(document).ready(function(){
var today = new Date();
var days = ['일', '월', '화', '수', '목', '금', '토'];
var dateStr = days[today.getDay()] + "요일, " + (today.getMonth() + 1) + "월 " + today.getDate() + ", " + today.getFullYear();
// 표시용 날짜 (한글)
$("#deliveryDate").text(dateStr);
// 전송용 날짜 (YYYY-MM-DD) - hidden input에 저장
var year = today.getFullYear();
var month = String(today.getMonth() + 1).padStart(2, '0');
var day = String(today.getDate()).padStart(2, '0');
var isoDate = year + "-" + month + "-" + day;
$("#deliveryDateISO").val(isoDate);
fn_loadData();
// 날짜 수정 시 ISO 형식도 업데이트
$("#deliveryDate").on('blur', function() {
var dateText = $(this).text().trim();
// 날짜 파싱 시도
try {
// "목요일, 11월 15, 2025" 형식 파싱
var match = dateText.match(/(\d+)월\s*(\d+),?\s*(\d{4})/);
if (match) {
var year = match[3];
var month = String(match[1]).padStart(2, '0');
var day = String(match[2]).padStart(2, '0');
var isoDate = year + "-" + month + "-" + day;
$("#deliveryDateISO").val(isoDate);
console.log("날짜 업데이트: " + dateText + " → " + isoDate);
}
} catch(e) {
console.error("날짜 파싱 실패:", e);
}
});
});
function fn_loadData() {
console.log("=== 거래명세서 데이터 로드 ===");
// localStorage에서 그리드 데이터 가져오기
var gridDataJson = localStorage.getItem('transactionStatementData');
if(!gridDataJson) {
alert("선택된 데이터가 없습니다.");
return;
}
var gridData = JSON.parse(gridDataJson);
console.log("그리드 데이터:", gridData);
// S/N 정보 수집 (비고에 표시) - 비동기 처리
fn_collectSerialNumbers(gridData, function(serialNumbers) {
// 그리드 데이터를 거래명세서 형식으로 변환
var firstItem = gridData[0];
var statementData = {
success: true,
customerName: firstItem.CUSTOMER,
supplierName: "㈜알피에스",
supplierRegNo: "314-81-75146",
supplierAddress: "대전광역시 유성구 국제과학10로 8",
supplierBusiness: "제조",
supplierType: "금속절삭가공기계의",
supplierContact: "TEL:042-602-3300/FAX:042-672",
note: "아래와 같이 공급합니다.",
serialNote: serialNumbers.join("\n"), // S/N 정보
items: []
};
// 각 그리드 행을 품목으로 변환
for(var i = 0; i < gridData.length; i++) {
var row = gridData[i];
statementData.items.push({
productName: row.PRODUCT_NAME,
spec: row.PRODUCT_NO,
quantity: row.SALES_QUANTITY || 0,
unitPrice: row.SALES_UNIT_PRICE || 0,
supplyPrice: row.SALES_SUPPLY_PRICE || 0,
vat: row.SALES_VAT || 0
});
}
console.log("=== 변환된 거래명세서 데이터 ===");
console.log(statementData);
// 데이터 채우기
fn_fillData(statementData);
});
// localStorage 정리
localStorage.removeItem('transactionStatementData');
}
// S/N 정보 수집 함수 (비동기)
function fn_collectSerialNumbers(gridData, callback) {
var serialNumbers = [];
var pendingRequests = 0;
var completed = false;
for(var i = 0; i < gridData.length; i++) {
var row = gridData[i];
if(row.SERIAL_NO && row.SERIAL_NO.trim() !== '') {
var serialNo = row.SERIAL_NO.trim();
// "외 N건" 형식인 경우 서버에서 모든 S/N 조회
if(serialNo.includes('외') && serialNo.includes('건')) {
pendingRequests++;
$.ajax({
url: '/salesMgmt/getAllSerialNumbers.do',
type: 'POST',
data: { projectNo: row.PROJECT_NO },
async: false, // 동기 처리로 변경
success: function(response) {
if(response.success && response.serialNumbers && response.serialNumbers.length > 0) {
// 모든 S/N을 쉼표로 구분하여 한 줄로 표시
var snText = "Spindle S/N: " + response.serialNumbers.join(", ");
serialNumbers.push(snText);
}
},
error: function() {
console.error("S/N 조회 실패");
},
complete: function() {
pendingRequests--;
}
});
} else {
// 단일 S/N 또는 쉼표로 구분된 여러 S/N
if(serialNo.includes(',')) {
// 쉼표로 구분된 경우 한 줄로 표시
serialNumbers.push("Spindle S/N: " + serialNo);
} else {
serialNumbers.push("Spindle S/N: " + serialNo);
}
}
}
}
// 모든 요청 완료 후 콜백 실행
callback(serialNumbers);
}
function fn_fillData(data) {
console.log("=== fn_fillData 호출 ===");
console.log("customerName:", data.customerName);
console.log("items:", data.items);
// 수신자 정보
if(data.customerName) {
$("#receiverName").text(data.customerName);
}
// 공급자 정보
if(data.supplierRegNo) {
$("#registrationNo").text(data.supplierRegNo);
}
if(data.supplierName) {
$("#supplierName").text(data.supplierName);
}
if(data.supplierAddress) {
$("#supplierAddr").text(data.supplierAddress);
}
if(data.supplierBusiness) {
$("#supplierBiz").text(data.supplierBusiness);
}
if(data.supplierType) {
$("#supplierType").text(data.supplierType);
}
if(data.supplierContact) {
$("#supplierTel").text(data.supplierContact);
}
// 공급 텍스트
if(data.note) {
$("#supplyText").text(data.note);
}
var totalSupply = 0;
var totalVat = 0;
var totalQuantity = 0;
var tbody = $("#itemsBody");
tbody.empty();
if(data.items && data.items.length > 0) {
console.log("품목 개수:", data.items.length);
$.each(data.items, function(i, item) {
console.log("품목 " + (i+1) + ":", item);
var quantity = parseInt(item.QUANTITY || item.quantity || 0);
var supply = parseInt(item.SUPPLYPRICE || item.supplyPrice || 0);
var vat = parseInt(item.VAT || item.vat || 0);
totalQuantity += quantity;
totalSupply += supply;
totalVat += vat;
var row = $("<tr>");
row.append($("<td contenteditable='true' class='text-left'>").text(item.PRODUCTNAME || item.productName || ""));
row.append($("<td contenteditable='true'>").text(item.SPEC || item.spec || ""));
row.append($("<td contenteditable='true'>").text(quantity));
row.append($("<td contenteditable='true' class='text-right'>").text(fn_num(item.UNITPRICE || item.unitPrice || 0)));
row.append($("<td contenteditable='true' class='text-right'>").text(fn_num(supply)));
row.append($("<td contenteditable='true' class='text-right'>").text(fn_num(vat)));
tbody.append(row);
});
// 빈 행 추가 (최소 9개 행 유지)
for(var i = data.items.length; i < 9; i++) {
var row = $("<tr>");
for(var j = 0; j < 6; j++) {
row.append($("<td contenteditable='true'>").html("&nbsp;"));
}
tbody.append(row);
}
} else {
console.log("품목 데이터가 없습니다.");
// 빈 행 9개 추가
for(var i = 0; i < 9; i++) {
var row = $("<tr>");
for(var j = 0; j < 6; j++) {
row.append($("<td contenteditable='true'>").html("&nbsp;"));
}
tbody.append(row);
}
}
// tfoot의 합계 행 업데이트
$("#totalQuantity").text(totalQuantity);
$("#totalSupplyPrice").text(fn_num(totalSupply));
$("#totalVat").text(fn_num(totalVat));
// 합계 금액
var total = totalSupply + totalVat;
console.log("총 공급가액:", totalSupply);
console.log("총 세액:", totalVat);
console.log("총 금액:", total);
$("#totalText").text(total.toString());
$("#totalNum").text(fn_num(total));
// 비고에 S/N 정보 표시
if(data.serialNote && data.serialNote.trim() !== '') {
$("#noteContent").text(data.serialNote);
console.log("비고에 S/N 표시:", data.serialNote);
}
// contenteditable 셀 변경 시 합계 자동 업데이트
fn_attachCellListeners();
}
// 품목 테이블의 셀 변경 감지 및 합계 재계산
function fn_attachCellListeners() {
// 품목 테이블의 모든 contenteditable 셀에 이벤트 리스너 추가
$("#itemsBody td[contenteditable='true']").off('blur').on('blur', function() {
fn_recalculateTotal();
});
}
// 합계 재계산 함수
function fn_recalculateTotal() {
console.log("=== 합계 재계산 ===");
var totalQuantity = 0;
var totalSupply = 0;
var totalVat = 0;
// 품목 테이블의 각 행을 순회하며 합계 계산
$("#itemsBody tr").each(function() {
var cells = $(this).find("td");
if(cells.length >= 6) {
// 수량 (3번째 열, index 2)
var quantityText = $(cells[2]).text().trim().replace(/,/g, '');
var quantity = parseInt(quantityText) || 0;
// 공급가액 (5번째 열, index 4)
var supplyText = $(cells[4]).text().trim().replace(/,/g, '');
var supply = parseInt(supplyText) || 0;
// 세액 (6번째 열, index 5)
var vatText = $(cells[5]).text().trim().replace(/,/g, '');
var vat = parseInt(vatText) || 0;
totalQuantity += quantity;
totalSupply += supply;
totalVat += vat;
}
});
console.log("재계산된 총 수량:", totalQuantity);
console.log("재계산된 총 공급가액:", totalSupply);
console.log("재계산된 총 세액:", totalVat);
// tfoot의 합계 행 업데이트
$("#totalQuantity").text(totalQuantity);
$("#totalSupplyPrice").text(fn_num(totalSupply));
$("#totalVat").text(fn_num(totalVat));
// 상단 합계 금액 업데이트
var total = totalSupply + totalVat;
$("#totalText").text(total.toString());
$("#totalNum").text(fn_num(total));
}
function fn_num(n) {
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function fn_save() {
if(!confirm("수정한 내용을 저장하시겠습니까?")) {
return;
}
// 수정된 데이터 수집
var data = {
projectNos: "${param.projectNos}",
deliveryDate: $("#deliveryDateISO").val(),
receiverName: $("#receiverName").text(),
registrationNo: $("#registrationNo").text(),
supplierName: $("#supplierName").text(),
supplierAddr: $("#supplierAddr").text(),
supplierBiz: $("#supplierBiz").text(),
supplierType: $("#supplierType").text(),
supplierTel: $("#supplierTel").text(),
supplyText: $("#supplyText").text(),
totalAmount: $("#totalText").text(),
noteContent: $("#noteContent").text(),
items: []
};
// 품목 데이터 수집
$("#itemsBody tr:not(.total-row)").each(function() {
var cells = $(this).find("td");
if(cells.length === 6) {
var item = {
productName: $(cells[0]).text().trim(),
spec: $(cells[1]).text().trim(),
quantity: $(cells[2]).text().trim(),
unitPrice: $(cells[3]).text().trim(),
supplyPrice: $(cells[4]).text().trim(),
vat: $(cells[5]).text().trim()
};
// 빈 행이 아닌 경우만 추가
if(item.productName || item.spec || item.quantity) {
data.items.push(item);
}
}
});
$.ajax({
url: "/salesMgmt/saveTransactionStatement.do",
type: "POST",
data: JSON.stringify(data),
contentType: "application/json",
dataType: "json",
success: function(response) {
if(response && response.success) {
alert("저장되었습니다.");
} else {
alert("저장 실패: " + (response.message || "알 수 없는 오류"));
}
},
error: function() {
alert("저장 중 오류가 발생했습니다.");
}
});
}
function fn_print() {
window.print();
}
function fn_close() {
window.close();
}
</script>
</head>
<body>
<div class="container">
<!-- 제목 -->
<div class="header">거래 명세서</div>
<!-- 상단 정보 테이블 -->
<table class="info-table">
<!-- 첫번째 행: 납품일 -->
<tr class="row-1">
<td class="date-label-cell">납품일</td>
<td class="date-value-cell" colspan="4">
<span id="deliveryDate" contenteditable="true">수요일, 9월 24, 2025</span>
<input type="hidden" id="deliveryDateISO" value="">
</td>
<td class="supplier-label-cell" rowspan="5">
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%;">
<div>공</div>
<div>급</div>
<div>자</div>
</div>
</td>
<td class="reg-label-cell">등록 번호</td>
<td class="reg-value-cell" colspan="4">
<span id="registrationNo" contenteditable="true">314-81-75146</span>
</td>
</tr>
<!-- 두번째 행: 수신자 + 상호/성명 -->
<tr class="row-2">
<td class="receiver-cell" rowspan="3" colspan="5">
<span class="receiver-name" contenteditable="true" id="receiverName">㈜OOO</span>
<span class="receiver-suffix">귀하</span>
</td>
<td class="supplier-field-label">상호(법인명)</td>
<td class="supplier-field-value">
<span contenteditable="true" id="supplierName">㈜알피에스</span>
</td>
<td class="supplier-field-label-small">성 명</td>
<td class="supplier-field-value-small" colspan="2">
<span contenteditable="true">이동헌</span>
<img src="/images/stamp_seal.png" alt="도장" style="width: 35px; height: 35px; margin-left: 10px; vertical-align: middle;">
</td>
</tr>
<!-- 세번째 행: 사업장주소 -->
<tr class="row-3">
<td class="supplier-field-label">사업장주소</td>
<td class="supplier-field-value" colspan="4">
<span contenteditable="true" id="supplierAddr">대전광역시 유성구 국제과학10로 8</span>
</td>
</tr>
<!-- 네번째 행: 업태/종목 -->
<tr class="row-4">
<td class="supplier-field-label">업 태</td>
<td class="supplier-field-value">
<span contenteditable="true" id="supplierBiz">제조</span>
</td>
<td class="supplier-field-label-small">종 목</td>
<td class="supplier-field-value-small" colspan="2">
<span contenteditable="true" id="supplierType">금속절삭가공기계외</span>
</td>
</tr>
<!-- 다섯번째 행: 아래와 같이 공급합니다 + 전화번호 -->
<tr class="row-5">
<td class="supply-text-cell" colspan="5">
<span contenteditable="true" id="supplyText">아래와 같이 공급합니다.</span>
</td>
<td class="supplier-field-label">전화 번호</td>
<td class="supplier-field-value" colspan="4">
<span contenteditable="true" id="supplierTel">TEL:042-602-3300 / FAX:042-672</span>
</td>
</tr>
</table>
<!-- 합계 금액 -->
<div class="amount-row">
<span class="amount-label">(공급가액+세액)</span>
<span class="amount-text"><span id="totalText" contenteditable="true">3300000</span> 원정</span>
<span class="amount-won-symbol">₩</span>
<span class="amount-won-symbol">₩</span>
<span class="amount-number"><span id="totalNum" contenteditable="true">3,300,000</span></span>
</div>
<!-- 품목 테이블 -->
<table class="items-table">
<thead>
<tr>
<th style="width: 25%;">품 명</th>
<th style="width: 15%;">규 격</th>
<th style="width: 10%;">수 량</th>
<th style="width: 15%;">단 가</th>
<th style="width: 17.5%;">공 급 가 액</th>
<th style="width: 17.5%;">세 액</th>
</tr>
</thead>
<tbody id="itemsBody">
</tbody>
<tfoot>
<tr class="note-row">
<td colspan="6" style="text-align: left; padding: 20px;">
<div style="font-weight: bold; margin-bottom: 5px;">&lt;비고&gt;</div>
<div contenteditable="true" id="noteContent"></div>
</td>
</tr>
<tr class="total-row">
<td colspan="2" style="text-align: center; font-weight: bold;">계</td>
<td id="totalQuantity" style="text-align: right; font-weight: bold;">0</td>
<td></td>
<td id="totalSupplyPrice" style="text-align: right; font-weight: bold;">0</td>
<td id="totalVat" style="text-align: right; font-weight: bold;">0</td>
</tr>
</tfoot>
</table>
<!-- 하단 -->
<table class="footer-table">
<tr>
<td>
<div class="footer-label">인계확인 :</div>
<div class="footer-sign">(인)</div>
</td>
<td>
<div class="footer-label">인수확인 :</div>
<div class="footer-sign">(인)</div>
</td>
</tr>
</table>
</div>
<!-- 버튼 -->
<div class="button-area no-print">
<button class="btn btn-print" onclick="fn_save()" style="background-color: #4CAF50;">저장</button>
<button class="btn btn-print" onclick="fn_print()">인쇄</button>
<button class="btn btn-close" onclick="fn_close()">닫기</button>
</div>
</body>
</html>