1044 lines
43 KiB
Plaintext
1044 lines
43 KiB
Plaintext
<%@ page language="java" contentType="text/html; charset=UTF-8"
|
|
pageEncoding="UTF-8"%>
|
|
<%@ page import="com.pms.common.utils.*"%>
|
|
<%@ page import="java.util.*"%>
|
|
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
|
|
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
|
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
|
|
<%@include file="/init_new.jsp"%>
|
|
<%
|
|
PersonBean person = (PersonBean) session.getAttribute(Constants.PERSON_BEAN);
|
|
String userId = CommonUtils.checkNull(person.getUserId());
|
|
%>
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
<title><%=Constants.SYSTEM_NAME%></title>
|
|
|
|
<script type="text/javascript">
|
|
// S/N 관리 전역 변수
|
|
var snList = [];
|
|
var snCounter = 1;
|
|
|
|
$(function() {
|
|
console.log("=== salesRegForm.jsp 로드 ===");
|
|
console.log("SALES_QUANTITY: ${saleInfo.SALES_QUANTITY}");
|
|
console.log("SALES_UNIT_PRICE: ${saleInfo.SALES_UNIT_PRICE}");
|
|
console.log("SALES_SUPPLY_PRICE: ${saleInfo.SALES_SUPPLY_PRICE}");
|
|
console.log("SALES_VAT: ${saleInfo.SALES_VAT}");
|
|
console.log("SALES_TOTAL_AMOUNT: ${saleInfo.SALES_TOTAL_AMOUNT}");
|
|
console.log("SALES_CURRENCY: ${saleInfo.SALES_CURRENCY}");
|
|
console.log("SALES_EXCHANGE_RATE: ${saleInfo.SALES_EXCHANGE_RATE}");
|
|
|
|
$('.select2').select2();
|
|
|
|
// 날짜 선택기 초기화
|
|
_fnc_datepick();
|
|
|
|
// 출하일: 저장된 값이 없으면 오늘 날짜 설정
|
|
if(!$("#shippingDate").val() || $("#shippingDate").val().trim() === '') {
|
|
var today = new Date();
|
|
var yyyy = today.getFullYear();
|
|
var mm = String(today.getMonth() + 1).padStart(2, '0');
|
|
var dd = String(today.getDate()).padStart(2, '0');
|
|
$("#shippingDate").val(yyyy + '-' + mm + '-' + dd);
|
|
}
|
|
|
|
// 기존 담당자 값 설정
|
|
var managerValue = "${saleInfo.MANAGER}";
|
|
console.log("MANAGER 값:", managerValue);
|
|
console.log("SHIPPING_METHOD:", "${saleInfo.SHIPPING_METHOD}");
|
|
console.log("INCOTERMS:", "${saleInfo.INCOTERMS}");
|
|
console.log("SERIAL_NO:", "${saleInfo.SERIAL_NO}");
|
|
if(managerValue) {
|
|
$("#manager").val(managerValue).trigger('change');
|
|
console.log("담당자 설정 후 선택값:", $("#manager").val());
|
|
}
|
|
|
|
// 판매환종 초기값 설정 (견적환종과 동기화)
|
|
initializeSalesCurrency();
|
|
|
|
// S/N 필드 클릭 이벤트
|
|
$("#serialNo").click(function() {
|
|
fn_openSnManagePopup();
|
|
});
|
|
|
|
// 페이지 로드 시 기존 S/N 데이터가 있으면 파싱하여 hidden 필드에 저장
|
|
var initialSnValue = $("#serialNo").val();
|
|
if(initialSnValue && initialSnValue.trim() != '') {
|
|
var snArray = initialSnValue.split(',');
|
|
var initialSnList = [];
|
|
for(var i = 0; i < snArray.length; i++) {
|
|
if(snArray[i].trim() != '') {
|
|
initialSnList.push({
|
|
id: i + 1,
|
|
value: snArray[i].trim()
|
|
});
|
|
}
|
|
}
|
|
if(initialSnList.length > 0) {
|
|
$("#serialNoList").val(JSON.stringify(initialSnList));
|
|
snCounter = initialSnList.length + 1;
|
|
console.log("초기 S/N 데이터 로드:", initialSnList);
|
|
}
|
|
}
|
|
|
|
// 닫기 버튼
|
|
$("#btnClose").click(function() {
|
|
self.close();
|
|
});
|
|
|
|
// 저장 버튼
|
|
$("#btnSave").click(function() {
|
|
fn_save();
|
|
});
|
|
|
|
// 품목이 여러 개인 경우 자동으로 첫 번째 품목 선택 - 주석처리: 품목은 하나만 존재
|
|
/*
|
|
if($(".item-radio").length > 0) {
|
|
$(".item-radio").first().prop("checked", true);
|
|
fn_calculateSelectedItem();
|
|
}
|
|
*/
|
|
});
|
|
|
|
// === Phase 2: 품목 선택 관련 함수 (단일 선택) - 주석처리: 품목은 하나만 존재 ===
|
|
/*
|
|
// 행 클릭 시 라디오 버튼 선택
|
|
function fn_selectItem(itemObjid) {
|
|
$(".item-radio[value='" + itemObjid + "']").prop("checked", true);
|
|
fn_calculateSelectedItem();
|
|
}
|
|
|
|
// 선택한 품목의 정보를 입력 필드에 반영
|
|
function fn_calculateSelectedItem() {
|
|
var selectedRadio = $(".item-radio:checked");
|
|
|
|
if(selectedRadio.length === 0) {
|
|
$("#selectedItemInfo").text("품목을 선택하세요");
|
|
return;
|
|
}
|
|
|
|
var quantity = parseFloat(selectedRadio.data("quantity")) || 0;
|
|
var unitPrice = parseFloat(selectedRadio.data("unit-price")) || 0;
|
|
var supplyPrice = parseFloat(selectedRadio.data("supply-price")) || 0;
|
|
var vat = parseFloat(selectedRadio.data("vat")) || 0;
|
|
var totalAmount = parseFloat(selectedRadio.data("total")) || 0;
|
|
|
|
// 선택한 품목 정보 표시
|
|
var itemObjid = selectedRadio.val();
|
|
var itemRow = $("tr[data-item-objid='" + itemObjid + "']");
|
|
var partNo = itemRow.find("td:eq(1)").text().trim();
|
|
var partName = itemRow.find("td:eq(2)").text().trim();
|
|
|
|
$("#selectedItemInfo").html(
|
|
"<strong>" + partName + "</strong> (" + partNo + ") - " +
|
|
"수량: <strong>" + quantity.toLocaleString() + "</strong>개 | " +
|
|
"공급가액: <strong>" + supplyPrice.toLocaleString() + "</strong>원"
|
|
);
|
|
|
|
// 입력 필드에 자동 반영
|
|
$("#salesQuantity").val(quantity);
|
|
$("#salesUnitPrice").val(unitPrice);
|
|
$("#salesSupplyPrice").val(supplyPrice);
|
|
$("#salesVat").val(vat);
|
|
$("#salesTotalAmount").val(totalAmount);
|
|
|
|
// hidden 필드에 선택한 품목 OBJID 저장
|
|
if($("#selectedItemObjid").length === 0) {
|
|
$("<input>").attr({
|
|
type: "hidden",
|
|
id: "selectedItemObjid",
|
|
name: "selectedItemObjid"
|
|
}).appendTo("form");
|
|
}
|
|
$("#selectedItemObjid").val(itemObjid);
|
|
}
|
|
*/
|
|
|
|
// === 기존 함수들 ===
|
|
|
|
// 판매공급가액 계산 함수
|
|
function fn_calculateSupplyPrice() {
|
|
var currency = $("#salesCurrency").val();
|
|
var exchangeRate = parseFloat($("#salesExchangeRate").val()) || 1;
|
|
var quantity = parseFloat($("#salesQuantity").val()) || 0;
|
|
var unitPrice = parseFloat($("#salesUnitPrice").val()) || 0;
|
|
|
|
// 외화 기준 금액 계산
|
|
var foreignSupplyPrice = quantity * unitPrice;
|
|
|
|
// KRW가 아니고 환율이 있으면 환율 적용
|
|
if(currency && currency !== 'KRW' && exchangeRate > 0) {
|
|
var krwSupplyPrice = Math.round(foreignSupplyPrice * exchangeRate);
|
|
$("#salesSupplyPrice").val(krwSupplyPrice);
|
|
} else {
|
|
$("#salesSupplyPrice").val(foreignSupplyPrice);
|
|
}
|
|
|
|
fn_calculateVat();
|
|
}
|
|
|
|
// 부가세 계산 함수
|
|
function fn_calculateVat() {
|
|
var supplyPrice = parseFloat($("#salesSupplyPrice").val()) || 0;
|
|
var vat = Math.round(supplyPrice * 0.1);
|
|
$("#salesVat").val(vat);
|
|
fn_calculateTotalAmount();
|
|
}
|
|
|
|
// 판매총액 계산 함수 (공급가액 + 부가세)
|
|
function fn_calculateTotalAmount() {
|
|
var supplyPrice = parseFloat($("#salesSupplyPrice").val()) || 0;
|
|
var vat = parseFloat($("#salesVat").val()) || 0;
|
|
var totalAmount = supplyPrice + vat;
|
|
$("#salesTotalAmount").val(totalAmount);
|
|
}
|
|
|
|
// 판매환종 초기값 설정 (견적환종과 동기화하되, 사용자가 변경 가능)
|
|
function initializeSalesCurrency() {
|
|
// Controller에서 계산한 값 사용 (잔량 기반)
|
|
var existingSalesCurrency = "${saleInfo.SALES_CURRENCY}";
|
|
var contractCurrency = "${orderInfo.SALES_CURRENCY}"; // 견적환종 (SALES_CURRENCY로 통일)
|
|
var contractExchangeRate = "${orderInfo.SALES_EXCHANGE_RATE}"; // 견적환율
|
|
|
|
console.log("=== 판매환종 초기화 디버그 ===");
|
|
console.log("기존 판매환종:", existingSalesCurrency);
|
|
console.log("견적환종:", contractCurrency);
|
|
console.log("견적환율:", contractExchangeRate);
|
|
|
|
// 이미 저장된 판매환종이 있으면 그대로 사용
|
|
if(existingSalesCurrency && existingSalesCurrency.trim() !== '') {
|
|
$("#salesCurrency").val(existingSalesCurrency).trigger('change');
|
|
console.log("기존 판매환종 사용:", existingSalesCurrency);
|
|
}
|
|
// 없으면 견적환종을 기본값으로 설정
|
|
else if(contractCurrency && contractCurrency.trim() !== '') {
|
|
$("#salesCurrency").val(contractCurrency).trigger('change');
|
|
|
|
// 견적환율도 함께 설정
|
|
if(contractExchangeRate && contractExchangeRate.trim() !== '') {
|
|
$("#salesExchangeRate").val(contractExchangeRate);
|
|
}
|
|
|
|
console.log("판매환종 자동 설정: " + contractCurrency + " (견적환종과 동기화)");
|
|
} else {
|
|
console.log("견적환종이 없습니다. orderInfo를 확인하세요.");
|
|
}
|
|
}
|
|
|
|
// 환율 변경 시 원화 금액 재계산
|
|
function fn_recalculateByExchangeRate() {
|
|
var currency = $("#salesCurrency").val();
|
|
var exchangeRate = parseFloat($("#salesExchangeRate").val()) || 1;
|
|
|
|
// KRW가 아닌 경우에만 환율 적용
|
|
if(currency && currency !== 'KRW' && exchangeRate > 0) {
|
|
var quantity = parseFloat($("#salesQuantity").val()) || 0;
|
|
var unitPrice = parseFloat($("#salesUnitPrice").val()) || 0;
|
|
|
|
// 외화 기준 금액 계산
|
|
var foreignSupplyPrice = quantity * unitPrice;
|
|
|
|
// 원화 환산 금액 계산
|
|
var krwSupplyPrice = Math.round(foreignSupplyPrice * exchangeRate);
|
|
var krwVat = Math.round(krwSupplyPrice * 0.1);
|
|
var krwTotalAmount = krwSupplyPrice + krwVat;
|
|
|
|
$("#salesSupplyPrice").val(krwSupplyPrice);
|
|
$("#salesVat").val(krwVat);
|
|
$("#salesTotalAmount").val(krwTotalAmount);
|
|
} else {
|
|
// KRW인 경우 일반 계산
|
|
fn_calculateSupplyPrice();
|
|
}
|
|
}
|
|
|
|
// S/N 화면 표시 업데이트
|
|
function fn_updateSnDisplay() {
|
|
var count = snList.length;
|
|
if(count > 0) {
|
|
// 모든 S/N을 쉼표로 연결
|
|
var snValues = [];
|
|
for(var i = 0; i < snList.length; i++) {
|
|
snValues.push(snList[i].value);
|
|
}
|
|
$("#serialNo").val(snValues.join(', '));
|
|
} else {
|
|
$("#serialNo").val('');
|
|
}
|
|
}
|
|
|
|
// S/N 관리 팝업 열기
|
|
function fn_openSnManagePopup() {
|
|
// 최신 데이터 다시 로드 (display 업데이트 없이)
|
|
var serialNoValue = $("#serialNo").val();
|
|
var serialNoListValue = $("#serialNoList").val();
|
|
|
|
console.log("팝업 열기 시작");
|
|
console.log("serialNoList 값:", serialNoListValue);
|
|
|
|
// 데이터가 있을 때만 로드
|
|
if(serialNoListValue && serialNoListValue.trim() != '') {
|
|
// JSON 형태로 저장된 데이터가 있으면 파싱
|
|
try {
|
|
snList = JSON.parse(serialNoListValue);
|
|
console.log("JSON 파싱 성공, snList:", snList);
|
|
// counter 재설정
|
|
if(snList.length > 0) {
|
|
var maxId = Math.max.apply(Math, snList.map(function(item) { return item.id; }));
|
|
snCounter = maxId + 1;
|
|
}
|
|
} catch(e) {
|
|
console.log("JSON 파싱 오류:", e);
|
|
snList = [];
|
|
}
|
|
} else if(serialNoValue && serialNoValue.trim() != '') {
|
|
// hidden 필드가 없으면 display 필드 파싱
|
|
console.log("hidden 필드 없음, display 필드에서 파싱");
|
|
snList = [];
|
|
var snArray = serialNoValue.split(',');
|
|
for(var i = 0; i < snArray.length; i++) {
|
|
if(snArray[i].trim() != '') {
|
|
snList.push({
|
|
id: snCounter++,
|
|
value: snArray[i].trim()
|
|
});
|
|
}
|
|
}
|
|
} else {
|
|
console.log("저장된 데이터 없음, 빈 배열로 시작");
|
|
snList = [];
|
|
}
|
|
|
|
console.log("로드된 S/N 목록 (복사본):", JSON.parse(JSON.stringify(snList)));
|
|
console.log("snList.length:", snList.length);
|
|
|
|
// 실제 데이터 확인
|
|
if(snList.length > 0) {
|
|
console.log("첫 번째 아이템:", snList[0]);
|
|
}
|
|
|
|
// 팝업 HTML 생성
|
|
var popupHtml = '<div style="padding:10px; color:#333;">';
|
|
popupHtml += ' <h3 style="margin:0 0 15px 0; text-align:center; color:#333;">S/N 관리</h3>';
|
|
popupHtml += ' <div id="snListContainer" style="margin-bottom:15px; max-height:300px; overflow-y:auto; color:#333;"></div>';
|
|
popupHtml += ' <div style="margin-bottom:15px; display:flex; gap:5px;">';
|
|
popupHtml += ' <input type="text" id="newSnInput" placeholder="S/N 입력" style="flex:1; padding:8px; border:1px solid #ddd; border-radius:4px; color:#333;">';
|
|
popupHtml += ' <button type="button" onclick="fn_addSn()" class="plm_btns">추가</button>';
|
|
popupHtml += ' </div>';
|
|
popupHtml += ' <div style="text-align:center; margin-top:20px; display:flex; gap:10px; justify-content:center;">';
|
|
popupHtml += ' <button type="button" onclick="fn_openSequentialSnPopup()" class="plm_btns">연속번호생성</button>';
|
|
popupHtml += ' <button type="button" onclick="fn_confirmSnList()" class="plm_btns">확인</button>';
|
|
popupHtml += ' <button type="button" onclick="fn_closeSnPopup()" class="plm_btns">취소</button>';
|
|
popupHtml += ' </div>';
|
|
popupHtml += '</div>';
|
|
|
|
// 팝업 오픈 (Swal 사용)
|
|
Swal.fire({
|
|
html: popupHtml,
|
|
width: '700px',
|
|
showConfirmButton: false,
|
|
showCloseButton: true,
|
|
customClass: {
|
|
popup: 'sn-manage-popup'
|
|
},
|
|
onOpen: function() {
|
|
setTimeout(function() {
|
|
fn_renderSnList();
|
|
// 엔터키로 추가
|
|
$(".swal2-html-container #newSnInput").keypress(function(e) {
|
|
if(e.which == 13) {
|
|
fn_addSn();
|
|
return false;
|
|
}
|
|
});
|
|
}, 50);
|
|
}
|
|
});
|
|
}
|
|
|
|
// S/N 목록 렌더링
|
|
function fn_renderSnList() {
|
|
console.log("fn_renderSnList 실행, snList 길이:", snList.length);
|
|
console.log("snList 내용:", snList);
|
|
|
|
var html = '<table style="width:100%; margin-bottom:10px; border-collapse:collapse; border:1px solid #ddd; color:#333;">';
|
|
html += '<colgroup><col width="15%"><col width="65%"><col width="20%"></colgroup>';
|
|
html += '<thead><tr style="background:#f5f5f5; color:#333;">';
|
|
html += '<th style="padding:10px; border:1px solid #ddd; text-align:center; color:#333;">번호</th>';
|
|
html += '<th style="padding:10px; border:1px solid #ddd; text-align:center; color:#333;">S/N</th>';
|
|
html += '<th style="padding:10px; border:1px solid #ddd; text-align:center; color:#333;">삭제</th>';
|
|
html += '</tr></thead>';
|
|
html += '<tbody>';
|
|
|
|
if(snList.length == 0) {
|
|
html += '<tr><td colspan="3" style="text-align:center; padding:30px; color:#999; border:1px solid #ddd;">등록된 S/N이 없습니다.</td></tr>';
|
|
} else {
|
|
for(var i = 0; i < snList.length; i++) {
|
|
html += '<tr>';
|
|
html += '<td style="text-align:center; padding:8px; border:1px solid #ddd; color:#333;">' + (i+1) + '</td>';
|
|
html += '<td style="padding:8px; border:1px solid #ddd; color:#333;">' + snList[i].value + '</td>';
|
|
html += '<td style="text-align:center; padding:8px; border:1px solid #ddd;">';
|
|
html += '<button type="button" onclick="fn_deleteSn(' + snList[i].id + ')" class="plm_btns" style="padding:4px 8px; font-size:12px;">삭제</button>';
|
|
html += '</td>';
|
|
html += '</tr>';
|
|
}
|
|
}
|
|
|
|
html += '</tbody></table>';
|
|
|
|
$(".swal2-html-container #snListContainer").html(html);
|
|
}
|
|
|
|
// S/N 추가
|
|
function fn_addSn() {
|
|
var newSn = $(".swal2-html-container #newSnInput").val().trim();
|
|
|
|
if(newSn == '') {
|
|
alert('S/N을 입력해주세요.');
|
|
return;
|
|
}
|
|
|
|
// 중복 체크
|
|
for(var i = 0; i < snList.length; i++) {
|
|
if(snList[i].value == newSn) {
|
|
alert('이미 등록된 S/N입니다.');
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 추가
|
|
snList.push({
|
|
id: snCounter++,
|
|
value: newSn
|
|
});
|
|
|
|
$(".swal2-html-container #newSnInput").val('');
|
|
fn_renderSnList();
|
|
}
|
|
|
|
// S/N 삭제
|
|
function fn_deleteSn(snId) {
|
|
for(var i = 0; i < snList.length; i++) {
|
|
if(snList[i].id == snId) {
|
|
snList.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
fn_renderSnList();
|
|
}
|
|
|
|
// 연속번호 생성 팝업 열기
|
|
function fn_openSequentialSnPopup() {
|
|
Swal.fire({
|
|
title: '연속번호 생성',
|
|
html:
|
|
'<div style="text-align:left; padding:5px; color:#333;">' +
|
|
'<div style="margin-bottom:10px;">' +
|
|
'<label style="display:block; margin-bottom:3px; font-size:13px; color:#333;">시작번호 <span style="color:red;">*</span></label>' +
|
|
'<input type="text" id="seqStartNo" placeholder="예: ITEM-001" style="width:100%; padding:6px; border:1px solid #ddd; border-radius:3px; font-size:13px; color:#333;">' +
|
|
'</div>' +
|
|
'<div style="margin-bottom:10px;">' +
|
|
'<label style="display:block; margin-bottom:3px; font-size:13px; color:#333;">생성개수 <span style="color:red;">*</span></label>' +
|
|
'<input type="number" id="seqCount" placeholder="예: 10" min="1" max="100" style="width:100%; padding:6px; border:1px solid #ddd; border-radius:3px; font-size:13px; color:#333;">' +
|
|
'</div>' +
|
|
'<div style="background:#f8f9fa; padding:8px; border-radius:3px; color:#666; font-size:11px; line-height:1.5;">' +
|
|
'예: ITEM-001, 개수 3 → ITEM-001, ITEM-002, ITEM-003<br>' +
|
|
'※ 최대 100개까지 생성 가능' +
|
|
'</div>' +
|
|
'</div>',
|
|
width: '400px',
|
|
showCancelButton: true,
|
|
confirmButtonText: '생성',
|
|
cancelButtonText: '취소',
|
|
preConfirm: () => {
|
|
var startNo = $("#seqStartNo").val().trim();
|
|
var count = parseInt($("#seqCount").val());
|
|
|
|
if(!startNo) {
|
|
Swal.showValidationMessage('시작번호를 입력해주세요.');
|
|
return false;
|
|
}
|
|
|
|
if(!count || count < 1) {
|
|
Swal.showValidationMessage('생성개수를 입력해주세요.');
|
|
return false;
|
|
}
|
|
|
|
if(count > 100) {
|
|
Swal.showValidationMessage('최대 100개까지 생성 가능합니다.');
|
|
return false;
|
|
}
|
|
|
|
return {startNo: startNo, count: count};
|
|
}
|
|
}).then((result) => {
|
|
if(result.isConfirmed && result.value) {
|
|
fn_generateSequentialSn(result.value.startNo, result.value.count);
|
|
} else {
|
|
fn_openSnManagePopup();
|
|
}
|
|
});
|
|
}
|
|
|
|
// 연속번호 생성 로직
|
|
function fn_generateSequentialSn(startNo, count) {
|
|
// 숫자 부분 추출 (마지막 연속된 숫자)
|
|
var match = startNo.match(/^(.*?)(\d+)$/);
|
|
|
|
if(!match) {
|
|
alert('올바른 형식이 아닙니다. 마지막에 숫자가 있어야 합니다. (예: ITEM-001)');
|
|
fn_openSequentialSnPopup();
|
|
return;
|
|
}
|
|
|
|
var prefix = match[1]; // 접두사
|
|
var startNum = parseInt(match[2]); // 시작 숫자
|
|
var numLength = match[2].length; // 숫자 자릿수
|
|
|
|
// 연속번호 생성
|
|
var addedCount = 0;
|
|
for(var i = 0; i < count; i++) {
|
|
var currentNum = startNum + i;
|
|
var paddedNum = String(currentNum).padStart(numLength, '0');
|
|
var newSn = prefix + paddedNum;
|
|
|
|
// 중복 체크
|
|
var isDuplicate = false;
|
|
for(var j = 0; j < snList.length; j++) {
|
|
if(snList[j].value == newSn) {
|
|
isDuplicate = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 중복이 아니면 추가
|
|
if(!isDuplicate) {
|
|
snList.push({
|
|
id: snCounter++,
|
|
value: newSn
|
|
});
|
|
addedCount++;
|
|
}
|
|
}
|
|
|
|
// S/N 목록 다시 열기
|
|
$("#serialNoList").val(JSON.stringify(snList));
|
|
fn_openSnManagePopup();
|
|
}
|
|
|
|
// S/N 목록 확인 및 적용
|
|
function fn_confirmSnList() {
|
|
console.log("확인 버튼 클릭, snList:", snList);
|
|
console.log("snList.length:", snList.length);
|
|
|
|
// S/N 목록을 쉼표로 구분하여 input에 저장
|
|
var snValues = [];
|
|
for(var i = 0; i < snList.length; i++) {
|
|
snValues.push(snList[i].value);
|
|
}
|
|
|
|
// hidden 필드에 전체 목록 저장 (서버 전송용)
|
|
var jsonString = JSON.stringify(snList);
|
|
console.log("저장할 JSON:", jsonString);
|
|
$("#serialNoList").val(jsonString);
|
|
console.log("저장 후 확인:", $("#serialNoList").val());
|
|
|
|
fn_updateSnDisplay();
|
|
fn_updateSplitSnDisplay();
|
|
|
|
Swal.close();
|
|
}
|
|
|
|
// S/N 팝업 닫기 (취소)
|
|
function fn_closeSnPopup() {
|
|
Swal.close();
|
|
}
|
|
|
|
// === 분할S/N 관련 함수 ===
|
|
var usedSplitSerialNos = "${usedSplitSerialNos}"; // 이미 다른 출하에서 사용된 S/N
|
|
var selectedSplitSns = []; // 현재 선택된 분할S/N
|
|
|
|
// 페이지 로드 시 기존 분할S/N 초기화
|
|
$(function() {
|
|
var existingSplitSn = $("#splitSerialNoHidden").val();
|
|
if(existingSplitSn && existingSplitSn.trim() !== '') {
|
|
selectedSplitSns = existingSplitSn.split(',');
|
|
for(var i = 0; i < selectedSplitSns.length; i++) {
|
|
selectedSplitSns[i] = selectedSplitSns[i].trim();
|
|
}
|
|
}
|
|
fn_updateSplitSnDisplay();
|
|
});
|
|
|
|
// 사용 가능한 S/N 목록 계산 (전체 S/N - 이미 사용된 S/N + 현재 수정 중인 분할S/N)
|
|
function fn_getAvailableSns() {
|
|
var allSns = [];
|
|
var serialNoVal = $("#serialNo").val();
|
|
if(serialNoVal && serialNoVal.trim() !== '') {
|
|
var snArr = serialNoVal.split(',');
|
|
for(var i = 0; i < snArr.length; i++) {
|
|
if(snArr[i].trim() !== '') {
|
|
allSns.push(snArr[i].trim());
|
|
}
|
|
}
|
|
}
|
|
|
|
// 이미 사용된 S/N 파싱
|
|
var usedSns = [];
|
|
if(usedSplitSerialNos && usedSplitSerialNos.trim() !== '') {
|
|
var usedArr = usedSplitSerialNos.split(',');
|
|
for(var i = 0; i < usedArr.length; i++) {
|
|
if(usedArr[i].trim() !== '') {
|
|
usedSns.push(usedArr[i].trim());
|
|
}
|
|
}
|
|
}
|
|
|
|
// 사용 가능한 S/N = 전체 - 이미사용(다른 출하)
|
|
var available = [];
|
|
for(var i = 0; i < allSns.length; i++) {
|
|
var isUsed = false;
|
|
for(var j = 0; j < usedSns.length; j++) {
|
|
if(allSns[i] === usedSns[j]) {
|
|
isUsed = true;
|
|
break;
|
|
}
|
|
}
|
|
if(!isUsed) {
|
|
available.push(allSns[i]);
|
|
}
|
|
}
|
|
return available;
|
|
}
|
|
|
|
// 분할S/N 선택 팝업
|
|
function fn_openSplitSnPopup() {
|
|
var availableSns = fn_getAvailableSns();
|
|
|
|
if(availableSns.length === 0) {
|
|
alert('선택 가능한 S/N이 없습니다.\nS/N을 먼저 등록하거나, 이미 모든 S/N이 다른 출하에 배정되었습니다.');
|
|
return;
|
|
}
|
|
|
|
var popupHtml = '<div style="padding:10px; color:#333;">';
|
|
popupHtml += ' <h3 style="margin:0 0 15px 0; text-align:center; color:#333;">분할S/N 선택</h3>';
|
|
popupHtml += ' <div style="margin-bottom:10px; text-align:right;">';
|
|
popupHtml += ' <button type="button" onclick="fn_splitSnSelectAll()" class="plm_btns" style="padding:4px 10px; font-size:12px;">전체선택</button>';
|
|
popupHtml += ' <button type="button" onclick="fn_splitSnDeselectAll()" class="plm_btns" style="padding:4px 10px; font-size:12px;">전체해제</button>';
|
|
popupHtml += ' </div>';
|
|
popupHtml += ' <div id="splitSnListContainer" style="max-height:300px; overflow-y:auto;">';
|
|
popupHtml += ' <table style="width:100%; border-collapse:collapse; border:1px solid #ddd;">';
|
|
popupHtml += ' <colgroup><col width="15%"><col width="15%"><col width="70%"></colgroup>';
|
|
popupHtml += ' <thead><tr style="background:#f5f5f5;">';
|
|
popupHtml += ' <th style="padding:8px; border:1px solid #ddd; text-align:center;">선택</th>';
|
|
popupHtml += ' <th style="padding:8px; border:1px solid #ddd; text-align:center;">번호</th>';
|
|
popupHtml += ' <th style="padding:8px; border:1px solid #ddd; text-align:center;">S/N</th>';
|
|
popupHtml += ' </tr></thead><tbody>';
|
|
|
|
for(var i = 0; i < availableSns.length; i++) {
|
|
var isChecked = false;
|
|
for(var j = 0; j < selectedSplitSns.length; j++) {
|
|
if(availableSns[i] === selectedSplitSns[j]) {
|
|
isChecked = true;
|
|
break;
|
|
}
|
|
}
|
|
popupHtml += '<tr>';
|
|
popupHtml += '<td style="text-align:center; padding:6px; border:1px solid #ddd;">';
|
|
popupHtml += '<input type="checkbox" class="split-sn-chk" value="' + availableSns[i] + '" ' + (isChecked ? 'checked' : '') + ' />';
|
|
popupHtml += '</td>';
|
|
popupHtml += '<td style="text-align:center; padding:6px; border:1px solid #ddd;">' + (i+1) + '</td>';
|
|
popupHtml += '<td style="padding:6px; border:1px solid #ddd;">' + availableSns[i] + '</td>';
|
|
popupHtml += '</tr>';
|
|
}
|
|
|
|
popupHtml += ' </tbody></table>';
|
|
popupHtml += ' </div>';
|
|
popupHtml += ' <div style="margin-top:10px; text-align:center; color:#666; font-size:12px;">';
|
|
popupHtml += ' 선택한 S/N 개수가 판매수량에 자동 반영됩니다.';
|
|
popupHtml += ' </div>';
|
|
popupHtml += ' <div style="text-align:center; margin-top:15px; display:flex; gap:10px; justify-content:center;">';
|
|
popupHtml += ' <button type="button" onclick="fn_confirmSplitSn()" class="plm_btns">확인</button>';
|
|
popupHtml += ' <button type="button" onclick="Swal.close()" class="plm_btns">취소</button>';
|
|
popupHtml += ' </div>';
|
|
popupHtml += '</div>';
|
|
|
|
Swal.fire({
|
|
html: popupHtml,
|
|
width: '500px',
|
|
showConfirmButton: false,
|
|
showCloseButton: true
|
|
});
|
|
}
|
|
|
|
// 전체선택
|
|
function fn_splitSnSelectAll() {
|
|
$(".swal2-html-container .split-sn-chk").prop('checked', true);
|
|
}
|
|
|
|
// 전체해제
|
|
function fn_splitSnDeselectAll() {
|
|
$(".swal2-html-container .split-sn-chk").prop('checked', false);
|
|
}
|
|
|
|
// 분할S/N 선택 확인
|
|
function fn_confirmSplitSn() {
|
|
selectedSplitSns = [];
|
|
$(".swal2-html-container .split-sn-chk:checked").each(function() {
|
|
selectedSplitSns.push($(this).val());
|
|
});
|
|
|
|
// hidden 필드에 저장
|
|
$("#splitSerialNoHidden").val(selectedSplitSns.join(','));
|
|
|
|
// 판매수량 자동 업데이트
|
|
$("#salesQuantity").val(selectedSplitSns.length);
|
|
fn_calculateSupplyPrice();
|
|
|
|
fn_updateSplitSnDisplay();
|
|
Swal.close();
|
|
}
|
|
|
|
// 분할S/N 표시 업데이트
|
|
function fn_updateSplitSnDisplay() {
|
|
if(selectedSplitSns.length > 0) {
|
|
var displayText = selectedSplitSns.join(', ');
|
|
if(displayText.length > 80) {
|
|
displayText = displayText.substring(0, 77) + '... (' + selectedSplitSns.length + '건)';
|
|
}
|
|
$("#splitSnDisplay").text(displayText).css('color', '#333');
|
|
} else {
|
|
var serialNoVal = $("#serialNo").val();
|
|
if(serialNoVal && serialNoVal.trim() !== '') {
|
|
$("#splitSnDisplay").text('클릭하여 분할S/N 선택').css('color', '#999');
|
|
} else {
|
|
$("#splitSnDisplay").text('S/N을 먼저 등록하세요').css('color', '#999');
|
|
}
|
|
}
|
|
}
|
|
|
|
function fn_save() {
|
|
// 출하지시 상태는 자동으로 설정됨 (hidden 필드에 이미 "출하지시" 값 설정)
|
|
|
|
// S/N 데이터 처리: JSON에서 값만 추출하여 콤마로 연결
|
|
var serialNoListValue = $("#serialNoList").val();
|
|
console.log("=== 저장 전 S/N 처리 시작 ===");
|
|
console.log("serialNoList 값:", serialNoListValue);
|
|
|
|
if(serialNoListValue && serialNoListValue.trim() != '') {
|
|
try {
|
|
var snArray = JSON.parse(serialNoListValue);
|
|
console.log("파싱된 배열:", snArray);
|
|
var snValues = [];
|
|
for(var i = 0; i < snArray.length; i++) {
|
|
if(snArray[i].value) {
|
|
snValues.push(snArray[i].value);
|
|
}
|
|
}
|
|
console.log("추출된 값들:", snValues);
|
|
// 콤마로 구분된 문자열로 변환 (서버 저장용, 띄어쓰기 없이)
|
|
var finalSnString = snValues.join(',');
|
|
console.log("최종 S/N 문자열:", finalSnString);
|
|
$("#serialNo").val(finalSnString);
|
|
console.log("설정 후 serialNo 필드 값:", $("#serialNo").val());
|
|
} catch(e) {
|
|
console.error("S/N JSON 파싱 오류:", e);
|
|
}
|
|
} else {
|
|
console.log("serialNoList가 비어있음");
|
|
}
|
|
console.log("=== S/N 처리 완료 ===");
|
|
|
|
if (confirm("저장하시겠습니까?")) {
|
|
$.ajax({
|
|
url : "/salesMgmt/saveSales.do",
|
|
type : "POST",
|
|
data : $("#form1").serialize(),
|
|
dataType : "json",
|
|
success : function(data) {
|
|
alert(data.msg);
|
|
|
|
// 수정 모드(logId)면 바로 닫기 (부모 팝업이 자동 새로고침)
|
|
var logIdVal = $("input[name='logId']").val();
|
|
if(logIdVal && logIdVal.trim() !== '') {
|
|
self.close();
|
|
return;
|
|
}
|
|
|
|
// 신규 등록 모드: 저장 후 잔량 확인
|
|
$.ajax({
|
|
url: "/salesMgmt/salesRegForm.do",
|
|
type: "GET",
|
|
data: { orderNo: "${param.orderNo}" },
|
|
dataType: "html",
|
|
success: function(response) {
|
|
var match = response.match(/SALES_QUANTITY:\s*(\d+)/);
|
|
var remainingQuantity = match ? parseInt(match[1]) : 0;
|
|
|
|
console.log("서버에서 계산한 잔량:", remainingQuantity);
|
|
|
|
if(remainingQuantity > 0) {
|
|
if(confirm("잔량 " + remainingQuantity + "개가 남았습니다. 계속 등록하시겠습니까?")) {
|
|
if(opener && opener.fn_search) {
|
|
opener.fn_search();
|
|
}
|
|
location.reload();
|
|
} else {
|
|
if(opener && opener.fn_search) {
|
|
opener.fn_search();
|
|
}
|
|
self.close();
|
|
}
|
|
} else {
|
|
if(opener && opener.fn_search) {
|
|
opener.fn_search();
|
|
}
|
|
self.close();
|
|
}
|
|
},
|
|
error: function() {
|
|
if(opener && opener.fn_search) {
|
|
opener.fn_search();
|
|
}
|
|
self.close();
|
|
}
|
|
});
|
|
},
|
|
error : function(jqxhr, status, error) {
|
|
console.log("=== AJAX 에러 ===");
|
|
console.log("status:", status);
|
|
console.log("error:", error);
|
|
console.log("responseText:", jqxhr.responseText);
|
|
console.log("status code:", jqxhr.status);
|
|
alert("저장 중 오류가 발생했습니다.\n상태: " + status + "\n에러: " + error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// 날짜 선택기 초기화
|
|
function _fnc_datepick() {
|
|
var $dateinput = $("input.date_icon");
|
|
for (var i = 0; i < $dateinput.length; i++) {
|
|
$dateinput.eq(i).attr("size", "10");
|
|
$dateinput.eq(i).datepicker({
|
|
changeMonth : true,
|
|
changeYear : true
|
|
});
|
|
}
|
|
}
|
|
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<form name="form1" id="form1" action="" method="post">
|
|
<input type="hidden" name="orderNo" value="${param.orderNo}">
|
|
<input type="hidden" name="logId" value="${logId}">
|
|
<!-- serialNoList는 name 속성 없이 id만 사용 (클라이언트 전용) -->
|
|
<input type="hidden" id="serialNoList" value="${saleInfo.SERIAL_NO_LIST}" />
|
|
|
|
<section class="business_popup_min_width">
|
|
<div class="plm_menu_name">
|
|
<h2>
|
|
<span>
|
|
<c:choose>
|
|
<c:when test="${not empty logId}">출하 수정 (주문번호: ${param.orderNo})</c:when>
|
|
<c:otherwise>판매 등록 (주문번호: ${param.orderNo})</c:otherwise>
|
|
</c:choose>
|
|
</span>
|
|
</h2>
|
|
</div>
|
|
|
|
<div id="EntirePopupFormWrap">
|
|
<table class="">
|
|
<colgroup>
|
|
<col width="100%" />
|
|
</colgroup>
|
|
<tr>
|
|
<td>
|
|
<table class="pmsPopuptable">
|
|
<colgroup>
|
|
<col width="15%" />
|
|
<col width="35%" />
|
|
<col width="15%" />
|
|
<col width="35%" />
|
|
</colgroup>
|
|
|
|
<!-- 1행: 출하지시 (자동) -->
|
|
<tr>
|
|
<td class="input_title">
|
|
<label>출하지시</label>
|
|
</td>
|
|
<td colspan="3">
|
|
<span style="color:#2196F3; font-weight:bold;">
|
|
<c:choose>
|
|
<c:when test="${saleInfo.SHIPPING_ORDER_STATUS == '출하지시'}">
|
|
✓ 출하지시 완료
|
|
</c:when>
|
|
<c:otherwise>
|
|
저장 시 자동으로 출하지시 처리됩니다
|
|
</c:otherwise>
|
|
</c:choose>
|
|
</span>
|
|
<!-- 자동으로 출하지시 상태 설정 -->
|
|
<input type="hidden" name="shippingOrderStatus" id="shippingOrderStatus" value="출하지시" />
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- 2행: S/N -->
|
|
<tr>
|
|
<td class="input_title"><label for="serialNo">S/N</label></td>
|
|
<td colspan="3">
|
|
<input type="text" name="serialNo" id="serialNo"
|
|
placeholder="클릭하여 S/N 추가"
|
|
style="width:100%; cursor:pointer; background-color:#f8f9fa; color:#333 !important;"
|
|
value="${saleInfo.SERIAL_NO}"
|
|
readonly />
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- 2-1행: 분할S/N -->
|
|
<tr>
|
|
<td class="input_title"><label for="splitSerialNo">분할S/N</label></td>
|
|
<td colspan="3">
|
|
<input type="hidden" name="splitSerialNo" id="splitSerialNoHidden" value="${saleInfo.SPLIT_SERIAL_NO}" />
|
|
<div id="splitSnContainer" style="width:100%; min-height:34px; border:1px solid #ddd; border-radius:4px; padding:5px; background-color:#f8f9fa; cursor:pointer;"
|
|
onclick="fn_openSplitSnPopup()">
|
|
<span id="splitSnDisplay" style="color:#999;">S/N을 먼저 등록하세요</span>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- 3행: 판매수량, 출하일 -->
|
|
<tr>
|
|
<td class="input_title">
|
|
<label for="salesQuantity">판매수량</label>
|
|
<c:if test="${true}">
|
|
<span style="color:#666; font-size:11px; display:block; margin-top:2px;">
|
|
(잔량: ${saleInfo.REMAINING_QUANTITY != null ? saleInfo.REMAINING_QUANTITY : saleInfo.ORDER_QUANTITY}개)
|
|
</span>
|
|
</c:if>
|
|
</td>
|
|
<td>
|
|
<input type="number" name="salesQuantity" id="salesQuantity"
|
|
value="${saleInfo.SALES_QUANTITY != null && saleInfo.SALES_QUANTITY != '' && saleInfo.SALES_QUANTITY != 0 ? saleInfo.SALES_QUANTITY : ''}"
|
|
onchange="fn_calculateSupplyPrice()"
|
|
placeholder="판매할 수량 입력" />
|
|
</td>
|
|
<td class="input_title"><label for="shippingDate">출하일</label></td>
|
|
<td>
|
|
<input type="text" id="shippingDate" name="shippingDate" class="date_icon" value="${saleInfo.SHIPPING_DATE}" autocomplete="off" />
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- 4행: 출고방법, 담당자 -->
|
|
<tr>
|
|
<td class="input_title"><label for="shippingMethod">출고방법</label></td>
|
|
<td>
|
|
<select name="shippingMethod" id="shippingMethod" class="select2">
|
|
<option value="">선택</option>
|
|
<option value="내수/직납" ${saleInfo.SHIPPING_METHOD == '내수/직납' ? 'selected' : ''}>내수/직납</option>
|
|
<option value="내수/택배" ${saleInfo.SHIPPING_METHOD == '내수/택배' ? 'selected' : ''}>내수/택배</option>
|
|
<option value="내수/기타" ${saleInfo.SHIPPING_METHOD == '내수/기타' ? 'selected' : ''}>내수/기타</option>
|
|
<option value="수출" ${saleInfo.SHIPPING_METHOD == '수출' ? 'selected' : ''}>수출</option>
|
|
</select>
|
|
</td>
|
|
<td class="input_title"><label for="manager">담당자</label></td>
|
|
<td>
|
|
<select name="manager" id="manager" class="select2">
|
|
<option value="">선택</option>
|
|
${codeMap.managerList}
|
|
</select>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- 5행: 판매단가, 판매공급가액 -->
|
|
<tr>
|
|
<td class="input_title"><label for="salesUnitPrice">판매단가</label></td>
|
|
<td>
|
|
<input type="number" name="salesUnitPrice" id="salesUnitPrice"
|
|
value="${saleInfo.SALES_UNIT_PRICE}"
|
|
onchange="fn_calculateSupplyPrice()" />
|
|
</td>
|
|
<td class="input_title"><label for="salesSupplyPrice">판매공급가액</label></td>
|
|
<td>
|
|
<input type="number" name="salesSupplyPrice" id="salesSupplyPrice"
|
|
value="${saleInfo.SALES_SUPPLY_PRICE}"
|
|
readonly style="background-color:#f5f5f5;" />
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- 6행: 판매부가세, 판매총액 -->
|
|
<tr>
|
|
<td class="input_title"><label for="salesVat">판매부가세</label></td>
|
|
<td>
|
|
<input type="number" name="salesVat" id="salesVat"
|
|
value="${saleInfo.SALES_VAT}"
|
|
readonly style="background-color:#f5f5f5;" />
|
|
</td>
|
|
<td class="input_title"><label for="salesTotalAmount">판매총액</label></td>
|
|
<td>
|
|
<input type="number" name="salesTotalAmount" id="salesTotalAmount"
|
|
value="${saleInfo.SALES_TOTAL_AMOUNT}"
|
|
readonly style="background-color:#f5f5f5;" />
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- 7행: 판매환종, 판매환율 -->
|
|
<tr>
|
|
<td class="input_title"><label for="salesCurrency">판매환종</label></td>
|
|
<td>
|
|
<select name="salesCurrency" id="salesCurrency" class="select2" onchange="fn_recalculateByExchangeRate()">
|
|
<option value="">선택</option>
|
|
${codeMap.salesCurrency}
|
|
</select>
|
|
</td>
|
|
<td class="input_title"><label for="salesExchangeRate">판매환율</label></td>
|
|
<td>
|
|
<input type="number" name="salesExchangeRate" id="salesExchangeRate" step="0.01"
|
|
value="${saleInfo.SALES_EXCHANGE_RATE}"
|
|
onchange="fn_recalculateByExchangeRate()" />
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- 8행: 인도조건 -->
|
|
<tr>
|
|
<td class="input_title"><label for="incoterms">인도조건</label></td>
|
|
<td colspan="3">
|
|
<select name="incoterms" id="incoterms" class="select2">
|
|
<option value="">선택</option>
|
|
<option value="FOB" ${saleInfo.INCOTERMS == 'FOB' ? 'selected' : ''}>FOB</option>
|
|
<option value="EXW" ${saleInfo.INCOTERMS == 'EXW' ? 'selected' : ''}>EXW</option>
|
|
<option value="CIF" ${saleInfo.INCOTERMS == 'CIF' ? 'selected' : ''}>CIF</option>
|
|
<option value="DDP" ${saleInfo.INCOTERMS == 'DDP' ? 'selected' : ''}>DDP</option>
|
|
<option value="DAP" ${saleInfo.INCOTERMS == 'DAP' ? 'selected' : ''}>DAP</option>
|
|
</select>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- hidden: 수주수량 (서버 전송용) -->
|
|
<input type="hidden" name="orderQuantity" id="orderQuantity" value="${saleInfo.ORDER_QUANTITY}" />
|
|
|
|
</table>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
</div>
|
|
|
|
<div class="btn_wrap">
|
|
<div class="plm_btn_wrap_center">
|
|
<input type="button" value="저장" id="btnSave" class="plm_btns">
|
|
<input type="button" value="닫기" id="btnClose" class="plm_btns">
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</form>
|
|
|
|
</body>
|
|
</html>
|
|
|