발주서 메일발송 등..

This commit is contained in:
2025-12-01 15:13:37 +09:00
parent d640fe3ade
commit 56860187d4
7 changed files with 975 additions and 12 deletions

View File

@@ -123,14 +123,14 @@ $(document).ready(function(){
,edittype:"select", formatter:"select"
,editoptions:{ value: unit_cd }
}
,{name:"ORDER_QTY" , width:90, align:"right", sortable:false, editable:<%=isModify%>
,{name:"ORDER_QTY" , width:90, align:"right", sortable:false, editable:false
,formatter:"integer", formatoptions:{thousandsSeparator:","}
,editoptions:{
dataInit: function(e){ e.style.textAlign = "right"; }
,dataEvents: [{type:"change", fn:function(e){ gridFn.calcRowAll(e); }}]
}
}
,{name:"PARTNER_PRICE" , width:110, align:"right", sortable:false, editable:true
,{name:"PARTNER_PRICE" , width:110, align:"right", sortable:false, editable:false
,formatter:"integer", formatoptions:{thousandsSeparator:","}
,editoptions:{
dataInit: function(e){ e.style.textAlign = "right"; }
@@ -1833,7 +1833,7 @@ function fn_price_save(){
</div>
</div>
<div id="expenseApplyPopupFormWrap1" style="margin-top:0px;position:relative;">
<!-- <div id="expenseApplyPopupFormWrap1" style="margin-top:0px;position:relative;">
<div style="width:50%;position: absolute;left:0"></div>
<div style="width:48%;position: absolute;right:0">
<table class="pmsPopupForm" style="with:200px !important;border:1px solid">
@@ -1860,7 +1860,7 @@ function fn_price_save(){
</tr>
</table>
</div>
</div>
</div> -->
<!-- <div id="expenseApplyPopupFormWrap1" style="margin-top:35px;position: ">
<table class="pmsPopupForm">

View File

@@ -59,6 +59,12 @@ $(document).ready(function(){
$("#btnExcel").click(function() {
fn_excel();
});
// 발주서 송부 버튼 클릭
$("#btnSend").click(function(){
fn_sendPurchaseOrder();
});
//수주활동 복사 팝업
$("#btnCopy").click(function(){
var checkedObj = _tabulGrid.getSelectedData();
@@ -330,7 +336,16 @@ var columns = [
// {headerHozAlign:'center', hozAlign:'right', widthGrow:1.2, title:'총액', field:'TOTAL_PRICE_ALL',
// formatter:"money", formatterParams:{thousand:",", symbolAfter:"p", precision:false}
// },
{headerHozAlign:'center', hozAlign:'center', widthGrow:1, title:'메일발송', field:'MAIL_SEND_YN'},
{headerHozAlign:'center', hozAlign:'center', widthGrow:1, title:'메일발송', field:'MAIL_SEND_YN',
formatter: function(cell, formatterParams, onRendered){
var value = fnc_checkNull(cell.getValue());
if(value === 'Y'){
return '<span style="color:green;">발송완료</span>';
} else {
return '';
}
}
},
{headerHozAlign:'center', hozAlign:'center', widthGrow:1, title:'발주일', field:'REGDATE'}
];
@@ -699,6 +714,71 @@ function col_custom_fmt_un(cellvalue, options, cell) {
return cellvalue;
}
// 발주서 송부 (메일 발송 팝업)
function fn_sendPurchaseOrder(){
var selectedData = _tabulGrid.getSelectedData();
if(selectedData.length < 1){
Swal.fire("발주서를 송부할 행을 선택해주세요.");
return false;
} else if(selectedData.length > 1){
Swal.fire("한번에 한 개의 발주서만 발송 가능합니다.");
return false;
}
var status = fnc_checkNull(selectedData[0].STATUS);
var objId = fnc_checkNull(selectedData[0].OBJID);
var MULTI_YN = fnc_checkNull(selectedData[0].MULTI_YN);
var MULTI_MASTER_YN = fnc_checkNull(selectedData[0].MULTI_MASTER_YN);
var mailSendYn = fnc_checkNull(selectedData[0].MAIL_SEND_YN);
// 취소 상태 확인
if(status === "cancel"){
Swal.fire("취소된 발주서는 발송할 수 없습니다.");
return false;
}
// 동시발주 하위건 확인
if(MULTI_YN === 'Y' && MULTI_MASTER_YN !== 'Y'){
Swal.fire("동시발주 하위건은 마스터건으로 발송해주세요.");
return false;
}
// 이미 발송된 경우 재발송 확인
if(mailSendYn === 'Y'){
Swal.fire({
title: '이미 발송된 발주서입니다.',
text: '다시 발송하시겠습니까?',
icon: 'warning',
showCancelButton: true,
confirmButtonText: '재발송',
cancelButtonText: '취소'
}).then((result) => {
if(result.isConfirmed){
fn_openMailFormPopup(objId);
}
});
return false;
}
// 메일 발송 팝업 열기
fn_openMailFormPopup(objId);
}
// 발주서 메일 발송 팝업 열기
function fn_openMailFormPopup(purchaseOrderObjId){
if(!purchaseOrderObjId || purchaseOrderObjId === ''){
Swal.fire("잘못된 요청입니다.");
return;
}
var popup_width = 900;
var popup_height = 750;
var url = "/purchaseOrder/purchaseOrderMailFormPopup.do?purchaseOrderObjId=" + purchaseOrderObjId;
window.open(url, "purchaseOrderMailForm", "width="+popup_width+",height="+popup_height+",menubar=no,scrollbars=yes,resizable=yes");
}
</script>
<body class="<%--bodyNoScrollX--%>">
<form name="hiddenForm" id="hiddenForm" method="post">

View File

@@ -0,0 +1,539 @@
<%@ 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" %>
<%
PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
String connector = person.getUserId();
String purchaseOrderObjId = request.getParameter("purchaseOrderObjId");
%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>발주서 메일 발송</title>
<style>
body {
margin: 10px;
background-color: #f5f5f5;
font-size: 13px;
}
.mail-form-container {
background: white;
padding: 15px 20px;
border-radius: 6px;
box-shadow: 0 1px 5px rgba(0,0,0,0.1);
max-width: 800px;
margin: 0 auto;
}
.form-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
border-bottom: 2px solid #3085d6;
padding-bottom: 8px;
}
.form-group {
margin-bottom: 12px;
}
.form-group label {
display: block;
font-weight: bold;
margin-bottom: 5px;
color: #555;
font-size: 13px;
}
.form-group label.required:after {
content: " *";
color: red;
}
.form-group input[type="text"],
.form-group input[type="email"],
.form-group textarea {
width: 100%;
padding: 6px 8px;
border: 1px solid #ddd;
border-radius: 3px;
font-size: 13px;
box-sizing: border-box;
}
.form-group textarea {
min-height: 120px;
resize: vertical;
}
.manager-list {
border: 1px solid #ddd;
border-radius: 3px;
padding: 8px;
background-color: #fafafa;
max-height: 150px;
overflow-y: auto;
}
.manager-item {
padding: 5px;
margin-bottom: 3px;
background: white;
border-radius: 3px;
display: flex;
align-items: center;
font-size: 13px;
}
.manager-item input[type="checkbox"] {
margin-right: 8px;
width: 16px;
height: 16px;
cursor: pointer;
}
.manager-item label {
cursor: pointer;
margin: 0;
flex: 1;
font-size: 13px;
}
.no-managers {
color: #999;
text-align: center;
padding: 12px;
font-size: 13px;
}
.button-group {
text-align: center;
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid #eee;
}
.btn {
padding: 8px 20px;
margin: 0 4px;
border: none;
border-radius: 3px;
font-size: 13px;
cursor: pointer;
transition: all 0.3s;
}
.btn-primary {
background-color: #3085d6;
color: white;
}
.btn-primary:hover {
background-color: #2874c5;
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-secondary:hover {
background-color: #5a6268;
}
.info-text {
font-size: 11px;
color: #666;
margin-top: 3px;
}
.attachment-status {
padding: 8px 10px;
background-color: #e7f3ff;
border-left: 3px solid #3085d6;
border-radius: 3px;
margin-bottom: 12px;
font-size: 13px;
}
.attachment-status i {
color: #3085d6;
margin-right: 5px;
}
.order-info {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 3px;
padding: 10px;
margin-bottom: 12px;
}
.order-info-row {
display: flex;
margin-bottom: 5px;
}
.order-info-row:last-child {
margin-bottom: 0;
}
.order-info-label {
width: 100px;
font-weight: bold;
color: #555;
}
.order-info-value {
flex: 1;
color: #333;
}
</style>
</head>
<body>
<div class="mail-form-container">
<div class="form-title">발주서 메일 발송</div>
<!-- 발주서 정보 표시 -->
<div class="order-info" id="orderInfoArea">
<div class="order-info-row">
<span class="order-info-label">발주번호:</span>
<span class="order-info-value" id="displayPoNo">-</span>
</div>
<div class="order-info-row">
<span class="order-info-label">공급업체:</span>
<span class="order-info-value" id="displayPartnerName">-</span>
</div>
<div class="order-info-row">
<span class="order-info-label">프로젝트:</span>
<span class="order-info-value" id="displayProjectNo">-</span>
</div>
</div>
<div class="attachment-status">
<i class="fa fa-file-excel-o"></i>
<strong>첨부파일:</strong> 발주서 및 도면 파일이 자동으로 첨부됩니다.
</div>
<form id="mailForm">
<input type="hidden" id="purchaseOrderObjId" name="purchaseOrderObjId" value="<%=purchaseOrderObjId%>"/>
<!-- 공급업체 담당자 선택 -->
<div class="form-group">
<label>공급업체 담당자 선택</label>
<div id="managerListContainer" class="manager-list">
<div class="no-managers">담당자 정보를 불러오는 중...</div>
</div>
</div>
<!-- 수신인 이메일 -->
<div class="form-group">
<label for="toEmails" class="required">수신인 (To)</label>
<input type="text" id="toEmails" name="toEmails" placeholder="이메일 주소를 입력하세요 (여러 개는 쉼표로 구분)"/>
<div class="info-text">예: email1@example.com, email2@example.com</div>
</div>
<!-- 참조 이메일 -->
<div class="form-group">
<label for="ccEmails">참조 (CC)</label>
<input type="text" id="ccEmails" name="ccEmails" placeholder="참조 이메일 주소 (선택사항)"/>
<div class="info-text">발주 담당자 이메일이 자동으로 참조에 추가됩니다.</div>
</div>
<!-- 메일 제목 -->
<div class="form-group">
<label for="subject" class="required">제목</label>
<input type="text" id="subject" name="subject" placeholder="메일 제목을 입력하세요"/>
</div>
<!-- 메일 내용 -->
<div class="form-group">
<label for="contents" class="required">내용</label>
<textarea id="contents" name="contents" placeholder="메일 내용을 입력하세요"></textarea>
</div>
<!-- 버튼 -->
<div class="button-group">
<button type="button" class="btn btn-primary" onclick="fn_sendMail()">발송</button>
<button type="button" class="btn btn-secondary" onclick="window.close()">취소</button>
</div>
</form>
</div>
<script>
var purchaseOrderInfo = null;
var managerList = [];
$(document).ready(function(){
// 발주서 정보 및 담당자 목록 로드
fn_loadPurchaseOrderInfo();
});
// 발주서 정보 로드
function fn_loadPurchaseOrderInfo(){
var purchaseOrderObjId = $("#purchaseOrderObjId").val();
$.ajax({
url: "/purchaseOrder/getPurchaseOrderInfoForMail.do",
type: "POST",
data: { objId: purchaseOrderObjId },
dataType: "json",
success: function(data){
if(data.result === "success" && data.purchaseOrderInfo){
purchaseOrderInfo = data.purchaseOrderInfo;
// 발주서 정보 표시
$("#displayPoNo").text(fnc_checkNull(purchaseOrderInfo.PURCHASE_ORDER_NO) || '-');
$("#displayPartnerName").text(fnc_checkNull(purchaseOrderInfo.PARTNER_NAME) || '-');
$("#displayProjectNo").text(fnc_checkNull(purchaseOrderInfo.PROJECT_NO) || '-');
// 메일 제목 자동 생성
var poNo = fnc_checkNull(purchaseOrderInfo.PURCHASE_ORDER_NO);
var poTitle = fnc_checkNull(purchaseOrderInfo.TITLE);
$("#subject").val(poNo + " " + poTitle);
// 참조에 발주담당자 이메일 자동 추가
var salesMngEmail = fnc_checkNull(purchaseOrderInfo.SALES_MNG_USER_EMAIL);
if(salesMngEmail !== ""){
$("#ccEmails").val(salesMngEmail);
}
// 메일 내용 템플릿 생성
fn_generateMailTemplate();
// 공급업체 담당자 목록 로드
var partnerObjId = fnc_checkNull(purchaseOrderInfo.PARTNER_OBJID);
if(partnerObjId !== ""){
fn_loadPartnerManagers(partnerObjId);
} else {
$("#managerListContainer").html('<div class="no-managers">공급업체 정보가 없습니다.</div>');
}
} else {
Swal.fire({
title: '오류',
text: '발주서 정보를 불러올 수 없습니다.',
icon: 'error'
}).then(() => {
window.close();
});
}
},
error: function(){
Swal.fire({
title: '오류',
text: '발주서 정보 조회 중 오류가 발생했습니다.',
icon: 'error'
}).then(() => {
window.close();
});
}
});
}
// 공급업체 담당자 목록 로드
function fn_loadPartnerManagers(partnerObjId){
$.ajax({
url: "/purchaseOrder/getPartnerManagerList.do",
type: "POST",
data: { partnerObjId: partnerObjId },
dataType: "json",
success: function(data){
if(data.result === "success" && data.managers && data.managers.length > 0){
managerList = data.managers;
fn_renderManagerList();
} else {
// 공급업체의 대표 이메일 표시
var supplyEmail = fnc_checkNull(purchaseOrderInfo.SUPPLY_USER_EMAIL);
var supplyUserName = fnc_checkNull(purchaseOrderInfo.SUPPLY_USER_NAME);
if(supplyEmail !== ""){
var html = '<div class="manager-item">';
html += '<input type="checkbox" id="manager_default" data-email="' + supplyEmail + '" onchange="fn_updateRecipients()" checked>';
html += '<label for="manager_default">' + (supplyUserName || '담당자') + ' (' + supplyEmail + ')</label>';
html += '</div>';
$("#managerListContainer").html(html);
fn_updateRecipients();
} else {
$("#managerListContainer").html('<div class="no-managers">등록된 담당자가 없습니다. 수신인을 직접 입력해주세요.</div>');
}
}
},
error: function(){
$("#managerListContainer").html('<div class="no-managers">담당자 정보를 불러올 수 없습니다. 수신인을 직접 입력해주세요.</div>');
}
});
}
// 담당자 목록 렌더링
function fn_renderManagerList(){
var html = '';
for(var i = 0; i < managerList.length; i++){
var manager = managerList[i];
var name = fnc_checkNull(manager.name || manager.NAME);
var email = fnc_checkNull(manager.email || manager.EMAIL);
if(name !== ""){
html += '<div class="manager-item">';
html += '<input type="checkbox" id="manager_' + i + '" data-email="' + email + '" onchange="fn_updateRecipients()">';
html += '<label for="manager_' + i + '">' + name;
if(email !== ""){
html += ' (' + email + ')';
}
html += '</label>';
html += '</div>';
}
}
if(html === ''){
// 공급업체의 대표 이메일 표시
var supplyEmail = fnc_checkNull(purchaseOrderInfo.SUPPLY_USER_EMAIL);
var supplyUserName = fnc_checkNull(purchaseOrderInfo.SUPPLY_USER_NAME);
if(supplyEmail !== ""){
html = '<div class="manager-item">';
html += '<input type="checkbox" id="manager_default" data-email="' + supplyEmail + '" onchange="fn_updateRecipients()" checked>';
html += '<label for="manager_default">' + (supplyUserName || '담당자') + ' (' + supplyEmail + ')</label>';
html += '</div>';
} else {
html = '<div class="no-managers">등록된 담당자가 없습니다. 수신인을 직접 입력해주세요.</div>';
}
}
$("#managerListContainer").html(html);
// 기본 담당자가 있으면 자동 선택
if($("#manager_default").length > 0){
fn_updateRecipients();
}
}
// 담당자 선택 시 수신인 필드 업데이트
function fn_updateRecipients(){
var selectedEmails = [];
$("input[type='checkbox'][id^='manager_']:checked").each(function(){
var email = $(this).attr("data-email");
if(email && email !== ""){
selectedEmails.push(email);
}
});
$("#toEmails").val(selectedEmails.join(", "));
}
// 메일 내용 템플릿 생성
function fn_generateMailTemplate(){
var partnerName = fnc_checkNull(purchaseOrderInfo.PARTNER_NAME);
var poNo = fnc_checkNull(purchaseOrderInfo.PURCHASE_ORDER_NO);
var projectNo = fnc_checkNull(purchaseOrderInfo.PROJECT_NO);
var deliveryDate = fnc_checkNull(purchaseOrderInfo.DELIVERY_DATE);
var template = "안녕하세요.\n\n";
template += partnerName + " 귀하\n\n";
template += "발주서를 첨부파일로 송부드립니다.\n\n";
template += "발주번호: " + poNo + "\n";
if(projectNo !== ""){
template += "프로젝트: " + projectNo + "\n";
}
if(deliveryDate !== ""){
template += "입고요청일: " + deliveryDate + "\n";
}
template += "\n첨부된 발주서를 확인하시고, 납기 준수 부탁드립니다.\n\n";
template += "문의사항이 있으시면 연락 주시기 바랍니다.\n\n";
template += "감사합니다.\n";
$("#contents").val(template);
}
// 메일 발송
function fn_sendMail(){
// 입력값 검증
var toEmails = $("#toEmails").val().trim();
var subject = $("#subject").val().trim();
var contents = $("#contents").val().trim();
if(toEmails === ""){
Swal.fire("수신인을 입력해주세요.");
$("#toEmails").focus();
return;
}
// 이메일 형식 검증
var emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
var emails = toEmails.split(",").map(function(e){ return e.trim(); });
for(var i = 0; i < emails.length; i++){
if(!emailPattern.test(emails[i])){
Swal.fire("올바른 이메일 형식이 아닙니다: " + emails[i]);
$("#toEmails").focus();
return;
}
}
if(subject === ""){
Swal.fire("제목을 입력해주세요.");
$("#subject").focus();
return;
}
if(contents === ""){
Swal.fire("내용을 입력해주세요.");
$("#contents").focus();
return;
}
// 발송 확인
Swal.fire({
title: '메일 발송',
text: "발주서를 발송하시겠습니까?",
icon: 'question',
showCancelButton: true,
confirmButtonText: '발송',
cancelButtonText: '취소'
}).then((result) => {
if(result.isConfirmed){
fn_submitMailForm();
}
});
}
// 메일 발송 요청
function fn_submitMailForm(){
Swal.fire({
title: '발송 중...',
text: '발주서 메일을 발송하고 있습니다.',
allowOutsideClick: false,
onOpen: () => {
Swal.showLoading();
}
});
var formData = {
objId: $("#purchaseOrderObjId").val(),
toEmails: $("#toEmails").val(),
ccEmails: $("#ccEmails").val(),
subject: $("#subject").val(),
contents: $("#contents").val()
};
$.ajax({
url: "/purchaseOrder/sendPurchaseOrderMail.do",
type: "POST",
data: formData,
dataType: "json",
timeout: 120000, // 2분 타임아웃 (파일 첨부 시간 고려)
success: function(data){
Swal.close();
if(data.result === "success"){
Swal.fire({
title: '발송 완료',
text: '발주서가 성공적으로 발송되었습니다.',
icon: 'success'
}).then(() => {
// 부모 창 새로고침
if(window.opener && typeof window.opener.fn_search === 'function'){
window.opener.fn_search();
}
window.close();
});
} else {
Swal.fire({
title: '발송 실패',
text: data.message || '메일 발송 중 오류가 발생했습니다.',
icon: 'error'
});
}
},
error: function(){
Swal.close();
Swal.fire({
title: '오류',
text: '메일 발송 중 시스템 오류가 발생했습니다.',
icon: 'error'
});
}
});
}
</script>
</body>
</html>

View File

@@ -1644,4 +1644,135 @@ public class PurchaseOrderController {
}
return result;
}
/**
* 발주서 메일 발송 팝업
* @param request
* @param paramMap - purchaseOrderObjId
* @return
*/
@RequestMapping("/purchaseOrder/purchaseOrderMailFormPopup.do")
public String purchaseOrderMailFormPopup(HttpServletRequest request, @RequestParam Map paramMap){
return "/purchaseOrder/purchaseOrderMailFormPopup";
}
/**
* 발주서 정보 조회 (메일 발송용) (AJAX)
* @param request
* @param paramMap - objId (PURCHASE_ORDER_MASTER_OBJID)
* @return
*/
@ResponseBody
@RequestMapping("/purchaseOrder/getPurchaseOrderInfoForMail.do")
public Map getPurchaseOrderInfoForMail(HttpServletRequest request, @RequestParam Map paramMap){
Map resultMap = new HashMap();
try {
String objId = CommonUtils.checkNull(paramMap.get("objId"));
if("".equals(objId)){
resultMap.put("result", "error");
resultMap.put("message", "잘못된 요청입니다.");
return resultMap;
}
paramMap.put("PURCHASE_ORDER_MASTER_OBJID", objId);
Map purchaseOrderInfo = purchaseOrderService.getPurchaseOrderMasterInfo(request, paramMap);
if(purchaseOrderInfo != null){
// 키를 대문자로 변환
purchaseOrderInfo = CommonUtils.toUpperCaseMapKey(purchaseOrderInfo);
resultMap.put("result", "success");
resultMap.put("purchaseOrderInfo", purchaseOrderInfo);
} else {
resultMap.put("result", "error");
resultMap.put("message", "발주서 정보를 찾을 수 없습니다.");
}
} catch (Exception e) {
e.printStackTrace();
resultMap.put("result", "error");
resultMap.put("message", "발주서 정보 조회 중 오류가 발생했습니다.");
}
return resultMap;
}
/**
* 공급업체 담당자 목록 조회 (AJAX)
* @param request
* @param paramMap - partnerObjId
* @return
*/
@ResponseBody
@RequestMapping("/purchaseOrder/getPartnerManagerList.do")
public Map getPartnerManagerList(HttpServletRequest request, @RequestParam Map paramMap){
Map resultMap = new HashMap();
try {
String partnerObjId = CommonUtils.checkNull(paramMap.get("partnerObjId"));
if("".equals(partnerObjId)){
resultMap.put("result", "error");
resultMap.put("message", "공급업체 정보가 없습니다.");
return resultMap;
}
// 공급업체 담당자 목록 조회
paramMap.put("SUPPLY_OBJID", partnerObjId);
List managerList = commonService.selectList("admin.getSupplyManagerList", request, paramMap);
if(managerList != null && managerList.size() > 0){
// 키를 대문자로 변환
managerList = CommonUtils.keyChangeUpperList(managerList);
resultMap.put("result", "success");
resultMap.put("managers", managerList);
} else {
resultMap.put("result", "success");
resultMap.put("managers", new ArrayList());
}
} catch (Exception e) {
e.printStackTrace();
resultMap.put("result", "error");
resultMap.put("message", "담당자 목록 조회 중 오류가 발생했습니다.");
}
return resultMap;
}
/**
* 발주서 메일 발송 (AJAX)
* @param session
* @param request
* @param paramMap - objId, toEmails, ccEmails, subject, contents
* @return
*/
@ResponseBody
@RequestMapping("/purchaseOrder/sendPurchaseOrderMail.do")
public Map sendPurchaseOrderMail(HttpSession session, HttpServletRequest request, @RequestParam Map paramMap){
Map resultMap = new HashMap();
try {
PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
paramMap.put("WRITER", CommonUtils.checkNull(person.getUserId()));
paramMap.put("WRITER_EMAIL", CommonUtils.checkNull(person.getEmail()));
String objId = CommonUtils.checkNull(paramMap.get("objId"));
if("".equals(objId)){
resultMap.put("result", "error");
resultMap.put("message", "잘못된 요청입니다.");
return resultMap;
}
// 메일 발송 서비스 호출
resultMap = purchaseOrderService.sendPurchaseOrderMailManual(request, paramMap);
} catch (Exception e) {
e.printStackTrace();
resultMap.put("result", "error");
resultMap.put("message", "메일 발송 중 오류가 발생했습니다: " + e.getMessage());
}
return resultMap;
}
}

View File

@@ -9366,4 +9366,17 @@ SELECT
LIMIT 1
</select>
<!-- 공급업체 담당자 목록 조회 (발주서 메일 발송용) -->
<select id="getSupplyManagerList" parameterType="map" resultType="map">
SELECT
SCM.OBJID,
SCM.CHARGER_NAME AS NAME,
SCM.EMAIL,
SCM.PHONE,
SCM.TEL
FROM SUPPLY_CHARGER_MNG SCM
WHERE SCM.SUPPLY_OBJID = #{SUPPLY_OBJID}
ORDER BY SCM.REGDATE DESC
</select>
</mapper>

View File

@@ -3078,6 +3078,8 @@ SELECT POM.OBJID
,POM.MULTI_MASTER_OBJID
,CASE WHEN POM.MULTI_MASTER_YN = 'Y' THEN '' ELSE POM.MULTI_YN END MULTI_YN_MAKED
,POM.MAIL_SEND_YN
,POM.MAIL_SEND_DATE
,POM.STATUS
,A.APPR_STATUS
,CASE WHEN POM.STATUS = 'cancel' then '취소'
@@ -5862,4 +5864,13 @@ FROM(
AND P.PART_OBJID = #{objId}
</select>
<!-- 발주서 메일 발송 상태 업데이트 -->
<update id="updateMailSendStatus" parameterType="map">
UPDATE PURCHASE_ORDER_MASTER
SET
MAIL_SEND_YN = #{MAIL_SEND_YN}
,MAIL_SEND_DATE = #{MAIL_SEND_DATE}
WHERE OBJID = #{OBJID}
</update>
</mapper>

View File

@@ -2279,6 +2279,7 @@ public class PurchaseOrderService {
/**
* 발주서 결재완료 후처리
* - 자동 메일 발송 기능 비활성화 (2024.12 - 발주서 송부 버튼으로 수동 발송으로 변경)
*/
public void afterApprovalCompleteRouteInfo(SqlSession sqlSession, Map<String, Object> routeMap, Map<String, Object> targetMap, Map sqlParamMap) {
String targetObjId = CommonUtils.checkNull(sqlParamMap.get("targetObjId"));
@@ -2305,6 +2306,10 @@ public class PurchaseOrderService {
sqlParamMap.put("act_status", "0001065"); //발주완료
sqlSession.update("purchaseOrder.salesPartChgActStatusByPurchaseOrderId", sqlParamMap);
// 2024.12 - 자동 메일 발송 비활성화
// 발주서 메일 발송은 목록 화면의 "발주서 송부" 버튼을 통해 수동으로 발송합니다.
// 수동 발송 메서드: sendPurchaseOrderMailManual()
/*
//2. 발주서 메일발송
try{
@@ -2332,13 +2337,6 @@ public class PurchaseOrderService {
String subject = poNo + poTitle;
//내용
/*Map param = new HashMap();
param.put("SUBJECT" , subject);
param.put("CAR_CODE" , "CAR-BENZ0001");
param.put("CAR_NAME" , "G63 AMG 6x6");
param.put("PRODUCT_GROUP_NAME", "메르세데스벤츠");
param.put("PRODUCT_NAME" , "지바겐");
String contents = MailUtil.getHTMLContents("mailTemplate2", param);*/
String contents_table = this.makePoContentsTable(masterInfo, detailList, apprList, multiMasterList);
String contents = this.makeMailContents(contents_table);
@@ -2370,6 +2368,7 @@ public class PurchaseOrderService {
}catch(Exception e){
e.printStackTrace();
}
*/
}
/**
@@ -2914,4 +2913,194 @@ public class PurchaseOrderService {
sqlSession.close();
}
}
/**
* 발주서 메일 수동 발송 (버튼 클릭 시)
* @param request
* @param paramMap - objId, toEmails, ccEmails, subject, contents
* @return 발송 결과
*/
public Map sendPurchaseOrderMailManual(HttpServletRequest request, Map paramMap) {
Map resultMap = new HashMap();
try {
String targetObjId = CommonUtils.checkNull(paramMap.get("objId"));
String toEmails = CommonUtils.checkNull(paramMap.get("toEmails"));
String ccEmails = CommonUtils.checkNull(paramMap.get("ccEmails"));
String subject = CommonUtils.checkNull(paramMap.get("subject"));
String contents = CommonUtils.checkNull(paramMap.get("contents"));
String writerEmail = CommonUtils.checkNull(paramMap.get("WRITER_EMAIL"));
String writer = CommonUtils.checkNull(paramMap.get("WRITER"));
// 발주서 정보 조회
Map infoParam = new HashMap();
infoParam.put("objId", targetObjId);
infoParam.put("PURCHASE_ORDER_MASTER_OBJID", targetObjId);
Map masterInfo = commonService.selectOne("purchaseOrder.getPurchaseOrderMasterInfo", null, infoParam);
if(masterInfo == null) {
resultMap.put("result", "error");
resultMap.put("message", "발주서 정보를 찾을 수 없습니다.");
return resultMap;
}
// 발주부품목록, 결재정보, 동시발주목록, 도면목록 조회
infoParam.put("MULTI_MASTER_OBJID", targetObjId);
List detailList = commonService.selectList("purchaseOrder.getPURCHASE_ORDER_PART", null, infoParam);
List apprList = commonService.getApprovalLine(infoParam);
List multiMasterList = commonService.selectList("purchaseOrder.selectPurchaseOrderMasterList", null, infoParam);
ArrayList partFileList = commonService.selectList("purchaseOrder.purchaseOrderPartFileListForMail", null, infoParam);
SimpleDateFormat frm = new SimpleDateFormat("yyyyMMddHHmm");
SimpleDateFormat frm2 = new SimpleDateFormat("yyyy-MM-dd");
Calendar cal = Calendar.getInstance();
String todayKor = frm.format(cal.getTime());
String poNo = CommonUtils.checkNull((String)masterInfo.get("PURCHASE_ORDER_NO"));
String excelName = "발주서_" + poNo + "_" + todayKor + ".xls";
String zipName = "도면_" + poNo + "_" + todayKor + ".zip";
masterInfo.put("EXCELFILE_NAME", excelName);
masterInfo.put("APPR_COMPLETE_DATE", frm2.format(cal.getTime()));
// 발주서 테이블 내용 생성
String contents_table = this.makePoContentsTable(masterInfo, detailList, apprList, multiMasterList);
// 메일 본문 생성 (사용자 입력 내용 + 발주서 테이블)
String mailContents = this.makeMailContentsWithUserInput(contents, contents_table);
// 수신인 이메일 목록 생성
ArrayList<String> toUserIdList = new ArrayList<String>();
ArrayList<String> toEmailList = new ArrayList<String>();
String[] toEmailArr = toEmails.split(",");
for(String email : toEmailArr) {
email = email.trim();
if(!"".equals(email)) {
toEmailList.add(email);
toUserIdList.add(""); // 이름은 비워둠
}
}
// 참조 이메일 목록 생성
ArrayList<String> ccEmailList = new ArrayList<String>();
if(!"".equals(ccEmails)) {
String[] ccEmailArr = ccEmails.split(",");
for(String email : ccEmailArr) {
email = email.trim();
if(!"".equals(email)) {
ccEmailList.add(email);
}
}
}
// 작성자 이메일을 참조에 추가 (중복 방지)
if(!"".equals(writerEmail) && !ccEmailList.contains(writerEmail)) {
ccEmailList.add(writerEmail);
}
// 첨부파일 목록 생성
ArrayList<HashMap> attachFileList = new ArrayList<HashMap>();
// 1. 발주서 엑셀 파일 첨부
HashMap excelFile = this.makeMailAttachFileOrderSheet(masterInfo, contents_table);
if(excelFile != null) {
attachFileList.add(excelFile);
}
// 2. 도면 파일 압축 첨부
if(partFileList != null && partFileList.size() > 0) {
File zf = MailUtil.zipFileListMail(zipName, partFileList);
if(zf != null && zf.exists()) {
HashMap hm = new HashMap();
hm.put(Constants.Db.COL_FILE_REAL_NAME, zf.getName());
hm.put(Constants.Db.COL_FILE_SAVED_NAME, zf.getName());
hm.put(Constants.Db.COL_FILE_PATH, zf.getPath().replace("\\" + zf.getName(), ""));
attachFileList.add(hm);
}
}
// 메일 발송
String fromUser = writer;
String fromEmail = writerEmail;
if("".equals(fromEmail)) {
fromEmail = CommonUtils.checkNull((String)masterInfo.get("WRITER_EMAIL"));
}
if("".equals(fromUser)) {
fromUser = CommonUtils.checkNull((String)masterInfo.get("WRITER"));
}
boolean sendResult = MailUtil.sendMailWithAttachFile(fromUser, fromEmail, toUserIdList, toEmailList, ccEmailList, null, null, subject, mailContents, attachFileList, "PURCHASE_ORDER");
if(sendResult) {
// 메일 발송 성공 시 DB 업데이트
SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
try {
Map updateParam = new HashMap();
updateParam.put("OBJID", targetObjId);
updateParam.put("MAIL_SEND_YN", "Y");
updateParam.put("MAIL_SEND_DATE", frm2.format(cal.getTime()));
sqlSession.update("purchaseOrder.updateMailSendStatus", updateParam);
sqlSession.commit();
} catch(Exception e) {
sqlSession.rollback();
e.printStackTrace();
} finally {
sqlSession.close();
}
resultMap.put("result", "success");
resultMap.put("message", "메일이 성공적으로 발송되었습니다.");
} else {
resultMap.put("result", "error");
resultMap.put("message", "메일 발송에 실패했습니다.");
}
} catch(Exception e) {
e.printStackTrace();
resultMap.put("result", "error");
resultMap.put("message", "메일 발송 중 오류가 발생했습니다: " + e.getMessage());
}
return resultMap;
}
/**
* 사용자 입력 내용과 발주서 테이블을 합친 메일 본문 생성
* @param userContents 사용자가 입력한 메일 내용
* @param poContentsTable 발주서 테이블 HTML
* @return 완성된 메일 본문
*/
private String makeMailContentsWithUserInput(String userContents, String poContentsTable) {
StringBuffer sb = new StringBuffer();
sb.append("<!DOCTYPE html><html lang='ko' dir='ltr'>");
sb.append("<head>");
sb.append(" <meta http-equiv='Content-Type' content='text/html; charset=euc-kr'>");
sb.append(" <style>");
sb.append(" @import url(http://fonts.googleapis.com/earlyaccess/notosanskr.css);");
sb.append(" @import url(https://fonts.googleapis.com/css?family=Roboto:100,300,500,500,700,900);");
sb.append(" body {font-family: 'Noto Sans KR', sans-serif; background: #fff; width: 100%; position:relative;}");
sb.append(" .table {border-collapse: collapse; border-top:2px solid rgb(72, 155, 191); margin-top: 10px;}");
sb.append(" .table td {border: 1px solid #ccc; border-radius: 5px; font-size: 13px; padding: 5px; border-bottom:1px solid #000;}");
sb.append(" </style>");
sb.append("</head>");
sb.append("<body style='margin:0; padding:20px;'>");
// 사용자 입력 내용 (줄바꿈을 <br>로 변환)
sb.append("<div style='white-space: pre-line; margin-bottom: 30px;'>");
sb.append(userContents.replace("\n", "<br>"));
sb.append("</div>");
// 구분선
sb.append("<hr style='border: 1px solid #ddd; margin: 20px 0;'>");
// 발주서 테이블
sb.append("<div style='margin-top: 20px;'>");
sb.append("<h3 style='margin-bottom: 10px;'>[ 발주서 상세 ]</h3>");
sb.append(poContentsTable);
sb.append("</div>");
sb.append("<div style='margin-top: 20px; color: #666;'>※발신전용 메일입니다.</div>");
sb.append("</body>");
sb.append("</html>");
return sb.toString();
}
}