Compare commits
139 Commits
V202510170
...
feature/ne
| Author | SHA1 | Date | |
|---|---|---|---|
| 11638ac43c | |||
| 50baa3d75e | |||
| 399062a9a0 | |||
| ded4a70b01 | |||
|
|
44b43df82b | ||
| f26e92d9e3 | |||
|
|
4ca4bd2227 | ||
| 0dd9b8ab6d | |||
|
|
37980f8565 | ||
| 7558206e8b | |||
|
|
1ad393e939 | ||
| f9b20817a3 | |||
|
|
49dd4b7c88 | ||
|
|
37285fbce6 | ||
| e29305c1dc | |||
|
|
a41c75c0f8 | ||
| d4500e3f0b | |||
| b5e50366d4 | |||
| 8bb05b060c | |||
| 21652fc0d1 | |||
| 590ab6531b | |||
|
|
b58b021dc9 | ||
|
|
eef76746ac | ||
| a0acfc8067 | |||
|
|
37d5427236 | ||
| 5d07b23098 | |||
|
|
3f15362325 | ||
| ee0389ad8a | |||
| 87d6e0db0f | |||
|
|
7fa815a0cc | ||
| aa7b03c959 | |||
|
|
81d4b5a169 | ||
| 5cf3394df8 | |||
|
|
0ac007eeb7 | ||
| 50ed8a4ee1 | |||
|
|
79ed8ee646 | ||
|
|
8645e74168 | ||
| d61856111f | |||
|
|
5e92d29028 | ||
| 56d4387519 | |||
|
|
f6d26de480 | ||
|
|
1d3d8f53e7 | ||
| 75febfdddb | |||
|
|
c600012172 | ||
| 7256f40e0f | |||
| 8c9ec367af | |||
| d1a3ae950d | |||
| 7b4167d483 | |||
| 4d56d649e1 | |||
|
|
6e7bdbf284 | ||
| 25293d9273 | |||
|
|
5ce29bf7b9 | ||
|
|
42bb799b73 | ||
| 643ece2589 | |||
|
|
d09ae17a85 | ||
|
|
30b341753c | ||
| c8cf1be26b | |||
|
|
cfa031b20b | ||
| 4a9577c554 | |||
| 128c3102c5 | |||
|
|
56ee15565f | ||
|
|
7a718f9fe7 | ||
| 0c44d19201 | |||
|
|
d4da64ddfb | ||
| 33f4327726 | |||
|
|
8954ae559e | ||
| 39593ab3bc | |||
| c27a20012a | |||
| 29989da63e | |||
|
|
91b97be376 | ||
| c7b4238a54 | |||
|
|
7a37d6efd0 | ||
|
|
84b4984112 | ||
| 2998a7609d | |||
|
|
3c1a16f43c | ||
| 523ad33216 | |||
|
|
9ff43f0781 | ||
|
|
775d56a7f9 | ||
| 1d72f21e15 | |||
|
|
697dbe84a8 | ||
| 3c1c27c119 | |||
|
|
318e371249 | ||
| 5613e3bd54 | |||
|
|
661e1df716 | ||
|
|
e8650f058d | ||
| 104378e52f | |||
|
|
59359559a6 | ||
|
|
a297e660d4 | ||
| 08d58ec68e | |||
| ace0e3c35f | |||
|
|
2feeb8c341 | ||
|
|
e149bc99f1 | ||
| 1db4c83499 | |||
|
|
c793cd05ae | ||
| e95d38cff6 | |||
|
|
f944e754b6 | ||
|
|
c46f118f58 | ||
|
|
e2c60f4d63 | ||
| 7ae55dfa1f | |||
|
|
caa19e50d1 | ||
| 5f7b435c70 | |||
|
|
657a8d3234 | ||
| 9c3bea7e95 | |||
|
|
ff74ac99ca | ||
|
|
d69dfe8d95 | ||
|
|
01ebc04dfd | ||
| 37702b64eb | |||
|
|
af1c73345d | ||
| 15af60c44e | |||
| 6969e469dc | |||
| bff85e1da5 | |||
|
|
588fef91ce | ||
|
|
df0a534f0e | ||
|
|
bc6a7dcf7e | ||
| 934c220abc | |||
| a802714411 | |||
| 13164de870 | |||
|
|
a19388eb1c | ||
|
|
1d93f6ef3a | ||
| ba7edde4da | |||
| 709f2b4d4b | |||
| 5514ab3bb7 | |||
|
|
63043f6909 | ||
|
|
2b12fc3f6e | ||
| 23ebcd3b88 | |||
| 967b8b6529 | |||
| b463ab84e3 | |||
| 09d225803d | |||
|
|
9c8d71c924 | ||
| daf5d9bce4 | |||
|
|
1185aa491f | ||
|
|
dff92cedf7 | ||
| 3a72111b20 | |||
| a4d1af4d91 | |||
| 5d288975ae | |||
| f00462ed70 | |||
| 42d1cd4cd0 | |||
|
|
f171212092 | ||
| 221a2a3f8a |
BIN
.playwright-mcp/structure_popup_main.png
Normal file
BIN
.playwright-mcp/structure_popup_main.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 426 KiB |
File diff suppressed because it is too large
Load Diff
@@ -2869,4 +2869,138 @@
|
||||
</if>
|
||||
ORDER BY SUBSTRING(PROJECT_NO,POSITION('-' IN PROJECT_NO)+1) DESC
|
||||
</select>
|
||||
|
||||
<!-- M-BOM 관리 목록 조회 -->
|
||||
<select id="mBomMgmtGridList" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT
|
||||
PM.OBJID,
|
||||
PM.CONTRACT_OBJID,
|
||||
PM.PROJECT_NO,
|
||||
CM.CATEGORY_CD,
|
||||
COALESCE(
|
||||
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.CATEGORY_CD LIMIT 1),
|
||||
''
|
||||
) AS CATEGORY_NAME,
|
||||
CM.PRODUCT,
|
||||
COALESCE(
|
||||
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.PRODUCT LIMIT 1),
|
||||
''
|
||||
) AS PRODUCT_NAME,
|
||||
CM.AREA_CD,
|
||||
COALESCE(
|
||||
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.AREA_CD LIMIT 1),
|
||||
''
|
||||
) AS AREA_NAME,
|
||||
TO_CHAR(PM.REGDATE, 'YYYY-MM-DD') AS RECEIPT_DATE,
|
||||
CM.CUSTOMER_OBJID,
|
||||
COALESCE(
|
||||
(SELECT SUPPLY_NAME FROM SUPPLY_MNG WHERE OBJID = CM.CUSTOMER_OBJID::NUMERIC LIMIT 1),
|
||||
''
|
||||
) AS CUSTOMER_NAME,
|
||||
CM.PAID_TYPE,
|
||||
COALESCE(
|
||||
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.PAID_TYPE LIMIT 1),
|
||||
CASE
|
||||
WHEN CM.PAID_TYPE = 'paid' THEN '유상'
|
||||
WHEN CM.PAID_TYPE = 'free' THEN '무상'
|
||||
ELSE ''
|
||||
END
|
||||
) AS PAID_TYPE_NAME,
|
||||
COALESCE(PM.PART_NO, '') AS PART_NO,
|
||||
COALESCE(PM.PART_NAME, '') AS PART_NAME,
|
||||
-- S/N: CONTRACT_ITEM_SERIAL에서 조회
|
||||
(SELECT
|
||||
CASE
|
||||
WHEN COUNT(*) = 0 THEN ''
|
||||
WHEN COUNT(*) = 1 THEN MIN(CIS.SERIAL_NO)
|
||||
ELSE MIN(CIS.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건'
|
||||
END
|
||||
FROM CONTRACT_ITEM CI
|
||||
LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND UPPER(CIS.STATUS) = 'ACTIVE'
|
||||
WHERE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID
|
||||
AND CI.PART_OBJID = PM.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE'
|
||||
AND CIS.SERIAL_NO IS NOT NULL) AS SERIAL_NO,
|
||||
COALESCE(PM.QUANTITY::numeric, 0) AS QUANTITY,
|
||||
-- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT, 없으면 CONTRACT_MGMT
|
||||
COALESCE(
|
||||
(SELECT CI.DUE_DATE
|
||||
FROM CONTRACT_ITEM CI
|
||||
WHERE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID
|
||||
AND CI.PART_OBJID = PM.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE' LIMIT 1),
|
||||
PM.DUE_DATE,
|
||||
CM.req_del_date
|
||||
) AS REQ_DEL_DATE,
|
||||
-- 고객요청사항: CONTRACT_ITEM에서 가져옴
|
||||
COALESCE(
|
||||
(SELECT CI.CUSTOMER_REQUEST
|
||||
FROM CONTRACT_ITEM CI
|
||||
WHERE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID
|
||||
AND CI.PART_OBJID = PM.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE' LIMIT 1),
|
||||
''
|
||||
) AS CUSTOMER_REQUEST,
|
||||
-- E-BOM 정보: PM.PART_OBJID가 E-BOM OBJID를 직접 가리킴
|
||||
PM.PART_OBJID AS BOM_REPORT_OBJID,
|
||||
COALESCE(
|
||||
(SELECT PBR.STATUS
|
||||
FROM PART_BOM_REPORT PBR
|
||||
WHERE PBR.OBJID::VARCHAR = PM.PART_OBJID
|
||||
LIMIT 1),
|
||||
''
|
||||
) AS EBOM_STATUS,
|
||||
COALESCE(
|
||||
(SELECT TO_CHAR(PBR.REGDATE, 'YYYY-MM-DD')
|
||||
FROM PART_BOM_REPORT PBR
|
||||
WHERE PBR.OBJID::VARCHAR = PM.PART_OBJID
|
||||
LIMIT 1),
|
||||
''
|
||||
) AS EBOM_REGDATE,
|
||||
COALESCE(PM.MBOM_STATUS, '') AS MBOM_STATUS,
|
||||
'1.0' AS MBOM_VERSION,
|
||||
TO_CHAR(PM.REGDATE, 'YYYY-MM-DD') AS MBOM_REGDATE
|
||||
FROM
|
||||
PROJECT_MGMT PM
|
||||
LEFT JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
|
||||
WHERE 1=1
|
||||
AND PM.PROJECT_NO IS NOT NULL
|
||||
AND PM.PROJECT_NO != ''
|
||||
<!-- 품번 검색 (대소문자 구분 없음) -->
|
||||
<if test="search_part_no != null and search_part_no != ''">
|
||||
AND UPPER(PM.PART_NO) LIKE '%' || UPPER(#{search_part_no}) || '%'
|
||||
</if>
|
||||
<!-- 품명 검색 (대소문자 구분 없음) -->
|
||||
<if test="search_part_name != null and search_part_name != ''">
|
||||
AND UPPER(PM.PART_NAME) LIKE '%' || UPPER(#{search_part_name}) || '%'
|
||||
</if>
|
||||
ORDER BY PM.REGDATE DESC
|
||||
</select>
|
||||
|
||||
<!-- E-BOM을 PROJECT_MGMT에 할당 -->
|
||||
<update id="assignEbomToProject" parameterType="map">
|
||||
UPDATE PROJECT_MGMT
|
||||
SET
|
||||
PART_OBJID = #{bomReportObjid}
|
||||
WHERE OBJID = #{projectMgmtObjid}
|
||||
</update>
|
||||
|
||||
<!-- E-BOM 정보 조회 -->
|
||||
<select id="getEbomInfo" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT
|
||||
T.OBJID,
|
||||
T.PRODUCT_CD,
|
||||
CODE_NAME(T.PRODUCT_CD) as PRODUCT_NAME,
|
||||
T.PART_NO,
|
||||
T.PART_NAME,
|
||||
T.STATUS,
|
||||
T.REVISION,
|
||||
TO_CHAR(T.REGDATE, 'YYYY-MM-DD') AS REG_DATE,
|
||||
UI.USER_NAME AS WRITER_NAME,
|
||||
UI.DEPT_NAME
|
||||
FROM
|
||||
PART_BOM_REPORT T
|
||||
LEFT JOIN USER_INFO UI ON UI.USER_ID = T.WRITER
|
||||
WHERE T.OBJID::VARCHAR = #{objid}
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
@@ -3916,22 +3916,46 @@
|
||||
ELSE O.PAID_TYPE
|
||||
END
|
||||
FROM CONTRACT_MGMT AS O WHERE O.OBJID = T.CONTRACT_OBJID) AS FREE_OF_CHARGE
|
||||
,(SELECT SUM(O.QUANTITY) FROM CONTRACT_ITEM AS O WHERE O.CONTRACT_OBJID = T.CONTRACT_OBJID) AS CONTRACT_QTY
|
||||
,(SELECT STRING_AGG(O.PART_NO, ', ' ORDER BY O.SEQ) FROM CONTRACT_ITEM AS O WHERE O.CONTRACT_OBJID = T.CONTRACT_OBJID AND O.PART_NO IS NOT NULL AND O.PART_NO != '') AS PRODUCT_ITEM_CODE
|
||||
,(SELECT STRING_AGG(O.PART_NAME, ', ' ORDER BY O.SEQ) FROM CONTRACT_ITEM AS O WHERE O.CONTRACT_OBJID = T.CONTRACT_OBJID AND O.PART_NAME IS NOT NULL AND O.PART_NAME != '') AS PRODUCT_ITEM_NAME
|
||||
,(SELECT STRING_AGG(S.SERIAL_NO, ', ' ORDER BY S.SEQ)
|
||||
FROM CONTRACT_ITEM AS I
|
||||
LEFT JOIN CONTRACT_ITEM_SERIAL AS S ON S.ITEM_OBJID = I.OBJID AND S.STATUS = 'ACTIVE'
|
||||
WHERE I.CONTRACT_OBJID = T.CONTRACT_OBJID
|
||||
AND S.SERIAL_NO IS NOT NULL) AS SERIAL_NO
|
||||
-- 영업관리_주문서관리_수주등록
|
||||
,EBOM_STATUS
|
||||
,MBOM_STATUS
|
||||
,(SELECT O.PURCHASE_DATE FROM PURCHASE_ORDER_MASTER AS O WHERE O.CONTRACT_MGMT_OBJID = T.CONTRACT_OBJID LIMIT 1) AS ORDER_DATE
|
||||
,RECEIVING_RATE
|
||||
,PRODUCTION_TEAM_12
|
||||
,PRODUCTION_TEAM_3
|
||||
,(SELECT O.SHIPPING_DATE::TEXT FROM SHIPMENT_LOG AS O WHERE O.TARGET_OBJID = T.CONTRACT_OBJID LIMIT 1) AS SHIPMENT_DATE
|
||||
,COALESCE(T.QUANTITY::numeric, 0) AS CONTRACT_QTY
|
||||
,T.PART_NO AS PRODUCT_ITEM_CODE
|
||||
,T.PART_NAME AS PRODUCT_ITEM_NAME
|
||||
-- S/N: 해당 품목의 시리얼 번호 (여러 개일 경우 "S/N 외 N건" 형식)
|
||||
,(SELECT
|
||||
CASE
|
||||
WHEN COUNT(*) = 0 THEN ''
|
||||
WHEN COUNT(*) = 1 THEN MIN(S.SERIAL_NO)
|
||||
ELSE MIN(S.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건'
|
||||
END
|
||||
FROM CONTRACT_ITEM AS I
|
||||
LEFT JOIN CONTRACT_ITEM_SERIAL AS S ON S.ITEM_OBJID = I.OBJID AND S.STATUS = 'ACTIVE'
|
||||
WHERE I.CONTRACT_OBJID = T.CONTRACT_OBJID
|
||||
AND I.PART_OBJID = T.PART_OBJID
|
||||
AND S.SERIAL_NO IS NOT NULL) AS SERIAL_NO
|
||||
-- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT.DUE_DATE, 없으면 CONTRACT_MGMT.due_date
|
||||
,COALESCE(
|
||||
(SELECT CI.DUE_DATE
|
||||
FROM CONTRACT_ITEM CI
|
||||
WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID
|
||||
AND CI.PART_OBJID = T.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE'),
|
||||
T.DUE_DATE,
|
||||
(SELECT CM.due_date FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID)
|
||||
) AS REQ_DEL_DATE
|
||||
-- 영업관리_주문서관리_수주등록
|
||||
,EBOM_STATUS
|
||||
,MBOM_STATUS
|
||||
-- 발주일: CONTRACT_MGMT 테이블에서 가져오기 (영업관리_주문서관리와 동일)
|
||||
,(SELECT O.ORDER_DATE FROM CONTRACT_MGMT AS O WHERE O.OBJID = T.CONTRACT_OBJID) AS ORDER_DATE
|
||||
,RECEIVING_RATE
|
||||
,PRODUCTION_TEAM_12
|
||||
,PRODUCTION_TEAM_3
|
||||
-- 출하일: sales_registration 테이블에서 가져오기 (영업관리_판매관리와 동일)
|
||||
,COALESCE(
|
||||
(SELECT TO_CHAR(SR.shipping_date, 'YYYY-MM-DD')
|
||||
FROM sales_registration SR
|
||||
WHERE SR.project_no = T.PROJECT_NO),
|
||||
''
|
||||
) AS SHIPMENT_DATE
|
||||
,(((SELECT SUM(COALESCE(DESIGN_RATE,'0')::INTEGER) / COUNT(1) FROM PMS_WBS_TASK AS O WHERE O.CONTRACT_OBJID = T.OBJID)
|
||||
+(SELECT SUM(COALESCE(PURCHASE_RATE,'0')::INTEGER) / COUNT(1) FROM PMS_WBS_TASK AS O WHERE O.CONTRACT_OBJID = T.OBJID)
|
||||
+(SELECT SUM(COALESCE(PRODUCE_RATE,'0')::INTEGER) / COUNT(1) FROM PMS_WBS_TASK AS O WHERE O.CONTRACT_OBJID = T.OBJID)
|
||||
|
||||
@@ -816,6 +816,7 @@
|
||||
<select id="getSalesMgmtGridList" parameterType="map" resultType="map">
|
||||
/* salesNcollectMgmt.getSalesMgmtGridList - sales_registration LEFT JOIN으로 최적화 */
|
||||
SELECT
|
||||
T.OBJID,
|
||||
T.PROJECT_NO,
|
||||
T.CONTRACT_OBJID,
|
||||
CODE_NAME(T.CATEGORY_CD) AS ORDER_TYPE,
|
||||
@@ -832,7 +833,7 @@
|
||||
FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID) AS PAYMENT_TYPE,
|
||||
T.PART_NO AS PRODUCT_NO,
|
||||
T.PART_NAME AS PRODUCT_NAME,
|
||||
-- S/N: CONTRACT_ITEM_SERIAL에서만 조회 (품목별로 정확하게 매칭)
|
||||
-- S/N: 해당 품목의 시리얼 번호 (여러 개일 경우 "S/N 외 N건" 형식)
|
||||
(SELECT
|
||||
CASE
|
||||
WHEN COUNT(*) = 0 THEN ''
|
||||
@@ -840,11 +841,10 @@
|
||||
ELSE MIN(CIS.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건'
|
||||
END
|
||||
FROM CONTRACT_ITEM CI
|
||||
LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID
|
||||
LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND UPPER(CIS.STATUS) = 'ACTIVE'
|
||||
WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID
|
||||
AND CI.PART_OBJID = T.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE'
|
||||
AND UPPER(CIS.STATUS) = 'ACTIVE'
|
||||
AND CIS.SERIAL_NO IS NOT NULL) AS SERIAL_NO,
|
||||
COALESCE(T.QUANTITY::numeric, 0) AS ORDER_QUANTITY,
|
||||
-- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT, 없으면 CONTRACT_MGMT
|
||||
@@ -898,6 +898,8 @@
|
||||
FROM PROJECT_MGMT AS T
|
||||
LEFT JOIN sales_registration SR ON T.PROJECT_NO = SR.project_no
|
||||
WHERE 1 = 1
|
||||
AND T.PROJECT_NO IS NOT NULL
|
||||
AND T.PROJECT_NO != ''
|
||||
<if test="orderType != null and orderType != ''">
|
||||
AND T.CATEGORY_CD = #{orderType}
|
||||
</if>
|
||||
@@ -914,6 +916,9 @@
|
||||
AND SUPPLY_NAME LIKE CONCAT('%', #{customer}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="customer_objid != null and customer_objid != ''">
|
||||
AND T.CUSTOMER_OBJID = #{customer_objid}
|
||||
</if>
|
||||
<if test="paymentType != null and paymentType != ''">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM CONTRACT_MGMT CM
|
||||
@@ -975,7 +980,8 @@
|
||||
<if test="incoterms != null and incoterms != ''">
|
||||
/* INCOTERMS 필드 없음 - 검색 조건 무시 */
|
||||
</if>
|
||||
ORDER BY T.REGDATE DESC
|
||||
-- 등록일 기준 최신순 정렬 (프로젝트 번호는 보조 정렬)
|
||||
ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
|
||||
</select>
|
||||
|
||||
<!-- 매출관리 그리드 목록 개수 - project_mgmt 테이블 기반 -->
|
||||
@@ -986,6 +992,8 @@
|
||||
COUNT(1)::integer AS TOTAL_CNT
|
||||
FROM PROJECT_MGMT AS T
|
||||
WHERE 1 = 1
|
||||
AND T.PROJECT_NO IS NOT NULL
|
||||
AND T.PROJECT_NO != ''
|
||||
<if test="orderType != null and orderType != ''">
|
||||
AND T.CATEGORY_CD = #{orderType}
|
||||
</if>
|
||||
@@ -1002,6 +1010,9 @@
|
||||
AND SUPPLY_NAME LIKE CONCAT('%', #{customer}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="customer_objid != null and customer_objid != ''">
|
||||
AND T.CUSTOMER_OBJID = #{customer_objid}
|
||||
</if>
|
||||
<if test="paymentType != null and paymentType != ''">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM CONTRACT_MGMT CM
|
||||
@@ -1321,16 +1332,19 @@
|
||||
FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID) AS PAYMENT_TYPE,
|
||||
T.PART_NO AS PRODUCT_NO,
|
||||
T.PART_NAME AS PRODUCT_NAME,
|
||||
-- S/N 정보 (CONTRACT_ITEM_SERIAL에서만 조회, 품목별로 정확하게 매칭)
|
||||
(SELECT STRING_AGG(CIS.SERIAL_NO, ',' ORDER BY CIS.SEQ)
|
||||
FROM CONTRACT_ITEM CI
|
||||
LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID
|
||||
WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID
|
||||
AND CI.PART_OBJID = T.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE'
|
||||
AND UPPER(CIS.STATUS) = 'ACTIVE'
|
||||
AND CIS.SERIAL_NO IS NOT NULL
|
||||
) AS SERIAL_NO,
|
||||
-- S/N: 해당 품목의 시리얼 번호 (여러 개일 경우 "S/N 외 N건" 형식)
|
||||
(SELECT
|
||||
CASE
|
||||
WHEN COUNT(*) = 0 THEN ''
|
||||
WHEN COUNT(*) = 1 THEN MIN(CIS.SERIAL_NO)
|
||||
ELSE MIN(CIS.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건'
|
||||
END
|
||||
FROM CONTRACT_ITEM CI
|
||||
LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND UPPER(CIS.STATUS) = 'ACTIVE'
|
||||
WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID
|
||||
AND CI.PART_OBJID = T.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE'
|
||||
AND CIS.SERIAL_NO IS NOT NULL) AS SERIAL_NO,
|
||||
COALESCE(T.QUANTITY::numeric, 0) AS ORDER_QUANTITY,
|
||||
-- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT, 없으면 CONTRACT_MGMT
|
||||
COALESCE(
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
<property name="suffix" value=".jsp"/>
|
||||
<property name="order" value="1"/>
|
||||
</bean>
|
||||
|
||||
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
|
||||
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
|
||||
<property name="messageConverters">
|
||||
|
||||
@@ -25,7 +25,7 @@ $(document).ready(function(){
|
||||
$("#btnUpload").click(function(){
|
||||
var files = $("#file1")[0].files;
|
||||
if(files.length > 0){
|
||||
fnc_fileMultiUpload(files, null, "${param.targetObjId}", "${param.docType}", "${param.docTypeName}", null, "srAreaDraw",null, "${param.parentTargetObjId}");
|
||||
fnc_fileMultiUpload(files, null, "${param.targetObjId}", "${param.docType}", "${param.docTypeName}", null, "refeshAttachFileArea",null, "${param.parentTargetObjId}");
|
||||
//file객체 초기화
|
||||
$("#file1").val("");
|
||||
}else{
|
||||
@@ -39,6 +39,14 @@ $(document).ready(function(){
|
||||
|
||||
function refeshAttachFileArea(){
|
||||
srAreaDraw();
|
||||
// 부모 창의 그리드도 새로고침
|
||||
if(opener && typeof opener.fn_search == "function"){
|
||||
opener.fn_search();
|
||||
}
|
||||
// Tabulator 그리드가 있는 경우
|
||||
if(opener && opener._tabulGrid){
|
||||
opener._tabulGrid.replaceData();
|
||||
}
|
||||
}
|
||||
|
||||
//형상 영역을 display 한다.
|
||||
@@ -94,19 +102,15 @@ function fn_fileCallback(areaId,fileType){
|
||||
appendText +=" <col width='20%'>";
|
||||
appendText +=" </colgroup>";
|
||||
*/
|
||||
appendText+= "<tr>";
|
||||
appendText+= " <td>"+[i+1]+"</td>";
|
||||
appendText+= " <td class='align_l'><a href='javascript:fnc_downloadFile(\""+data[i].OBJID+"\")'> "+data[i].REAL_FILE_NAME+"</a>";
|
||||
/* if(data[i].WRITER=="${connectUserId}" || 'plm_admin'== "${connectUserId}"){
|
||||
|
||||
appendText+= "<a href='javascript:fileDelete(\""+data[i].OBJID+"\",\""+areaId+"\")'><div class='delete_btn'></div></a>";
|
||||
|
||||
}
|
||||
*/
|
||||
appendText+= "</td>";
|
||||
appendText+= " <td>"+data[i].REGDATE+"</td>"
|
||||
appendText+= " <td>"+data[i].FILE_SIZE+"</td>"
|
||||
appendText+= "</tr>";
|
||||
appendText+= "<tr>";
|
||||
appendText+= " <td>"+[i+1]+"</td>";
|
||||
appendText+= " <td class='align_l'><a href='javascript:fnc_downloadFile(\""+data[i].OBJID+"\")'> "+data[i].REAL_FILE_NAME+"</a>";
|
||||
// 도면 삭제 버튼 활성화
|
||||
appendText+= "<a href='javascript:fileDelete(\""+data[i].OBJID+"\")'><div class='delete_btn'></div></a>";
|
||||
appendText+= "</td>";
|
||||
appendText+= " <td>"+data[i].REGDATE+"</td>"
|
||||
appendText+= " <td>"+data[i].FILE_SIZE+"</td>"
|
||||
appendText+= "</tr>";
|
||||
}
|
||||
//Swal.fire(appendText);
|
||||
$("#"+areaId+"FileArea").append(appendText);
|
||||
@@ -130,23 +134,57 @@ function fn_fileCallback(areaId,fileType){
|
||||
|
||||
/*첨부 파일 삭제 */
|
||||
function fileDelete(fileObjId){
|
||||
if(confirm("파일을 삭제하시겠습니까?")){
|
||||
$.ajax({
|
||||
url:"/common/deleteFileInfo.do",
|
||||
type:"POST",
|
||||
data:{"objId":fileObjId},
|
||||
dataType:"json",
|
||||
async:true,
|
||||
success:function(data){
|
||||
fn_fileCallback("sr","${docType}");
|
||||
if(fnc_checkNull(callbackFnc) != ""){
|
||||
opener.eval(callbackFnc+"();");
|
||||
}
|
||||
},
|
||||
error: function(jqxhr, status, error){
|
||||
}
|
||||
});
|
||||
}
|
||||
Swal.fire({
|
||||
title: '파일을 삭제하시겠습니까?',
|
||||
text: '삭제된 파일은 복구할 수 없습니다.',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#3085d6',
|
||||
cancelButtonColor: '#d33',
|
||||
confirmButtonText: '삭제',
|
||||
cancelButtonText: '취소',
|
||||
reverseButtons: false
|
||||
}).then(function(result) {
|
||||
if (result.isConfirmed) {
|
||||
$.ajax({
|
||||
url:"/common/deleteFileInfo.do",
|
||||
type:"POST",
|
||||
data:{"objId":fileObjId},
|
||||
dataType:"json",
|
||||
async:true,
|
||||
success:function(data){
|
||||
Swal.fire({
|
||||
title: '삭제 완료',
|
||||
text: '파일이 삭제되었습니다.',
|
||||
icon: 'success',
|
||||
confirmButtonText: '확인'
|
||||
}).then(function() {
|
||||
fn_fileCallback("sr","${docType}");
|
||||
// 부모 창의 그리드 새로고침
|
||||
if(opener && typeof opener.fn_search == "function"){
|
||||
opener.fn_search();
|
||||
}
|
||||
// Tabulator 그리드가 있는 경우
|
||||
if(opener && opener._tabulGrid){
|
||||
opener._tabulGrid.replaceData();
|
||||
}
|
||||
// 기존 콜백 함수도 실행
|
||||
if(fnc_checkNull(callbackFnc) != ""){
|
||||
opener.eval(callbackFnc+"();");
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function(jqxhr, status, error){
|
||||
Swal.fire({
|
||||
title: '삭제 실패',
|
||||
text: '파일 삭제 중 오류가 발생했습니다.',
|
||||
icon: 'error',
|
||||
confirmButtonText: '확인'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
@@ -168,10 +206,11 @@ function fileDelete(fileObjId){
|
||||
-->
|
||||
<td rowspan="2" class="input_title align_c">파일첨부</td>
|
||||
<td colspan="8">
|
||||
<div id="srDropZone" class="dropzone">Drag & Drop Files Here</div>
|
||||
<!-- <input type="file" name="file1" id="file1" multiple>
|
||||
<input type="button" id="btnUpload" value="Upload" class="upload_btns"> -->
|
||||
<div id="srFileAreaTable" class="spec_data_in_table">
|
||||
<div style="margin-bottom: 10px;">
|
||||
<input type="file" name="file1" id="file1" multiple>
|
||||
<input type="button" id="btnUpload" value="업로드" class="plm_btns">
|
||||
</div>
|
||||
<div id="srFileAreaTable" class="spec_data_in_table">
|
||||
<div style="overflow-y:scroll;width:100%">
|
||||
<table id="" class="fileListscrollThead" style="width:100% !important;">
|
||||
<colgroup>
|
||||
|
||||
@@ -28,7 +28,7 @@ $(document).ready(function(){
|
||||
$("#btnUpload").click(function(){
|
||||
var files = $("#file1")[0].files;
|
||||
if(files.length > 0){
|
||||
fnc_fileMultiUpload(files, null, "${param.targetObjId}", "${param.docType}", "${param.docTypeName}", null, "srAreaDraw",null, "${param.parentTargetObjId}");
|
||||
fnc_fileMultiUpload(files, null, "${param.targetObjId}", "${param.docType}", "${param.docTypeName}", null, "refeshAttachFileArea",null, "${param.parentTargetObjId}");
|
||||
//file객체 초기화
|
||||
$("#file1").val("");
|
||||
}else{
|
||||
@@ -39,6 +39,14 @@ $(document).ready(function(){
|
||||
|
||||
function refeshAttachFileArea(){
|
||||
srAreaDraw();
|
||||
// 부모 창의 그리드도 새로고침
|
||||
if(opener && typeof opener.fn_search == "function"){
|
||||
opener.fn_search();
|
||||
}
|
||||
// Tabulator 그리드가 있는 경우
|
||||
if(opener && opener._tabulGrid){
|
||||
opener._tabulGrid.replaceData();
|
||||
}
|
||||
}
|
||||
|
||||
//형상 영역을 display 한다.
|
||||
@@ -164,7 +172,16 @@ function fileDelete(fileObjId){
|
||||
dataType:"json",
|
||||
async:false,
|
||||
success:function(data){
|
||||
fn_fileCallback("sr","${docType}");
|
||||
fn_fileCallback("sr","${docType}");
|
||||
// 부모 창의 그리드 새로고침
|
||||
if(opener && typeof opener.fn_search == "function"){
|
||||
opener.fn_search();
|
||||
}
|
||||
// Tabulator 그리드가 있는 경우
|
||||
if(opener && opener._tabulGrid){
|
||||
opener._tabulGrid.replaceData();
|
||||
}
|
||||
// 기존 콜백 함수도 실행
|
||||
if(fnc_checkNull(callbackFnc) != ""){
|
||||
opener.eval(callbackFnc+"();");
|
||||
}
|
||||
@@ -193,9 +210,10 @@ function fileDelete(fileObjId){
|
||||
<tr>
|
||||
<td rowspan="2" class="input_title align_c">파일첨부</td>
|
||||
<td colspan="8">
|
||||
<div id="srDropZone" class="dropzone">Drag & Drop Files Here</div>
|
||||
<input type="file" name="file1" id="file1" multiple>
|
||||
<input type="button" id="btnUpload" value="Upload" class="upload_btns">
|
||||
<div style="margin-bottom: 10px;">
|
||||
<input type="file" name="file1" id="file1" multiple>
|
||||
<input type="button" id="btnUpload" value="업로드" class="plm_btns">
|
||||
</div>
|
||||
<div id="srFileAreaTable" class="spec_data_in_table">
|
||||
<div style="overflow-y:scroll;">
|
||||
<table id="" class="fileListscrollThead" style="width:100% !important;">
|
||||
|
||||
@@ -216,7 +216,7 @@ function fileDelete(fileObjId, areaId, fileType){
|
||||
<table class="pmsPopupForm">
|
||||
<!-- FTC주문서 첨부 영역 -->
|
||||
<tr>
|
||||
<td class="input_title align_c">FTC주문서</td>
|
||||
<td class="input_title align_c">FCST주문서</td>
|
||||
<td colspan="8">
|
||||
<div style="width: 100%; overflow: hidden; box-sizing: border-box;">
|
||||
<div id="ftcDropZone" class="dropzone" style="width: 100% !important; max-width: 100%; box-sizing: border-box; margin-bottom: 5px;">Drag & Drop Files Here</div>
|
||||
|
||||
@@ -217,7 +217,7 @@ function addComma(num) {
|
||||
}
|
||||
|
||||
var columns = [
|
||||
{title:'EST_OBJID' ,field:'EST_OBJID' ,visible:false},
|
||||
{title:'EST_OBJID' ,field:'EST_OBJID' ,visible:false, frozen:true},
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '영업번호', field : 'CONTRACT_NO', frozen:true,
|
||||
formatter:fnc_createGridAnchorTag,
|
||||
cellClick:function(e, cell){
|
||||
@@ -668,21 +668,271 @@ function fn_sendEstimateMail(contractObjId){
|
||||
return;
|
||||
}
|
||||
|
||||
// 1단계: 견적서 템플릿 정보 조회
|
||||
Swal.fire({
|
||||
title: '메일 발송 중...',
|
||||
title: '견적서 조회 중...',
|
||||
text: '잠시만 기다려주세요.',
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => {
|
||||
onOpen: () => {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: "/contractMgmt/sendEstimateMail.do",
|
||||
url: "/contractMgmt/getEstimateTemplateList.do",
|
||||
type: "POST",
|
||||
data: { objId: contractObjId },
|
||||
dataType: "json",
|
||||
success: function(data){
|
||||
if(data.result === "success" && data.list && data.list.length > 0){
|
||||
// 최종 차수 견적서 찾기
|
||||
var latestEstimate = data.list[0]; // 이미 차수 내림차순으로 정렬되어 있음
|
||||
var templateObjId = latestEstimate.OBJID || latestEstimate.objid;
|
||||
var templateType = latestEstimate.TEMPLATE_TYPE || latestEstimate.template_type || latestEstimate.templateType;
|
||||
|
||||
// 2단계: 견적서 페이지를 새 창으로 열고 PDF 생성
|
||||
fn_generatePdfAndSendMail(contractObjId, templateObjId, templateType);
|
||||
} else {
|
||||
Swal.close();
|
||||
Swal.fire({
|
||||
title: '오류',
|
||||
text: '견적서를 찾을 수 없습니다.',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error){
|
||||
Swal.close();
|
||||
console.error("견적서 조회 오류:", xhr, status, error);
|
||||
Swal.fire({
|
||||
title: '오류',
|
||||
text: '견적서 조회 중 오류가 발생했습니다.',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// PDF 생성 및 메일 발송
|
||||
function fn_generatePdfAndSendMail(contractObjId, templateObjId, templateType){
|
||||
Swal.fire({
|
||||
title: 'PDF 생성 중...',
|
||||
text: '견적서를 PDF로 변환하고 있습니다.',
|
||||
allowOutsideClick: false,
|
||||
onOpen: () => {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
|
||||
// 견적서 페이지 URL 생성
|
||||
var url = "";
|
||||
if(templateType === "1"){
|
||||
url = "/contractMgmt/estimateTemplate1.do?templateObjId=" + templateObjId;
|
||||
} else if(templateType === "2"){
|
||||
url = "/contractMgmt/estimateTemplate2.do?templateObjId=" + templateObjId;
|
||||
}
|
||||
|
||||
// 숨겨진 iframe으로 페이지 로드
|
||||
var iframe = $('<iframe>', {
|
||||
id: 'pdfGeneratorFrame',
|
||||
src: url,
|
||||
style: 'position:absolute;width:0;height:0;border:none;'
|
||||
}).appendTo('body');
|
||||
|
||||
// iframe 로드 완료 대기
|
||||
iframe.on('load', function(){
|
||||
try {
|
||||
var iframeWindow = this.contentWindow;
|
||||
|
||||
// 데이터 로딩 완료를 기다림 (최대 10초)
|
||||
var checkDataLoaded = function(attempts) {
|
||||
if(attempts > 100) { // 10초 (100ms * 100)
|
||||
$('#pdfGeneratorFrame').remove();
|
||||
Swal.close();
|
||||
Swal.fire({
|
||||
title: '타임아웃',
|
||||
text: '견적서 데이터 로딩 시간이 초과되었습니다.',
|
||||
icon: 'error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if(iframeWindow.dataLoaded === true) {
|
||||
console.log('데이터 로딩 완료 확인, PDF 생성 시작');
|
||||
|
||||
// iframe 내의 PDF 생성 함수 호출
|
||||
if(typeof iframeWindow.fn_generateAndUploadPdf === 'function'){
|
||||
iframeWindow.fn_generateAndUploadPdf(function(pdfBase64){
|
||||
// iframe 제거
|
||||
$('#pdfGeneratorFrame').remove();
|
||||
|
||||
if(pdfBase64){
|
||||
// PDF Base64와 함께 메일 발송 요청
|
||||
fn_sendMailWithPdf(contractObjId, pdfBase64);
|
||||
} else {
|
||||
Swal.close();
|
||||
Swal.fire({
|
||||
title: '오류',
|
||||
text: 'PDF 생성에 실패했습니다.',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// iframe 제거
|
||||
$('#pdfGeneratorFrame').remove();
|
||||
|
||||
Swal.close();
|
||||
Swal.fire({
|
||||
title: '오류',
|
||||
text: '견적서 페이지 로드에 실패했습니다.',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// 아직 로딩 중이면 100ms 후 다시 확인
|
||||
setTimeout(function() {
|
||||
checkDataLoaded(attempts + 1);
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
// 데이터 로딩 체크 시작
|
||||
checkDataLoaded(0);
|
||||
} catch(e) {
|
||||
console.error('PDF 생성 오류:', e);
|
||||
$('#pdfGeneratorFrame').remove();
|
||||
|
||||
Swal.close();
|
||||
Swal.fire({
|
||||
title: '오류',
|
||||
text: 'PDF 생성 중 오류가 발생했습니다.',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 타임아웃 설정 (30초)
|
||||
setTimeout(function(){
|
||||
if($('#pdfGeneratorFrame').length > 0){
|
||||
$('#pdfGeneratorFrame').remove();
|
||||
Swal.close();
|
||||
Swal.fire({
|
||||
title: '타임아웃',
|
||||
text: 'PDF 생성 시간이 초과되었습니다.',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
// PDF Base64와 함께 메일 발송 (청크 방식)
|
||||
function fn_sendMailWithPdf(contractObjId, pdfBase64){
|
||||
console.log('===== 메일 발송 시작 =====');
|
||||
console.log('contractObjId:', contractObjId);
|
||||
console.log('PDF Base64 길이:', pdfBase64 ? pdfBase64.length : 0);
|
||||
console.log('========================');
|
||||
|
||||
Swal.fire({
|
||||
title: '메일 발송 중...',
|
||||
text: 'PDF 업로드 중...',
|
||||
allowOutsideClick: false,
|
||||
onOpen: () => {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
|
||||
// 청크 크기: 100KB (Base64) - POST 크기 제한 고려
|
||||
var chunkSize = 100 * 1024;
|
||||
var totalChunks = Math.ceil(pdfBase64.length / chunkSize);
|
||||
var uploadedChunks = 0;
|
||||
var sessionId = 'pdf_' + contractObjId + '_' + new Date().getTime();
|
||||
|
||||
console.log('PDF Base64 전체 길이:', pdfBase64.length);
|
||||
console.log('청크 크기:', chunkSize);
|
||||
console.log('총 청크 수:', totalChunks);
|
||||
|
||||
// 청크 업로드 함수
|
||||
function uploadChunk(chunkIndex) {
|
||||
var start = chunkIndex * chunkSize;
|
||||
var end = Math.min(start + chunkSize, pdfBase64.length);
|
||||
var chunk = pdfBase64.substring(start, end);
|
||||
|
||||
console.log('청크 ' + (chunkIndex + 1) + '/' + totalChunks + ' 업로드 중...');
|
||||
|
||||
$.ajax({
|
||||
url: "/contractMgmt/uploadPdfChunk.do",
|
||||
type: "POST",
|
||||
data: {
|
||||
sessionId: sessionId,
|
||||
chunkIndex: chunkIndex,
|
||||
totalChunks: totalChunks,
|
||||
chunk: chunk
|
||||
},
|
||||
dataType: "json",
|
||||
timeout: 30000,
|
||||
success: function(data){
|
||||
if(data.result === "success"){
|
||||
uploadedChunks++;
|
||||
|
||||
// 진행률 업데이트
|
||||
var progress = Math.round((uploadedChunks / totalChunks) * 100);
|
||||
Swal.update({
|
||||
text: 'PDF 업로드 중... ' + progress + '%'
|
||||
});
|
||||
|
||||
// 다음 청크 업로드
|
||||
if(chunkIndex + 1 < totalChunks){
|
||||
uploadChunk(chunkIndex + 1);
|
||||
} else {
|
||||
// 모든 청크 업로드 완료, 메일 발송 요청
|
||||
console.log('모든 청크 업로드 완료, 메일 발송 시작');
|
||||
sendMailWithUploadedPdf(contractObjId, sessionId);
|
||||
}
|
||||
} else {
|
||||
Swal.close();
|
||||
Swal.fire({
|
||||
title: '업로드 실패',
|
||||
text: 'PDF 업로드 중 오류가 발생했습니다.',
|
||||
icon: 'error',
|
||||
confirmButtonText: '확인'
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error){
|
||||
Swal.close();
|
||||
console.error("청크 업로드 오류:", xhr, status, error);
|
||||
Swal.fire({
|
||||
title: '오류',
|
||||
text: 'PDF 업로드 중 시스템 오류가 발생했습니다.',
|
||||
icon: 'error',
|
||||
confirmButtonText: '확인'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 첫 번째 청크부터 시작
|
||||
uploadChunk(0);
|
||||
}
|
||||
|
||||
// 업로드된 PDF로 메일 발송
|
||||
function sendMailWithUploadedPdf(contractObjId, sessionId){
|
||||
Swal.update({
|
||||
text: '메일 발송 중...'
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: "/contractMgmt/sendEstimateMail.do",
|
||||
type: "POST",
|
||||
data: {
|
||||
objId: contractObjId,
|
||||
pdfSessionId: sessionId
|
||||
},
|
||||
dataType: "json",
|
||||
timeout: 60000,
|
||||
success: function(data){
|
||||
console.log('메일 발송 응답:', data);
|
||||
Swal.close();
|
||||
if(data.result === "success"){
|
||||
Swal.fire({
|
||||
@@ -691,7 +941,6 @@ function fn_sendEstimateMail(contractObjId){
|
||||
icon: 'success',
|
||||
confirmButtonText: '확인'
|
||||
}).then(() => {
|
||||
// 메일 발송 상태 업데이트를 위해 목록 새로고침
|
||||
fn_search();
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -165,9 +165,10 @@ body {
|
||||
.items-table th,
|
||||
.items-table td {
|
||||
border: 1px solid #000;
|
||||
padding: 6px 8px;
|
||||
padding: 3px 5px;
|
||||
text-align: center;
|
||||
font-size: 9pt;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.items-table th {
|
||||
@@ -265,11 +266,12 @@ textarea {
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
min-height: 30px;
|
||||
min-height: 25px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.editable {
|
||||
background-color: #fffef0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
@media print {
|
||||
@@ -462,8 +464,8 @@ function fn_calculateAmount(row) {
|
||||
function fn_calculateTotal() {
|
||||
var total = 0;
|
||||
|
||||
// 품목 행만 순회 (계 행, 원화환산 행, 비고 행 제외)
|
||||
$("#itemsTableBody tr").not(".total-row, .total-krw-row, .remarks-row").each(function(){
|
||||
// 품목 행만 순회 (계 행, 원화환산 행, 비고 행, 참조사항 행, 회사명 행 제외)
|
||||
$("#itemsTableBody tr").not(".total-row, .total-krw-row, .remarks-row, .notes-row, .footer-row").each(function(){
|
||||
var amount = $(this).find(".item-amount").val() || "0";
|
||||
// 콤마와 통화 기호 제거 후 숫자로 변환
|
||||
amount = amount.replace(/,/g, "").replace(/₩/g, "").replace(/\$/g, "").replace(/€/g, "").replace(/¥/g, "");
|
||||
@@ -580,8 +582,8 @@ function fn_initItemDescSelect(itemId) {
|
||||
|
||||
// 행 추가 함수
|
||||
function fn_addItemRow() {
|
||||
// 계 행, 원화환산 행, 비고 행 제외하고 품목 행 개수 계산
|
||||
var itemRows = $("#itemsTableBody tr").not(".total-row, .total-krw-row, .remarks-row");
|
||||
// 계 행, 원화환산 행, 비고 행, 참조사항 행, 회사명 행 제외하고 품목 행 개수 계산
|
||||
var itemRows = $("#itemsTableBody tr").not(".total-row, .total-krw-row, .remarks-row, .notes-row, .footer-row");
|
||||
var nextNo = itemRows.length + 1;
|
||||
|
||||
// 새 행 생성
|
||||
@@ -817,32 +819,36 @@ function fn_loadTemplateData(templateObjId){
|
||||
// 결재상태 저장
|
||||
g_apprStatus = template.appr_status || template.APPR_STATUS || template.apprStatus || "작성중";
|
||||
|
||||
// 대문자/소문자 모두 지원
|
||||
var executor = template.EXECUTOR || template.executor || "";
|
||||
var recipient = template.RECIPIENT || template.recipient || "";
|
||||
var estimateNo = template.ESTIMATE_NO || template.estimate_no || template.estimateNo || "";
|
||||
var contactPerson = template.CONTACT_PERSON || template.contact_person || template.contactPerson || "";
|
||||
var greetingText = template.GREETING_TEXT || template.greeting_text || template.greetingText || "";
|
||||
var note1 = template.NOTE1 || template.note1 || "";
|
||||
var note2 = template.NOTE2 || template.note2 || "";
|
||||
var note3 = template.NOTE3 || template.note3 || "";
|
||||
var note4 = template.NOTE4 || template.note4 || "";
|
||||
var managerName = template.MANAGER_NAME || template.manager_name || template.managerName || "영업부";
|
||||
var managerContact = template.MANAGER_CONTACT || template.manager_contact || template.managerContact || "";
|
||||
|
||||
// 기본 정보 채우기
|
||||
$("#executor").val(executor);
|
||||
// 대문자/소문자 모두 지원
|
||||
var executor = template.EXECUTOR || template.executor || "";
|
||||
var recipient = template.RECIPIENT || template.recipient || "";
|
||||
var recipientName = template.RECIPIENT_NAME || template.recipient_name || template.recipientName || "";
|
||||
var estimateNo = template.ESTIMATE_NO || template.estimate_no || template.estimateNo || "";
|
||||
var contactPerson = template.CONTACT_PERSON || template.contact_person || template.contactPerson || "";
|
||||
var greetingText = template.GREETING_TEXT || template.greeting_text || template.greetingText || "";
|
||||
var note1 = template.NOTE1 || template.note1 || "";
|
||||
var note2 = template.NOTE2 || template.note2 || "";
|
||||
var note3 = template.NOTE3 || template.note3 || "";
|
||||
var note4 = template.NOTE4 || template.note4 || "";
|
||||
var managerName = template.MANAGER_NAME || template.manager_name || template.managerName || "영업부";
|
||||
var managerContact = template.MANAGER_CONTACT || template.manager_contact || template.managerContact || "";
|
||||
|
||||
// 데이터 로드 중 플래그 설정 (수신인 자동 로드 방지)
|
||||
window.isLoadingData = true;
|
||||
|
||||
// 수신처 설정
|
||||
// 기본 정보 채우기
|
||||
$("#executor").val(executor);
|
||||
|
||||
// 데이터 로드 중 플래그 설정 (수신인 자동 로드 방지)
|
||||
window.isLoadingData = true;
|
||||
|
||||
// 수신처 설정
|
||||
if(recipient && recipient !== "") {
|
||||
// OBJID로 셀렉트박스 선택
|
||||
$("#recipient").val(recipient).trigger('change');
|
||||
|
||||
// 플래그 해제
|
||||
setTimeout(function() {
|
||||
window.isLoadingData = false;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// 플래그 해제
|
||||
setTimeout(function() {
|
||||
window.isLoadingData = false;
|
||||
}, 100);
|
||||
|
||||
$("#estimate_no").val(estimateNo);
|
||||
$("#contact_person").val(contactPerson);
|
||||
@@ -856,12 +862,6 @@ function fn_loadTemplateData(templateObjId){
|
||||
$("#manager_contact").val(managerContact);
|
||||
}
|
||||
|
||||
// 하단 비고 로드
|
||||
$("#note1").val(note1);
|
||||
$("#note2").val(note2);
|
||||
$("#note3").val(note3);
|
||||
$("#note4").val(note4);
|
||||
|
||||
// 테이블 내 비고는 나중에 설정 (textarea 생성 후)
|
||||
var noteRemarks = template.NOTE_REMARKS || template.note_remarks || template.noteRemarks || "";
|
||||
|
||||
@@ -930,6 +930,24 @@ function fn_loadTemplateData(templateObjId){
|
||||
itemsHtml += '</td>';
|
||||
itemsHtml += '</tr>';
|
||||
|
||||
// 참조사항 행 추가
|
||||
itemsHtml += '<tr class="notes-row">';
|
||||
itemsHtml += '<td colspan="8" style="vertical-align: top; padding: 10px; text-align: left; border: 1px solid #000;">';
|
||||
itemsHtml += '<div style="font-weight: bold; margin-bottom: 10px; text-align: left;"><참조사항></div>';
|
||||
itemsHtml += '<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note1" value="1. 견적유효기간: 일" style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>';
|
||||
itemsHtml += '<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note2" value="2. 납품기간: 발주 후 1주 이내" style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>';
|
||||
itemsHtml += '<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note3" value="3. VAT 별도" style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>';
|
||||
itemsHtml += '<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note4" value="4. 결제 조건 : 기존 결제조건에 따름." style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>';
|
||||
itemsHtml += '</td>';
|
||||
itemsHtml += '</tr>';
|
||||
|
||||
// 하단 회사명 행 추가
|
||||
itemsHtml += '<tr class="footer-row">';
|
||||
itemsHtml += '<td colspan="8" style="text-align: right; padding: 15px; font-size: 10pt; font-weight: bold; border: none;">';
|
||||
itemsHtml += '㈜알피에스';
|
||||
itemsHtml += '</td>';
|
||||
itemsHtml += '</tr>';
|
||||
|
||||
// HTML 삽입
|
||||
$("#itemsTableBody").html(itemsHtml);
|
||||
|
||||
@@ -976,17 +994,28 @@ function fn_loadTemplateData(templateObjId){
|
||||
fn_initItemDescSelect(itemId);
|
||||
}
|
||||
|
||||
// 테이블 내 비고 값 설정 (textarea 생성 직후)
|
||||
$("#note_remarks").val(noteRemarks);
|
||||
|
||||
// 합계 계산
|
||||
fn_calculateTotal();
|
||||
// 테이블 내 비고 값 설정 (textarea 생성 직후)
|
||||
$("#note_remarks").val(noteRemarks);
|
||||
|
||||
// 참조사항 값 설정 (input 생성 직후)
|
||||
$("#note1").val(note1 || "1. 견적유효기간: 일");
|
||||
$("#note2").val(note2 || "2. 납품기간: 발주 후 1주 이내");
|
||||
$("#note3").val(note3 || "3. VAT 별도");
|
||||
$("#note4").val(note4 || "4. 결제 조건 : 기존 결제조건에 따름.");
|
||||
|
||||
// 합계 계산
|
||||
fn_calculateTotal();
|
||||
|
||||
// 결재상태에 따라 버튼 제어
|
||||
fn_controlButtons();
|
||||
|
||||
// 데이터 로딩 완료 플래그 설정
|
||||
window.dataLoaded = true;
|
||||
console.log("견적서 데이터 로딩 완료");
|
||||
}
|
||||
} else {
|
||||
console.error("데이터 로드 실패:", data);
|
||||
window.dataLoaded = false;
|
||||
Swal.fire("데이터를 불러오는데 실패했습니다.");
|
||||
}
|
||||
},
|
||||
@@ -1025,8 +1054,8 @@ function fn_loadCustomerContact(customerObjId) {
|
||||
|
||||
function fn_save() {
|
||||
var items = [];
|
||||
// 계 행, 원화환산 행, 비고 행 제외하고 품목 행만 저장
|
||||
$("#itemsTableBody tr").not(".total-row, .total-krw-row, .remarks-row").each(function(idx) {
|
||||
// 계 행, 원화환산 행, 비고 행, 참조사항 행, 회사명 행 제외하고 품목 행만 저장
|
||||
$("#itemsTableBody tr").not(".total-row, .total-krw-row, .remarks-row, .notes-row, .footer-row").each(function(idx) {
|
||||
var row = $(this);
|
||||
var quantity = row.find(".item-qty").val() || "";
|
||||
var unitPrice = row.find(".item-price").val() || "";
|
||||
@@ -1128,6 +1157,7 @@ function fn_save() {
|
||||
<colgroup>
|
||||
<col width="80px" />
|
||||
<col width="*" />
|
||||
<col width="50px" />
|
||||
<col width="300px" />
|
||||
</colgroup>
|
||||
<tr>
|
||||
@@ -1135,6 +1165,7 @@ function fn_save() {
|
||||
<td class="editable">
|
||||
<input type="text" id="executor" class="date_icon" value="" style="width: 150px;">
|
||||
</td>
|
||||
<td rowspan="4" style="border: none"></td>
|
||||
<td rowspan="4" style="text-align: center; border: none; vertical-align: middle; padding: 0;">
|
||||
<div style="width: 100%; text-align: center; margin-bottom: 5px;">
|
||||
<img src="/images/company_stamp.png" alt="회사 도장" style="width: 100%; height: auto;"
|
||||
@@ -1143,10 +1174,7 @@ function fn_save() {
|
||||
<div class="company-stamp-text">㈊알피에스<br>RPS CO., LTD<br>대표이사이동준</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: left; font-size: 9pt; line-height: 1.8; padding: 0 5px;">
|
||||
담당자 : <input type="text" id="manager_name" value="" readonly style="width: 120px; border: none; border-bottom: 1px solid #ddd; font-size: 9pt; padding: 2px; background-color: #f5f5f5;"><br>
|
||||
연락처 : <input type="text" id="manager_contact" value="" readonly style="width: 120px; border: none; border-bottom: 1px solid #ddd; font-size: 9pt; padding: 2px; background-color: #f5f5f5;">
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -1173,15 +1201,19 @@ function fn_save() {
|
||||
</table>
|
||||
|
||||
<!-- 인사말 -->
|
||||
<div style="margin-bottom: 10px; padding: 0px 5px; line-height: 1.6; font-size: 10pt;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 10px; padding: 0px 5px;">
|
||||
<!-- 왼쪽: 인사말 -->
|
||||
<div style="line-height: 1.6; font-size: 10pt;">
|
||||
견적을 요청해 주셔서 대단히 감사합니다.<br>
|
||||
하기와 같이 견적서를 제출합니다.
|
||||
</div>
|
||||
|
||||
<!-- 부가세 별도 표시 -->
|
||||
<div style="text-align: right; margin-top: -30px; margin-bottom: 20px; padding-right: 10px; font-size: 10pt;">
|
||||
부가세 별도
|
||||
<!-- 오른쪽: 담당자 정보 및 부가세 별도 -->
|
||||
<div style="text-align: right; font-size: 9pt; line-height: 1.8;">
|
||||
담당자 : <input type="text" id="manager_name" value="" readonly style="width: 120px; border: none; border-bottom: 1px solid #ddd; font-size: 9pt; padding: 2px; background-color: #f5f5f5;"><br>
|
||||
연락처 : <input type="text" id="manager_contact" value="" readonly style="width: 120px; border: none; border-bottom: 1px solid #ddd; font-size: 9pt; padding: 2px; background-color: #f5f5f5;"><br><br>
|
||||
<span style="font-size: 10pt; margin-top: 5px; display: inline-block;">부가세 별도</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 품목 테이블 -->
|
||||
<table class="items-table">
|
||||
@@ -1245,31 +1277,274 @@ function fn_save() {
|
||||
<textarea id="note_remarks" style="width: 100%; height: 70px; border: none; resize: none; font-family: inherit; font-size: 10pt; text-align: left;"></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 참조사항 행 -->
|
||||
<tr class="notes-row">
|
||||
<td colspan="8" style="vertical-align: top; padding: 10px; text-align: left; border: 1px solid #000;">
|
||||
<div style="font-weight: bold; margin-bottom: 10px; text-align: left;"><참조사항></div>
|
||||
<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note1" value="1. 견적유효기간: 일" style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>
|
||||
<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note2" value="2. 납품기간: 발주 후 1주 이내" style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>
|
||||
<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note3" value="3. VAT 별도" style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>
|
||||
<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note4" value="4. 결제 조건 : 기존 결제조건에 따름." style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 하단 회사명 행 -->
|
||||
<tr class="footer-row">
|
||||
<td colspan="8" style="text-align: right; padding: 15px; font-size: 10pt; font-weight: bold; border: none;">
|
||||
㈜알피에스
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- 참조사항 섹션 -->
|
||||
<div class="notes-section">
|
||||
<div class="notes-title"><참조사항></div>
|
||||
<div class="editable"><input type="text" id="note1" value="1. 견적유효기간: 일"></div>
|
||||
<div class="editable"><input type="text" id="note2" value="2. 납품기간: 발주 후 1주 이내"></div>
|
||||
<div class="editable"><input type="text" id="note3" value="3. VAT 별도"></div>
|
||||
<div class="editable"><input type="text" id="note4" value="4. 결제 조건 : 기존 결제조건에 따름."></div>
|
||||
</div>
|
||||
|
||||
<!-- 하단 회사명 -->
|
||||
<div class="footer-company">
|
||||
㈜알피에스
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 버튼 영역 -->
|
||||
<div class="btn-area no-print">
|
||||
<button type="button" id="btnAddRow" class="estimate-btn">행 추가</button>
|
||||
<button type="button" id="btnPrint" class="estimate-btn">인쇄</button>
|
||||
<button type="button" id="btnDownloadPdf" class="estimate-btn">PDF 다운로드</button>
|
||||
<button type="button" id="btnSave" class="estimate-btn">저장</button>
|
||||
<button type="button" id="btnClose" class="estimate-btn">닫기</button>
|
||||
</div>
|
||||
|
||||
<!-- html2canvas 및 jsPDF 라이브러리 -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js"></script>
|
||||
<script>
|
||||
|
||||
// PDF 다운로드 버튼 클릭 이벤트
|
||||
$("#btnDownloadPdf").click(function(){
|
||||
fn_generatePdf();
|
||||
});
|
||||
|
||||
// PDF 생성 함수
|
||||
function fn_generatePdf() {
|
||||
// 라이브러리 로드 확인
|
||||
if(typeof html2canvas === 'undefined') {
|
||||
Swal.fire({
|
||||
title: '오류',
|
||||
text: 'html2canvas 라이브러리가 로드되지 않았습니다.',
|
||||
icon: 'error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof jsPDF === 'undefined') {
|
||||
Swal.fire({
|
||||
title: '오류',
|
||||
text: 'jsPDF 라이브러리가 로드되지 않았습니다.',
|
||||
icon: 'error'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
title: 'PDF 생성 중...',
|
||||
text: '잠시만 기다려주세요.',
|
||||
allowOutsideClick: false,
|
||||
onOpen: () => {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
|
||||
// 버튼 영역 임시 숨김
|
||||
$('.btn-area').hide();
|
||||
|
||||
// PDF 생성을 위해 스타일 조정
|
||||
var container = $('.estimate-container');
|
||||
var originalBg = container.css('background');
|
||||
var originalShadow = container.css('box-shadow');
|
||||
var originalPadding = container.css('padding');
|
||||
|
||||
// 깔끔한 PDF를 위해 배경, 그림자, 패딩 제거
|
||||
container.css({
|
||||
'background': 'white',
|
||||
'box-shadow': 'none',
|
||||
'padding': '10mm'
|
||||
});
|
||||
|
||||
// body 배경색도 흰색으로
|
||||
var originalBodyBg = $('body').css('background-color');
|
||||
$('body').css('background-color', 'white');
|
||||
|
||||
// 견적서 컨테이너 캡처
|
||||
html2canvas(document.querySelector('.estimate-container'), {
|
||||
scale: 2, // 적절한 해상도 (파일 크기 최적화)
|
||||
useCORS: true,
|
||||
logging: false,
|
||||
backgroundColor: '#ffffff'
|
||||
}).then(function(canvas) {
|
||||
// 스타일 복원
|
||||
container.css({
|
||||
'background': originalBg,
|
||||
'box-shadow': originalShadow,
|
||||
'padding': originalPadding
|
||||
});
|
||||
$('body').css('background-color', originalBodyBg);
|
||||
// 버튼 영역 다시 표시
|
||||
$('.btn-area').show();
|
||||
|
||||
try {
|
||||
// Canvas를 JPEG 이미지로 변환 (PNG보다 파일 크기 작음)
|
||||
var imgData = canvas.toDataURL('image/jpeg', 0.85); // 85% 품질
|
||||
|
||||
// PDF 생성 (A4 크기)
|
||||
var pdf = new jsPDF('p', 'mm', 'a4');
|
||||
var imgWidth = 210; // A4 width in mm
|
||||
var pageHeight = 297; // A4 height in mm
|
||||
var imgHeight = canvas.height * imgWidth / canvas.width;
|
||||
var heightLeft = imgHeight;
|
||||
var position = 0;
|
||||
|
||||
// 첫 페이지 추가 (JPEG 압축)
|
||||
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, undefined, 'FAST');
|
||||
heightLeft -= pageHeight;
|
||||
|
||||
// 페이지가 넘어가면 추가 페이지 생성
|
||||
while (heightLeft >= 0) {
|
||||
position = heightLeft - imgHeight;
|
||||
pdf.addPage();
|
||||
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, undefined, 'FAST');
|
||||
heightLeft -= pageHeight;
|
||||
}
|
||||
|
||||
// 파일명 생성
|
||||
var estimateNo = $("#estimate_no").val() || "견적서";
|
||||
var fileName = estimateNo + '.pdf';
|
||||
|
||||
// PDF 다운로드
|
||||
pdf.save(fileName);
|
||||
|
||||
Swal.close();
|
||||
Swal.fire({
|
||||
title: 'PDF 생성 완료',
|
||||
text: 'PDF 파일이 다운로드되었습니다.',
|
||||
icon: 'success',
|
||||
timer: 2000
|
||||
});
|
||||
} catch(pdfError) {
|
||||
Swal.close();
|
||||
console.error('PDF 생성 오류:', pdfError);
|
||||
Swal.fire({
|
||||
title: '오류',
|
||||
text: 'PDF 생성 중 오류가 발생했습니다: ' + pdfError.message,
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
}).catch(function(error) {
|
||||
$('.btn-area').show();
|
||||
Swal.close();
|
||||
console.error('Canvas 캡처 오류:', error);
|
||||
Swal.fire({
|
||||
title: '오류',
|
||||
text: '페이지 캡처 중 오류가 발생했습니다: ' + error.message,
|
||||
icon: 'error'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// PDF를 Base64로 생성하여 서버로 전송하는 함수
|
||||
function fn_generateAndUploadPdf(callback) {
|
||||
console.log('fn_generateAndUploadPdf 호출됨');
|
||||
|
||||
// 라이브러리 로드 확인
|
||||
if(typeof html2canvas === 'undefined' || typeof jsPDF === 'undefined') {
|
||||
console.error('필요한 라이브러리가 로드되지 않았습니다.');
|
||||
if(callback && typeof callback === 'function') {
|
||||
callback(null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 버튼 영역 임시 숨김
|
||||
$('.btn-area').hide();
|
||||
|
||||
// PDF 생성을 위해 스타일 조정
|
||||
var container = $('.estimate-container');
|
||||
var originalBg = container.css('background');
|
||||
var originalShadow = container.css('box-shadow');
|
||||
var originalPadding = container.css('padding');
|
||||
|
||||
// 깔끔한 PDF를 위해 배경, 그림자, 패딩 제거
|
||||
container.css({
|
||||
'background': 'white',
|
||||
'box-shadow': 'none',
|
||||
'padding': '10mm'
|
||||
});
|
||||
|
||||
// body 배경색도 흰색으로
|
||||
var originalBodyBg = $('body').css('background-color');
|
||||
$('body').css('background-color', 'white');
|
||||
|
||||
// 견적서 컨테이너 캡처
|
||||
html2canvas(document.querySelector('.estimate-container'), {
|
||||
scale: 2, // 적절한 해상도 (파일 크기 최적화)
|
||||
useCORS: true,
|
||||
logging: false,
|
||||
backgroundColor: '#ffffff'
|
||||
}).then(function(canvas) {
|
||||
console.log('Canvas 캡처 완료');
|
||||
|
||||
// 스타일 복원
|
||||
container.css({
|
||||
'background': originalBg,
|
||||
'box-shadow': originalShadow,
|
||||
'padding': originalPadding
|
||||
});
|
||||
$('body').css('background-color', originalBodyBg);
|
||||
|
||||
// 버튼 영역 다시 표시
|
||||
$('.btn-area').show();
|
||||
|
||||
try {
|
||||
// Canvas를 JPEG 이미지로 변환 (PNG보다 파일 크기 작음)
|
||||
var imgData = canvas.toDataURL('image/jpeg', 0.85); // 85% 품질
|
||||
console.log('이미지 변환 완료');
|
||||
|
||||
// PDF 생성
|
||||
var pdf = new jsPDF('p', 'mm', 'a4');
|
||||
var imgWidth = 210;
|
||||
var pageHeight = 297;
|
||||
var imgHeight = canvas.height * imgWidth / canvas.width;
|
||||
var heightLeft = imgHeight;
|
||||
var position = 0;
|
||||
|
||||
// JPEG 이미지 추가 (압축됨)
|
||||
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, undefined, 'FAST');
|
||||
heightLeft -= pageHeight;
|
||||
|
||||
while (heightLeft >= 0) {
|
||||
position = heightLeft - imgHeight;
|
||||
pdf.addPage();
|
||||
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, undefined, 'FAST');
|
||||
heightLeft -= pageHeight;
|
||||
}
|
||||
|
||||
console.log('PDF 생성 완료');
|
||||
|
||||
// PDF를 Base64로 변환
|
||||
var pdfBase64 = pdf.output('dataurlstring').split(',')[1];
|
||||
console.log('PDF Base64 생성 완료, 길이:', pdfBase64.length);
|
||||
|
||||
// 콜백 함수 호출 (메일 발송 등에 사용)
|
||||
if(callback && typeof callback === 'function') {
|
||||
callback(pdfBase64);
|
||||
}
|
||||
} catch(pdfError) {
|
||||
console.error('PDF 생성 중 오류:', pdfError);
|
||||
if(callback && typeof callback === 'function') {
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
|
||||
}).catch(function(error) {
|
||||
$('.btn-area').show();
|
||||
console.error('Canvas 캡처 오류:', error);
|
||||
if(callback && typeof callback === 'function') {
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -26,14 +26,14 @@ userMenuList = (ArrayList)request.getAttribute("userMenuList");
|
||||
}
|
||||
*/
|
||||
.menu_off{
|
||||
color:#fff !important; padding: 2px 3px; font-size:9px; background-color:#89b4fa; border-radius:2px;
|
||||
display: inline-block; min-width: 40px; text-align: center; margin: 0px;
|
||||
color:#fff !important; padding: 4px 8px; font-size:11px; background-color:#89b4fa; border-radius:3px;
|
||||
display: inline-block; min-width: 55px; text-align: center; margin: 0px;
|
||||
transition: all 0.3s ease; text-decoration: none !important; border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.menu_on{
|
||||
color:#fff !important; padding: 2px 3px; font-size:9px; background-color:#5e9cff; border-radius:2px;
|
||||
display: inline-block; min-width: 40px; text-align: center; margin: 0px;
|
||||
color:#fff !important; padding: 4px 8px; font-size:11px; background-color:#5e9cff; border-radius:3px;
|
||||
display: inline-block; min-width: 55px; text-align: center; margin: 0px;
|
||||
transition: all 0.3s ease; text-decoration: none !important; border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -392,7 +392,7 @@ function fn_setApprovalCnt(){
|
||||
|
||||
if("2".equals(lev)){
|
||||
%>
|
||||
<a href="#" class="menu menu_off" menuObjId="<%=menuObjid%>" style="background-color: #1e40af; color: #fff; padding: 3px 6px; font-size: 10px; border-radius: 3px; text-decoration: none; display: inline-block; min-width: 50px; text-align: center; margin-right: 4px;"><%=menuKorName%></a>
|
||||
<a href="#" class="menu menu_off" menuObjId="<%=menuObjid%>" style="background-color: #1e40af; color: #fff; padding: 4px 8px; font-size: 11px; border-radius: 3px; text-decoration: none; display: inline-block; min-width: 55px; text-align: center; margin-right: 5px;"><%=menuKorName%></a>
|
||||
<%
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,23 @@
|
||||
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
|
||||
<%@ page import="com.pms.common.utils.*"%>
|
||||
<%@ page import="java.util.*"%>
|
||||
<%
|
||||
// URL 파라미터 가져오기 (request.getParameter는 자동으로 URL 디코딩됨)
|
||||
String bomPartNo = request.getParameter("BOM_PART_NO");
|
||||
String bomPartName = request.getParameter("BOM_PART_NAME");
|
||||
String bomVersion = request.getParameter("BOM_VERSION");
|
||||
|
||||
// 디버깅: 콘솔에 출력
|
||||
System.out.println("=== BOM 파라미터 디버깅 ===");
|
||||
System.out.println("bomPartNo: " + bomPartNo);
|
||||
System.out.println("bomPartName: " + bomPartName);
|
||||
System.out.println("bomVersion: " + bomVersion);
|
||||
|
||||
// null 체크
|
||||
if(bomPartNo == null) bomPartNo = "";
|
||||
if(bomPartName == null) bomPartName = "";
|
||||
if(bomVersion == null) bomVersion = "";
|
||||
%>
|
||||
<%@include file="/init_jqGrid.jsp"%>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@@ -21,7 +38,12 @@
|
||||
<script type="text/javascript">
|
||||
var grid;
|
||||
$(document).ready(function(){
|
||||
|
||||
$('.select2').select2();
|
||||
// 품번/품명 Select2 AJAX 초기화 (common.js의 새 함수 사용)
|
||||
// initPartSelect2Ajax("#bom_part_no", "#bom_part_name", "#bom_part_objid", {
|
||||
// debug: false // 디버깅 모드 비활성화
|
||||
// });
|
||||
|
||||
$(".dataTr").each(function(i){
|
||||
var lev = $(this).attr("data-LEVEL");
|
||||
if(lev == 1){
|
||||
@@ -29,6 +51,8 @@ $(document).ready(function(){
|
||||
}
|
||||
});
|
||||
|
||||
$("#product_cd").val("${param.PRODUCT_CD}").select2();
|
||||
|
||||
$(".btnToggle").click(function(){
|
||||
var choosedIsMinus = false;
|
||||
var choosedSrc = $(this).attr("src");
|
||||
@@ -161,16 +185,59 @@ $(document).ready(function(){
|
||||
//fnc_productUPGNEWList("","${product_mgmt_spec}","param_upg_no", "${upg_no}");
|
||||
|
||||
//fnc_datepick();
|
||||
fnc_setFileDropZone("excelImportDropZone", "${objid}", "PART_EXCEL_IMPORT", "Part Excel Import Template", "setExcelFileArea",true,"fileDelete","/part/excelImportFileProc.do");
|
||||
// CSV 파일만 업로드 가능하도록 설정 (2025-10-29)
|
||||
fnc_setFileDropZone("excelImportDropZone", "${objid}", "PART_EXCEL_IMPORT", "Part Excel Import Template", "setExcelFileArea",true,"fileDelete","/part/excelImportFileProc.do", "csv");
|
||||
fnc_setFileDropZone("partAttachFileDropZone", "${objid}", "PART_IMPORT_ATTACH", "Import Part Attach File", "setPartFileArea",false,null,"/part/partImportFileProc.do");
|
||||
|
||||
$("#templateDownload").click(function(){
|
||||
location.href="/template/BOM_REPORT_EXCEL_IMPORT_TEMPLATE.xlsx";
|
||||
});
|
||||
|
||||
var unit_cd = $.parseJSON($("#unit_cd").val()); //jqGrid 구분
|
||||
var part_type = $.parseJSON($("#part_type").val()); //jqGrid 구분
|
||||
var sup_code = $.parseJSON($("#sup_code").val()); //jqGrid 구분
|
||||
// BOM 복사 버튼 클릭
|
||||
$("#btnCopyBom").click(function(){
|
||||
var bomObjid = $("#copy_bom_select").val();
|
||||
//alert(bomObjid);
|
||||
if(!bomObjid || bomObjid == "") {
|
||||
Swal.fire("복사할 BOM을 선택하세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 기존 그리드 데이터 초기화
|
||||
grid.jqGrid('clearGridData');
|
||||
|
||||
// BOM 데이터 조회 및 로드
|
||||
$.ajax({
|
||||
url: "/partMng/getBomDataForCopy.do",
|
||||
type: "POST",
|
||||
data: {"BOM_REPORT_OBJID": bomObjid},
|
||||
dataType: "json",
|
||||
success: function(data){
|
||||
if(data && data.length > 0){
|
||||
// jqGrid에 데이터 추가
|
||||
for(var i=0; i<data.length; i++){
|
||||
grid.jqGrid('addRowData', i+1, data[i]);
|
||||
}
|
||||
gridFn.opennEdit(); // 수정 가능하도록
|
||||
Swal.fire("BOM 데이터를 불러왔습니다.");
|
||||
} else {
|
||||
Swal.fire("선택한 BOM에 데이터가 없습니다.");
|
||||
}
|
||||
},
|
||||
error: function(jqxhr, status, error){
|
||||
Swal.fire('BOM 데이터 조회 중 오류가 발생했습니다.');
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// JSON 파싱 시 에러 처리
|
||||
var unit_cd_val = $("#unit_cd").val();
|
||||
var part_type_val = $("#part_type").val();
|
||||
var sup_code_val = $("#sup_code").val();
|
||||
|
||||
var unit_cd = unit_cd_val && unit_cd_val.trim() !== '' ? $.parseJSON(unit_cd_val) : {};
|
||||
var part_type = part_type_val && part_type_val.trim() !== '' ? $.parseJSON(part_type_val) : {};
|
||||
var sup_code = sup_code_val && sup_code_val.trim() !== '' ? $.parseJSON(sup_code_val) : {};
|
||||
|
||||
//Excel File Upload된 파일 목록 부분을 초기화 한다.
|
||||
$("#excelImportList").hide();
|
||||
@@ -181,7 +248,7 @@ $(document).ready(function(){
|
||||
grid = $("#expenseDetailGrid").jqGrid({
|
||||
url: ""
|
||||
,datatype: "local"
|
||||
,colNames: ["상태","모품번","자품번","품명","수량","재질","사양(규격)","후처리","MAKER","부품 유형","REMARK"]
|
||||
,colNames: ["상태","모품번","품번","품명","수량","항목수량","재료","열처리경도","열처리방법","표면처리","공급업체","범주이름"]
|
||||
,colModel: [
|
||||
{name:"NOTE",index:"NOTE", width: 160, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
@@ -190,62 +257,90 @@ $(document).ready(function(){
|
||||
}
|
||||
}
|
||||
}
|
||||
,{name:"PARENT_PART_NO",index:"PARENT_PART_NO", width: 160, align:"center", hidden: false, sortable:false, editable:true
|
||||
,{name:"PARENT_PART_NO",index:"PARENT_PART_NO", width: 200, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
,{name:"PART_NO",index:"PART_NO", width: 160, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
,{name:"PART_NAME",index:"PART_NAME", width: 270, align:"center", hidden: false, sortable:false, editable:true
|
||||
,{name:"PART_NO",index:"PART_NO", width: 200, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
,{name:"QTY",index:"QTY", width: 50, align:"center", hidden: false, sortable:false, editable:true
|
||||
,{name:"PART_NAME",index:"PART_NAME", width: 250, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
,{name:"MATERIAL",index:"MATERIAL", width: 150, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
,{name:"SPEC",index:"SPEC", width: 150, align:"center", hidden: false, sortable:false, editable:true
|
||||
,{name:"QTY",index:"QTY", width: 60, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
,{name:"POST_PROCESSING",index:"POST_PROCESSING", width: 100, align:"center", hidden: false, sortable:false, editable:true
|
||||
,{name:"ITEM_QTY",index:"ITEM_QTY", width: 80, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
,{name:"MAKER",index:"MAKER", width: 150, align:"center", hidden: false, sortable:false, editable:true
|
||||
,{name:"MATERIAL",index:"MATERIAL", width: 120, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
,{name:"HEAT_TREATMENT_HARDNESS",index:"HEAT_TREATMENT_HARDNESS", width: 100, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
,{name:"HEAT_TREATMENT_METHOD",index:"HEAT_TREATMENT_METHOD", width: 100, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
,{name:"SURFACE_TREATMENT",index:"SURFACE_TREATMENT", width: 100, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 공급업체 - MAKER 컬럼으로 변경, 일반 텍스트 입력 (2025-10-29)
|
||||
,{name:"MAKER",index:"MAKER", width: 120, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 이전 SUPPLY_CODE 셀렉트박스 방식 (주석처리)
|
||||
,{name:"SUPPLY_CODE",index:"SUPPLY_CODE", width: 120, align:"center", hidden: false, sortable:false, editable:true
|
||||
,edittype :"select"
|
||||
,formatter :"select"
|
||||
,editoptions:{
|
||||
value: sup_code
|
||||
,dataInit : function(e){
|
||||
e.style.width = "92%";
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
,{name:"PART_TYPE" ,index:"PART_TYPE" , width:100, align:"center", hidden:false, sortable:false, editable: true
|
||||
,edittype :"select"
|
||||
,formatter :"select"
|
||||
@@ -257,14 +352,7 @@ $(document).ready(function(){
|
||||
}
|
||||
}
|
||||
}
|
||||
,{name:"REMARK",index:"REMARK", width: 170, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
//,cellEdit : true
|
||||
//,cellsubmit : "clientArray"
|
||||
,rownumbers : true
|
||||
@@ -342,12 +430,23 @@ var gridFn = {
|
||||
}
|
||||
,search : function() {
|
||||
grid.setGridParam({
|
||||
url: "/partMng/parsingExcelFile.do"
|
||||
url: "/partMng/parsingExcelFile.do"
|
||||
,datatype : "json"
|
||||
,postData:{"targetObjId":"${objid}","docType":"PART_EXCEL_IMPORT","OBJID":"${CONTRACT_OBJID}"}
|
||||
,loadComplete : function(data) {
|
||||
gridFn.footerSummary();
|
||||
,loadComplete : function(data) {
|
||||
// CSV 파일에서만 LEVEL 값을 PARENT_PART_NO 컬럼에 표시
|
||||
if(data && data.rows) {
|
||||
for(var i = 0; i < data.rows.length; i++) {
|
||||
var row = data.rows[i];
|
||||
// CSV 파일이고 LEVEL 값이 있는 경우
|
||||
if(row.IS_CSV === 'Y' && row.LEVEL && row.LEVEL !== '') {
|
||||
// LEVEL 값을 PARENT_PART_NO 컬럼에 표시 (화면용)
|
||||
grid.jqGrid('setCell', row.id, 'PARENT_PART_NO', row.LEVEL);
|
||||
}
|
||||
}
|
||||
}
|
||||
gridFn.footerSummary();
|
||||
}
|
||||
,gridComplete : function() {
|
||||
//gridFn.opennEdit(); //수정가능
|
||||
var valid = true;
|
||||
@@ -381,104 +480,147 @@ var gridFn = {
|
||||
}
|
||||
}
|
||||
|
||||
// 품번 중복 검증 함수 (PART_BOM_REPORT 테이블에서 헤더 품번 중복 체크)
|
||||
function fn_checkDuplicatePartNo(){
|
||||
var isDuplicate = false;
|
||||
|
||||
// 헤더의 품번 가져오기
|
||||
var bomPartNo = $('#bom_part_no').val();
|
||||
if(!bomPartNo || bomPartNo.trim() == ""){
|
||||
return false; // 품번이 없으면 중복 아님
|
||||
}
|
||||
|
||||
// 동기 AJAX로 DB에서 중복 체크
|
||||
$.ajax({
|
||||
url: "/partMng/checkDuplicatePartNo.do",
|
||||
type: "POST",
|
||||
data: {
|
||||
partNo: bomPartNo.trim(),
|
||||
bomReportObjid: $('#BOM_REPORT_OBJID').val() // 현재 수정 중인 BOM은 제외
|
||||
},
|
||||
dataType: "json",
|
||||
async: false, // 동기 처리
|
||||
success: function(data){
|
||||
if(data && data.isDuplicate){
|
||||
isDuplicate = true;
|
||||
}
|
||||
},
|
||||
error: function(jqxhr, status, error){
|
||||
console.error("품번 중복 체크 오류:", error);
|
||||
}
|
||||
});
|
||||
|
||||
return isDuplicate;
|
||||
}
|
||||
|
||||
function fn_save(){
|
||||
var ids = grid.jqGrid("getDataIDs");
|
||||
|
||||
if(ids!=""){
|
||||
if(fnc_valitate("form1")){
|
||||
|
||||
var valid = true;
|
||||
var existPart = true;
|
||||
var part = "";
|
||||
if (fnc_isEmpty(${bomInfo.OBJID})) {
|
||||
var partNo1 = "";
|
||||
|
||||
gridFn.closeEdit();
|
||||
$.each(grid.getRowData(), function(i, d) {
|
||||
if (i == 0) {
|
||||
partNo1 = d["PART_NO"];
|
||||
}
|
||||
var partType = d["PART_TYPE"];
|
||||
var partNo = d["PART_NO"];
|
||||
|
||||
if (partType == '0001788') {
|
||||
if (!fn_existPartNo(partNo)) {
|
||||
existPart = false;
|
||||
part = partNo;
|
||||
return false; // Exit the loop
|
||||
}
|
||||
}
|
||||
|
||||
if(!fnc_isEmpty(d["NOTE"])){
|
||||
valid = false;
|
||||
}
|
||||
});
|
||||
gridFn.opennEdit();
|
||||
|
||||
var project_no = $("#project_name").find("option:selected").text();
|
||||
if (partNo1.indexOf(project_no) < 0) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!existPart) {
|
||||
Swal.fire(part+'품번에 해당하는 구매품표준이 없습니다. 확인해 주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
Swal.fire('1레벨 품번에 프로젝트번호가 없습니다. 확인해 주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
var existsDup = false;
|
||||
var ARR_APPLICATION_PROJECT_NO = new Array();
|
||||
$(".APPLICATION_PROJECT_NO").each(function(){
|
||||
for(var i=0; i<ARR_APPLICATION_PROJECT_NO.length; i++){
|
||||
if(this.value != '' && ARR_APPLICATION_PROJECT_NO[i] == this.value){
|
||||
existsDup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ARR_APPLICATION_PROJECT_NO.push($(this).val());
|
||||
});
|
||||
if(existsDup){
|
||||
Swal.fire('동시적용 프로젝트번호에 중복건이 존재합니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
if(confirm("저장 하시겠습니까?")){
|
||||
// 품번, 품명만 입력하고 그리드 데이터가 없어도 저장 가능하도록 수정
|
||||
if(fnc_valitate("form1")){
|
||||
|
||||
var valid = true;
|
||||
var existPart = true;
|
||||
var part = "";
|
||||
|
||||
// 그리드에 데이터가 있는 경우에만 검증
|
||||
if (ids && ids.length > 0) {
|
||||
if (fnc_isEmpty(${bomInfo.OBJID})) {
|
||||
var partNo1 = "";
|
||||
|
||||
gridFn.closeEdit();
|
||||
$.ajax({
|
||||
url:"/partMng/partBomApplySave.do"
|
||||
,type:"POST"
|
||||
,data: $("#form1").serialize() + "&jqGrid="+ encodeURIComponent(JSON.stringify(grid.getRowData()))
|
||||
,dataType:"json"
|
||||
,success:function(data){
|
||||
/*
|
||||
if(data =="SUCCESS"){
|
||||
alert("저장되었습니다.");
|
||||
top.opener.fn_search();
|
||||
self.close();
|
||||
};
|
||||
*/
|
||||
console.log(data);
|
||||
if(data && data.RESULT == 'S'){
|
||||
alert("저장되었습니다.");
|
||||
if(typeof opener.fn_search =="function"){ opener.fn_search() };
|
||||
self.close();
|
||||
}else{
|
||||
alert(data.MSG);
|
||||
$.each(grid.getRowData(), function(i, d) {
|
||||
if (i == 0) {
|
||||
partNo1 = d["PART_NO"];
|
||||
}
|
||||
var partType = d["PART_TYPE"];
|
||||
var partNo = d["PART_NO"];
|
||||
|
||||
if (partType == '0001788') {
|
||||
if (!fn_existPartNo(partNo)) {
|
||||
existPart = false;
|
||||
part = partNo;
|
||||
return false; // Exit the loop
|
||||
}
|
||||
}
|
||||
,error: function(jqxhr, status, error){
|
||||
alert('에러가 발생하였습니다. 시스템 관리자에게 문의하여 주세요.');
|
||||
|
||||
if(!fnc_isEmpty(d["NOTE"])){
|
||||
valid = false;
|
||||
}
|
||||
});
|
||||
gridFn.opennEdit();
|
||||
|
||||
var project_no = $("#project_name").find("option:selected").text();
|
||||
if (partNo1 && partNo1.indexOf(project_no) < 0) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!existPart) {
|
||||
Swal.fire(part+'품번에 해당하는 구매품표준이 없습니다. 확인해 주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
Swal.fire('1레벨 품번에 프로젝트번호가 없습니다. 확인해 주세요.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
Swal.fire('저장할 데이터가 없습니다.');
|
||||
|
||||
// 품번 중복 검증 (PART_BOM_REPORT 테이블 - 헤더 품번)
|
||||
if(fn_checkDuplicatePartNo()){
|
||||
Swal.fire('입력한 품번이 이미 존재합니다. 다른 품번을 입력해주세요.');
|
||||
$('#bom_part_no').focus();
|
||||
return;
|
||||
}
|
||||
|
||||
var existsDup = false;
|
||||
var ARR_APPLICATION_PROJECT_NO = new Array();
|
||||
$(".APPLICATION_PROJECT_NO").each(function(){
|
||||
for(var i=0; i<ARR_APPLICATION_PROJECT_NO.length; i++){
|
||||
if(this.value != '' && ARR_APPLICATION_PROJECT_NO[i] == this.value){
|
||||
existsDup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ARR_APPLICATION_PROJECT_NO.push($(this).val());
|
||||
});
|
||||
if(existsDup){
|
||||
Swal.fire('동시적용 프로젝트번호에 중복건이 존재합니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 그리드 데이터가 없어도 저장 가능 (빈 BOM 생성)
|
||||
var confirmMsg = ids && ids.length > 0 ? "저장 하시겠습니까?" : "품번, 품명으로 빈 E-BOM을 생성하시겠습니까?";
|
||||
|
||||
if(confirm(confirmMsg)){
|
||||
if(ids && ids.length > 0) {
|
||||
gridFn.closeEdit();
|
||||
}
|
||||
|
||||
// 그리드 데이터가 없으면 빈 배열 전송
|
||||
var gridData = ids && ids.length > 0 ? grid.getRowData() : [];
|
||||
|
||||
$.ajax({
|
||||
url:"/partMng/partBomApplySave.do"
|
||||
,type:"POST"
|
||||
,data: $("#form1").serialize() + "&jqGrid="+ encodeURIComponent(JSON.stringify(gridData))
|
||||
,dataType:"json"
|
||||
,success:function(data){
|
||||
console.log(data);
|
||||
if(data && data.RESULT == 'S'){
|
||||
alert("저장되었습니다.");
|
||||
if(typeof opener.fn_search =="function"){ opener.fn_search() };
|
||||
self.close();
|
||||
}else{
|
||||
alert(data.MSG);
|
||||
}
|
||||
}
|
||||
,error: function(jqxhr, status, error){
|
||||
alert('에러가 발생하였습니다. 시스템 관리자에게 문의하여 주세요.');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -801,41 +943,55 @@ function fn_save(){
|
||||
|
||||
<section>
|
||||
<div class="plm_menu_name">
|
||||
<h2><span>PART 및 구조등록 Excel upload</span></h2>
|
||||
<h2><span>PART 및 구조등록 CSV upload</span></h2>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div style="font-size:10px;float:left;margin:13px 13px 5px 13px">
|
||||
<table class="" style="border:0px solid #dd2a00;">
|
||||
<colgroup>
|
||||
<col width="40px">
|
||||
<col width="180px">
|
||||
|
||||
<col width="100px">
|
||||
<col width="220px">
|
||||
|
||||
<col width="100px">
|
||||
<col width="150px">
|
||||
|
||||
<col width="50px">
|
||||
<col width="150px">
|
||||
|
||||
<col width="40px">
|
||||
<col width="150px">
|
||||
|
||||
<col width="200px">
|
||||
</colgroup>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<td style="font-size:12px;width: 70px;"><label for="street_number">제품구분</label></td>
|
||||
<td>
|
||||
<select name="product_cd" id="product_cd" disabled="disabled" style="width: 100px;">
|
||||
${code_map.product_cd}
|
||||
</select>
|
||||
</td>
|
||||
<colgroup>
|
||||
<col width="100px">
|
||||
<col width="180px">
|
||||
<col width="70px">
|
||||
<col width="180px">
|
||||
<col width="70px">
|
||||
<col width="180px">
|
||||
<col width="70px">
|
||||
<col width="150px">
|
||||
</colgroup>
|
||||
<tr>
|
||||
<td style="font-size:12px;"><label for="product_cd">제품구분</label></td>
|
||||
<td>
|
||||
<select name="product_cd" id="product_cd" required reqTitle="제품구분" style="width: 170px;" class="select2">
|
||||
<option value="">선택</option>
|
||||
${code_map.product_cd}
|
||||
</select>
|
||||
</td>
|
||||
<td style="font-size:12px;" class="align_c"><label for="bom_part_no">품번</label></td>
|
||||
<td>
|
||||
<input type="text" name="bom_part_no" id="bom_part_no" required reqTitle="품번" value="<%= bomPartNo %>" style="width: 170px;"/>
|
||||
</td>
|
||||
<td style="font-size:12px;" class="align_c"><label for="bom_part_name">품명</label></td>
|
||||
<td>
|
||||
<input type="text" name="bom_part_name" id="bom_part_name" required reqTitle="품명" value="<%= bomPartName %>" style="width: 170px;"/>
|
||||
</td>
|
||||
<td style="font-size:12px;" class="align_c"><label for="version">Version</label></td>
|
||||
<td>
|
||||
<input type="text" name="version" id="version" reqTitle="Version" value="<%= bomVersion %>" style="width: 140px;"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-size:12px;"><label for="copy_bom_select">E-BOM 복사 대상</label></td>
|
||||
<td colspan="5">
|
||||
<select name="copy_bom_select" id="copy_bom_select" style="width: 600px;" class="select2">
|
||||
<option value="">선택</option>
|
||||
${code_map.bom_list}
|
||||
</select>
|
||||
</td>
|
||||
<td colspan="2" style="text-align:center;">
|
||||
<input type="button" class="plm_btns" value="복사" id="btnCopyBom" style="width: 80px;">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- <td style="font-size:12px;"><label for="street_number">고객사</label></td>
|
||||
<td>
|
||||
@@ -942,10 +1098,10 @@ function fn_save(){
|
||||
<div style="width:100%; display: inline-block; float:left;">
|
||||
<div style=" margin: 0 8px;">
|
||||
<div id="partExcelPopupFormWrap">
|
||||
<div class="form_popup_title" style="position:relative;"> Excel upload<img src="/images/btnExcel.png" style="position:absolute; top:9px; right:135px;"/><span style="position:absolute; top:0px; right:10px; cursor:pointer;" id="templateDownload">Template Download</span></div>
|
||||
<div class="form_popup_title" style="position:relative;"> CSV upload<!--<img src="/images/btnExcel.png" style="position:absolute; top:9px; right:135px;"/><span style="position:absolute; top:0px; right:10px; cursor:pointer;" id="templateDownload">Template Download</span>--></div>
|
||||
<div id="excelUploadPopupForm">
|
||||
<div class="fileDnDWrap">
|
||||
<div id="excelImportDropZone" class="dropzone" style="height:50px;">Drag & Drop 엑셀 템플릿</div>
|
||||
<div id="excelImportDropZone" class="dropzone" style="height:50px;">Drag & Drop CSV 템플릿</div>
|
||||
<div id="excelImportList">
|
||||
<table id="excelImportTable" class="excelUploadPopupForm">
|
||||
<thead>
|
||||
|
||||
@@ -103,6 +103,15 @@ $(document).ready(function(){
|
||||
}
|
||||
}
|
||||
}
|
||||
// 공급업체 - MAKER 컬럼으로 변경, 일반 텍스트 입력 (2025-10-29)
|
||||
,{name:"MAKER" ,index:"MAKER" , width:100, align:"center", hidden:false, sortable:false, editable: true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 이전 SUP_CODE 셀렉트박스 방식 (주석처리)
|
||||
,{name:"SUP_CODE" ,index:"SUP_CODE" , width:100, align:"center", hidden:false, sortable:false, editable: true
|
||||
,edittype :"select"
|
||||
,formatter :"select"
|
||||
@@ -113,7 +122,8 @@ $(document).ready(function(){
|
||||
e.style.fontSize = 13;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
// ,{name:"SPEC",index:"SPEC", width: 150, align:"center", hidden: false, sortable:false, editable:true
|
||||
// ,editoptions:{
|
||||
// dataInit : function(e){
|
||||
|
||||
@@ -34,7 +34,15 @@ $(document).ready(function(){
|
||||
});
|
||||
|
||||
$("#btnEdit").click(function(){
|
||||
fn_edit();
|
||||
fn_enableEdit();
|
||||
});
|
||||
|
||||
$("#btnSave").click(function(){
|
||||
fn_save();
|
||||
});
|
||||
|
||||
$("#btnCancel").click(function(){
|
||||
location.reload();
|
||||
});
|
||||
|
||||
//설계변경
|
||||
@@ -71,31 +79,8 @@ $(document).ready(function(){
|
||||
$("input:radio[name='CHANGE_OPTION']").prop('disabled', true);
|
||||
$("input:radio[name='MANAGEMENT_FLAG']").prop('disabled', true);
|
||||
|
||||
$("select").attr("disabled",true);
|
||||
|
||||
$('input').prop('readonly', true);
|
||||
$('input').prop('disabled', true);
|
||||
|
||||
$('#btnEdit').prop('readonly', false);
|
||||
$('#btnEdit').prop('disabled', false);
|
||||
|
||||
$('#btnChangeDesign').prop('readonly', false);
|
||||
$('#btnChangeDesign').prop('disabled', false);
|
||||
|
||||
$('#btnClose').prop('readonly', false);
|
||||
$('#btnClose').prop('disabled', false);
|
||||
|
||||
$('#OBJID').prop('readonly', false);
|
||||
$('#STATUS').prop('readonly', false);
|
||||
$('#IS_LAST').prop('readonly', false);
|
||||
$('#ACTION_TYPE').prop('readonly', false);
|
||||
$('#REVISION').prop('readonly', false);
|
||||
|
||||
$('#OBJID').prop('disabled', false);
|
||||
$('#STATUS').prop('disabled', false);
|
||||
$('#IS_LAST').prop('disabled', false);
|
||||
$('#ACTION_TYPE').prop('disabled', false);
|
||||
$('#REVISION').prop('disabled', false);
|
||||
// 초기 상태: 읽기 전용 모드 설정
|
||||
fn_setReadOnly();
|
||||
|
||||
|
||||
window.resizeTo(800,690);
|
||||
@@ -303,6 +288,94 @@ function openImagePopUp(url){
|
||||
window.open(url,"problemImgPopUp","width="+img_width+",height="+height+", menubars=no, scrollbars=yes'");
|
||||
}
|
||||
|
||||
// 읽기 전용 모드 설정
|
||||
function fn_setReadOnly(){
|
||||
$("select").attr("disabled",true);
|
||||
|
||||
// 버튼이 아닌 input만 비활성화 (중요: type별로 선택)
|
||||
$('input[type="text"]').prop('readonly', true).prop('disabled', true);
|
||||
$('input[type="hidden"]').prop('readonly', false).prop('disabled', false);
|
||||
$('input[type="radio"]').prop('disabled', true);
|
||||
$('input[type="checkbox"]').prop('disabled', true);
|
||||
|
||||
// 모든 버튼은 명시적으로 활성화
|
||||
$('input[type="button"]').prop('disabled', false);
|
||||
|
||||
// 저장/취소 버튼 숨김, 수정 버튼 표시
|
||||
$('#btnSave').hide();
|
||||
$('#btnCancel').hide();
|
||||
$('#btnEdit').show();
|
||||
}
|
||||
|
||||
// 수정 모드 활성화
|
||||
function fn_enableEdit(){
|
||||
// 수정 가능한 필드만 활성화
|
||||
$('#PART_NAME').prop('readonly', false).prop('disabled', false);
|
||||
$('#MATERIAL').prop('readonly', false).prop('disabled', false);
|
||||
$('#HEAT_TREATMENT_HARDNESS').prop('readonly', false).prop('disabled', false);
|
||||
$('#HEAT_TREATMENT_METHOD').prop('readonly', false).prop('disabled', false);
|
||||
$('#SURFACE_TREATMENT').prop('readonly', false).prop('disabled', false);
|
||||
$('#MAKER').prop('readonly', false).prop('disabled', false);
|
||||
$('#PART_TYPE').prop('disabled', false);
|
||||
$('#REMARK').prop('readonly', false).prop('disabled', false);
|
||||
|
||||
// 버튼 표시 변경
|
||||
$('#btnEdit').hide();
|
||||
$('#btnSave').show();
|
||||
$('#btnCancel').show();
|
||||
}
|
||||
|
||||
// 저장 기능
|
||||
function fn_save(){
|
||||
if(!confirm("저장하시겠습니까?")){
|
||||
return;
|
||||
}
|
||||
|
||||
// 유효성 검사
|
||||
var partName = $('#PART_NAME').val();
|
||||
if(!partName || partName.trim() == ""){
|
||||
alert("품명을 입력해주세요.");
|
||||
$('#PART_NAME').focus();
|
||||
return;
|
||||
}
|
||||
|
||||
// 저장할 데이터 준비
|
||||
var saveData = {
|
||||
OBJID: $('#OBJID').val(),
|
||||
PART_NAME: $('#PART_NAME').val(),
|
||||
MATERIAL: $('#MATERIAL').val(),
|
||||
HEAT_TREATMENT_HARDNESS: $('#HEAT_TREATMENT_HARDNESS').val(),
|
||||
HEAT_TREATMENT_METHOD: $('#HEAT_TREATMENT_METHOD').val(),
|
||||
SURFACE_TREATMENT: $('#SURFACE_TREATMENT').val(),
|
||||
MAKER: $('#MAKER').val(),
|
||||
PART_TYPE: $('#PART_TYPE').val(),
|
||||
REMARK: $('#REMARK').val()
|
||||
};
|
||||
|
||||
// 저장 요청
|
||||
$.ajax({
|
||||
url: "/partMng/updatePartDetail.do",
|
||||
type: "POST",
|
||||
data: saveData,
|
||||
dataType: "json",
|
||||
success: function(data){
|
||||
if(data.result == "success"){
|
||||
alert("저장되었습니다.");
|
||||
// 부모 창 새로고침
|
||||
if(opener && opener.fn_search){
|
||||
opener.fn_search();
|
||||
}
|
||||
location.reload();
|
||||
}else{
|
||||
alert("저장에 실패했습니다: " + (data.message || ""));
|
||||
}
|
||||
},
|
||||
error: function(jqxhr, status, error){
|
||||
alert("저장 중 오류가 발생했습니다: " + error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fn_edit(){
|
||||
|
||||
if($("#ACTION_TYPE").val() == "changeDesign"){
|
||||
@@ -410,13 +483,16 @@ function fn_edit(){
|
||||
<label for="">공급업체</label>
|
||||
</td>
|
||||
<td class="input_sub_title" colspan="2">
|
||||
<input type="text" name="MAKER" id="MAKER" value="${resultMap.MAKER}">
|
||||
</td>
|
||||
<!-- <td class="input_sub_title" colspan="2">
|
||||
<select name="SUPPLY_CODE" id="SUPPLY_CODE" class="select2">
|
||||
${code_map.SUPPLY_CODE}
|
||||
</select>
|
||||
</td>
|
||||
</td> -->
|
||||
|
||||
<td class="input_title">
|
||||
<label for="">PART구분</label>
|
||||
<label for="">범주 이름</label>
|
||||
</td>
|
||||
<td class="input_sub_title" colspan="">
|
||||
<select name="PART_TYPE" id="PART_TYPE" class=""></select>
|
||||
@@ -670,6 +746,8 @@ function fn_edit(){
|
||||
<c:otherwise></c:otherwise>
|
||||
</c:choose>
|
||||
<input type="button" value="수정" id="btnEdit" class="plm_btns update">
|
||||
<input type="button" value="저장" id="btnSave" class="plm_btns update" style="display:none;">
|
||||
<input type="button" value="취소" id="btnCancel" class="plm_btns" style="display:none;">
|
||||
<%-- </c:when> --%>
|
||||
<%-- <c:otherwise></c:otherwise> --%>
|
||||
<%-- </c:choose> --%>
|
||||
|
||||
@@ -357,11 +357,14 @@ function fn_overlapPartMng(){
|
||||
<td class="input_title">
|
||||
<label for="">공급업체</label>
|
||||
</td>
|
||||
<td class="input_sub_title" colspan="2">
|
||||
<td class="input_sub_title" colspan=2">
|
||||
<input type="text" name="MAKER" id="MAKER" value="${resultMap.MAKER}">
|
||||
</td>
|
||||
<!-- <td class="input_sub_title" colspan="2">
|
||||
<select name="SUPPLY_CODE" id="SUPPLY_CODE" class="select2">
|
||||
${code_map.SUPPLY_CODE}
|
||||
</select>
|
||||
</td>
|
||||
</td> -->
|
||||
|
||||
<td class="input_title">
|
||||
<label for="">범주이름</label>
|
||||
|
||||
@@ -116,6 +116,16 @@ String connector = person.getUserId();
|
||||
fn_centerPopup(popup_width, popup_height, url);
|
||||
});
|
||||
|
||||
// 도면 다중 업로드 버튼 클릭
|
||||
$("#btnDrawingUpload").click(function() {
|
||||
$("#drawingFiles").click();
|
||||
});
|
||||
|
||||
// 파일 선택 이벤트
|
||||
$("#drawingFiles").change(function() {
|
||||
fn_uploadDrawingFiles(this.files);
|
||||
});
|
||||
|
||||
fn_search();
|
||||
});
|
||||
});
|
||||
@@ -139,14 +149,14 @@ String connector = person.getUserId();
|
||||
},
|
||||
*/
|
||||
//{headerHozAlign : 'center', hozAlign : 'left', width : '125', title : '모품번', field : 'PARENT_PART_INFO' },
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '180', title : '품번', field : 'PART_NO',
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '200', title : '품번', field : 'PART_NO',
|
||||
formatter:fnc_createGridAnchorTag,
|
||||
cellClick:function(e, cell){
|
||||
var objid = fnc_checkNull(cell.getData().OBJID);
|
||||
openPartMngPopup(objid);
|
||||
}
|
||||
},
|
||||
{headerHozAlign : 'center', hozAlign : 'left', /* width : '270', */ title : '품명', field : 'PART_NAME' },
|
||||
{headerHozAlign : 'center', hozAlign : 'left', /*width : '200',*/ title : '품명', field : 'PART_NAME' },
|
||||
// {headerHozAlign : 'center', hozAlign : 'center', width : '50', title : '수량', field : 'BOM_QTY' }, //Q_QTY QTY QTY_P
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '60', title : '3D', field : 'CU01_CNT',
|
||||
formatter:fnc_subInfoValueFormatter,
|
||||
@@ -179,7 +189,7 @@ String connector = person.getUserId();
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '열처리경도', field : 'HEAT_TREATMENT_HARDNESS' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '열처리방법', field : 'HEAT_TREATMENT_METHOD' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '표면처리', field : 'SURFACE_TREATMENT' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '150', title : '공급업체', field : 'SUPPLY_NAME' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '150', title : '공급업체', field : 'MAKER' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '범주 이름', field : 'PART_TYPE_TITLE' },
|
||||
|
||||
// {headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '재질', field : 'MATERIAL' },
|
||||
@@ -292,6 +302,174 @@ String connector = person.getUserId();
|
||||
fn_centerPopup(popup_width, popup_height, url);
|
||||
}
|
||||
|
||||
// 도면 다중 업로드 처리 함수
|
||||
function fn_uploadDrawingFiles(files) {
|
||||
if(!files || files.length === 0) {
|
||||
Swal.fire('파일을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 선택된 파트 확인 (필수 아님 - 전체 파트 대상)
|
||||
var selectedParts = _tabulGrid.getSelectedData();
|
||||
if(!selectedParts || selectedParts.length === 0) {
|
||||
// 선택 없으면 전체 파트 대상으로 진행
|
||||
var confirmMsg = '파트를 선택하지 않았습니다.\n';
|
||||
confirmMsg += '전체 파트를 대상으로 파일명과 일치하는 품번에 업로드됩니다.\n';
|
||||
confirmMsg += '계속하시겠습니까?';
|
||||
|
||||
if(!confirm(confirmMsg)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 파일 분류 및 처리
|
||||
var filesByType = {
|
||||
'3D': [], // stp 파일
|
||||
'2D': [], // dwg 파일
|
||||
'PDF': [] // pdf 파일
|
||||
};
|
||||
|
||||
// 파일 확장자 확인 및 분류
|
||||
for(var i = 0; i < files.length; i++) {
|
||||
var file = files[i];
|
||||
var fileName = file.name;
|
||||
var lastDotIndex = fileName.lastIndexOf('.');
|
||||
|
||||
if(lastDotIndex === -1) {
|
||||
continue; // 확장자가 없는 파일은 스킵
|
||||
}
|
||||
|
||||
var ext = fileName.substring(lastDotIndex + 1).toLowerCase();
|
||||
|
||||
if(ext === 'stp' || ext === 'step') {
|
||||
filesByType['3D'].push(file);
|
||||
} else if(ext === 'dwg') {
|
||||
filesByType['2D'].push(file);
|
||||
} else if(ext === 'pdf') {
|
||||
filesByType['PDF'].push(file);
|
||||
}
|
||||
}
|
||||
|
||||
// 업로드할 파일이 있는지 확인
|
||||
var totalFiles = filesByType['3D'].length + filesByType['2D'].length + filesByType['PDF'].length;
|
||||
if(totalFiles === 0) {
|
||||
Swal.fire('업로드 가능한 파일 형식이 없습니다. (stp, dwg, pdf만 가능)');
|
||||
return;
|
||||
}
|
||||
|
||||
// 확인 메시지
|
||||
var msg = '총 ' + totalFiles + '개의 파일을 업로드하시겠습니까?\n';
|
||||
msg += '- 3D (STP): ' + filesByType['3D'].length + '개\n';
|
||||
msg += '- 2D (DWG): ' + filesByType['2D'].length + '개\n';
|
||||
msg += '- PDF: ' + filesByType['PDF'].length + '개';
|
||||
|
||||
Swal.fire({
|
||||
title: '도면 다중 업로드',
|
||||
text: msg,
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '업로드',
|
||||
cancelButtonText: '취소'
|
||||
}).then(function(result) {
|
||||
if(result.isConfirmed) {
|
||||
fn_processDrawingUpload(filesByType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 실제 업로드 처리
|
||||
function fn_processDrawingUpload(filesByType) {
|
||||
// 현재 그리드에 표시된 파트 데이터 가져오기
|
||||
var gridData = _tabulGrid.getData();
|
||||
if(!gridData || gridData.length === 0) {
|
||||
Swal.fire('페이지에 표시된 파트가 없습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 품번 목록 생성 (현재 화면에 보이는 파트만)
|
||||
var partNoList = [];
|
||||
for(var i = 0; i < gridData.length; i++) {
|
||||
var partNo = gridData[i].PART_NO;
|
||||
if(partNo) {
|
||||
partNoList.push(partNo);
|
||||
}
|
||||
}
|
||||
|
||||
// FormData 생성
|
||||
var formData = new FormData();
|
||||
|
||||
// 현재 화면의 품번 목록 전송
|
||||
formData.append('partNoList', JSON.stringify(partNoList));
|
||||
|
||||
// 모든 파일을 files 이름으로 추가
|
||||
var allFiles = filesByType['3D'].concat(filesByType['2D']).concat(filesByType['PDF']);
|
||||
for(var i = 0; i < allFiles.length; i++) {
|
||||
formData.append('files', allFiles[i]);
|
||||
}
|
||||
|
||||
// 로딩 표시
|
||||
Swal.fire({
|
||||
title: '업로드 중...',
|
||||
text: '파일을 업로드하는 중입니다. 잠시만 기다려주세요.',
|
||||
allowOutsideClick: false,
|
||||
allowEscapeKey: false,
|
||||
allowEnterKey: false,
|
||||
showConfirmButton: false,
|
||||
onOpen: function() {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
|
||||
// AJAX 업로드
|
||||
$.ajax({
|
||||
url: '/partMng/uploadDrawingFilesForPartList.do',
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(response) {
|
||||
Swal.close();
|
||||
|
||||
if(response.result === 'success') {
|
||||
var successMsg = '도면 업로드가 완료되었습니다.\n\n';
|
||||
successMsg += '- 성공: ' + response.successCount + '개\n';
|
||||
if(response.failCount > 0) {
|
||||
successMsg += '- 실패: ' + response.failCount + '개\n';
|
||||
}
|
||||
if(response.notFoundCount > 0) {
|
||||
successMsg += '- 품번 미존재: ' + response.notFoundCount + '개\n';
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
title: '업로드 완료',
|
||||
text: successMsg,
|
||||
icon: response.failCount > 0 ? 'warning' : 'success'
|
||||
}).then(function() {
|
||||
// 그리드 새로고침
|
||||
fn_search();
|
||||
// 파일 input 초기화
|
||||
$("#drawingFiles").val('');
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: '업로드 실패',
|
||||
text: response.message || '도면 업로드 중 오류가 발생했습니다.',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
Swal.close();
|
||||
console.error('Upload error:', error);
|
||||
Swal.fire({
|
||||
title: '업로드 실패',
|
||||
text: '서버 오류가 발생했습니다: ' + error,
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body class="backcolor">
|
||||
@@ -314,8 +492,10 @@ String connector = person.getUserId();
|
||||
<!--
|
||||
<input type="button" value="삭제" class="plm_btns" id="btnDelete">
|
||||
-->
|
||||
<input type="button" value="도면 다중 업로드" class="plm_btns" id="btnDrawingUpload">
|
||||
<input type="button" value="조회" class="plm_btns" id="btnSearch">
|
||||
<input type="button" value="Excel Download" class="plm_btns" id="btnExcel">
|
||||
<input type="file" id="drawingFiles" multiple style="display:none;" accept=".stp,.step,.dwg,.pdf">
|
||||
</div>
|
||||
</div>
|
||||
<div id="plmSearchZon">
|
||||
|
||||
@@ -115,6 +115,16 @@ ui-jqgrid tr.jqgrow td {
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// 도면 다중 업로드 버튼 클릭
|
||||
$("#btnDrawingUpload").click(function() {
|
||||
$("#drawingFiles").click();
|
||||
});
|
||||
|
||||
// 파일 선택 이벤트
|
||||
$("#drawingFiles").change(function() {
|
||||
fn_uploadDrawingFiles(this.files);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -131,14 +141,14 @@ ui-jqgrid tr.jqgrow td {
|
||||
// {headerHozAlign : 'center', hozAlign : 'center', width : '60', title : '순', field : 'RNUM' ,frozen:true},
|
||||
|
||||
// {headerHozAlign : 'center', hozAlign : 'left', width : '125', title : '모품번', field : 'PARENT_PART_INFO' ,frozen:true},
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '180', title : '품번', field : 'PART_NO',frozen:true,
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '200', title : '품번', field : 'PART_NO',frozen:true,
|
||||
formatter:fnc_createGridAnchorTag,
|
||||
cellClick:function(e, cell){
|
||||
var objid = fnc_checkNull(cell.getData().OBJID);
|
||||
openPartMngPopup(objid);
|
||||
}
|
||||
},
|
||||
{headerHozAlign : 'center', hozAlign : 'left', /*width : '270',*/ title : '품명', field : 'PART_NAME' ,frozen:true},
|
||||
{headerHozAlign : 'center', hozAlign : 'left', /*width : '200',*/ title : '품명', field : 'PART_NAME' ,frozen:true},
|
||||
// {headerHozAlign : 'center', hozAlign : 'center', width : '70', title : '수량', field : 'Q_QTY' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '60', title : '3D', field : 'CU01_CNT',
|
||||
formatter:fnc_subInfoValueFormatter,
|
||||
@@ -171,7 +181,7 @@ ui-jqgrid tr.jqgrow td {
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '열처리경도', field : 'HEAT_TREATMENT_HARDNESS' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '열처리방법', field : 'HEAT_TREATMENT_METHOD' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '표면처리', field : 'SURFACE_TREATMENT' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '150', title : '공급업체', field : 'SUPPLY_NAME' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '150', title : '공급업체', field : 'MAKER' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '범주 이름', field : 'PART_TYPE_TITLE' },
|
||||
|
||||
// {headerHozAlign : 'center', hozAlign : 'left', width : '190', title : '사양(규격)', field : 'SPEC' },
|
||||
@@ -185,8 +195,30 @@ ui-jqgrid tr.jqgrow td {
|
||||
// {headerHozAlign : 'center', hozAlign : 'center', width : '90', title : 'PART 구분', field : 'PART_TYPE_TITLE' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '비고', field : 'REMARK' }
|
||||
];
|
||||
|
||||
// 중복 요청 방지 플래그
|
||||
var isSearching = false;
|
||||
|
||||
function fn_search(){
|
||||
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/partMng/partMngTempGridList.do", columns, true);
|
||||
// 이미 검색 중이면 중단
|
||||
if (isSearching) {
|
||||
console.log('검색 중입니다. 잠시만 기다려주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
isSearching = true;
|
||||
|
||||
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/partMng/partMngTempGridList.do", columns, true, function() {
|
||||
// 검색 완료 후 플래그 해제
|
||||
isSearching = false;
|
||||
});
|
||||
|
||||
// 타임아웃 방어 (10초 후 자동 해제)
|
||||
setTimeout(function() {
|
||||
if (isSearching) {
|
||||
isSearching = false;
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
function openPartMngPopup(objId){
|
||||
@@ -388,6 +420,161 @@ ui-jqgrid tr.jqgrow td {
|
||||
fn_centerPopup(popup_width, popup_height, url);
|
||||
}
|
||||
|
||||
// 도면 다중 업로드 처리 함수
|
||||
function fn_uploadDrawingFiles(files) {
|
||||
if(!files || files.length === 0) {
|
||||
Swal.fire('파일을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 파일 분류 및 처리
|
||||
var filesByType = {
|
||||
'3D': [], // stp 파일
|
||||
'2D': [], // dwg 파일
|
||||
'PDF': [] // pdf 파일
|
||||
};
|
||||
|
||||
// 파일 확장자 확인 및 분류
|
||||
for(var i = 0; i < files.length; i++) {
|
||||
var file = files[i];
|
||||
var fileName = file.name;
|
||||
var lastDotIndex = fileName.lastIndexOf('.');
|
||||
|
||||
if(lastDotIndex === -1) {
|
||||
continue; // 확장자가 없는 파일은 스킵
|
||||
}
|
||||
|
||||
var ext = fileName.substring(lastDotIndex + 1).toLowerCase();
|
||||
|
||||
if(ext === 'stp' || ext === 'step') {
|
||||
filesByType['3D'].push(file);
|
||||
} else if(ext === 'dwg') {
|
||||
filesByType['2D'].push(file);
|
||||
} else if(ext === 'pdf') {
|
||||
filesByType['PDF'].push(file);
|
||||
}
|
||||
}
|
||||
|
||||
// 업로드할 파일이 있는지 확인
|
||||
var totalFiles = filesByType['3D'].length + filesByType['2D'].length + filesByType['PDF'].length;
|
||||
if(totalFiles === 0) {
|
||||
Swal.fire('업로드 가능한 파일 형식이 없습니다. (stp, dwg, pdf만 가능)');
|
||||
return;
|
||||
}
|
||||
|
||||
// 확인 메시지
|
||||
var msg = '총 ' + totalFiles + '개의 파일을 업로드하시겠습니까?\n';
|
||||
msg += '- 3D (STP): ' + filesByType['3D'].length + '개\n';
|
||||
msg += '- 2D (DWG): ' + filesByType['2D'].length + '개\n';
|
||||
msg += '- PDF: ' + filesByType['PDF'].length + '개';
|
||||
|
||||
Swal.fire({
|
||||
title: '도면 다중 업로드',
|
||||
text: msg,
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '업로드',
|
||||
cancelButtonText: '취소'
|
||||
}).then(function(result) {
|
||||
if(result.isConfirmed) {
|
||||
fn_processDrawingUpload(filesByType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 실제 업로드 처리
|
||||
function fn_processDrawingUpload(filesByType) {
|
||||
// 현재 그리드에 표시된 파트 데이터 가져오기
|
||||
var gridData = _tabulGrid.getData();
|
||||
if(!gridData || gridData.length === 0) {
|
||||
Swal.fire('페이지에 표시된 파트가 없습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 품번 목록 생성 (현재 화면에 보이는 파트만)
|
||||
var partNoList = [];
|
||||
for(var i = 0; i < gridData.length; i++) {
|
||||
var partNo = gridData[i].PART_NO;
|
||||
if(partNo) {
|
||||
partNoList.push(partNo);
|
||||
}
|
||||
}
|
||||
|
||||
// FormData 생성
|
||||
var formData = new FormData();
|
||||
|
||||
// 현재 화면의 품번 목록 전송
|
||||
formData.append('partNoList', JSON.stringify(partNoList));
|
||||
|
||||
// 모든 파일을 files 이름으로 추가
|
||||
var allFiles = filesByType['3D'].concat(filesByType['2D']).concat(filesByType['PDF']);
|
||||
for(var i = 0; i < allFiles.length; i++) {
|
||||
formData.append('files', allFiles[i]);
|
||||
}
|
||||
|
||||
// 로딩 표시
|
||||
Swal.fire({
|
||||
title: '업로드 중...',
|
||||
text: '파일을 업로드하는 중입니다. 잠시만 기다려주세요.',
|
||||
allowOutsideClick: false,
|
||||
allowEscapeKey: false,
|
||||
allowEnterKey: false,
|
||||
showConfirmButton: false,
|
||||
onOpen: function() {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
|
||||
// AJAX 업로드
|
||||
$.ajax({
|
||||
url: '/partMng/uploadDrawingFilesForPartList.do',
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(response) {
|
||||
Swal.close();
|
||||
|
||||
if(response.result === 'success') {
|
||||
var successMsg = '도면 업로드가 완료되었습니다.\n\n';
|
||||
successMsg += '- 성공: ' + response.successCount + '개\n';
|
||||
if(response.failCount > 0) {
|
||||
successMsg += '- 실패: ' + response.failCount + '개\n';
|
||||
}
|
||||
if(response.notFoundCount > 0) {
|
||||
successMsg += '- 품번 미존재: ' + response.notFoundCount + '개\n';
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
title: '업로드 완료',
|
||||
text: successMsg,
|
||||
icon: response.failCount > 0 ? 'warning' : 'success'
|
||||
}).then(function() {
|
||||
// 그리드 새로고침
|
||||
fn_search();
|
||||
// 파일 input 초기화
|
||||
$("#drawingFiles").val('');
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: '업로드 실패',
|
||||
text: response.message || '도면 업로드 중 오류가 발생했습니다.',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
Swal.close();
|
||||
console.error('Upload error:', error);
|
||||
Swal.fire({
|
||||
title: '업로드 실패',
|
||||
text: '서버 오류가 발생했습니다: ' + error,
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openExcelPopup() {
|
||||
/*
|
||||
if($("#customer_cd").val()==""){
|
||||
@@ -426,7 +613,9 @@ ui-jqgrid tr.jqgrow td {
|
||||
<input type="button" value="삭제" class="plm_btns" id="btnDelete">
|
||||
<input type="button" value="등록" class="plm_btns" id="btnReg">
|
||||
<input type="button" value="등록(Excel Upload)" class="plm_btns" onclick="openExcelPopup();">
|
||||
<input type="button" value="도면 다중 업로드" class="plm_btns" id="btnDrawingUpload">
|
||||
<input type="button" value="조회" class="plm_btns" id="btnSearch">
|
||||
<input type="file" id="drawingFiles" multiple style="display:none;" accept=".stp,.step,.dwg,.pdf">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -21,7 +21,10 @@ $(document).ready(function(){
|
||||
$("._table1").scrollLeft($("._table2").scrollLeft());
|
||||
});
|
||||
|
||||
|
||||
// 품번/품명 Select2 AJAX 초기화 (common.js의 새 함수 사용)
|
||||
initPartSelect2Ajax("#search_partNo", "#search_partName", "#search_partObjId", {
|
||||
debug: false // 디버깅 모드 비활성화
|
||||
});
|
||||
|
||||
$("#mainGrid").jqGrid({
|
||||
height : 630,
|
||||
@@ -76,6 +79,19 @@ $(document).ready(function(){
|
||||
|
||||
fn_search();
|
||||
});
|
||||
|
||||
//정전개 조회
|
||||
$("#btnSearchAscending").click(function(){
|
||||
$("#searchType").val("ascending");
|
||||
fn_search();
|
||||
});
|
||||
|
||||
//역전개 조회
|
||||
$("#btnSearchDescending").click(function(){
|
||||
$("#searchType").val("descending");
|
||||
fn_search();
|
||||
});
|
||||
|
||||
$("#btnExcel").click(function(){
|
||||
|
||||
$("#search_partNo").val($.trim($("#search_partNo").val()));
|
||||
@@ -690,18 +706,25 @@ function fn_excelExport(pGridObj,pFileName){
|
||||
<form name="form1" id="form1" action="" method="post">
|
||||
<input type="hidden" name="search" id="search" value="Y">
|
||||
<input type="hidden" name="actionType" id="actionType" value="" />
|
||||
<input type="hidden" name="searchType" id="searchType" value="ascending" />
|
||||
<div class="min_part_enroll">
|
||||
<div class="content-box">
|
||||
<div class=""> <!-- content-box-s -->
|
||||
<div class=plm_menu_name_gdnsi>
|
||||
<h2>
|
||||
<span>제품관리_BOM 조회</span>
|
||||
<c:if test="${!empty param.searchType}">
|
||||
<span style="font-size: 14px; color: #666; margin-left: 10px;">
|
||||
(${param.searchType eq 'descending' ? '역전개' : '정전개'})
|
||||
</span>
|
||||
</c:if>
|
||||
</h2>
|
||||
<div class="btnArea">
|
||||
<!--
|
||||
<input type="button" value="Excel Download" class="plm_btns" id="btnExcel">
|
||||
-->
|
||||
<input type="button" value="조회" class="plm_btns" id="btnSearch">
|
||||
<input type="button" value="정전개 조회" class="plm_btns" id="btnSearchAscending">
|
||||
<input type="button" value="역전개 조회" class="plm_btns" id="btnSearchDescending">
|
||||
</div>
|
||||
</div>
|
||||
<div id="plmSearchZon">
|
||||
@@ -739,6 +762,24 @@ function fn_excelExport(pGridObj,pFileName){
|
||||
<td class="align_r">
|
||||
<label for="" class="">품번</label>
|
||||
</td>
|
||||
<td>
|
||||
<select name="search_partNo" id="search_partNo" class="select2-part" style="width: 100%;">
|
||||
<option value="">품번 선택</option>
|
||||
</select>
|
||||
<input type="hidden" name="search_partObjId" id="search_partObjId" value=""/>
|
||||
</td>
|
||||
<td class="align_r">
|
||||
<label for="" class="">품명</label>
|
||||
</td>
|
||||
<td colspan="3">
|
||||
<select name="search_partName" id="search_partName" class="select2-part" style="width: 100%;">
|
||||
<option value="">품명 선택</option>
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<!-- <td class="align_r">
|
||||
<label for="" class="">품번</label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="search_partNo" id="search_partNo" value="${param.search_partNo}" class="text_area" style="width:200px;"/>
|
||||
</td>
|
||||
@@ -747,7 +788,7 @@ function fn_excelExport(pGridObj,pFileName){
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="search_partName" id="search_partName" value="${param.search_partName}" class="text_area" style="width:200px;"/>
|
||||
</td>
|
||||
</td> -->
|
||||
<!-- <td class="align_r">
|
||||
<label for="" class="">LEVEL</label>
|
||||
</td>
|
||||
@@ -771,6 +812,11 @@ function fn_excelExport(pGridObj,pFileName){
|
||||
|
||||
<div class="ascendig_text">
|
||||
<font size="2px">총 ${fn:length(List)}건</font>
|
||||
<c:if test="${!empty param.searchType && param.searchType eq 'descending'}">
|
||||
<font size="2px" style="color: #ff6600; margin-left: 10px;">
|
||||
※ 역전개: 선택한 부품을 사용하는 상위 BOM을 표시합니다
|
||||
</font>
|
||||
</c:if>
|
||||
</div>
|
||||
|
||||
<div class="in_table_scroll_wrap _table1" style="height:26px;width:99.4%;">
|
||||
@@ -788,23 +834,23 @@ function fn_excelExport(pGridObj,pFileName){
|
||||
</c:forEach>
|
||||
</c:when>
|
||||
</c:choose>
|
||||
<col width="120px" /> <!-- 품번 -->
|
||||
<col width="150px" /> <!-- 품명 -->
|
||||
<col width="200px" /> <!-- 품번 -->
|
||||
<col width="200px" /> <!-- 품명 -->
|
||||
<col width="35px" /> <!-- Qty -->
|
||||
<col width="50px" /> <!-- P_Qty -->
|
||||
<col width="30px" /> <!-- 3D -->
|
||||
<col width="30px" /> <!-- 2D -->
|
||||
<col width="30px" /> <!-- 2D PDF -->
|
||||
<col width="100px" /> <!-- 재료 -->
|
||||
<col width="130px" /> <!-- 열처리경도 -->
|
||||
<col width="150px" /> <!-- 열처리방법 -->
|
||||
<col width="150px" /> <!-- 표면처리 -->
|
||||
<col width="100px" /> <!-- 공급업체 -->
|
||||
<col width="90px" /> <!-- 재료 -->
|
||||
<col width="90px" /> <!-- 열처리경도 -->
|
||||
<col width="90px" /> <!-- 열처리방법 -->
|
||||
<col width="90px" /> <!-- 표면처리 -->
|
||||
<col width="90px" /> <!-- 공급업체 -->
|
||||
<col width="80px" /> <!-- PART 타입 -->
|
||||
<col width="60px" /> <!-- REVISION -->
|
||||
<col width="70px" /> <!-- EO No -->
|
||||
<col width="70px" /> <!-- EO Date -->
|
||||
<col width="230px" /> <!-- REMARK -->
|
||||
<col width="200px" /> <!-- REMARK -->
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr class="plm_thead">
|
||||
@@ -856,23 +902,23 @@ function fn_excelExport(pGridObj,pFileName){
|
||||
</c:forEach>
|
||||
</c:when>
|
||||
</c:choose>
|
||||
<col width="120px" /> <!-- 품번 -->
|
||||
<col width="150px" /> <!-- 품명 -->
|
||||
<col width="200px" /> <!-- 품번 -->
|
||||
<col width="200px" /> <!-- 품명 -->
|
||||
<col width="35px" /> <!-- Qty -->
|
||||
<col width="50px" /> <!-- P_Qty -->
|
||||
<col width="30px" /> <!-- 3D -->
|
||||
<col width="30px" /> <!-- 2D -->
|
||||
<col width="30px" /> <!-- 2D PDF -->
|
||||
<col width="100px" /> <!-- 재료 -->
|
||||
<col width="130px" /> <!-- 열처리경도 -->
|
||||
<col width="150px" /> <!-- 열처리방법 -->
|
||||
<col width="150px" /> <!-- 표면처리 -->
|
||||
<col width="100px" /> <!-- 공급업체 -->
|
||||
<col width="90px" /> <!-- 재료 -->
|
||||
<col width="90px" /> <!-- 열처리경도 -->
|
||||
<col width="90px" /> <!-- 열처리방법 -->
|
||||
<col width="90px" /> <!-- 표면처리 -->
|
||||
<col width="90px" /> <!-- 공급업체 -->
|
||||
<col width="80px" /> <!-- PART 타입 -->
|
||||
<col width="60px" /> <!-- REVISION -->
|
||||
<col width="70px" /> <!-- EO No -->
|
||||
<col width="70px" /> <!-- EO Date -->
|
||||
<col width="230px" /> <!-- REMARK -->
|
||||
<col width="200px" /> <!-- REMARK -->
|
||||
</colgroup>
|
||||
<c:choose>
|
||||
<c:when test="${empty List}">
|
||||
@@ -919,10 +965,10 @@ function fn_excelExport(pGridObj,pFileName){
|
||||
<td><a href="#" class="File file_${item.FILE_3D_CNT eq 0?'empty_':''}icon" data-OBJID="${item.PART_OBJID}" data-docType="3D_CAD" data-docTypeName="3D CAD 첨부파일"></a></td>
|
||||
<td><a href="#" class="File file_${item.FILE_2D_CNT eq 0?'empty_':''}icon" data-OBJID="${item.PART_OBJID}" data-docType="2D_DRAWING_CAD" data-docTypeName="2D(Drawing) CAD 첨부파일"></a></td>
|
||||
<td><a href="#" class="File file_${item.FILE_PDF_CNT eq 0?'empty_':''}icon" data-OBJID="${item.PART_OBJID}" data-docType="2D_PDF_CAD" data-docTypeName="2D(PDF) CAD 첨부파일"></a></td>
|
||||
<td title="${item.MATERIAL}" class="align_c">${item.MATERIAL}</td><!-- 재료 -->
|
||||
<td title="${item.SPEC}" class="align_l" style="text-align: left; padding-left: 5px;">${item.SPEC}</td><!-- 열처리경도 -->
|
||||
<td title="${item.POST_PROCESSING}" class="align_l" style="text-align: left; padding-left: 5px;">${item.POST_PROCESSING}</td><!-- 열처리방법 -->
|
||||
<td title="${item.POST_PROCESSING}" class="align_l" style="text-align: left; padding-left: 5px;">${item.POST_PROCESSING}</td><!-- 표면처리 -->
|
||||
<td title="${item.MATERIAL}" class="align_l">${item.MATERIAL}</td><!-- 재료 -->
|
||||
<td title="${item.HEAT_TREATMENT_HARDNESS}" class="align_l" style="text-align: left; padding-left: 5px;">${item.HEAT_TREATMENT_HARDNESS}</td><!-- 열처리경도 -->
|
||||
<td title="${item.HEAT_TREATMENT_METHOD}" class="align_l" style="text-align: left; padding-left: 5px;">${item.HEAT_TREATMENT_METHOD}</td><!-- 열처리방법 -->
|
||||
<td title="${item.SURFACE_TREATMENT}" class="align_l" style="text-align: left; padding-left: 5px;">${item.SURFACE_TREATMENT}</td><!-- 표면처리 -->
|
||||
<td title="${item.MAKER}" class="align_l" style="text-align: left; padding-left: 5px;">${item.MAKER}</td><!-- 공급업체 -->
|
||||
<td title="${item.PART_TYPE_TITLE}" class="align_c">${item.PART_TYPE_TITLE}</td><!-- PART_TYPE -->
|
||||
<td title="${item.REVISION}" class="align_c" style="text-align: left; padding-left: 5px;">${item.REVISION}</td><!-- REVISION -->
|
||||
|
||||
@@ -157,31 +157,54 @@ var columns = [
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '270', title : '유닛명', field : 'UNIT_NAME' },*/
|
||||
|
||||
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '150', title : '제품구분', field : 'PRODUCT_NAME' },
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '250', title : '품번', field : 'PART_NO' },
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '250', title : '품명', field : 'PART_NAME' },
|
||||
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '150', title : 'E-BOM', field : 'BOM_CNT',
|
||||
formatter: fnc_subInfoValueFormatter,
|
||||
cellClick:function(e, cell){
|
||||
var objId = fnc_checkNull(cell.getData().OBJID);
|
||||
//var bomReportObjId = fnc_checkNull(cell.getData().BOM_REPORMECHANICAL_TYPET_OBJID);
|
||||
fn_openSetStructure(objId);
|
||||
}
|
||||
},
|
||||
|
||||
// {headerHozAlign : 'center', hozAlign : 'center', width : '120', title : '등록자', field : 'DEPT_USER_NAME' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '150', title : '등록일', field : 'REG_DATE' },
|
||||
// {headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '배포일', field : 'DEPLOY_DATE' },
|
||||
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '150', title : 'Version', field : 'REVISION' },
|
||||
{headerHozAlign : 'center', hozAlign : 'left', /* width : '200', */ title : '배포사유', field : 'NOTE' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '150', title : '상태', field : 'STATUS' }
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '160', title : '제품구분', field : 'PRODUCT_NAME' },
|
||||
// {headerHozAlign : 'center', hozAlign : 'center', title : '제품구분', field : 'PRODUCT_CD' ,hidden: true},
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '210', title : '품번', field : 'PART_NO' },
|
||||
{headerHozAlign : 'center', hozAlign : 'left', title : '품명', field : 'PART_NAME' },
|
||||
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '150', title : 'E-BOM', field : 'BOM_CNT',
|
||||
formatter: fnc_subInfoValueFormatter,
|
||||
cellClick:function(e, cell){
|
||||
var objId = fnc_checkNull(cell.getData().OBJID);
|
||||
//var bomReportObjId = fnc_checkNull(cell.getData().BOM_REPORMECHANICAL_TYPET_OBJID);
|
||||
fn_openSetStructure(objId);
|
||||
}
|
||||
},
|
||||
|
||||
// {headerHozAlign : 'center', hozAlign : 'center', width : '120', title : '등록자', field : 'DEPT_USER_NAME' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '130', title : '등록일', field : 'REG_DATE' },
|
||||
// {headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '배포일', field : 'DEPLOY_DATE' },
|
||||
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '110', title : 'Version', field : 'REVISION' },
|
||||
// {headerHozAlign : 'center', hozAlign : 'left', /* width : '200', */ title : '배포사유', field : 'NOTE' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '110', title : '상태', field : 'STATUS' }
|
||||
];
|
||||
|
||||
// 중복 요청 방지를 위한 로딩 플래그
|
||||
var isSearching = false;
|
||||
|
||||
//var grid;
|
||||
function fn_search(){
|
||||
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/partMng/searchStructureGridList.do", columns, true);
|
||||
// 이미 검색 중이면 중복 요청 방지
|
||||
if (isSearching) {
|
||||
console.log('검색 중입니다. 잠시만 기다려주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
isSearching = true;
|
||||
|
||||
// 기존 그리드 검색 함수 실행
|
||||
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/partMng/searchStructureGridList.do", columns, true, function() {
|
||||
// 검색 완료 후 플래그 해제
|
||||
isSearching = false;
|
||||
});
|
||||
|
||||
// fnc_tabul_search가 콜백을 지원하지 않을 경우를 위한 타임아웃 처리
|
||||
setTimeout(function() {
|
||||
if (isSearching) {
|
||||
isSearching = false;
|
||||
}
|
||||
}, 10000); // 10초 타임아웃
|
||||
}
|
||||
|
||||
//양산제품에 해당하는 SPEC 정보 목록을 가져온다.
|
||||
@@ -462,7 +485,7 @@ function fn_openSetBomCopy(product_code){
|
||||
* 구조등록 팝업
|
||||
*/
|
||||
function fn_openSetStructure(objId){
|
||||
window.open("/partMng/setStructurePopupMainFS.do?objId="+objId, "", "width=1880, height=900, resizable=no");
|
||||
window.open("/partMng/setStructurePopupMainFS.do?objId="+objId, "", "width=1880, height=900, resizable=yes");
|
||||
}
|
||||
/**
|
||||
* 배포사유 입력 팝업
|
||||
@@ -504,7 +527,7 @@ function saveexcelpop() {
|
||||
|
||||
var STATUS = fnc_checkNull(selectedStructure[0].STATUS);
|
||||
|
||||
if(STATUS != 'create'){ //deploy
|
||||
if(STATUS != 'N'){ //deploy
|
||||
Swal.fire('등록중인 건만 등록/추가 할 수 있습니다.');
|
||||
return;
|
||||
}
|
||||
@@ -513,16 +536,37 @@ function saveexcelpop() {
|
||||
var project_name ="";
|
||||
var unit_code ="";
|
||||
var BOM_REPORT_OBJID ="";
|
||||
var BOM_PART_NO ="";
|
||||
var BOM_PART_NAME ="";
|
||||
var BOM_PRODUCT_CD ="";
|
||||
var BOM_VERSION ="";
|
||||
|
||||
for (var i = 0; i < selectedStructure.length; i++) {
|
||||
customer_cd = fnc_checkNull(selectedStructure[i].CUSTOMER_OBJID);
|
||||
project_name = fnc_checkNull(selectedStructure[i].CONTRACT_OBJID);
|
||||
unit_code = fnc_checkNull(selectedStructure[i].UNIT_CODE);
|
||||
//customer_cd = fnc_checkNull(selectedStructure[i].CUSTOMER_OBJID);
|
||||
//project_name = fnc_checkNull(selectedStructure[i].CONTRACT_OBJID);
|
||||
//unit_code = fnc_checkNull(selectedStructure[i].UNIT_CODE);
|
||||
BOM_REPORT_OBJID = fnc_checkNull(selectedStructure[i].OBJID);
|
||||
BOM_PART_NO = fnc_checkNull(selectedStructure[i].PART_NO);
|
||||
BOM_PART_NAME = fnc_checkNull(selectedStructure[i].PART_NAME);
|
||||
BOM_PRODUCT_CD = fnc_checkNull(selectedStructure[i].PRODUCT_CD);
|
||||
BOM_VERSION = fnc_checkNull(selectedStructure[i].REVISION);
|
||||
}
|
||||
|
||||
var url = "/partMng/openBomReportExcelImportPopUp.do?customer_cd="+customer_cd+"&project_name="+project_name+"&unit_code="+unit_code+"&BOM_REPORT_OBJID="+BOM_REPORT_OBJID;
|
||||
var target = "openBomReportExcelImportPopUp";
|
||||
// hiddenForm을 사용하여 POST 방식으로 팝업 열기 (한글 인코딩 문제 해결)
|
||||
var hiddenForm = document.hiddenForm;
|
||||
var url = "/partMng/openBomReportExcelImportPopUp.do";
|
||||
var target = "openBomReportExcelImportPopUp";
|
||||
|
||||
hiddenForm.PRODUCT_CD.value = BOM_PRODUCT_CD;
|
||||
hiddenForm.BOM_PART_NAME.value = BOM_PART_NAME;
|
||||
hiddenForm.BOM_PART_NO.value = BOM_PART_NO;
|
||||
hiddenForm.BOM_REPORT_OBJID.value = BOM_REPORT_OBJID;
|
||||
hiddenForm.BOM_VERSION.value = BOM_VERSION;
|
||||
|
||||
window.open('', target, 'width=1920, height=860, menubars=no, scrollbars=yes, resizable=yes');
|
||||
hiddenForm.action = url;
|
||||
hiddenForm.target = target;
|
||||
hiddenForm.submit();
|
||||
}
|
||||
}else{
|
||||
if($("#customer_cd").val()==""){
|
||||
@@ -540,8 +584,8 @@ function saveexcelpop() {
|
||||
|
||||
var url = "/partMng/openBomReportExcelImportPopUp.do?customer_cd="+$("#customer_cd").val()+"&project_name="+$("#project_name").val()+"&unit_code="+$("#unit_code").val();
|
||||
var target = "openBomReportExcelImportPopUp";
|
||||
window.open(url, target,"width=1920, height=860, menubars=no, scrollbars=yes, resizable=yes");
|
||||
}
|
||||
window.open(url, target,"width=1920, height=860, menubars=no, scrollbars=yes, resizable=yes");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
@@ -552,6 +596,11 @@ function saveexcelpop() {
|
||||
<input type="hidden" name="param_product_mgmt_spec" id="param_product_mgmt_spec">
|
||||
<input type="hidden" name="param_upg_no" id="param_upg_no">
|
||||
<input type="hidden" name="BOM_REPORT_OBJID" id="BOM_REPORT_OBJID">
|
||||
<!-- BOM Excel Import 팝업용 파라미터 -->
|
||||
<input type="hidden" name="PRODUCT_CD" id="PRODUCT_CD">
|
||||
<input type="hidden" name="BOM_PART_NAME" id="BOM_PART_NAME">
|
||||
<input type="hidden" name="BOM_PART_NO" id="BOM_PART_NO">
|
||||
<input type="hidden" name="BOM_VERSION" id="BOM_VERSION">
|
||||
</form>
|
||||
|
||||
<form name="form1" id="form1" action="" method="post">
|
||||
@@ -632,17 +681,19 @@ function saveexcelpop() {
|
||||
<label>등록일</label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="SEARCH_DEPLOY_DATE_FROM" id="SEARCH_DEPLOY_DATE_FROM" style="width:90px;" autocomplete="off" value="${param.SEARCH_DEPLOY_DATE_FROM}">~
|
||||
<input type="text" name="SEARCH_DEPLOY_DATE_TO" id="SEARCH_DEPLOY_DATE_TO" style="width:90px;" autocomplete="off" value="${param.SEARCH_DEPLOY_DATE_TO}">
|
||||
<input type="text" name="search_fromDate" id="search_fromDate" style="width:90px;" autocomplete="off" value="${param.search_fromDate}">~
|
||||
<input type="text" name="search_toDate" id="search_toDate" style="width:90px;" autocomplete="off" value="${param.search_toDate}">
|
||||
</td>
|
||||
|
||||
<td><label for="status">상태</label></td>
|
||||
<td>
|
||||
<select id="status" name="status" class="select2" style="width:170px;">
|
||||
<option value="">선택</option>
|
||||
<option value="create" ${param.status eq 'create'?'selected':''}>등록중</option>
|
||||
<option value="Y" ${param.status eq 'Y'?'selected':''}>Y</option>
|
||||
<option value="N" ${param.status eq 'N'?'selected':''}>N</option>
|
||||
<!-- <option value="create" ${param.status eq 'create'?'selected':''}>등록중</option>
|
||||
<option value="changeDesign" ${param.status eq 'changeDesign'?'selected':''}>설계변경미배포</option>
|
||||
<option value="deploy"${param.status eq 'deploy'?'selected':''}>배포완료</option>
|
||||
<option value="deploy"${param.status eq 'deploy'?'selected':''}>배포완료</option> -->
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -10,76 +10,86 @@
|
||||
<script>
|
||||
$(function(){
|
||||
|
||||
if(isCreateBom()){
|
||||
visibleChangeTalbe(false);
|
||||
}
|
||||
|
||||
$('.select2').select2();
|
||||
|
||||
//0001055 - - 고객요청
|
||||
//0001056 - - 자체설변
|
||||
$('#CHANGE_TYPE option').not($('#CHANGE_TYPE').find('[value=""],[value=0001055],[value=0001056]')).remove();
|
||||
//0001610 - - 파트삭제 test
|
||||
//0001611 - - 파트추가
|
||||
//0001626 - - 파트추가 real
|
||||
//0001637 - - 파트삭제
|
||||
//$('#CHANGE_OPTION option').not($('#CHANGE_OPTION').find('[value=""],[value=0001610],[value=0001611]')).remove(); //test
|
||||
$('#CHANGE_OPTION option').not($('#CHANGE_OPTION').find('[value=""],[value=0001626],[value=0001637],[value=0001790]')).remove();
|
||||
$('.select2').select2();
|
||||
|
||||
//Part 연결
|
||||
$("#moveLeft").click(function(){
|
||||
var rightPartArr = $(".partChks:checked", parent.frames['rightFrame'].document);
|
||||
var leftPartNoObj = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document);
|
||||
var leftPartChildObjId = leftPartNoObj.val();
|
||||
var leftPartNo = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-PART_NO");
|
||||
var leftPartNoQty = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-PART_NO_QTY");
|
||||
var leftParentObjId = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-OBJID");
|
||||
var leftPartLastObjId = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-LAST_PART_OBJID");
|
||||
var leftQtyParObjId = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-PART_OBJID");
|
||||
// Tabulator에서 선택된 오른쪽 행 데이터 가져오기
|
||||
var rightFrame = parent.frames['rightFrame'];
|
||||
var rightSelectedRows = rightFrame.getSelectedRows ? rightFrame.getSelectedRows() : [];
|
||||
|
||||
//같은 Part를 연결한건지 체크
|
||||
if(rightSelectedRows.length === 0) {
|
||||
alert("선택된 파트가 없습니다.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 왼쪽 프레임에서 선택된 행 데이터 가져오기
|
||||
var leftPartNoObj = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document);
|
||||
|
||||
var leftPartChildObjId = null;
|
||||
var leftPartNo = null;
|
||||
var leftPartNoQty = null;
|
||||
var leftParentObjId = null;
|
||||
var leftPartLastObjId = null;
|
||||
var leftQtyParObjId = null;
|
||||
var leftParentParts = "";
|
||||
|
||||
if(leftPartNoObj.length > 0) {
|
||||
leftPartChildObjId = leftPartNoObj.val();
|
||||
leftPartNo = leftPartNoObj.attr("data-PART_NO");
|
||||
leftPartNoQty = leftPartNoObj.attr("data-PART_NO_QTY");
|
||||
leftParentObjId = leftPartNoObj.attr("data-OBJID");
|
||||
leftPartLastObjId = leftPartNoObj.attr("data-LAST_PART_OBJID");
|
||||
leftQtyParObjId = leftPartNoObj.attr("data-PART_OBJID");
|
||||
leftParentParts = leftPartNoObj.attr("data-PARENT_PARTS") || "";
|
||||
}
|
||||
|
||||
// 같은 Part를 연결한건지 체크
|
||||
var isSamePart = false;
|
||||
$(rightPartArr).each(function(i){
|
||||
var rightPartNo = $(this).val();
|
||||
for(var i = 0; i < rightSelectedRows.length; i++){
|
||||
var rowData = rightSelectedRows[i].getData();
|
||||
var rightPartNo = rowData.PART_NO;
|
||||
if(rightPartNo == leftPartNo){
|
||||
alert("오류 Part No : ["+rightPartNo+"]\n같은 Part No끼리 연결할 수 없습니다.");
|
||||
isSamePart = true;
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(isSamePart) return false;
|
||||
|
||||
//연결하려는 part가 상위에 있는 part인지 확인.
|
||||
var leftParentParts = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-PARENT_PARTS");
|
||||
// 연결하려는 part가 상위에 있는 part인지 확인
|
||||
var deniedPartArr = [];
|
||||
if(fnc_checkNull(leftParentParts).indexOf(",") > 0){
|
||||
deniedPartArr = leftParentParts.split(",");
|
||||
}
|
||||
var isDeniedPart = false;
|
||||
|
||||
$(rightPartArr).each(function(i){
|
||||
var rightPartNo = $(this).val();
|
||||
var rightPartType = $(this).attr("data-PART_TYPE");
|
||||
for(var i = 0; i < rightSelectedRows.length; i++){
|
||||
var rowData = rightSelectedRows[i].getData();
|
||||
var rightPartNo = rowData.PART_NO;
|
||||
var rightPartType = rowData.PART_TYPE;
|
||||
|
||||
if("unique" == rightPartType){
|
||||
for(var i = 0 ; i < deniedPartArr.length ; i++){
|
||||
if(rightPartNo == deniedPartArr[i]){
|
||||
for(var j = 0 ; j < deniedPartArr.length ; j++){
|
||||
if(rightPartNo == deniedPartArr[j]){
|
||||
alert("오류 Part No : "+"["+rightPartNo+"]\n이미 상위에 등록된 Part No 입니다.");
|
||||
isDeniedPart = true;
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isDeniedPart) break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(isDeniedPart) return;
|
||||
//연결하려는 part가 상위에 있는 part인지 확인. end
|
||||
|
||||
// 선택된 파트의 OBJID 배열 생성
|
||||
var rightCheckedArr = [];
|
||||
$(rightPartArr).each(function(i){
|
||||
rightCheckedArr.push($(this).val());
|
||||
});
|
||||
for(var i = 0; i < rightSelectedRows.length; i++){
|
||||
var rowData = rightSelectedRows[i].getData();
|
||||
rightCheckedArr.push(rowData.OBJID);
|
||||
}
|
||||
|
||||
if(fnc_checkNull(leftPartNo) == ""){
|
||||
var flag = fn_checkSameTopPartNo(rightCheckedArr);
|
||||
@@ -89,20 +99,7 @@ $(function(){
|
||||
}
|
||||
}
|
||||
|
||||
if(!fnc_validate2('form1')){
|
||||
return;
|
||||
}
|
||||
|
||||
//0001611 - - 파트추가
|
||||
//0001610 - - 파트삭제
|
||||
if(!isCreateBom() && '0001626' != $('#CHANGE_OPTION').val()){
|
||||
alert('설변사유는 파트추가만 가능합니다.');
|
||||
$('#CHANGE_OPTION').val('0001626');
|
||||
$('.select2').select2();
|
||||
return;
|
||||
}
|
||||
|
||||
fn_relatePartInfo(leftPartNoObj.val(), rightCheckedArr, leftPartNoQty, leftPartLastObjId, leftPartChildObjId, leftQtyParObjId);
|
||||
fn_relatePartInfo(leftPartChildObjId, rightCheckedArr, leftPartNoQty, leftPartLastObjId, leftPartChildObjId, leftQtyParObjId);
|
||||
});
|
||||
//end of Part 연결
|
||||
|
||||
@@ -115,23 +112,6 @@ $(function(){
|
||||
var leftParentObjId = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-PARENT_OBJID");
|
||||
var leftPartLastObjId = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-LAST_PART_OBJID");
|
||||
|
||||
/* alert('leftPartNo ->'+leftPartNo+".");
|
||||
alert('leftParentObjId ->'+leftParentObjId+".");
|
||||
alert('leftParentPartNo ->'+leftParentPartNo+"."); */
|
||||
|
||||
if(!fnc_validate2('form1')){
|
||||
return;
|
||||
}
|
||||
|
||||
//0001611 - - 파트추가
|
||||
//0001610 - - 파트삭제
|
||||
if(!isCreateBom() && '0001637' != $('#CHANGE_OPTION').val()){
|
||||
alert('설변사유는 파트삭제만 가능합니다.');
|
||||
$('#CHANGE_OPTION').val('0001637');
|
||||
$('.select2').select2();
|
||||
return;
|
||||
}
|
||||
|
||||
fn_deletePartRelateInfo(leftPartNoObj.val(), leftPartLastObjId, leftParentPartNo, leftParentObjId, leftPartChildObjId);
|
||||
});
|
||||
//end of 연결된 part 삭제
|
||||
@@ -139,48 +119,47 @@ $(function(){
|
||||
//연결된 part 변경
|
||||
$("#moveChange").click(function(){
|
||||
var leftPartNoList = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document);
|
||||
var leftPartNo = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-PART_NO");
|
||||
var leftPartObjid = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-BOM_LAST_PART_OBJID");
|
||||
var leftParentPartObjid = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-PARENT_PART_NO");
|
||||
var rightPartNoList = $(".partChks:checked", parent.frames['rightFrame'].document);
|
||||
var rightPartNo = $(".partChks:checked", parent.frames['rightFrame'].document).attr("data-PART_NO");
|
||||
var rightPartRev = $(".partChks:checked", parent.frames['rightFrame'].document).attr("data-PART_REV");
|
||||
|
||||
var leftPartChildObjId = leftPartNoList.val();
|
||||
//var leftPartNoQty = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-PART_NO_QTY");
|
||||
|
||||
//alert("leftPartObjid::"+leftPartObjid);
|
||||
var leftPartBomQtyObjId = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-OBJID");
|
||||
|
||||
if(!fnc_validate2('form1')){
|
||||
return;
|
||||
}
|
||||
|
||||
if(0 == leftPartNoList.length){
|
||||
if(leftPartNoList.length === 0){
|
||||
alert("선택된 파트가 없습니다.");
|
||||
return false;
|
||||
}else if(1 == leftPartNoList.length){
|
||||
if(0 == rightPartNoList.length){
|
||||
alert("선택된 파트가 없습니다.");
|
||||
return false;
|
||||
}else if(1 == rightPartNoList.length){
|
||||
fn_changeRelatePartInfo(leftPartBomQtyObjId,rightPartNoList.val(),leftPartNoList.val(),leftParentPartObjid,leftPartChildObjId,leftPartObjid,rightPartNo,rightPartRev);
|
||||
}else{
|
||||
alert("한번에 1개의 파트만 변경가능합니다.");
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
}
|
||||
|
||||
if(leftPartNoList.length > 1){
|
||||
alert("한번에 1개의 파트만 변경가능합니다.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var leftPartNo = leftPartNoList.attr("data-PART_NO");
|
||||
var leftPartObjid = leftPartNoList.attr("data-BOM_LAST_PART_OBJID");
|
||||
var leftParentPartObjid = leftPartNoList.attr("data-PARENT_PART_NO");
|
||||
var leftPartChildObjId = leftPartNoList.val();
|
||||
var leftPartBomQtyObjId = leftPartNoList.attr("data-OBJID");
|
||||
|
||||
// Tabulator에서 선택된 오른쪽 행 데이터 가져오기
|
||||
var rightFrame = parent.frames['rightFrame'];
|
||||
var rightSelectedRows = rightFrame.getSelectedRows ? rightFrame.getSelectedRows() : [];
|
||||
|
||||
if(rightSelectedRows.length === 0){
|
||||
alert("선택된 파트가 없습니다.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(rightSelectedRows.length > 1){
|
||||
alert("한번에 1개의 파트만 변경가능합니다.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var rightRowData = rightSelectedRows[0].getData();
|
||||
var rightPartNo = rightRowData.PART_NO;
|
||||
var rightPartRev = rightRowData.REVISION || "";
|
||||
var rightObjId = rightRowData.OBJID;
|
||||
|
||||
fn_changeRelatePartInfo(leftPartBomQtyObjId, rightObjId, leftPartChildObjId, leftParentPartObjid, leftPartChildObjId, leftPartObjid, rightPartNo, rightPartRev);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function isCreateBom(){
|
||||
return '${info.STATUS}' == 'create';
|
||||
}
|
||||
|
||||
//1레벨에 같은 Part No가 등록되어있는지 확인.
|
||||
function fn_checkSameTopPartNo(rightCheckedArr){
|
||||
var result = false;
|
||||
@@ -188,7 +167,8 @@ function fn_checkSameTopPartNo(rightCheckedArr){
|
||||
$.ajax({
|
||||
url: "/partMng/checkSameTopPartNo.do",
|
||||
method: 'post',
|
||||
data: {"OBJID":$("#objId").val(), "rightCheckedArr":rightCheckedArr},
|
||||
traditional: true, // 배열 파라미터 처리를 위한 옵션
|
||||
data: {"OBJID":$("#objId").val(), "rightCheckedArr[]":rightCheckedArr}, // [] 추가
|
||||
dataType: 'json',
|
||||
async:false,
|
||||
success: function(data) {
|
||||
@@ -204,11 +184,6 @@ function fn_checkSameTopPartNo(rightCheckedArr){
|
||||
}
|
||||
//end of 1레벨에 같은 Part No가 등록되어있는지 확인.
|
||||
|
||||
function resetChange(){
|
||||
$('#CHANGE_TYPE').val();
|
||||
$('#CHANGE_OPTION').val();
|
||||
}
|
||||
|
||||
//구조 연결 해제
|
||||
function fn_deletePartRelateInfo(leftObjId, leftPartLastObjId, leftParentPartNo, leftParentObjId, leftPartChildObjId){
|
||||
if(leftObjId == null){
|
||||
@@ -219,17 +194,20 @@ function fn_deletePartRelateInfo(leftObjId, leftPartLastObjId, leftParentPartNo,
|
||||
if(!confirm("연결 해제하시겠습니까?")) return;
|
||||
|
||||
$.ajax({
|
||||
//url: "/partMng/deletePartRelateInfo.do",
|
||||
url: "/partMng/deleteStatusPartRelateInfo.do", //231211 위에서 변경
|
||||
url: "/partMng/deleteStatusPartRelateInfo.do",
|
||||
method: 'post',
|
||||
data: {"OBJID":$("#objId").val(), "leftObjId":leftObjId, "partObjId":leftPartLastObjId, "BOM_REPORT_OBJID":$("#objId").val()
|
||||
, "leftPartChildObjId":leftPartChildObjId, "leftParentPartNo":leftParentPartNo, "leftParentObjId":leftParentObjId,
|
||||
"CHANGE_TYPE":$('#CHANGE_TYPE').val(), "CHANGE_OPTION":$('#CHANGE_OPTION').val() },
|
||||
, "leftPartChildObjId":leftPartChildObjId, "leftParentPartNo":leftParentPartNo, "leftParentObjId":leftParentObjId},
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
if(data.result){
|
||||
$(parent.frames['leftFrame'].document.location.reload());
|
||||
//$(parent.frames['rightFrame'].fn_searchPart());
|
||||
|
||||
// 부모 창(E-BOM 목록) 새로고침
|
||||
if(window.opener && window.opener.fn_search) {
|
||||
window.opener.fn_search();
|
||||
}
|
||||
}
|
||||
}
|
||||
, error: function(jqxhr, status, error){
|
||||
@@ -257,27 +235,34 @@ function fn_relatePartInfo(leftObjId, rightCheckedArr, leftPartNoQty, leftPartLa
|
||||
$.ajax({
|
||||
url: "/partMng/relatePartInfo.do",
|
||||
method: 'post',
|
||||
data: {"leftObjId":leftObjId,"leftPartNoQty":leftPartNoQty, "OBJID":$("#objId").val(), "rightCheckedArr":rightCheckedArr, "partObjId":leftPartLastObjId, "BOM_REPORT_OBJID":$("#objId").val(),
|
||||
"leftPartChildObjId":leftPartChildObjId, "leftQtyParObjId":leftQtyParObjId,
|
||||
"CHANGE_TYPE":$('#CHANGE_TYPE').val(), "CHANGE_OPTION":$('#CHANGE_OPTION').val()},
|
||||
traditional: true, // 배열 파라미터 처리를 위한 옵션
|
||||
data: {
|
||||
"leftObjId": leftObjId,
|
||||
"leftPartNoQty": leftPartNoQty,
|
||||
"OBJID": $("#objId").val(),
|
||||
"rightCheckedArr[]": rightCheckedArr, // Spring의 @RequestParam(value = "rightCheckedArr[]") 형식
|
||||
"partObjId": leftPartLastObjId,
|
||||
"BOM_REPORT_OBJID": $("#objId").val(),
|
||||
"leftPartChildObjId": leftPartChildObjId,
|
||||
"leftQtyParObjId": leftQtyParObjId
|
||||
},
|
||||
dataType: 'json',
|
||||
async:false,
|
||||
success: function(data) {
|
||||
if(data.result){
|
||||
// 왼쪽 프레임 새로고침
|
||||
$(parent.frames['leftFrame'].document.location.reload());
|
||||
//$(parent.frames['rightFrame'].fn_searchPart());
|
||||
|
||||
$(".partChks", parent.frames['rightFrame'].document).prop("checked", false);
|
||||
// 오른쪽 프레임 선택 해제 (Tabulator)
|
||||
var rightFrame = parent.frames['rightFrame'];
|
||||
if(rightFrame.clearSelection) {
|
||||
rightFrame.clearSelection();
|
||||
}
|
||||
|
||||
//좌측에 선택된 정보를 유지하기위해 0.1초간의 딜레이(새로고침시간)을 주고 10번 기존에 선택된 정보를 선택하도록 한다. 10번사이 선택이 된다면 loop break;
|
||||
// for(var i = 0 ; i < 10 ; i++){
|
||||
// var leftPartChkLength = $("input[name='checkecPartNo']:checked", parent.frames['leftFrame'].document).length;
|
||||
// setTimeout(function(){
|
||||
// $("input[name='checkedPartNo']:radio[value='"+leftPartNo+"']", parent.frames['leftFrame'].document).prop("checked", true);
|
||||
// }, 100);
|
||||
// if(leftPartChkLength > 0) break;
|
||||
// }
|
||||
//end for
|
||||
// 부모 창(E-BOM 목록) 새로고침
|
||||
if(window.opener && window.opener.fn_search) {
|
||||
window.opener.fn_search();
|
||||
}
|
||||
}
|
||||
}
|
||||
, error: function(jqxhr, status, error){
|
||||
@@ -289,32 +274,36 @@ function fn_relatePartInfo(leftObjId, rightCheckedArr, leftPartNoQty, leftPartLa
|
||||
}
|
||||
//end of 구조 연결
|
||||
|
||||
function visibleChangeTalbe(visible){
|
||||
if(visible){
|
||||
$("#changeTable").show();
|
||||
}else{
|
||||
$("#changeTable").hide();
|
||||
}
|
||||
$('#CHANGE_TYPE,#CHANGE_OPTION').attr('required', visible);
|
||||
}
|
||||
|
||||
//구조 연결 변경
|
||||
function fn_changeRelatePartInfo(objId,rightObjId,leftObjId,leftPartNoQty,leftPartChildObjId,leftPartObjid,rightPartNo,rightPartRev){
|
||||
$.ajax({
|
||||
url: "/partMng/changeRelatePartInfo.do",
|
||||
method: 'post',
|
||||
data: {"rightObjId":rightObjId, "OBJID":objId,"BOM_REPORT_OBJID":$("#objId").val(),"CHANGE_TYPE":$('#CHANGE_TYPE').val(),
|
||||
"CHANGE_OPTION":$('#CHANGE_OPTION').val(), "leftObjId":leftObjId,"leftPartNoQty":leftPartNoQty,"leftPartChildObjId":leftPartChildObjId,
|
||||
data: {"rightObjId":rightObjId, "OBJID":objId,"BOM_REPORT_OBJID":$("#objId").val(),
|
||||
"leftObjId":leftObjId,"leftPartNoQty":leftPartNoQty,"leftPartChildObjId":leftPartChildObjId,
|
||||
"leftPartObjid":leftPartObjid, "rightPartNo":rightPartNo, "rightPartRev":rightPartRev},
|
||||
dataType: 'json',
|
||||
async:false,
|
||||
success: function(data) {
|
||||
if(data.result){
|
||||
// 왼쪽 프레임 새로고침
|
||||
$(parent.frames['leftFrame'].document.location.reload());
|
||||
|
||||
$(parent.frames['rightFrame'].fn_searchPart());
|
||||
// 오른쪽 프레임 검색 다시 수행
|
||||
var rightFrame = parent.frames['rightFrame'];
|
||||
if(rightFrame.fn_searchPart) {
|
||||
rightFrame.fn_searchPart();
|
||||
}
|
||||
|
||||
$(".partChks", parent.frames['rightFrame'].document).prop("checked", false);
|
||||
// 오른쪽 프레임 선택 해제 (Tabulator)
|
||||
if(rightFrame.clearSelection) {
|
||||
rightFrame.clearSelection();
|
||||
}
|
||||
|
||||
// 부모 창(E-BOM 목록) 새로고침
|
||||
if(window.opener && window.opener.fn_search) {
|
||||
window.opener.fn_search();
|
||||
}
|
||||
}
|
||||
}
|
||||
, error: function(jqxhr, status, error){
|
||||
@@ -329,42 +318,8 @@ function fn_changeRelatePartInfo(objId,rightObjId,leftObjId,leftPartNoQty,leftPa
|
||||
<body class="backcolor" style="border:border:1px solid #ccc;">
|
||||
<form name="form1" id="form1" action="" method="post">
|
||||
<input type="hidden" name="objId" id="objId" value="${param.objId}" />
|
||||
<!--
|
||||
<br><br><br><br><br>
|
||||
<br><br><br><br><br><br><br>
|
||||
-->
|
||||
<table class="pmsPopupForm" id="changeTable">
|
||||
<colgroup>
|
||||
<!--
|
||||
<col width="15%">
|
||||
-->
|
||||
<col width="*">
|
||||
</colgroup>
|
||||
<tr>
|
||||
<td class="input_title" >
|
||||
<label for="">설변구분</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="input_sub_title" colspan="">
|
||||
<select name="CHANGE_TYPE" id="CHANGE_TYPE" class="select2" type="select" required reqTitle="설변구분"><option value="">선택</option>${code_map.CHANGE_TYPE}</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="input_title">
|
||||
<label for="">설변사유</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="input_sub_title">
|
||||
<select name="CHANGE_OPTION" id="CHANGE_OPTION" class="select2" type="select" required reqTitle="설변사유"><option value="">선택</option>${code_map.CHANGE_OPTION}</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br>
|
||||
|
||||
<div id="structurePopupBtnW" style="padding-top:0px;">
|
||||
<div id="structurePopupBtnW" style="padding-top:20px;">
|
||||
<input type="button" value="변경" class="plm_btns" id="moveChange">
|
||||
<br>
|
||||
<input type="button" value="<<" class="plm_btns" id="moveLeft" style="margin-left:0px;">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<%
|
||||
java.util.Map map = (java.util.HashMap)request.getAttribute("info");
|
||||
%>
|
||||
<frameset rows="6%, 88%, 6%;" border="0" noresize>
|
||||
<frame src="/partMng/structureHeaderPopup.do">
|
||||
<frameset rows="100px, *, 50px" border="0" noresize>
|
||||
<frame src="/partMng/structureHeaderPopup.do?objId=<%=com.pms.common.utils.CommonUtils.checkNull(map.get("OBJID"))%>">
|
||||
<frame src="/partMng/structureBottomPopupFS.do?objId=<%=com.pms.common.utils.CommonUtils.checkNull(map.get("OBJID"))%>">
|
||||
<frame src="/partMng/structureBtnAreaPopup.do">
|
||||
</frameset><noframes></noframes>
|
||||
@@ -2,30 +2,13 @@
|
||||
<%@ page import="com.pms.common.utils.*"%>
|
||||
<%@ page import="java.util.*" %>
|
||||
<%@include file= "/init.jsp" %>
|
||||
<%
|
||||
ArrayList TREELIST = new ArrayList();
|
||||
TREELIST = (ArrayList)request.getAttribute("tree");
|
||||
|
||||
int z = 0;
|
||||
int Maxlevel = 0;
|
||||
//int[] levelarr;
|
||||
int[] levelarr = new int[TREELIST.size()];
|
||||
//if(TREELIST != null){
|
||||
//levelarr = new int[TREELIST.size()];
|
||||
for(int i=0; i< TREELIST.size(); i++){
|
||||
HashMap treemap = (HashMap)TREELIST.get(i);
|
||||
int level = Integer.parseInt(CommonUtils.checkNull(treemap.get("LEVEL")));
|
||||
// System.out.println("level : "+level);
|
||||
levelarr[i] = level;
|
||||
}
|
||||
|
||||
if(null != TREELIST && 0 < TREELIST.size()){
|
||||
HashMap MaxlevelMap = (HashMap)TREELIST.get(0);
|
||||
Maxlevel = Integer.parseInt(CommonUtils.checkNull(MaxlevelMap.get("MAX_LEVEL")));
|
||||
}
|
||||
//}
|
||||
|
||||
%>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title><%=Constants.SYSTEM_NAME%></title>
|
||||
<link href="/css/tabulator/tabulator.min.css" rel="stylesheet">
|
||||
<script type="text/javascript" src="/js/tabulator/tabulator.min.js"></script>
|
||||
<style>
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
@@ -33,172 +16,348 @@ int[] levelarr = new int[TREELIST.size()];
|
||||
}
|
||||
#structureTableWrap1 {
|
||||
top: 56px;
|
||||
width: 99%;
|
||||
}
|
||||
#structureName {
|
||||
margin-bottom: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
#structureName2 {
|
||||
margin-bottom: 10px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
/* Tabulator 커스텀 스타일 */
|
||||
.tabulator-row.level-1 { background-color: #fde9d9 !important; }
|
||||
.tabulator-row.level-2 { background-color: #daeef3 !important; }
|
||||
.tabulator-row.level-3 { background-color: #e4dfec !important; }
|
||||
.tabulator-row.level-4 { background-color: #ebf1de !important; }
|
||||
.tabulator-row.level-5 { background-color: #f2f2f2 !important; }
|
||||
.tabulator-row.level-6 { background-color: #f2dcdb !important; }
|
||||
.tabulator-row.level-7 { background-color: #eeece1 !important; }
|
||||
.tabulator-row.level-8 { background-color: #dce6f1 !important; }
|
||||
.tabulator-row.level-9 { background-color: #FFFFEB !important; }
|
||||
.tabulator-row.level-10 { background-color: #ffffff !important; }
|
||||
|
||||
/* 파일 아이콘 스타일은 basic.css에서 관리 */
|
||||
</style>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title><%=Constants.SYSTEM_NAME%></title>
|
||||
<script>
|
||||
var _tabulGrid;
|
||||
var selectedRowData = null;
|
||||
|
||||
$(function(){
|
||||
if('${param.readonly}' == 'readonly'){
|
||||
$("#structureName2").hide();
|
||||
}
|
||||
|
||||
$(".qty").keyup(function(e){
|
||||
var key = e.keyCode;
|
||||
|
||||
if(key == 13){
|
||||
var leftObjId = $(this).attr("data-CHILD_OBJID");
|
||||
var leftQtyParObjId = $(this).attr("data-PART_OBJID");
|
||||
var leftPartLastObjId = $(this).attr("data-LAST_PART_OBJID");
|
||||
var leftPartNo = $(this).attr("data-PART_NO");
|
||||
var qty = $(this).val();
|
||||
|
||||
/* var isNumeric = $.isNumeric(qty);
|
||||
|
||||
if(!isNumeric){
|
||||
Swal.fire("숫자만 입력 가능합니다. 수량을 1개로 조정합니다.");
|
||||
$(this).val(1);
|
||||
fn_saveQty(leftObjId, 1);
|
||||
return;
|
||||
} */
|
||||
fn_saveQty(leftObjId, qty, leftPartNo, leftQtyParObjId, leftPartLastObjId);
|
||||
}
|
||||
});
|
||||
|
||||
$("#btnExcel").click(function() {
|
||||
fn_excel();
|
||||
});
|
||||
|
||||
//첨부팝업
|
||||
$(".File").click(function(){
|
||||
var popup_width = 800;
|
||||
var popup_height = 335;
|
||||
|
||||
var objId = $(this).attr("data-OBJID");
|
||||
var docType =$(this).attr("data-docType");
|
||||
var docTypeName = $(this).attr("data-docTypeName");
|
||||
var params = "?targetObjId="+objId+"&docType="+docType+"&docTypeName="+docTypeName;
|
||||
var url = "/projectConcept/FileRegistPopup.do"+params;
|
||||
|
||||
fn_centerPopup(popup_width, popup_height, url);
|
||||
// 도면 업로드 버튼 클릭
|
||||
$("#btnDrawingUpload").click(function() {
|
||||
$("#drawingFiles").click();
|
||||
});
|
||||
|
||||
// 파일 선택 이벤트
|
||||
$("#drawingFiles").change(function() {
|
||||
fn_uploadDrawingFiles(this.files);
|
||||
});
|
||||
|
||||
$(".seq").keyup(function(e){
|
||||
var key = e.keyCode;
|
||||
|
||||
if(key == 13){
|
||||
var leftObjId = $(this).attr("data-CHILD_OBJID");
|
||||
var leftPartNo = $(this).attr("data-PART_NO");
|
||||
var seq = $(this).val();
|
||||
var partNo = $(this).attr("data-PART_NO");
|
||||
var isNumeric = $.isNumeric(seq);
|
||||
|
||||
if(!isNumeric){
|
||||
Swal.fire("숫자만 입력 가능합니다.");
|
||||
$(this).val(1);
|
||||
fn_saveSeq(leftObjId, 1,partNo);
|
||||
return;
|
||||
// Tabulator 초기화
|
||||
fn_initGrid();
|
||||
});
|
||||
|
||||
// Tabulator 그리드 초기화
|
||||
function fn_initGrid() {
|
||||
var maxLevel = ${empty MAXLEV ? 1 : MAXLEV};
|
||||
|
||||
// 컬럼 정의
|
||||
var columns = [
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 60,
|
||||
title: '선택',
|
||||
field: 'RADIO',
|
||||
formatter: function(cell) {
|
||||
var rowData = cell.getData();
|
||||
return '<input type="radio" name="checkedPartNo" value="' + rowData.CHILD_OBJID + '" ' +
|
||||
'data-OBJID="' + rowData.OBJID + '" ' +
|
||||
'data-PART_NO="' + rowData.PART_NO + '" ' +
|
||||
'data-PARENT_PART_NO="' + rowData.PARENT_PART_NO + '" ' +
|
||||
'data-PART_NO_QTY="' + rowData.LAST_PART_OBJID + '" ' +
|
||||
'data-PARENT_PARTS="' + rowData.PARENT_PARTS + '" ' +
|
||||
'data-LAST_PART_OBJID="' + rowData.LAST_PART_OBJID + '" ' +
|
||||
'data-PARENT_OBJID="' + rowData.PARENT_OBJID + '" ' +
|
||||
'data-PART_OBJID="' + rowData.PART_OBJID + '" ' +
|
||||
'data-BOM_LAST_PART_OBJID="' + rowData.BOM_LAST_PART_OBJID + '">';
|
||||
},
|
||||
cellClick: function(e, cell) {
|
||||
var radio = $(e.target);
|
||||
if(radio.is(':radio')) {
|
||||
selectedRowData = cell.getData();
|
||||
// 다른 라디오 버튼 해제
|
||||
$('input[name=checkedPartNo]').not(radio).prop('checked', false);
|
||||
}
|
||||
}
|
||||
fn_saveSeq(leftObjId, seq,partNo);
|
||||
}
|
||||
];
|
||||
|
||||
// 수준 컬럼 그룹 (헤더는 하나, 서브 컬럼은 여러개)
|
||||
var levelColumns = [];
|
||||
for(var i = 1; i <= maxLevel; i++) {
|
||||
levelColumns.push({
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 30,
|
||||
title: i,
|
||||
field: 'LEVEL_' + i,
|
||||
formatter: function(cell) {
|
||||
return cell.getValue() === '*' ? '*' : '';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
columns.push({
|
||||
title: '수준',
|
||||
headerHozAlign: 'center',
|
||||
columns: levelColumns
|
||||
});
|
||||
|
||||
// 나머지 컬럼 추가
|
||||
columns.push(
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 150,
|
||||
title: '품번',
|
||||
field: 'PART_NO',
|
||||
formatter: function(cell) {
|
||||
var rowData = cell.getData();
|
||||
return '<a href="#" onclick="openPartMngPopup(\'' +
|
||||
rowData.PART_OBJID + '\',\'' +
|
||||
rowData.LAST_PART_OBJID + '\',\'' +
|
||||
rowData.CHILD_OBJID + '\',\'' +
|
||||
rowData.BOM_LAST_PART_OBJID + '\');">' +
|
||||
rowData.PART_NO + '</a>';
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 200,
|
||||
title: '품명',
|
||||
field: 'PART_NAME'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 60,
|
||||
title: '수량',
|
||||
field: 'QTY_TEMP',
|
||||
editor: function(cell, onRendered, success, cancel) {
|
||||
var rowData = cell.getData();
|
||||
if(rowData.STATUS === 'adding') {
|
||||
var input = $('<input type="text" style="width:100%; text-align:center;" maxlength="3"/>');
|
||||
input.val(cell.getValue() || '0');
|
||||
input.on('keydown', function(e) {
|
||||
if(e.keyCode === 13) {
|
||||
success(input.val());
|
||||
} else if(e.keyCode === 27) {
|
||||
cancel();
|
||||
}
|
||||
});
|
||||
input.on('blur', function() {
|
||||
success(input.val());
|
||||
});
|
||||
return input[0];
|
||||
}
|
||||
return false;
|
||||
},
|
||||
cellEdited: function(cell) {
|
||||
var rowData = cell.getData();
|
||||
fn_saveQty(rowData.CHILD_OBJID, cell.getValue(), rowData.PART_OBJID);
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: '항목 수량',
|
||||
field: 'ITEM_QTY'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 60,
|
||||
title: '3D',
|
||||
field: 'CU01_CNT',
|
||||
formatter: function(cell) {
|
||||
var rowData = cell.getData();
|
||||
var isEmpty = cell.getValue() == 0;
|
||||
return '<a href="#" class="File file_' + (isEmpty ? 'empty_' : '') + 'icon" ' +
|
||||
'data-OBJID="' + rowData.LAST_PART_OBJID + '" ' +
|
||||
'data-docType="3D_CAD" ' +
|
||||
'data-docTypeName="3D CAD 첨부파일"></a>';
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 60,
|
||||
title: '2D',
|
||||
field: 'CU02_CNT',
|
||||
formatter: function(cell) {
|
||||
var rowData = cell.getData();
|
||||
var isEmpty = cell.getValue() == 0;
|
||||
return '<a href="#" class="File file_' + (isEmpty ? 'empty_' : '') + 'icon" ' +
|
||||
'data-OBJID="' + rowData.LAST_PART_OBJID + '" ' +
|
||||
'data-docType="2D_DRAWING_CAD" ' +
|
||||
'data-docTypeName="2D(Drawing) CAD 첨부파일"></a>';
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 60,
|
||||
title: 'PDF',
|
||||
field: 'CU03_CNT',
|
||||
formatter: function(cell) {
|
||||
var rowData = cell.getData();
|
||||
var isEmpty = cell.getValue() == 0;
|
||||
return '<a href="#" class="File file_' + (isEmpty ? 'empty_' : '') + 'icon" ' +
|
||||
'data-OBJID="' + rowData.LAST_PART_OBJID + '" ' +
|
||||
'data-docType="2D_PDF_CAD" ' +
|
||||
'data-docTypeName="2D(PDF) CAD 첨부파일"></a>';
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 100,
|
||||
title: '재료',
|
||||
field: 'MATERIAL'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 120,
|
||||
title: '열처리경도',
|
||||
field: 'HEAT_TREATMENT_HARDNESS'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 120,
|
||||
title: '열처리방법',
|
||||
field: 'HEAT_TREATMENT_METHOD'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 120,
|
||||
title: '표면처리',
|
||||
field: 'SURFACE_TREATMENT'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 120,
|
||||
title: '공급업체',
|
||||
field: 'MAKER'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 120,
|
||||
title: '범주 이름',
|
||||
field: 'PART_TYPE_TITLE'
|
||||
}
|
||||
);
|
||||
|
||||
_tabulGrid = new Tabulator("#structureGrid", {
|
||||
layout: "fitColumns",
|
||||
height: "650px",
|
||||
pagination: false,
|
||||
columns: columns,
|
||||
rowFormatter: function(row) {
|
||||
var data = row.getData();
|
||||
$(row.getElement()).addClass('level-' + data.LEVEL);
|
||||
},
|
||||
ajaxURL: "/partMng/getStructureTreeJson.do",
|
||||
ajaxParams: {
|
||||
objId: "${info.OBJID}",
|
||||
bomReportObjId: "${info.OBJID}",
|
||||
search_type: "working" // adding 상태 포함해서 조회
|
||||
},
|
||||
ajaxResponse: function(url, params, response) {
|
||||
// 서버 응답 데이터 가공
|
||||
var processedData = [];
|
||||
if(response && response.length > 0) {
|
||||
var maxLevel = response[0].MAX_LEVEL || 1;
|
||||
|
||||
response.forEach(function(item) {
|
||||
// Level 표시를 위한 동적 필드 생성
|
||||
for(var i = 1; i <= maxLevel; i++) {
|
||||
item['LEVEL_' + i] = (item.LEVEL == i) ? '*' : '';
|
||||
}
|
||||
processedData.push(item);
|
||||
});
|
||||
}
|
||||
return processedData;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
$(".qty").blur(function(){
|
||||
// 파일 아이콘 클릭 이벤트 (동적으로 생성된 요소)
|
||||
$(document).on('click', '.File', function(e) {
|
||||
e.preventDefault();
|
||||
var popup_width = 800;
|
||||
var popup_height = 335;
|
||||
|
||||
var objId = $(this).attr("data-OBJID");
|
||||
var qty = $(this).val();
|
||||
fn_saveQty(objId, qty);
|
||||
var docType = $(this).attr("data-docType");
|
||||
var docTypeName = $(this).attr("data-docTypeName");
|
||||
var params = "?targetObjId=" + objId + "&docType=" + docType + "&docTypeName=" + docTypeName;
|
||||
var url = "/projectConcept/FileRegistPopup.do" + params;
|
||||
|
||||
fn_centerPopup(popup_width, popup_height, url);
|
||||
});
|
||||
*/
|
||||
|
||||
$(document).ready(function(){
|
||||
$(".dataTr").each(function(i){
|
||||
var lev = $(this).attr("data-LEVEL");
|
||||
if(lev == 1){
|
||||
$(this).css("background-color", "#fde9d9");
|
||||
}else if(lev == 2){
|
||||
$(this).css("background-color", "#daeef3");
|
||||
}else if(lev == 3){
|
||||
$(this).css("background-color", "#e4dfec");
|
||||
}else if(lev == 4){
|
||||
$(this).css("background-color", "#ebf1de");
|
||||
}else if(lev == 5){
|
||||
$(this).css("background-color", "#f2f2f2");
|
||||
}else if(lev == 6){
|
||||
$(this).css("background-color", "#f2dcdb");
|
||||
}else if(lev == 7){
|
||||
$(this).css("background-color", "#eeece1");
|
||||
}else if(lev == 8){
|
||||
$(this).css("background-color", "#dce6f1");
|
||||
}else if(lev == 9){
|
||||
$(this).css("background-color", "#FFFFEB");
|
||||
}else if(lev == 10){
|
||||
$(this).css("background-color", "#ffffff");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function fn_saveQty(leftObjId,qty, leftQtyParObjId){
|
||||
// 수량 저장
|
||||
function fn_saveQty(leftObjId, qty, leftQtyParObjId){
|
||||
$.ajax({
|
||||
url: "/partMng/structureQtySave.do",
|
||||
method: 'post',
|
||||
//data: {"CHILD_OBJID":leftObjId,"QTY":qty}, //231211 아래로 변경(설변)
|
||||
data: {"BOM_REPORT_OBJID":"${info.OBJID}","CHILD_OBJID":leftObjId,"QTY_TEMP":qty, "OBJID":leftQtyParObjId},
|
||||
data: {
|
||||
"BOM_REPORT_OBJID": "${info.OBJID}",
|
||||
"CHILD_OBJID": leftObjId,
|
||||
"QTY_TEMP": qty,
|
||||
"OBJID": leftQtyParObjId
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
if(data.result){
|
||||
Swal.fire('저장하였습니다.');
|
||||
_tabulGrid.replaceData(); // 그리드 새로고침
|
||||
|
||||
// 부모 창(E-BOM 목록) 새로고침
|
||||
if(window.opener && window.opener.fn_search) {
|
||||
window.opener.fn_search();
|
||||
}
|
||||
}
|
||||
}
|
||||
, error: function(jqxhr, status, error){
|
||||
Swal.fire(jqxhr.statusText + ", " + status + ", " + error);
|
||||
Swal.fire(jqxhr.status);
|
||||
Swal.fire(jqxhr.responseText);
|
||||
},
|
||||
error: function(jqxhr, status, error){
|
||||
Swal.fire('저장 중 오류가 발생했습니다.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function fn_saveSeq(leftObjId,seq,partNo){
|
||||
var foucs = "seq_"+partNo;
|
||||
$.ajax({
|
||||
url: "/partMng/structureSeqSave.do",
|
||||
method: 'post',
|
||||
data: {"BOM_REPORT_OBJID":"${info.OBJID}","CHILD_OBJID":leftObjId,"SEQ":seq},
|
||||
dataType: 'json',
|
||||
async : false,
|
||||
success: function(data) {
|
||||
Swal.fire('변경되었습니다');
|
||||
var url = window.location.href;
|
||||
|
||||
url = url + "&foucs="+foucs;
|
||||
|
||||
location.href = url;
|
||||
//Swal.fire("#"+seq+"_"+partNo);
|
||||
//$("#"+seq+"_"+partNo).focus();
|
||||
//parent.frames['leftFrame'].reload();
|
||||
}
|
||||
, error: function(jqxhr, status, error){
|
||||
Swal.fire(jqxhr.statusText + ", " + status + ", " + error);
|
||||
Swal.fire(jqxhr.status);
|
||||
Swal.fire(jqxhr.responseText);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Part 상세 팝업
|
||||
function openPartMngPopup(objId, lastPartObjid, childObjid, BOM_LAST_PART_OBJID){
|
||||
var popup_width = 800;
|
||||
var popup_height = 500;
|
||||
var popup_height = 500;
|
||||
|
||||
var hiddenForm = document.hiddenForm;
|
||||
var url = "/partMng/partMngFormPopUp.do";
|
||||
@@ -213,20 +372,201 @@ function openPartMngPopup(objId, lastPartObjid, childObjid, BOM_LAST_PART_OBJID)
|
||||
hiddenForm.action = url;
|
||||
hiddenForm.OBJID.value = fnc_checkNull(BOM_LAST_PART_OBJID, objId);
|
||||
if('working' == '${param.actionType}')
|
||||
hiddenForm.OBJID.value = lastPartObjid; //231211 최신 파트로 조회
|
||||
hiddenForm.OBJID.value = lastPartObjid;
|
||||
hiddenForm.LAST_PART_OBJID.value = lastPartObjid;
|
||||
hiddenForm.CHILD_OBJID.value = childObjid;
|
||||
hiddenForm.target = target;
|
||||
hiddenForm.submit();
|
||||
}
|
||||
|
||||
// Excel 다운로드
|
||||
function fn_excel() {
|
||||
document.form1.actionType.value = "excel";
|
||||
var form = document.form1;
|
||||
form.action="/partMng/structurePopupLeft.do";
|
||||
form.action = "/partMng/structurePopupLeft.do";
|
||||
form.submit();
|
||||
}
|
||||
|
||||
// 선택된 행 데이터 가져오기 (Center 프레임에서 사용)
|
||||
function getSelectedRowData() {
|
||||
return selectedRowData;
|
||||
}
|
||||
|
||||
// 필터 적용 함수
|
||||
function fn_applyFilter() {
|
||||
var partNo = $("#filterPartNo").val().trim();
|
||||
var partName = $("#filterPartName").val().trim();
|
||||
|
||||
// 빈 값이 아닌 필터만 추가
|
||||
var filters = [];
|
||||
if(partNo) {
|
||||
filters.push({field: "PART_NO", type: "like", value: partNo});
|
||||
}
|
||||
if(partName) {
|
||||
filters.push({field: "PART_NAME", type: "like", value: partName});
|
||||
}
|
||||
|
||||
// 필터가 있으면 적용, 없으면 전체 표시
|
||||
if(filters.length > 0) {
|
||||
_tabulGrid.setFilter(filters);
|
||||
} else {
|
||||
_tabulGrid.clearFilter();
|
||||
}
|
||||
}
|
||||
|
||||
// 필터 초기화 함수
|
||||
function fn_resetFilter() {
|
||||
$("#filterPartNo").val("");
|
||||
$("#filterPartName").val("");
|
||||
_tabulGrid.clearFilter();
|
||||
}
|
||||
|
||||
// 도면 업로드 처리 함수
|
||||
function fn_uploadDrawingFiles(files) {
|
||||
if(!files || files.length === 0) {
|
||||
Swal.fire('파일을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 품번 목록 조회 (현재 BOM의 모든 품번)
|
||||
var bomObjId = "${info.OBJID}";
|
||||
if(!bomObjId) {
|
||||
Swal.fire('BOM 정보를 찾을 수 없습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 파일 분류 및 처리
|
||||
var filesByType = {
|
||||
'3D': [], // stp 파일
|
||||
'2D': [], // dwg 파일
|
||||
'PDF': [] // pdf 파일
|
||||
};
|
||||
|
||||
// 파일 확장자 확인 및 분류
|
||||
for(var i = 0; i < files.length; i++) {
|
||||
var file = files[i];
|
||||
var fileName = file.name;
|
||||
var lastDotIndex = fileName.lastIndexOf('.');
|
||||
|
||||
if(lastDotIndex === -1) {
|
||||
continue; // 확장자가 없는 파일은 스킵
|
||||
}
|
||||
|
||||
var ext = fileName.substring(lastDotIndex + 1).toLowerCase();
|
||||
|
||||
console.log('파일명: ' + fileName + ', 확장자: ' + ext); // 디버깅용
|
||||
|
||||
if(ext === 'stp' || ext === 'step') {
|
||||
filesByType['3D'].push(file);
|
||||
} else if(ext === 'dwg') {
|
||||
filesByType['2D'].push(file);
|
||||
} else if(ext === 'pdf') {
|
||||
filesByType['PDF'].push(file);
|
||||
}
|
||||
}
|
||||
|
||||
// 업로드할 파일이 있는지 확인
|
||||
var totalFiles = filesByType['3D'].length + filesByType['2D'].length + filesByType['PDF'].length;
|
||||
if(totalFiles === 0) {
|
||||
Swal.fire('업로드 가능한 파일 형식이 없습니다. (stp, dwg, pdf만 가능)');
|
||||
return;
|
||||
}
|
||||
|
||||
// 확인 메시지
|
||||
var msg = '총 ' + totalFiles + '개의 파일을 업로드하시겠습니까?\n';
|
||||
msg += '- 3D (STP): ' + filesByType['3D'].length + '개\n';
|
||||
msg += '- 2D (DWG): ' + filesByType['2D'].length + '개\n';
|
||||
msg += '- PDF: ' + filesByType['PDF'].length + '개';
|
||||
|
||||
Swal.fire({
|
||||
title: '도면 다증 업로드',
|
||||
text: msg,
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '업로드',
|
||||
cancelButtonText: '취소'
|
||||
}).then(function(result) {
|
||||
if(result.isConfirmed) {
|
||||
fn_processDrawingUpload(bomObjId, filesByType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 실제 업로드 처리
|
||||
function fn_processDrawingUpload(bomObjId, filesByType) {
|
||||
// FormData 생성
|
||||
var formData = new FormData();
|
||||
formData.append('bomObjId', bomObjId);
|
||||
|
||||
// 모든 파일을 files 이름으로 추가 (MultipartRequest가 처리)
|
||||
var allFiles = filesByType['3D'].concat(filesByType['2D']).concat(filesByType['PDF']);
|
||||
for(var i = 0; i < allFiles.length; i++) {
|
||||
formData.append('files', allFiles[i]);
|
||||
}
|
||||
|
||||
// 로딩 표시 (구버전 SweetAlert2)
|
||||
Swal.fire({
|
||||
title: '업로드 중...',
|
||||
text: '파일을 업로드하는 중입니다. 잠시만 기다려주세요.',
|
||||
allowOutsideClick: false,
|
||||
allowEscapeKey: false,
|
||||
allowEnterKey: false,
|
||||
showConfirmButton: false,
|
||||
onOpen: function() {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
|
||||
// AJAX 업로드
|
||||
$.ajax({
|
||||
url: '/partMng/uploadDrawingFiles.do',
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(response) {
|
||||
Swal.close();
|
||||
|
||||
if(response.result === 'success') {
|
||||
var successMsg = '도면 업로드가 완료되었습니다.\n\n';
|
||||
successMsg += '- 성공: ' + response.successCount + '개\n';
|
||||
if(response.failCount > 0) {
|
||||
successMsg += '- 실패: ' + response.failCount + '개\n';
|
||||
}
|
||||
if(response.notFoundCount > 0) {
|
||||
successMsg += '- 품번 미존재: ' + response.notFoundCount + '개\n';
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
title: '업로드 완료',
|
||||
text: successMsg,
|
||||
icon: response.failCount > 0 ? 'warning' : 'success'
|
||||
}).then(function() {
|
||||
// 그리드 새로고침
|
||||
_tabulGrid.replaceData();
|
||||
// 파일 input 초기화
|
||||
$("#drawingFiles").val('');
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: '업로드 실패',
|
||||
text: response.message || '도면 업로드 중 오류가 발생했습니다.',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
Swal.close();
|
||||
console.error('Upload error:', error);
|
||||
console.error('Response:', xhr.responseText);
|
||||
Swal.fire({
|
||||
title: '업로드 실패',
|
||||
text: '서버 오류가 발생했습니다: ' + error,
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="backcolor">
|
||||
@@ -240,134 +580,27 @@ function fn_excel() {
|
||||
<input type="hidden" name="objid" id="objid" value="${info.OBJID}" />
|
||||
<input type="hidden" name="objId" id="objId" value="${info.OBJID}" />
|
||||
<input type="hidden" name="actionType" value="" />
|
||||
<div id="structureTableWrap1" style="width: 99%;">
|
||||
<div id="structureName">(${info.CUSTOMER_NAME}_${info.CUSTOMER_PROJECT_NAME}_${info.UNIT_NAME})_${info.REV}<input type="button" value="Excel Download" class="plm_btns structure_btn" id="btnExcel" style="float:right;"></div>
|
||||
<div id="structureName2"><font size="2px">※수량 변경 후, 엔터치시면 저장됩니다.(신규 추가 파트만 가능)</font></div>
|
||||
<div class="plm_scroll_table" style="height:90%;overflow-y:scroll;">
|
||||
<table id="structurePopupTableHead" class="plm_table">
|
||||
<colgroup>
|
||||
<% if(Maxlevel == 0){%>
|
||||
<col width="25px" />
|
||||
<%}else{%>
|
||||
<col width="25px" />
|
||||
<%}
|
||||
for(int i=0; i< Maxlevel; i++){%>
|
||||
<col width="25px" />
|
||||
<%}%>
|
||||
<col width="130px" />
|
||||
<col width="160px" />
|
||||
<col width="50px" />
|
||||
<col width="30px" /> <!-- 3D -->
|
||||
<col width="30px" /> <!-- 2D -->
|
||||
<col width="30px" /> <!-- 2D PDF -->
|
||||
<col width="70px" /> <!-- Material -->
|
||||
<col width="190px" /> <!-- SPEC. -->
|
||||
<col width="90px" /> <!-- MAKER. -->
|
||||
<col width="70px" /> <!-- Revision -->
|
||||
<col width="75px" /> <!-- EO no -->
|
||||
<col width="75px" /> <!-- EO Date -->
|
||||
<col width="70px" /> <!-- 부품 유향 -->
|
||||
<!-- <col width="15%" /> -->
|
||||
</colgroup>
|
||||
<tr class="plm_thead">
|
||||
<td colspan="${MAXLEV + 1}">Level</td>
|
||||
<td>품번</td>
|
||||
<td>품명</td>
|
||||
<td>수량</td>
|
||||
<td>3D</td>
|
||||
<td>2D</td>
|
||||
<td>PDF</td>
|
||||
<td>재질</td>
|
||||
<td>사양(규격)</td>
|
||||
<td>MAKER</td>
|
||||
<td>Revision</td>
|
||||
<td>EO No</td>
|
||||
<td>EO Date</td>
|
||||
<td>PART 구분</td>
|
||||
<!-- <td>순번</td> -->
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table id="structurePopupTable2" class="plm_table">
|
||||
<colgroup>
|
||||
<% if(Maxlevel == 0){%>
|
||||
<col width="25px" />
|
||||
<%}else{%>
|
||||
<col width="25px" />
|
||||
<%}
|
||||
for(int i=0; i< Maxlevel; i++){%>
|
||||
<col width="25px" />
|
||||
<%}%>
|
||||
<col width="130px" />
|
||||
<col width="160px" />
|
||||
<col width="50px" />
|
||||
<col width="30px" /> <!-- 3D -->
|
||||
<col width="30px" /> <!-- 2D -->
|
||||
<col width="30px" /> <!-- 2D PDF -->
|
||||
<col width="70px" /> <!-- Material -->
|
||||
<col width="190px" /> <!-- SPEC. -->
|
||||
<col width="90px" /> <!-- MAKER. -->
|
||||
<col width="70px" /> <!-- Revision -->
|
||||
<col width="75px" /> <!-- EO no -->
|
||||
<col width="75px" /> <!-- EO Date -->
|
||||
<col width="70px" /> <!-- 부품 유향 -->
|
||||
<!-- <col width="15%" /> -->
|
||||
</colgroup>
|
||||
<c:choose>
|
||||
<c:when test="${!empty tree}">
|
||||
<c:forEach var="item" items="${tree}" varStatus="status">
|
||||
<tr class="dataTr" data-LEVEL="${item.LEVEL}">
|
||||
<td><input type="radio" name="checkedPartNo" value="${item.CHILD_OBJID}" data-OBJID="${item.OBJID}" data-PART_NO="${item.PART_NO}"
|
||||
data-PARENT_PART_NO="${item.PARENT_PART_NO}" data-PART_NO_QTY="${item.LAST_PART_OBJID}"
|
||||
data-PARENT_PARTS="${item.PARENT_PARTS}" data-LAST_PART_OBJID="${item.LAST_PART_OBJID}"
|
||||
data-PARENT_OBJID="${item.PARENT_OBJID}" data-PART_OBJID="${item.PART_OBJID}"
|
||||
data-BOM_LAST_PART_OBJID="${item.BOM_LAST_PART_OBJID}"
|
||||
>
|
||||
</td>
|
||||
<%
|
||||
for(int i=0; i< Maxlevel; i++){
|
||||
if(levelarr[z] == (i+1)){%>
|
||||
<td>*</td>
|
||||
<% }else{ %>
|
||||
<td> </td>
|
||||
<% }
|
||||
}
|
||||
z++;
|
||||
%>
|
||||
|
||||
<td style="text-align:left !important;" id="lpadpartno"><pre><a href="#" onclick="openPartMngPopup('${item.PART_OBJID}','${item.LAST_PART_OBJID}','${item.CHILD_OBJID}','${item.BOM_LAST_PART_OBJID}');">${item.PART_NO}</a></pre></td>
|
||||
<td class="align_l">${item.PART_NAME}</td>
|
||||
|
||||
<c:if test="${item.STATUS ne 'adding'}">
|
||||
<td class="align_c">${item.QTY_TEMP}</td>
|
||||
</c:if>
|
||||
<c:if test="${item.STATUS eq 'adding'}">
|
||||
<td class="align_c"><input type="text" name="qty_${item.PART_NO}" id="qty_${item.PART_NO}" class="qty" style="width:40px; text-align:center;" maxlength="3" value="${empty item.QTY_TEMP?'0':item.QTY_TEMP}" data-CHILD_OBJID="${item.CHILD_OBJID}" data-PART_NO="${item.PART_NO}" data-PARENT_PART_NO="${item.PARENT_PART_NO}" ></td>
|
||||
</c:if>
|
||||
|
||||
<td><a href="#" class="File file_${item.CU01_CNT eq 0?'empty_':''}icon" data-OBJID="${item.LAST_PART_OBJID}" data-docType="3D_CAD" data-docTypeName="3D CAD 첨부파일"></a></td>
|
||||
<td><a href="#" class="File file_${item.CU02_CNT eq 0?'empty_':''}icon" data-OBJID="${item.LAST_PART_OBJID}" data-docType="2D_DRAWING_CAD" data-docTypeName="2D(Drawing) CAD 첨부파일"></a></td>
|
||||
<td><a href="#" class="File file_${item.CU03_CNT eq 0?'empty_':''}icon" data-OBJID="${item.LAST_PART_OBJID}" data-docType="2D_PDF_CAD" data-docTypeName="2D(PDF) CAD 첨부파일"></a></td>
|
||||
<td title="${item.MATERIAL}" class="align_l" style="text-align: left; padding-left: 10px;">${item.MATERIAL}</td><!-- 재질 -->
|
||||
<td title="${item.SPEC}" class="align_l" style="text-align: left; padding-left: 10px;">${item.SPEC}</td><!-- SPEC -->
|
||||
<td title="${item.MAKER}" class="align_l" style="text-align: left; padding-left: 10px;">${item.MAKER}</td><!-- SPEC -->
|
||||
<td title="${item.REVISION}" class="align_l" style="text-align: left; padding-left: 10px;">${item.REVISION}</td><!-- REVISION -->
|
||||
<td title="${item.EO_NO}" class="align_c">${item.EO_NO}</td><!-- REVISION -->
|
||||
<td title="${item.EO_DATE}" class="align_c">${item.EO_DATE}</td><!-- REVISION -->
|
||||
<td title="${item.PART_TYPE_TITLE}" class="align_c">${item.PART_TYPE_TITLE}</td><!-- SIZE -->
|
||||
</tr>
|
||||
</c:forEach>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<tr>
|
||||
<td colspan="5" align="center">등록된 구조정보가 없습니다.</td>
|
||||
</tr>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 필터 값을 저장하기 위한 hidden 필드 (top frame에서 값 전달) -->
|
||||
<input type="hidden" id="filterPartNo" />
|
||||
<input type="hidden" id="filterPartName" />
|
||||
<div id="structureTableWrap1">
|
||||
<div id="structureName">
|
||||
<span style="font-weight: bold; color: #333;">
|
||||
<c:choose>
|
||||
<c:when test="${not empty info.PART_NO}">
|
||||
${info.PART_NO}
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
(${info.CUSTOMER_NAME}_${info.CUSTOMER_PROJECT_NAME}_${info.UNIT_NAME})_${info.REVISION}
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</span>
|
||||
<input type="button" value="Excel Download" class="plm_btns structure_btn" id="btnExcel" style="float:right;">
|
||||
<input type="button" value="도면 다중 업로드" class="plm_btns structure_btn" id="btnDrawingUpload" style="float:right; margin-right:5px;">
|
||||
<input type="file" id="drawingFiles" multiple style="display:none;" accept=".stp,.step,.dwg,.pdf">
|
||||
</div>
|
||||
<div id="structureGrid"></div>
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -9,14 +9,20 @@ java.text.SimpleDateFormat frm= new java.text.SimpleDateFormat ("yyyy_MM_dd_HH_m
|
||||
Calendar cal = Calendar.getInstance();
|
||||
String todayKor = frm.format(cal.getTime());
|
||||
|
||||
ArrayList list = (ArrayList)request.getAttribute("tree");
|
||||
Map sumPriceMap = (Map)request.getAttribute("info");
|
||||
|
||||
// 품번 정보가 있으면 파일명에 사용
|
||||
String partNo = CommonUtils.checkNull(sumPriceMap.get("PART_NO"));
|
||||
String excelName = "구조등록";
|
||||
String encodeName = excelName+todayKor+".xls";
|
||||
if(!partNo.isEmpty()) {
|
||||
excelName = partNo;
|
||||
}
|
||||
String encodeName = excelName+"_"+todayKor+".xls";
|
||||
String fileName = java.net.URLEncoder.encode(encodeName,"UTF-8");
|
||||
|
||||
response.setHeader("Content-Disposition", "attachment;filename="+fileName+"");
|
||||
response.setHeader("Content-Description", "JSP Generated Data");
|
||||
ArrayList list = (ArrayList)request.getAttribute("tree");
|
||||
Map sumPriceMap = (Map)request.getAttribute("info");
|
||||
|
||||
int z = 0;
|
||||
int Maxlevel = 0;
|
||||
@@ -50,7 +56,19 @@ if(null != list && 0 < list.size()){
|
||||
<section class="min_part_search">
|
||||
<div class="pdm_menu_name">
|
||||
<h2>
|
||||
<span>구조등록(${info.CUSTOMER_NAME}_${info.CUSTOMER_PROJECT_NAME}_${info.UNIT_NAME})</span>
|
||||
<span>구조등록
|
||||
<%
|
||||
if(!partNo.isEmpty()) {
|
||||
%>
|
||||
(<%=partNo%>)
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
(${info.CUSTOMER_NAME}_${info.CUSTOMER_PROJECT_NAME}_${info.UNIT_NAME})_${info.REVISION}
|
||||
<%
|
||||
}
|
||||
%>
|
||||
</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="contents_page_basic_margin">
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title><%=Constants.SYSTEM_NAME%></title>
|
||||
<link href="/css/tabulator/tabulator.min.css" rel="stylesheet">
|
||||
<script type="text/javascript" src="/js/tabulator/tabulator.min.js"></script>
|
||||
<style>
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
@@ -15,6 +17,31 @@
|
||||
#structureTableWrap2 .structure_btn {
|
||||
top: 25px;
|
||||
}
|
||||
/* Tabulator 커스텀 스타일 */
|
||||
.tabulator {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
.tabulator .tabulator-header {
|
||||
background-color: #f0f0f0;
|
||||
border-bottom: 2px solid #ddd;
|
||||
}
|
||||
.tabulator .tabulator-header .tabulator-col {
|
||||
background-color: #f0f0f0;
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
.tabulator .tabulator-tableHolder .tabulator-table {
|
||||
background-color: #fff;
|
||||
}
|
||||
.tabulator .tabulator-row {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.tabulator .tabulator-row:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.tabulator .tabulator-cell {
|
||||
padding: 8px;
|
||||
border-right: 1px solid #eee;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
$(function(){
|
||||
@@ -72,49 +99,138 @@ $(function(){
|
||||
fnc_productUPGNEWList("","${resultMap.PRODUCT_MGMT_SPEC}","search_product_mgmt_upg", "");
|
||||
|
||||
$('.select2').select2();
|
||||
|
||||
// Tabulator 초기화
|
||||
fn_initRightGrid();
|
||||
|
||||
// 초기 로드 제거 - 검색 버튼 클릭 시에만 데이터 로드
|
||||
// 사용자가 검색 조건 입력 후 조회하도록 유도
|
||||
});
|
||||
|
||||
function fn_searchPart(){
|
||||
$("#structurePopupTable2").empty();
|
||||
var _rightGrid;
|
||||
|
||||
function fn_initRightGrid(){
|
||||
var columns = [
|
||||
{
|
||||
formatter: "rowSelection",
|
||||
titleFormatter: "rowSelection",
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 50,
|
||||
headerSort: false,
|
||||
cellClick: function(e, cell){
|
||||
e.stopPropagation();
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 150,
|
||||
title: '품번',
|
||||
field: 'PART_NO',
|
||||
formatter: function(cell) {
|
||||
var rowData = cell.getData();
|
||||
return '<a href="#" onclick="openPartMngPopup(\'' + rowData.OBJID + '\');">' + rowData.PART_NO + '</a>';
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 180,
|
||||
title: '품명',
|
||||
field: 'PART_NAME'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 200,
|
||||
title: '규격',
|
||||
field: 'SPEC'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 120,
|
||||
title: 'MAKER',
|
||||
field: 'MAKER'
|
||||
}
|
||||
];
|
||||
|
||||
_rightGrid = new Tabulator("#rightPartGrid", {
|
||||
layout: "fitColumns",
|
||||
height: "600px",
|
||||
pagination: false,
|
||||
columns: columns,
|
||||
placeholder: "검색 버튼을 클릭하여 품목을 조회하세요",
|
||||
selectable: true, // 다중 선택 가능
|
||||
selectableRangeMode: "click" // 클릭으로 선택
|
||||
});
|
||||
}
|
||||
|
||||
function fn_searchPart(){
|
||||
// 전체 품목 데이터 조회 (검색 조건 적용)
|
||||
$.ajax({
|
||||
url: "/partMng/getPartMngList_ajax.do",
|
||||
method: 'post',
|
||||
data: $("#form1").serialize(),
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
|
||||
var trHtml = "";
|
||||
trHtml += " <colgroup>";
|
||||
trHtml += " <col width='5%' />";
|
||||
trHtml += " <col width='30%' />";
|
||||
trHtml += " <col width='30%' />";
|
||||
trHtml += " <col width='45%' />";
|
||||
trHtml += " <col width='15%' />";
|
||||
trHtml += " </colgroup>";
|
||||
|
||||
if(data.length > 0){
|
||||
$.each(data, function(i){
|
||||
trHtml += " <tr>";
|
||||
//trHtml += " <input type=\"checkbox\" name=\"partChks\" class=\"partChks\" value=\""+data[i].OBJID+"\" data-PART_TYPE='"+data[i].PART_TYPE+"' data-PART_NO='"+data[i].PART_NO+"' ></td>";
|
||||
trHtml += " <td><input type=\"checkbox\" name=\"partChks\" class=\"partChks\" value=\""+data[i].OBJID+"\" data-PART_TYPE='"+data[i].PART_TYPE+"' data-PART_NO='"+data[i].PART_NO+"' data-PART_REV='"+data[i].REVISION+"'></td>";
|
||||
trHtml += " <td class='align_l' title='"+data[i].PART_NO+"'><a href='#' onclick=\"openPartMngPopup('"+data[i].OBJID+"');\">"+data[i].PART_NO+"</a></td>";
|
||||
trHtml += " <td class='align_l' title='"+data[i].PART_NAME+"'>"+data[i].PART_NAME+"</td>";
|
||||
trHtml += " <td class='align_l' title='"+data[i].SPEC+"'>"+data[i].SPEC+"</td>";
|
||||
trHtml += " <td class='align_l' title='"+data[i].MAKER+"'>"+data[i].MAKER+"</td>";
|
||||
trHtml += " </tr>";
|
||||
});
|
||||
}else{
|
||||
trHtml += " <tr>";
|
||||
trHtml += " <td colspan=\"5\" align=\"center\">조회된 Part가 없습니다.</td>";
|
||||
trHtml += " </tr>";
|
||||
}
|
||||
$("#structurePopupTable2").append(trHtml);
|
||||
},
|
||||
error: function(jqxhr, status, error){
|
||||
|
||||
}
|
||||
});
|
||||
url: "/partMng/getPartMngList_ajax.do",
|
||||
type: "POST",
|
||||
data: $("#form1").serialize(),
|
||||
dataType: "json",
|
||||
beforeSend: function(){
|
||||
// 로딩 스피너 표시
|
||||
Swal.fire({
|
||||
title: '조회 중...',
|
||||
html: '품목 데이터를 불러오는 중입니다.',
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
|
||||
if(_rightGrid){
|
||||
_rightGrid.clearData();
|
||||
}
|
||||
},
|
||||
success: function(response) {
|
||||
// 로딩 스피너 닫기
|
||||
Swal.close();
|
||||
|
||||
console.log('조회 완료:', response.data ? response.data.length + '건' : '0건');
|
||||
|
||||
// Tabulator에 데이터 설정
|
||||
if(_rightGrid){
|
||||
_rightGrid.setData(response.data || []);
|
||||
}
|
||||
|
||||
// 결과 메시지
|
||||
if(!response.data || response.data.length === 0){
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: '조회 결과 없음',
|
||||
text: '검색 조건에 맞는 품목이 없습니다.'
|
||||
});
|
||||
} else {
|
||||
// 성공 토스트 메시지
|
||||
Swal.fire({
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
icon: 'success',
|
||||
title: response.data.length + '건 조회 완료',
|
||||
showConfirmButton: false,
|
||||
timer: 2000
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function(jqxhr, status, error){
|
||||
// 로딩 스피너 닫기
|
||||
Swal.close();
|
||||
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: '조회 실패',
|
||||
text: '데이터 조회 중 오류가 발생했습니다.'
|
||||
});
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,6 +255,19 @@ function openPartMngPopup(objId){
|
||||
hiddenForm.submit();
|
||||
}
|
||||
|
||||
// 선택된 행 데이터 반환 (Center 프레임에서 호출)
|
||||
function getSelectedRows() {
|
||||
if(!_rightGrid) return [];
|
||||
return _rightGrid.getSelectedRows();
|
||||
}
|
||||
|
||||
// 선택 해제 함수
|
||||
function clearSelection() {
|
||||
if(_rightGrid) {
|
||||
_rightGrid.deselectRow();
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body class="backcolor">
|
||||
@@ -147,85 +276,45 @@ function openPartMngPopup(objId){
|
||||
<input type="hidden" name="ACTION_TYPE" id="ACTION_TYPE">
|
||||
</form>
|
||||
<form name="form1" id="form1" action="" method="post">
|
||||
<input type="hidden" name="bomReportObjId" id="bomReportObjId" value="${param.OBJID}">
|
||||
<input type="hidden" name="bomReportObjId" id="bomReportObjId" value="${param.objId}">
|
||||
<input type="hidden" name="is_last" id="is_last" value="1">
|
||||
<input type="hidden" name="searchTargetStatus" id="searchTargetStatus" value="${info.STATUS}">
|
||||
<div id="structureName2"></div>
|
||||
<div id="structureTableWrap2" style="">
|
||||
<div class="searchIdName">
|
||||
<!-- 검색 필터 영역 -->
|
||||
<div id="plmSearchZon">
|
||||
<table>
|
||||
<!-- <tr>
|
||||
<td class="input_title"><label for="">기종명</label></td>
|
||||
<td class="input_sub_title" >
|
||||
<select name="search_product_mgmt_objid" id="search_product_mgmt_objid" class="select2"></select>
|
||||
</td>
|
||||
<td class="input_title"><label for="">UPG No</label></td>
|
||||
<td>
|
||||
<select name="search_product_mgmt_upg" id="search_product_mgmt_upg" title="UPG" required type="select" class="select2"></select>
|
||||
</td>
|
||||
</tr> -->
|
||||
<tr>
|
||||
<td class="input_title"><label for="">품번</label></td>
|
||||
<td class="input_sub_title" style="width:150px;">
|
||||
<input type="text" name="search_part_no" id="search_part_no" value="" style="width:90%">
|
||||
<td><label for="search_part_no">품번</label></td>
|
||||
<td>
|
||||
<input type="text" name="search_part_no" id="search_part_no" value="">
|
||||
</td>
|
||||
|
||||
<td class="label"><label for="search_part_name">품명</label></td>
|
||||
<td>
|
||||
<input type="text" name="search_part_name" id="search_part_name" value="">
|
||||
</td>
|
||||
<td class="input_title"><label for="">품명</label></td>
|
||||
<td>
|
||||
<input type="text" name="search_part_name" id="search_part_name" value="" style="width:80%">
|
||||
</td>
|
||||
<td class="input_sub_title" style="float:right">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="input_title"><label for="">규격</label></td>
|
||||
<td class="input_sub_title" style="width:150px;">
|
||||
<input type="text" name="search_spec" id="search_spec" value="" style="width:90%">
|
||||
<td><label for="search_spec">규격</label></td>
|
||||
<td>
|
||||
<input type="text" name="search_spec" id="search_spec" value="">
|
||||
</td>
|
||||
|
||||
<td class="label"><label for="search_maker">MAKER</label></td>
|
||||
<td>
|
||||
<input type="text" name="search_maker" id="search_maker" value="">
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input type="button" value="조회" class="plm_btns" id="btnSearch">
|
||||
</td>
|
||||
<td class="input_title"><label for="">MAKER</label></td>
|
||||
<td>
|
||||
<input type="text" name="search_maker" id="search_maker" value="" style="width:80%">
|
||||
</td>
|
||||
<td class="input_sub_title" style="float:right">
|
||||
<input type="button" value="조회" class="plm_btns structure_btn" id="btnSearch" >
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="plm_table_wrap">
|
||||
<div style="overflow-y:scroll;">
|
||||
<table id="structurePopupTableHead" class="plm_table">
|
||||
<colgroup>
|
||||
<col width="5%" />
|
||||
<col width="30%" />
|
||||
<col width="30%" />
|
||||
<col width="45%" />
|
||||
<col width="15%" />
|
||||
</colgroup>
|
||||
<tr class="plm_thead">
|
||||
<td><input type="checkbox" name="" value="" id="allChk" class="checkBox"></td>
|
||||
<td>품번</td>
|
||||
<td>품명</td>
|
||||
<td>규격</td>
|
||||
<td>MAKER</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="plm_scroll_table" style="height:100%;">
|
||||
<table id="structurePopupTable2" class="plm_table">
|
||||
<colgroup>
|
||||
<col width="5%" />
|
||||
<col width="30%" />
|
||||
<col width="30%" />
|
||||
<col width="40%" />
|
||||
<col width="20%" />
|
||||
</colgroup>
|
||||
<tr class="partListTr">
|
||||
<td colspan="5" align="center">조회된 부품이 없습니다.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div id="rightPartGrid"></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -6,15 +6,132 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title><%=Constants.SYSTEM_NAME%></title>
|
||||
<title><%=Constants.SYSTEM_NAME%></title>
|
||||
<style>
|
||||
body, html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: visible;
|
||||
height: auto;
|
||||
}
|
||||
.plm_menu_name {
|
||||
margin: 5px 15px 0 15px !important;
|
||||
}
|
||||
.plm_menu_name h2 {
|
||||
margin: 5px 0 !important;
|
||||
padding: 5px 0;
|
||||
}
|
||||
#plmSearchZon {
|
||||
margin-bottom: 0 !important;
|
||||
padding-bottom: 5px !important;
|
||||
overflow: visible !important;
|
||||
position: relative;
|
||||
z-index: 1000;
|
||||
}
|
||||
.select2-container {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
.select2-dropdown {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
$(document).ready(function(){
|
||||
// 엔터키 이벤트 바인딩
|
||||
$("#filterPartNo, #filterPartName, #filterVersion").on('keypress', function(e){
|
||||
if(e.keyCode == 13 || e.which == 13){
|
||||
e.preventDefault();
|
||||
fn_applyFilter();
|
||||
}
|
||||
});
|
||||
|
||||
// 페이지 로드 시 품번/품명이 있으면 자동으로 필터 적용
|
||||
var partNo = $("#filterPartNo").val().trim();
|
||||
var partName = $("#filterPartName").val().trim();
|
||||
if(partNo || partName) {
|
||||
setTimeout(function() {
|
||||
fn_applyFilter();
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// 필터 적용 함수
|
||||
function fn_applyFilter() {
|
||||
var partNo = $("#filterPartNo").val().trim();
|
||||
var partName = $("#filterPartName").val().trim();
|
||||
|
||||
try {
|
||||
// 프레임 구조: parent(headerFs) -> parent(main) -> frames[1](bottomFs) -> frames[0](leftFrame)
|
||||
var leftFrame = parent.parent.frames[1].frames['leftFrame'];
|
||||
|
||||
if(leftFrame && leftFrame.fn_applyFilter) {
|
||||
leftFrame.$("#filterPartNo").val(partNo);
|
||||
leftFrame.$("#filterPartName").val(partName);
|
||||
leftFrame.fn_applyFilter();
|
||||
} else {
|
||||
console.error("leftFrame not found or fn_applyFilter not available");
|
||||
}
|
||||
} catch(e) {
|
||||
console.error("Error accessing leftFrame:", e);
|
||||
alert("필터를 적용할 수 없습니다. 프레임 구조를 확인해주세요.");
|
||||
}
|
||||
}
|
||||
|
||||
// 필터 초기화 함수
|
||||
function fn_resetFilter() {
|
||||
$("#filterPartNo").val("");
|
||||
$("#filterPartName").val("");
|
||||
|
||||
try {
|
||||
// 프레임 구조: parent(headerFs) -> parent(main) -> frames[1](bottomFs) -> frames[0](leftFrame)
|
||||
var leftFrame = parent.parent.frames[1].frames['leftFrame'];
|
||||
|
||||
if(leftFrame && leftFrame.fn_resetFilter) {
|
||||
leftFrame.fn_resetFilter();
|
||||
}
|
||||
} catch(e) {
|
||||
console.error("Error accessing leftFrame:", e);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="backcolor">
|
||||
<form name="form1" action="" method="post">
|
||||
<form name="form1" id="form1" action="" method="post">
|
||||
<div class="plm_menu_name">
|
||||
<h2>
|
||||
<span>구조등록</span>
|
||||
<span>E-BOM 확인/수정</span>
|
||||
<c:if test="${not empty partNo}">
|
||||
<span style="font-size: 14px; font-weight: normal; color: #666; margin-left: 20px;">
|
||||
품번: <strong style="color: #333;">${partNo}</strong>
|
||||
<c:if test="${not empty partName}">
|
||||
/ 품명: <strong style="color: #333;">${partName}</strong>
|
||||
</c:if>
|
||||
</span>
|
||||
</c:if>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<!-- 검색 필터 영역 -->
|
||||
<div id="plmSearchZon">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="label"><label for="filterPartNo">품번</label></td>
|
||||
<td>
|
||||
<input type="text" id="filterPartNo" name="filterPartNo" value="${partNo}">
|
||||
</td>
|
||||
|
||||
<td class="label"><label for="filterPartName">품명</label></td>
|
||||
<td>
|
||||
<input type="text" id="filterPartName" name="filterPartName" value="${partName}">
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<button type="button" class="plm_btns" onclick="fn_applyFilter()">검색</button>
|
||||
<button type="button" class="plm_btns" onclick="fn_resetFilter()">초기화</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,324 @@
|
||||
<%@ 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"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<%@ page import="java.util.*" %>
|
||||
<%@include file= "/init.jsp" %>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title><%=Constants.SYSTEM_NAME%></title>
|
||||
<style>
|
||||
.ebom-select-container {
|
||||
padding: 10px;
|
||||
}
|
||||
.header-section {
|
||||
background: #f5f5f5;
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #ddd;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.header-section h3 {
|
||||
margin: 0;
|
||||
color: #333;
|
||||
}
|
||||
.current-ebom-section {
|
||||
background: #f9f9f9;
|
||||
padding: 20px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.current-ebom-section h4 {
|
||||
margin: 0 0 15px 0;
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
}
|
||||
.ebom-info-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: white;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.ebom-info-table th {
|
||||
background: #f5f5f5;
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
border: 1px solid #ddd;
|
||||
width: 150px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.ebom-info-table td {
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
.ebom-list-section {
|
||||
margin-bottom: 10px;
|
||||
height: 400px !important;
|
||||
}
|
||||
.ebom-list-section .plm_tab_div {
|
||||
height: 200px !important;
|
||||
}
|
||||
.ebom-list-section #mainGrid {
|
||||
height: 200px !important;
|
||||
}
|
||||
.ebom-preview-section {
|
||||
padding: 10px;
|
||||
background: #fafafa;
|
||||
}
|
||||
.ebom-preview-section h4 {
|
||||
margin: 0 0 10px 0;
|
||||
color: #555;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// 현재 할당된 E-BOM이 있는지 여부
|
||||
var hasCurrentEbom = ${not empty currentEbom};
|
||||
var currentEbomObjid = "${not empty currentEbom ? currentEbom.OBJID : ''}";
|
||||
|
||||
$(document).ready(function(){
|
||||
$('.select2').select2();
|
||||
|
||||
// 날짜 선택기 초기화
|
||||
fnc_datepick("search_fromDate", "search_toDate");
|
||||
|
||||
// Enter 키로 검색
|
||||
$("input").keyup(function(e) {
|
||||
if (e.keyCode == 13) {
|
||||
fn_searchEbomList();
|
||||
}
|
||||
});
|
||||
|
||||
// 조회 버튼
|
||||
$("#btnSearch").click(function(){
|
||||
fn_searchEbomList();
|
||||
});
|
||||
|
||||
// 변경 버튼
|
||||
$("#btnChange").click(function(){
|
||||
fn_showEbomList();
|
||||
});
|
||||
|
||||
// 초기 상태 설정
|
||||
if(hasCurrentEbom) {
|
||||
// 할당된 E-BOM이 있으면 상세보기만 표시
|
||||
fn_showCurrentEbom();
|
||||
} else {
|
||||
// 할당된 E-BOM이 없으면 목록 표시
|
||||
fn_showEbomList();
|
||||
}
|
||||
});
|
||||
|
||||
// 현재 할당된 E-BOM 표시
|
||||
function fn_showCurrentEbom() {
|
||||
$("#currentEbomSection").show();
|
||||
$("#ebomListSection").hide();
|
||||
|
||||
// 현재 E-BOM 미리보기 로드
|
||||
$("#ebomPreviewFrame").attr("src", "/partMng/structurePopupLeft.do?readonly=readonly&objId=" + currentEbomObjid);
|
||||
}
|
||||
|
||||
// E-BOM 목록 표시 (변경 모드)
|
||||
function fn_showEbomList() {
|
||||
$("#currentEbomSection").hide();
|
||||
$("#ebomListSection").show();
|
||||
|
||||
// 목록 조회
|
||||
fn_searchEbomList();
|
||||
}
|
||||
|
||||
// 그리드 컬럼 정의
|
||||
var columns = [
|
||||
{headerHozAlign: 'center', hozAlign: 'center', width: 160, title: '제품구분', field: 'PRODUCT_NAME'},
|
||||
{headerHozAlign: 'center', hozAlign: 'left', width: 210, title: '품번', field: 'PART_NO'},
|
||||
{headerHozAlign: 'center', hozAlign: 'left', title: '품명', field: 'PART_NAME'},
|
||||
{headerHozAlign: 'center', hozAlign: 'center', width: 130, title: '등록일', field: 'REG_DATE'},
|
||||
{headerHozAlign: 'center', hozAlign: 'center', width: 110, title: 'Version', field: 'REVISION'},
|
||||
{headerHozAlign: 'center', hozAlign: 'center', width: 110, title: '상태', field: 'STATUS'}
|
||||
];
|
||||
|
||||
// E-BOM List 조회 (상태='Y'만)
|
||||
function fn_searchEbomList() {
|
||||
// 상태='Y' 조건 추가
|
||||
$("#status").val("Y");
|
||||
|
||||
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/partMng/searchStructureGridList.do", columns, true);
|
||||
|
||||
// 행 클릭 이벤트 - 단일 선택만 가능
|
||||
if(_tabulGrid) {
|
||||
_tabulGrid.on("rowClick", function(e, row){
|
||||
// 이전 선택 해제
|
||||
_tabulGrid.deselectRow();
|
||||
// 현재 행 선택
|
||||
_tabulGrid.selectRow(row);
|
||||
|
||||
var data = row.getData();
|
||||
fn_selectEbom(data.OBJID, data.PART_NO, data.PART_NAME);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// E-BOM 선택 시 미리보기
|
||||
function fn_selectEbom(bomReportObjid, partNo, partName) {
|
||||
$("#selectedBomObjid").val(bomReportObjid);
|
||||
$("#selectedPartNo").text(partNo);
|
||||
$("#selectedPartName").text(partName);
|
||||
|
||||
// 하단 iframe에 E-BOM 상세 로드
|
||||
$("#ebomPreviewFrame").attr("src", "/partMng/structurePopupLeft.do?readonly=readonly&objId=" + bomReportObjid);
|
||||
|
||||
// 선택 표시
|
||||
$("#selectedInfo").show();
|
||||
}
|
||||
|
||||
// E-BOM 할당
|
||||
function fn_assignEbom() {
|
||||
var bomReportObjid = $("#selectedBomObjid").val();
|
||||
var projectMgmtObjid = "${param.projectMgmtObjid}";
|
||||
|
||||
if(!bomReportObjid) {
|
||||
alert("E-BOM을 선택해주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
var confirmMsg = hasCurrentEbom
|
||||
? "선택한 E-BOM으로 변경하시겠습니까?"
|
||||
: "선택한 E-BOM을 할당하시겠습니까?";
|
||||
|
||||
if(confirm(confirmMsg)) {
|
||||
$.ajax({
|
||||
url: "/productionplanning/assignEbomToMbom.do",
|
||||
type: "POST",
|
||||
data: {
|
||||
projectMgmtObjid: projectMgmtObjid,
|
||||
bomReportObjid: bomReportObjid
|
||||
},
|
||||
dataType: "json",
|
||||
success: function(result) {
|
||||
if(result.success) {
|
||||
var successMsg = hasCurrentEbom
|
||||
? "E-BOM이 변경되었습니다."
|
||||
: "E-BOM이 할당되었습니다.";
|
||||
alert(successMsg);
|
||||
if(window.opener && window.opener.fn_search) {
|
||||
window.opener.fn_search(); // 부모 창 새로고침
|
||||
}
|
||||
window.close();
|
||||
} else {
|
||||
alert(result.message || "할당에 실패했습니다.");
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
alert("오류가 발생했습니다: " + error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<form name="form1" id="form1" method="post">
|
||||
<input type="hidden" name="status" id="status" value="Y">
|
||||
<input type="hidden" id="selectedBomObjid">
|
||||
|
||||
<div class="ebom-select-container">
|
||||
<!-- 상단: 정보 및 버튼 -->
|
||||
<div class="header-section">
|
||||
<h3>E-BOM ${not empty currentEbom ? '상세 및 변경' : '선택'} - 품번: ${param.partNo} / 품명: ${param.partName}</h3>
|
||||
<div>
|
||||
<c:if test="${not empty currentEbom}">
|
||||
<input type="button" value="E-BOM 변경" class="plm_btns" id="btnChange" style="background-color: #FF9800; color: white;">
|
||||
</c:if>
|
||||
<input type="button" value="닫기" class="plm_btns" onclick="window.close()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 현재 할당된 E-BOM 정보 (할당된 경우만 표시) -->
|
||||
<c:if test="${not empty currentEbom}">
|
||||
<div id="currentEbomSection" class="current-ebom-section">
|
||||
<h4>📋 현재 할당된 E-BOM 정보</h4>
|
||||
<table class="ebom-info-table">
|
||||
<tr>
|
||||
<th>제품구분</th>
|
||||
<td>${currentEbom.PRODUCT_NAME}</td>
|
||||
<th>품번</th>
|
||||
<td>${currentEbom.PART_NO}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>품명</th>
|
||||
<td>${currentEbom.PART_NAME}</td>
|
||||
<th>Version</th>
|
||||
<td>${currentEbom.REVISION}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>등록일</th>
|
||||
<td>${currentEbom.REG_DATE}</td>
|
||||
<th>작성자</th>
|
||||
<td>${currentEbom.DEPT_NAME} / ${currentEbom.WRITER_NAME}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="text-align: center;">
|
||||
<p style="color: #666; margin: 10px 0;">다른 E-BOM으로 변경하려면 "E-BOM 변경" 버튼을 클릭하세요.</p>
|
||||
</div>
|
||||
</div>
|
||||
</c:if>
|
||||
|
||||
<!-- 중간: E-BOM List (처음에는 숨김 또는 표시) -->
|
||||
<div id="ebomListSection" class="ebom-list-section" style="display:none;">
|
||||
<div class="content-box">
|
||||
<div class="content-box-s">
|
||||
<div class="plm_menu_name_gdnsi">
|
||||
<h2><span>E-BOM List (상태: Y)</span></h2>
|
||||
<div class="btnArea">
|
||||
<input type="button" value="조회" class="plm_btns" id="btnSearch">
|
||||
<input type="button" value="E-BOM 할당" class="plm_btns" onclick="fn_assignEbom()" style="background-color: #4CAF50; color: white;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="plmSearchZon">
|
||||
<table>
|
||||
<tr>
|
||||
<td><label for="product_cd">제품구분</label></td>
|
||||
<td>
|
||||
<select name="product_cd" id="product_cd" style="width:170px" class="select2" autocomplete="off">
|
||||
<option value="">선택</option>
|
||||
${code_map.product_cd}
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<td class="label"><label for="">품번</label></td>
|
||||
<td><input type="text" name="SEARCH_PART_NO" id="SEARCH_PART_NO"></td>
|
||||
|
||||
<td class="label"><label for="">품명</label></td>
|
||||
<td><input type="text" name="SEARCH_PART_NAME" id="SEARCH_PART_NAME"></td>
|
||||
|
||||
<td class="align_r"><label>등록일</label></td>
|
||||
<td>
|
||||
<input type="text" name="search_fromDate" id="search_fromDate" style="width:90px;" autocomplete="off">~
|
||||
<input type="text" name="search_toDate" id="search_toDate" style="width:90px;" autocomplete="off">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 하단: 미리보기 -->
|
||||
<div class="ebom-preview-section">
|
||||
<h4>E-BOM 미리보기 (읽기 전용)</h4>
|
||||
<div style="width:100%; height:700px; border:1px solid #ddd; overflow: auto; background: white;">
|
||||
<iframe id="ebomPreviewFrame" style="width:100%; height:100%; border:none; pointer-events: none;"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
326
WebContent/WEB-INF/view/productionplanning/mBomMgmtList.jsp
Normal file
326
WebContent/WEB-INF/view/productionplanning/mBomMgmtList.jsp
Normal file
@@ -0,0 +1,326 @@
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
|
||||
<%@ page import="com.pms.common.utils.*"%>
|
||||
<%@ page import="java.util.*" %>
|
||||
<%@include file= "/init.jsp" %>
|
||||
<c:set var="now" value="<%=new java.util.Date() %>"/>
|
||||
<c:set var="sysYear"><fmt:formatDate value="${now}" pattern="yyyy" /></c:set>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title><%=Constants.SYSTEM_NAME%></title>
|
||||
<script type="text/javascript" src="/js/tabulator/tabulator_custom.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
// Tabulator 그리드 전역 변수
|
||||
var _tabulGrid;
|
||||
|
||||
$(document).ready(function(){
|
||||
|
||||
_fnc_datepick(); // 날짜 선택기 초기화
|
||||
$('.select2').select2(); // select2 초기화
|
||||
|
||||
// Enter 키로 검색
|
||||
$("input").keyup(function(e) {
|
||||
if (e.keyCode == 13) {
|
||||
fn_search();
|
||||
}
|
||||
});
|
||||
|
||||
// 조회 버튼
|
||||
$("#btnSearch").click(function(){
|
||||
fn_search();
|
||||
});
|
||||
|
||||
// 전체 체크박스
|
||||
$(document).on('click', '#checkAll', function() {
|
||||
$('.rowCheck').prop('checked', $(this).prop('checked'));
|
||||
});
|
||||
|
||||
// 초기 조회
|
||||
fn_search();
|
||||
});
|
||||
|
||||
// 날짜 선택기 초기화 함수
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 그리드 컬럼 정의
|
||||
var columns = [
|
||||
{title:'OBJID', field:'OBJID', visible: false},
|
||||
{title:'CONTRACT_OBJID', field:'CONTRACT_OBJID', visible: false},
|
||||
{title:'BOM_REPORT_OBJID', field:'BOM_REPORT_OBJID', visible: false},
|
||||
|
||||
// 체크박스
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 50,
|
||||
title: '<input type="checkbox" id="checkAll">',
|
||||
field: 'CHK',
|
||||
formatter: function(cell, formatterParams, onRendered) {
|
||||
return '<input type="checkbox" class="rowCheck" data-objid="' + cell.getRow().getData().OBJID + '">';
|
||||
},
|
||||
headerSort: false
|
||||
},
|
||||
|
||||
// 프로젝트번호
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 120,
|
||||
title: '프로젝트번호',
|
||||
field: 'PROJECT_NO'
|
||||
},
|
||||
|
||||
// 주문유형
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: '주문유형',
|
||||
field: 'CATEGORY_NAME'
|
||||
},
|
||||
|
||||
// 제품구분
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: '제품구분',
|
||||
field: 'PRODUCT_NAME'
|
||||
},
|
||||
|
||||
// 국내/해외
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 80,
|
||||
title: '국내/해외',
|
||||
field: 'AREA_NAME'
|
||||
},
|
||||
|
||||
// 접수일
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: '접수일',
|
||||
field: 'RECEIPT_DATE'
|
||||
},
|
||||
|
||||
// 고객사
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 150,
|
||||
title: '고객사',
|
||||
field: 'CUSTOMER_NAME'
|
||||
},
|
||||
|
||||
// 유/무상
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 80,
|
||||
title: '유/무상',
|
||||
field: 'PAID_TYPE_NAME'
|
||||
},
|
||||
|
||||
// 품번
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 150,
|
||||
title: '품번',
|
||||
field: 'PART_NO'
|
||||
},
|
||||
|
||||
// 품명
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 200,
|
||||
title: '품명',
|
||||
field: 'PART_NAME'
|
||||
},
|
||||
|
||||
// S/N
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 120,
|
||||
title: 'S/N',
|
||||
field: 'SERIAL_NO'
|
||||
},
|
||||
|
||||
// 수주수량
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'right',
|
||||
width: 80,
|
||||
title: '수주수량',
|
||||
field: 'QUANTITY'
|
||||
},
|
||||
|
||||
// 요청납기
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: '요청납기',
|
||||
field: 'REQ_DEL_DATE'
|
||||
},
|
||||
|
||||
// 고객사요청사항
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 200,
|
||||
title: '고객사요청사항',
|
||||
field: 'CUSTOMER_REQUEST'
|
||||
},
|
||||
|
||||
// E-BOM
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: 'E-BOM',
|
||||
field: 'EBOM_STATUS',
|
||||
formatter: fnc_subInfoValueFormatter,
|
||||
cellClick: function(e, cell) {
|
||||
var bomReportObjid = fnc_checkNull(cell.getData().BOM_REPORT_OBJID);
|
||||
var projectMgmtObjid = fnc_checkNull(cell.getData().OBJID);
|
||||
var partNo = fnc_checkNull(cell.getData().PART_NO);
|
||||
var partName = fnc_checkNull(cell.getData().PART_NAME);
|
||||
|
||||
// E-BOM이 있든 없든 선택 팝업 열기 (할당된 경우 상세 + 변경 가능)
|
||||
fn_openEBomSelectPopup(projectMgmtObjid, partNo, partName, bomReportObjid);
|
||||
}
|
||||
},
|
||||
|
||||
// E-BOM 작성일
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: '작성일',
|
||||
field: 'EBOM_REGDATE'
|
||||
},
|
||||
|
||||
// M-BOM
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: 'M-BOM',
|
||||
field: 'MBOM_STATUS',
|
||||
formatter: fnc_subInfoValueFormatter,
|
||||
cellClick: function(e, cell) {
|
||||
var objid = fnc_checkNull(cell.getData().OBJID);
|
||||
fn_openMBomPopup(objid);
|
||||
}
|
||||
},
|
||||
|
||||
// M-BOM Version
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 80,
|
||||
title: 'Version',
|
||||
field: 'MBOM_VERSION'
|
||||
},
|
||||
|
||||
// M-BOM 작성일
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: '작성일',
|
||||
field: 'MBOM_REGDATE'
|
||||
}
|
||||
];
|
||||
|
||||
// 검색 함수
|
||||
function fn_search(){
|
||||
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/productionplanning/mBomMgmtGridList.do", columns, true);
|
||||
}
|
||||
|
||||
// E-BOM 팝업
|
||||
function fn_openEBomPopup(bomReportObjId) {
|
||||
var popup_width = 1800;
|
||||
var popup_height = 800;
|
||||
var url = "/partMng/structurePopupLeft.do?readonly=readonly&objId=" + bomReportObjId;
|
||||
fn_centerPopup(popup_width, popup_height, url, 'ebomPopup');
|
||||
}
|
||||
|
||||
// E-BOM 선택 팝업
|
||||
function fn_openEBomSelectPopup(projectMgmtObjid, partNo, partName, bomReportObjid) {
|
||||
var popup_width = 1200;
|
||||
var popup_height = 900;
|
||||
var url = "/productionplanning/mBomEbomSelectPopup.do?projectMgmtObjid=" + projectMgmtObjid
|
||||
+ "&partNo=" + encodeURIComponent(partNo)
|
||||
+ "&partName=" + encodeURIComponent(partName);
|
||||
|
||||
// 이미 할당된 E-BOM이 있으면 bomReportObjid 추가
|
||||
if(bomReportObjid) {
|
||||
url += "&bomReportObjid=" + bomReportObjid;
|
||||
}
|
||||
|
||||
fn_centerPopup(popup_width, popup_height, url, 'ebomSelectPopup');
|
||||
}
|
||||
|
||||
// M-BOM 팝업
|
||||
function fn_openMBomPopup(objId) {
|
||||
var popup_width = 1800;
|
||||
var popup_height = 800;
|
||||
var url = "/productionplanning/mBomFormPopup.do?objId=" + objId;
|
||||
fn_centerPopup(popup_width, popup_height, url, 'mbomPopup');
|
||||
}
|
||||
</script>
|
||||
|
||||
<form name="form1" id="form1" method="post">
|
||||
<input type="hidden" name="actionType" id="actionType">
|
||||
<div class="content-box">
|
||||
<div class="content-box-s">
|
||||
<div class="plm_menu_name_gdnsi">
|
||||
<h2>
|
||||
<span>생산관리_M-BOM관리</span>
|
||||
</h2>
|
||||
<div class="btnArea">
|
||||
<input type="button" class="plm_btns" value="조회" id="btnSearch">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 검색 영역 -->
|
||||
<div id="plmSearchZon">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="label"><label for="search_part_no">품번</label></td>
|
||||
<td><input type="text" name="search_part_no" id="search_part_no" value="${param.search_part_no}"></td>
|
||||
|
||||
<td class="label"><label for="search_part_name">품명</label></td>
|
||||
<td><input type="text" name="search_part_name" id="search_part_name" value="${param.search_part_name}"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 그리드 영역 -->
|
||||
<%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -67,7 +67,8 @@ $(document).ready(function(){
|
||||
|
||||
var columns = [
|
||||
{title:'CATEGORY_CD' ,field:'CATEGORY_CD' ,visible:false},
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '100', title : '프로젝트번호', field : 'PROJECT_NO' ,frozen:true,
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '140', title : '프로젝트번호', field : 'PROJECT_NO' ,frozen:true,
|
||||
sorter:"string",
|
||||
formatter:fnc_createGridAnchorTag,
|
||||
cellClick:function(e, cell){
|
||||
var objid = fnc_checkNull(cell.getData().OBJID);
|
||||
@@ -77,54 +78,54 @@ var columns = [
|
||||
|
||||
{title:"프로젝트정보", headerHozAlign:'center', //고객정보
|
||||
columns:[
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '주문유형', field : 'CATEGORY_NAME' },
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '90', title : '제품구분', field : 'PRODUCT_NAME' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '국내/해외', field : 'AREA_NAME' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '접수일', field : 'REG_DATE' },
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '120', title : '고객사', field : 'CUSTOMER_NAME' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '유/무상', field : 'FREE_OF_CHARGE'},
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '150', title : '품번', field : 'PRODUCT_ITEM_CODE' },
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '150', title : '품명', field : 'PRODUCT_ITEM_NAME' },
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '150', title : 'S/N', field : 'SERIAL_NO' },
|
||||
{headerHozAlign : 'center', hozAlign : 'right', width : '90', title : '수주수량', field : 'CONTRACT_QTY' },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '요청납기', field : 'REQ_DEL_DATE' }
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '주문유형', field : 'CATEGORY_NAME', sorter:"string" },
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '90', title : '제품구분', field : 'PRODUCT_NAME', sorter:"string" },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '국내/해외', field : 'AREA_NAME', sorter:"string" },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '접수일', field : 'REG_DATE', sorter:"date", sorterParams:{format:"YYYY-MM-DD"} },
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '120', title : '고객사', field : 'CUSTOMER_NAME', sorter:"string" },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '유/무상', field : 'FREE_OF_CHARGE', sorter:"string"},
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '150', title : '품번', field : 'PRODUCT_ITEM_CODE', sorter:"string" },
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '150', title : '품명', field : 'PRODUCT_ITEM_NAME', sorter:"string" },
|
||||
{headerHozAlign : 'center', hozAlign : 'left', width : '150', title : 'S/N', field : 'SERIAL_NO', sorter:"string" },
|
||||
{headerHozAlign : 'center', hozAlign : 'right', width : '90', title : '수주수량', field : 'CONTRACT_QTY', sorter:"number" },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '요청납기', field : 'REQ_DEL_DATE', sorter:"date", sorterParams:{format:"YYYY-MM-DD"} }
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
title:'설계', headerHozAlign:'center',
|
||||
columns:[
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : 'E-BOM', field : 'EBOM_STATUS'}
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : 'E-BOM', field : 'EBOM_STATUS', sorter:"string"}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
title:'생산관리', headerHozAlign:'center',
|
||||
columns:[
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : 'M-BOM', field : 'MBOM_STATUS'}
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : 'M-BOM', field : 'MBOM_STATUS', sorter:"string"}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
title:'구매', headerHozAlign:'center',
|
||||
columns:[
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '발주일', field : 'ORDER_DATE'},
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '입고율', field : 'RECEIVING_RATE'}
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '발주일', field : 'ORDER_DATE', sorter:"date", sorterParams:{format:"YYYY-MM-DD"}},
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '입고율', field : 'RECEIVING_RATE', sorter:"number"}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
title:'생산', headerHozAlign:'center',
|
||||
columns:[
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '제조1,2팀', field : 'PRODUCTION_TEAM_12'},
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '제조3팀', field : 'PRODUCTION_TEAM_3'}
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '제조1,2팀', field : 'PRODUCTION_TEAM_12', sorter:"string"},
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '제조3팀', field : 'PRODUCTION_TEAM_3', sorter:"string"}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
title:'출하', headerHozAlign:'center',
|
||||
columns:[
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '출하일', field : 'SHIPMENT_DATE'}
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '출하일', field : 'SHIPMENT_DATE', sorter:"date", sorterParams:{format:"YYYY-MM-DD"}}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@@ -22,7 +22,7 @@ $(document).ready(function(){
|
||||
$("#btnUpload").click(function(){
|
||||
var files = $("#file1")[0].files;
|
||||
if(files.length > 0){
|
||||
fnc_fileMultiUpload(files, null, "${param.targetObjId}", "${param.docType}", "${param.docTypeName}", null, "srAreaDraw");
|
||||
fnc_fileMultiUpload(files, null, "${param.targetObjId}", "${param.docType}", "${param.docTypeName}", null, "refeshAttachFileArea");
|
||||
//file객체 초기화
|
||||
$("#file1").val("");
|
||||
}else{
|
||||
@@ -35,6 +35,14 @@ $(document).ready(function(){
|
||||
|
||||
function refeshAttachFileArea(){
|
||||
srAreaDraw();
|
||||
// 부모 창의 그리드도 새로고침
|
||||
if(opener && typeof opener.fn_search == "function"){
|
||||
opener.fn_search();
|
||||
}
|
||||
// Tabulator 그리드가 있는 경우
|
||||
if(opener && opener._tabulGrid){
|
||||
opener._tabulGrid.replaceData();
|
||||
}
|
||||
}
|
||||
|
||||
//형상 영역을 display 한다.
|
||||
@@ -112,7 +120,15 @@ function fileDelete(fileObjId){
|
||||
dataType:"json",
|
||||
async:true,
|
||||
success:function(data){
|
||||
fn_fileCallback("sr","${docType}");
|
||||
fn_fileCallback("sr","${docType}");
|
||||
// 부모 창의 그리드 새로고침
|
||||
if(opener && typeof opener.fn_search == "function"){
|
||||
opener.fn_search();
|
||||
}
|
||||
// Tabulator 그리드가 있는 경우
|
||||
if(opener && opener._tabulGrid){
|
||||
opener._tabulGrid.replaceData();
|
||||
}
|
||||
},
|
||||
error: function(jqxhr, status, error){
|
||||
}
|
||||
@@ -135,12 +151,13 @@ function fileDelete(fileObjId){
|
||||
-->
|
||||
<table class="pmsPopupForm">
|
||||
<tr>
|
||||
<td rowspan="2" class="input_title align_c">파일첨부</td>
|
||||
<td colspan="5">
|
||||
<!-- <div id="srDropZone" class="dropzone">Drag & Drop Files Here</div> -->
|
||||
<!-- <input type="file" name="file1" id="file1" multiple>
|
||||
<input type="button" id="btnUpload" value="Upload" class="upload_btns"> -->
|
||||
<div id="srFileAreaTable" class="spec_data_in_table">
|
||||
<td rowspan="2" class="input_title align_c">파일첨부</td>
|
||||
<td colspan="5">
|
||||
<div style="margin-bottom: 10px;">
|
||||
<input type="file" name="file1" id="file1" multiple>
|
||||
<input type="button" id="btnUpload" value="업로드" class="plm_btns">
|
||||
</div>
|
||||
<div id="srFileAreaTable" class="spec_data_in_table">
|
||||
<div style="overflow-y:scroll;">
|
||||
<table id="" class="fileListscrollThead" style="width:100% !important;">
|
||||
<colgroup>
|
||||
|
||||
@@ -296,6 +296,7 @@ div.plm_menu_name h2 span,
|
||||
.input_title {
|
||||
background: var(--label-bg);
|
||||
color: var(--text-primary);
|
||||
border-left: 1px solid var(--border-color);
|
||||
border-right: 1px solid var(--border-color);
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
@@ -644,7 +645,7 @@ div.plm_menu_name h2 span,
|
||||
border-collapse: collapse;
|
||||
background: var(--surface);
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.plm_table .plm_thead {
|
||||
@@ -658,7 +659,7 @@ div.plm_menu_name h2 span,
|
||||
border-right: 1px solid var(--border-color);
|
||||
padding: 14px 12px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.plm_table .plm_thead td:last-child {
|
||||
@@ -671,6 +672,7 @@ div.plm_menu_name h2 span,
|
||||
border-right: 1px solid var(--border-color);
|
||||
padding: 12px;
|
||||
vertical-align: middle;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.plm_table td:last-child {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
}
|
||||
.ui-jqgrid .ui-jqgrid-view,.ui-jqgrid .ui-paging-info, .ui-jqgrid .ui-pg-table,
|
||||
.ui-jqgrid .ui-pg-selbox {
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.ui-jqgrid .ui-jqgrid-titlebar{
|
||||
height: 40px;
|
||||
@@ -19,11 +19,12 @@
|
||||
}
|
||||
|
||||
.ui-jqgrid .ui-widget-header{
|
||||
border-bottom:1px solid #D3D3D3;
|
||||
border-bottom:1px solid #e0e0e0;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.ui-jqgrid .ui-search-toolbar input[type="text"]{
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
border: 1px solid #CCCCCC;
|
||||
border-radius: 4px;
|
||||
}
|
||||
@@ -41,17 +42,21 @@
|
||||
|
||||
/*** Column headers ******/
|
||||
.ui-jqgrid .ui-jqgrid-hdiv {
|
||||
/*background: #F9F9F9;*/
|
||||
border: 1px solid #D3D3D3;
|
||||
background: #f5f5f5;
|
||||
border: 1px solid #e0e0e0;
|
||||
line-height: 15px;
|
||||
/*height: 45px;*/
|
||||
font-weight: bold;
|
||||
color: #777777;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||
color: #666666;
|
||||
text-shadow: none;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.ui-jqgrid .ui-jqgrid-htable th {
|
||||
height: auto;
|
||||
background: #f5f5f5 !important;
|
||||
border-bottom: 1px solid #e0e0e0 !important;
|
||||
color: #666666 !important;
|
||||
}
|
||||
|
||||
.ui-jqgrid .ui-jqgrid-htable th div {
|
||||
@@ -71,6 +76,7 @@
|
||||
border-bottom: 1px solid #DDDDDD;
|
||||
border-right:none;
|
||||
padding:8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
|
||||
@@ -160,5 +166,5 @@
|
||||
font-weight: bold;
|
||||
display: none;
|
||||
border-width: 2px !important;
|
||||
font-size:11px;
|
||||
font-size:13px;
|
||||
}
|
||||
|
||||
@@ -24,8 +24,9 @@ echo ""
|
||||
|
||||
# 2단계: 도커 중지 및 삭제
|
||||
echo -e "${YELLOW}[2/3] 기존 도커 컨테이너 중지 및 삭제 중...${NC}"
|
||||
docker-compose -f docker-compose.dev.yml down 2>&1 | grep -v "level=warning"
|
||||
if [ $? -ne 0 ]; then
|
||||
podman-compose -f docker-compose.dev.yml down 2>&1 | grep -v "level=warning" || true
|
||||
# PIPESTATUS[0]로 docker-compose의 실제 종료 코드 확인
|
||||
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
||||
echo -e "${RED}✗ 도커 중지 실패!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
@@ -34,14 +35,14 @@ echo ""
|
||||
|
||||
# 3단계: 도커 이미지 재빌드 및 시작
|
||||
echo -e "${YELLOW}[3/3] 도커 이미지 재빌드 및 시작 중...${NC}"
|
||||
docker-compose -f docker-compose.dev.yml build --no-cache 2>&1 | grep -v "level=warning" | tail -5
|
||||
if [ $? -ne 0 ]; then
|
||||
podman-compose -f docker-compose.dev.yml build --no-cache 2>&1 | grep -v "level=warning" | tail -5 || true
|
||||
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
||||
echo -e "${RED}✗ 도커 빌드 실패!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
docker-compose -f docker-compose.dev.yml up -d 2>&1 | grep -v "level=warning"
|
||||
if [ $? -ne 0 ]; then
|
||||
podman-compose -f docker-compose.dev.yml up -d 2>&1 | grep -v "level=warning" || true
|
||||
if [ ${PIPESTATUS[0]} -ne 0 ]; then
|
||||
echo -e "${RED}✗ 도커 시작 실패!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
@@ -53,7 +54,7 @@ echo -e "${YELLOW}Tomcat 시작 대기 중...${NC}"
|
||||
sleep 10
|
||||
|
||||
# 상태 확인
|
||||
docker ps | grep wace-plm-dev > /dev/null
|
||||
podman ps | grep wace-plm-dev > /dev/null
|
||||
if [ $? -eq 0 ]; then
|
||||
echo ""
|
||||
echo -e "${GREEN}================================${NC}"
|
||||
@@ -64,11 +65,11 @@ if [ $? -eq 0 ]; then
|
||||
echo ""
|
||||
|
||||
# 로그 확인 팁
|
||||
echo -e "${YELLOW}💡 로그 확인: ${NC}docker logs -f wace-plm-dev"
|
||||
echo -e "${YELLOW}💡 컨테이너 중지: ${NC}docker-compose -f docker-compose.dev.yml down"
|
||||
echo -e "${YELLOW}💡 로그 확인: ${NC}podman logs -f wace-plm-dev"
|
||||
echo -e "${YELLOW}💡 컨테이너 중지: ${NC}podman-compose -f docker-compose.dev.yml down"
|
||||
else
|
||||
echo -e "${RED}✗ 컨테이너가 실행되지 않았습니다!${NC}"
|
||||
echo -e "${YELLOW}로그 확인: ${NC}docker logs wace-plm-dev"
|
||||
echo -e "${YELLOW}로그 확인: ${NC}podman logs wace-plm-dev"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -672,11 +672,14 @@ public class MailUtil {
|
||||
//◆첨부파일
|
||||
if(attachFileList != null){
|
||||
for(HashMap hmFile : attachFileList){
|
||||
String filepath = CommonUtils.checkNull(hmFile.get(Constants.Db.COL_FILE_PATH)) +"\\"+ CommonUtils.checkNull(hmFile.get(Constants.Db.COL_FILE_SAVED_NAME));
|
||||
String filepath = CommonUtils.checkNull(hmFile.get(Constants.Db.COL_FILE_PATH)) + File.separator + CommonUtils.checkNull(hmFile.get(Constants.Db.COL_FILE_SAVED_NAME));
|
||||
File attach_file = new File(filepath);
|
||||
//String attach_fileName = attach_file.getName();
|
||||
String attach_fileName = CommonUtils.checkNull(hmFile.get(Constants.Db.COL_FILE_REAL_NAME));
|
||||
|
||||
//System.out.println("첨부파일 처리: " + filepath);
|
||||
//System.out.println("파일 존재 여부: " + attach_file.exists());
|
||||
|
||||
//첨부파일용 MimeBodyPart 객체생성
|
||||
MimeBodyPart mbp_attachFile = new MimeBodyPart();
|
||||
mbp_attachFile.setFileName(MimeUtility.encodeText(attach_fileName, "euc-kr", "B"));
|
||||
@@ -770,6 +773,142 @@ public class MailUtil {
|
||||
return sendMailNew("", toUserEmail, subject, contents, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* UTF-8 인코딩으로 메일 발송 (견적서 등 한글 내용이 많은 경우)
|
||||
* 기존 sendMailWithAttachFile과 동일하지만 UTF-8 인코딩 사용
|
||||
*/
|
||||
public static boolean sendMailWithAttachFileUTF8(String fromUserId, String fromEmail
|
||||
, ArrayList<String> toUserIdList, ArrayList<String> toEmailList, ArrayList<String> ccEmailList, ArrayList<String> bccEmailList
|
||||
, String important, String subject, String contents
|
||||
, ArrayList<HashMap> attachFileList
|
||||
, String mailType
|
||||
) {
|
||||
|
||||
if(Constants.Mail.sendMailSwitch == false){
|
||||
System.out.println("MailUtil.sendMailWithAttachFileUTF8 ::: Constants.Mail.sendMailSwitch is FALSE");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
/*변수 초기화*/
|
||||
fromUserId = CommonUtils.checkNull(fromUserId, "admin");
|
||||
fromEmail = Constants.Mail.SMTP_USER;
|
||||
important = CommonUtils.checkNull(important);
|
||||
subject = CommonUtils.checkNull(subject , "empty subject" );
|
||||
contents = CommonUtils.checkNull(contents, "empty contents");
|
||||
mailType = CommonUtils.checkNull(mailType, "empty mailType");
|
||||
|
||||
System.out.println("MailUtil.sendMailWithAttachFileUTF8()..");
|
||||
System.out.println("MailUtil.sendMailWithAttachFileUTF8(fromUserId ):"+fromUserId);
|
||||
System.out.println("MailUtil.sendMailWithAttachFileUTF8(fromEmail ):"+fromEmail);
|
||||
System.out.println("MailUtil.sendMailWithAttachFileUTF8(toEmailList ):"+toEmailList);
|
||||
System.out.println("MailUtil.sendMailWithAttachFileUTF8(ccEmailList ):"+ccEmailList);
|
||||
System.out.println("MailUtil.sendMailWithAttachFileUTF8(subject ):"+subject);
|
||||
|
||||
if(toEmailList == null){ return false; }
|
||||
|
||||
//◆◆◆ 1. mail session ◆◆◆
|
||||
Properties prop = new Properties();
|
||||
prop.put("mail.smtp.host", Constants.Mail.SMTP_HOST);
|
||||
prop.put("mail.smtp.port", Constants.Mail.SMTP_PORT);
|
||||
prop.put("mail.smtp.auth" , "true");
|
||||
prop.put("mail.smtp.starttls.enable" , "true");
|
||||
prop.put("mail.smtps.checkserveridentity", "true");
|
||||
prop.put("mail.smtps.ssl.trust" , "*");
|
||||
prop.put("mail.debug" , "true");
|
||||
prop.put("mail.smtp.socketFactory.class" , "javax.net.ssl.SSLSocketFactory");
|
||||
prop.put("mail.smtp.ssl.enable" , "true");
|
||||
prop.put("mail.smtp.socketFactory.port" , Constants.Mail.SMTP_PORT);
|
||||
prop.put("mail.smtp.ssl.trust" , "smtp.gmail.com");
|
||||
prop.put("mail.transport.protocol", "smtp");
|
||||
prop.put("mail.smtp.ssl.protocols", "TLSv1.2");
|
||||
|
||||
Session mailSession = Session.getDefaultInstance(prop, new javax.mail.Authenticator() {
|
||||
protected PasswordAuthentication getPasswordAuthentication() {
|
||||
return new PasswordAuthentication(Constants.Mail.SMTP_USER, Constants.Mail.SMTP_USER_PW);
|
||||
}
|
||||
});
|
||||
mailSession.setDebug(true);
|
||||
mailSession.setDebugOut(new PrintStream(new ByteArrayOutputStream()));
|
||||
|
||||
//◆◆◆ 2. message ◆◆◆
|
||||
MimeMessage message = new MimeMessage(mailSession);
|
||||
message.setFrom(new InternetAddress( fromEmail ));
|
||||
|
||||
// 수신자
|
||||
if(toEmailList != null){
|
||||
for (String _mail : toEmailList) {
|
||||
if(CommonUtils.checkNull(_mail).equals("")){
|
||||
continue;
|
||||
}
|
||||
message.addRecipient(Message.RecipientType.TO, new InternetAddress(_mail));
|
||||
}
|
||||
}
|
||||
// 참조
|
||||
if(ccEmailList != null){
|
||||
for (String _mail : ccEmailList) {
|
||||
if(CommonUtils.checkNull(_mail).equals("")){
|
||||
continue;
|
||||
}
|
||||
message.addRecipient(Message.RecipientType.CC, new InternetAddress(_mail));
|
||||
}
|
||||
}
|
||||
// 숨은참조
|
||||
if(bccEmailList != null){
|
||||
for (String _mail : bccEmailList) {
|
||||
if(CommonUtils.checkNull(_mail).equals("")){
|
||||
continue;
|
||||
}
|
||||
message.addRecipient(Message.RecipientType.BCC, new InternetAddress(_mail));
|
||||
}
|
||||
}
|
||||
// 중요도
|
||||
if("".equals(important)){
|
||||
message.setHeader("Importance", "Normal");
|
||||
}else{
|
||||
message.setHeader("Importance", important);
|
||||
}
|
||||
// 제목 (UTF-8)
|
||||
message.setSubject(subject, "UTF-8");
|
||||
|
||||
Multipart multipart = new MimeMultipart();
|
||||
// 내용 (UTF-8)
|
||||
MimeBodyPart mbp_content = new MimeBodyPart();
|
||||
mbp_content.setContent(contents, "text/html;charset=UTF-8");
|
||||
mbp_content.setHeader("Content-Transfer-Encoding", "base64");
|
||||
multipart.addBodyPart(mbp_content);
|
||||
|
||||
// 첨부파일
|
||||
if(attachFileList != null){
|
||||
for(HashMap hmFile : attachFileList){
|
||||
String filepath = CommonUtils.checkNull(hmFile.get(Constants.Db.COL_FILE_PATH)) + File.separator + CommonUtils.checkNull(hmFile.get(Constants.Db.COL_FILE_SAVED_NAME));
|
||||
File attach_file = new File(filepath);
|
||||
String attach_fileName = CommonUtils.checkNull(hmFile.get(Constants.Db.COL_FILE_REAL_NAME));
|
||||
|
||||
MimeBodyPart mbp_attachFile = new MimeBodyPart();
|
||||
mbp_attachFile.setFileName(MimeUtility.encodeText(attach_fileName, "UTF-8", "B"));
|
||||
mbp_attachFile.setDataHandler(new DataHandler(new FileDataSource(attach_file)));
|
||||
mbp_attachFile.setHeader("Content-Type", Files.probeContentType(Paths.get(attach_file.getCanonicalPath())));
|
||||
mbp_attachFile.setDescription(attach_fileName.split("\\.")[0], "UTF-8");
|
||||
multipart.addBodyPart(mbp_attachFile);
|
||||
}
|
||||
}
|
||||
message.setContent(multipart);
|
||||
message.setSentDate(new Date());
|
||||
|
||||
// 메일 발송
|
||||
Transport.send(message);
|
||||
System.out.println("메일 발송 성공 (UTF-8)");
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("메일 발송 실패 (UTF-8): " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 첨부파일 목록을 파일하나로 압축해서 반환
|
||||
* @param zipFileName 압축파일명
|
||||
|
||||
@@ -21,6 +21,8 @@ import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import com.oreilly.servlet.MultipartRequest;
|
||||
import com.pms.common.FileRenameClass;
|
||||
import com.pms.common.JsonUtil;
|
||||
import com.pms.common.SqlMapConfig;
|
||||
import com.pms.common.bean.PersonBean;
|
||||
@@ -31,6 +33,9 @@ import com.pms.service.PartMgmtService;
|
||||
import com.pms.service.PartMngService;
|
||||
import com.pms.service.ProductMgmtService;
|
||||
import com.pms.service.ProjectConceptService;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
@Controller
|
||||
public class PartMngController {
|
||||
@@ -455,6 +460,38 @@ public class PartMngController {
|
||||
|
||||
return "/partMng/partMngDetailPopUp";
|
||||
}
|
||||
|
||||
/**
|
||||
* PART Detail 수정 저장
|
||||
* @param request
|
||||
* @param paramMap
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/partMng/updatePartDetail.do")
|
||||
@ResponseBody
|
||||
public Map<String, Object> updatePartDetail(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
Map<String, Object> resultMap = new HashMap<String, Object>();
|
||||
|
||||
try{
|
||||
// 품목 정보 업데이트
|
||||
int result = partMngService.updatePartDetail(request, paramMap);
|
||||
|
||||
if(result > 0){
|
||||
resultMap.put("result", "success");
|
||||
resultMap.put("message", "저장되었습니다.");
|
||||
}else{
|
||||
resultMap.put("result", "fail");
|
||||
resultMap.put("message", "저장에 실패했습니다.");
|
||||
}
|
||||
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
resultMap.put("result", "error");
|
||||
resultMap.put("message", "저장 중 오류가 발생했습니다.");
|
||||
}
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
/**
|
||||
* PART Detail PopUp
|
||||
* @param request
|
||||
@@ -575,17 +612,30 @@ public class PartMngController {
|
||||
* @param paramMap
|
||||
* @return
|
||||
*/
|
||||
/**
|
||||
* PART 목록 조회 (전체 데이터 조회 - 재귀 CTE 제거로 성능 개선)
|
||||
* @param request
|
||||
* @param paramMap
|
||||
* @return JSON 형태의 전체 데이터
|
||||
*/
|
||||
@RequestMapping("/partMng/getPartMngList_ajax.do")
|
||||
public String getPartList_ajax(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
@ResponseBody
|
||||
public Map<String, Object> getPartList_ajax(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
paramMap.put("status", "complete");
|
||||
|
||||
System.out.println("getPArtList_ajax paramMap : "+paramMap);
|
||||
paramMap.put("IS_LAST","1");
|
||||
//paramMap.put("STATUS", "release");
|
||||
|
||||
System.out.println("getPartList_ajax paramMap : "+paramMap);
|
||||
|
||||
// 전체 데이터 조회 (페이징 제거, 재귀 CTE 제거로 속도 대폭 향상)
|
||||
List list = partMngService.getToConnectPartMngList(request, paramMap);
|
||||
//List list = CommonUtils.keyChangeUpperList(partMngService.getPartMngList(request,paramMap));
|
||||
request.setAttribute("RESULT", CommonUtils.getJsonArray(list));
|
||||
return "/ajax/ajaxResult";
|
||||
|
||||
// 응답 데이터 구성
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("data", list);
|
||||
|
||||
System.out.println("데이터 조회 완료 - " + list.size() + "건");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
@@ -875,6 +925,14 @@ public class PartMngController {
|
||||
|
||||
@RequestMapping("/partMng/structureHeaderPopup.do")
|
||||
public String structureHeaderPopup(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
Map code_map = new HashMap();
|
||||
try {
|
||||
// 제품구분 (PART_TYPE)
|
||||
code_map.put("product_cd", commonService.bizMakeOptionList("0000001", CommonUtils.nullToEmpty((String)paramMap.get("product")),"common.getCodeselect"));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
request.setAttribute("code_map", code_map);
|
||||
return "/partMng/structurePopupTop";
|
||||
}
|
||||
|
||||
@@ -928,6 +986,31 @@ public class PartMngController {
|
||||
return "/partMng/structurePopupLeft"+("excel".equals(actionType) ? "Excel" : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* 구조등록 좌측 프레임 JSON 데이터
|
||||
* @param request
|
||||
* @param paramMap
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/partMng/getStructureTreeJson.do")
|
||||
@ResponseBody
|
||||
public List<Map> getStructureTreeJson(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
List bomTreeList = null;
|
||||
|
||||
try{
|
||||
Map info = partMngService.getBOMStructureStandardInfo(request,paramMap);
|
||||
// search_type 파라미터를 info에 복사 (adding 상태 조회를 위해)
|
||||
if(paramMap.containsKey("search_type")) {
|
||||
info.put("search_type", paramMap.get("search_type"));
|
||||
}
|
||||
bomTreeList = partMngService.getBOMPartTreeList(info);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return bomTreeList != null ? bomTreeList : new ArrayList<Map>();
|
||||
}
|
||||
|
||||
@RequestMapping("/partMng/structurePopupCenter.do")
|
||||
public String structurePopupCenter(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
|
||||
@@ -1079,9 +1162,17 @@ public class PartMngController {
|
||||
//유닛명
|
||||
code_map.put("unit_code",commonService.bizMakeOptionList("", CommonUtils.nullToEmpty((String)paramMap.get("unit_code")),"common.getUnitCodeList"));
|
||||
|
||||
if("Y".equals(search)){
|
||||
if("Y".equals(search)){
|
||||
String searchType = CommonUtils.checkNull(paramMap.get("searchType"), "ascending");
|
||||
|
||||
if("descending".equals(searchType)){
|
||||
// 역전개 조회
|
||||
resultList = partMngService.getStructureDescendingList(request,paramMap);
|
||||
}else{
|
||||
// 정전개 조회 (기본값)
|
||||
resultList = partMngService.getStructureAscendingList(request,paramMap);
|
||||
}
|
||||
}
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -1538,37 +1629,58 @@ public class PartMngController {
|
||||
String objid="";
|
||||
try{
|
||||
objid = CommonUtils.createObjId();
|
||||
code_map.put("customer_cd",commonService.bizMakeOptionList("", (String)paramMap.get("customer_cd"),"common.getsupplyselect"));
|
||||
code_map.put("project_name",commonService.bizMakeOptionList("", (String)paramMap.get("project_name"),"common.getProjectNameList"));
|
||||
code_map.put("unit_code",commonService.bizMakeOptionList((String)paramMap.get("unit_code"), (String)paramMap.get("unit_code"),"common.getBomCodeList"));
|
||||
//code_map.put("customer_cd",commonService.bizMakeOptionList("", (String)paramMap.get("customer_cd"),"common.getsupplyselect"));
|
||||
//code_map.put("project_name",commonService.bizMakeOptionList("", (String)paramMap.get("project_name"),"common.getProjectNameList"));
|
||||
//code_map.put("unit_code",commonService.bizMakeOptionList((String)paramMap.get("unit_code"), (String)paramMap.get("unit_code"),"common.getBomCodeList"));
|
||||
|
||||
//제품구분
|
||||
code_map.put("product_cd", commonService.bizMakeOptionList("0000001", CommonUtils.nullToEmpty((String)paramMap.get("product_cd")),"common.getCodeselect")); //제품구분
|
||||
|
||||
sqlSession = SqlMapConfig.getInstance().getSqlSession();
|
||||
Map sqlMap = new HashMap();
|
||||
String unitCode = CommonUtils.checkNull(paramMap.get("unit_code"));
|
||||
sqlMap.put("unitCode", unitCode);
|
||||
Map unitInfoMap = (HashMap)sqlSession.selectOne("common.getUnitCodeList", sqlMap);
|
||||
//String unitName = CommonUtils.checkNull(unitInfoMap.get("name")).replaceAll("\u00A0+", " ");
|
||||
String UNIT_NAME = CommonUtils.checkNull(unitInfoMap.get("name"));
|
||||
|
||||
sqlParam.put("customer_cd", CommonUtils.checkNull(paramMap.get("customer_cd")));
|
||||
//sqlParam.put("project_name", CommonUtils.checkNull(paramMap.get("project_name")));
|
||||
sqlParam.put("unit_name_eq", UNIT_NAME);
|
||||
regiestSameContractUnitNameBomList = partMngService.getBOMStandardStructureList(request, sqlParam);
|
||||
sqlSession = SqlMapConfig.getInstance().getSqlSession();
|
||||
|
||||
// 통합 템플릿으로 변경하면서 unit_code 관련 로직은 사용하지 않음
|
||||
/*
|
||||
// UNIT_NAME 조회 (unit_code가 유효한 경우에만)
|
||||
String UNIT_NAME = "";
|
||||
String unitCode = CommonUtils.checkNull(paramMap.get("unit_code"));
|
||||
if(sqlSession != null && !StringUtils.isBlank(unitCode) && !"undefined".equals(unitCode)){
|
||||
try {
|
||||
Map sqlMap = new HashMap();
|
||||
sqlMap.put("unitCode", unitCode);
|
||||
Map unitInfoMap = (HashMap)sqlSession.selectOne("common.getUnitCodeList", sqlMap);
|
||||
if(unitInfoMap != null){
|
||||
UNIT_NAME = CommonUtils.checkNull(unitInfoMap.get("name"));
|
||||
}
|
||||
} catch(Exception e) {
|
||||
// unit_code 조회 실패시 빈 문자열 사용
|
||||
System.out.println("Failed to get UNIT_NAME for unitCode: " + unitCode);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
sqlParam.put("customer_cd", CommonUtils.checkNull(paramMap.get("customer_cd")));
|
||||
//sqlParam.put("project_name", CommonUtils.checkNull(paramMap.get("project_name")));
|
||||
sqlParam.put("unit_name_eq", UNIT_NAME);
|
||||
regiestSameContractUnitNameBomList = partMngService.getBOMStandardStructureList(request, sqlParam);
|
||||
*/
|
||||
|
||||
// unit_code 없이 BOM 구조 조회
|
||||
sqlParam.put("customer_cd", CommonUtils.checkNull(paramMap.get("customer_cd")));
|
||||
sqlParam.put("unit_name_eq", "");
|
||||
regiestSameContractUnitNameBomList = partMngService.getBOMStandardStructureList(request, sqlParam);
|
||||
|
||||
//paramMap.put("product_code",(String)paramMap.get("product_code"));
|
||||
//info = partMngService.getBOMStructureRev(request,paramMap);
|
||||
bomContractInfo = partMngService.getBOMContractinfo(request,paramMap);
|
||||
|
||||
Map param = new HashMap();
|
||||
//unit
|
||||
param.put("parentCodeId", "0000059");
|
||||
code_map.put("unit_cd", commonService.getJqGridSelectBoxJsonData("common.getCodeList2", param, "선택"));
|
||||
param.put("parentCodeId", "0000062");
|
||||
code_map.put("part_type", commonService.getJqGridSelectBoxJsonData("common.getCodeList2", param, "선택"));
|
||||
code_map.put("sup_code", commonService.getJqGridSelectBoxJsonData("common.getmatersupplyselect", param, "선택"));
|
||||
// part_type 셀렉트박스 데이터 전달
|
||||
Map param = new HashMap();
|
||||
param.put("parentCodeId", "0000062");
|
||||
code_map.put("part_type", commonService.getJqGridSelectBoxJsonData("common.getCodeList2", param, "선택"));
|
||||
code_map.put("sup_code", commonService.getJqGridSelectBoxJsonData("common.getmatersupplyselect", param, "선택"));
|
||||
|
||||
// 활성화된 BOM 목록 전달 (복사용) - bizMakeOptionList 사용
|
||||
code_map.put("bom_list", commonService.bizMakeOptionList("", "", "partMng.getActiveBomList"));
|
||||
|
||||
bomInfo = partMngService.getBOMStructureStandardInfo(request, paramMap);
|
||||
if(bomInfo == null) bomInfo = new HashMap();
|
||||
@@ -1586,10 +1698,10 @@ public class PartMngController {
|
||||
}else {
|
||||
//신규등록일 때만 보이게
|
||||
}
|
||||
multiMasterList = commonService.selectList("purchaseOrder.selectPurchaseOrderMasterList", request, paramMap);
|
||||
//multiMasterList = commonService.selectList("purchaseOrder.selectPurchaseOrderMasterList", request, paramMap);
|
||||
|
||||
//동시발주용 정보
|
||||
request.setAttribute("multiMasterList", multiMasterList);
|
||||
//request.setAttribute("multiMasterList", multiMasterList);
|
||||
request.setAttribute("regiestSameContractUnitNameBomList", regiestSameContractUnitNameBomList); //동일 계약에 동일유닛네임으로 저장된 bom list
|
||||
|
||||
request.setAttribute("code_map",code_map);
|
||||
@@ -1597,9 +1709,9 @@ public class PartMngController {
|
||||
request.setAttribute("resultList",resultList);
|
||||
request.setAttribute("info",bomContractInfo);
|
||||
request.setAttribute("objid",objid);
|
||||
request.setAttribute("CUSTOMER_OBJID",(String)paramMap.get("customer_cd"));
|
||||
request.setAttribute("CONTRACT_OBJID",(String)paramMap.get("project_name"));
|
||||
request.setAttribute("UNIT_CODE",(String)paramMap.get("unit_code"));
|
||||
//request.setAttribute("CUSTOMER_OBJID",(String)paramMap.get("customer_cd"));
|
||||
//request.setAttribute("CONTRACT_OBJID",(String)paramMap.get("project_name"));
|
||||
//request.setAttribute("UNIT_CODE",(String)paramMap.get("unit_code"));
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
@@ -1632,6 +1744,21 @@ public class PartMngController {
|
||||
return parsingPartList;
|
||||
}
|
||||
|
||||
/**
|
||||
* BOM 복사를 위한 데이터 조회 (엑셀 파싱 형식과 동일하게 반환)
|
||||
*/
|
||||
@RequestMapping("/partMng/getBomDataForCopy.do")
|
||||
@ResponseBody
|
||||
public ArrayList getBomDataForCopy(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
ArrayList bomDataList = new ArrayList();
|
||||
try{
|
||||
bomDataList = (ArrayList)partMngService.getBomDataForCopy(request, paramMap);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return bomDataList;
|
||||
}
|
||||
|
||||
/**
|
||||
* BOM 파트&구조 저장
|
||||
*/
|
||||
@@ -1652,6 +1779,45 @@ public class PartMngController {
|
||||
CommonUtils.setReqResult(request, "", "F", e, resultMap);
|
||||
}
|
||||
//return result;
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 품번 중복 체크 (PART_BOM_REPORT 테이블)
|
||||
* @param request
|
||||
* @param paramMap
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/partMng/checkDuplicatePartNo.do")
|
||||
@ResponseBody
|
||||
public Map<String, Object> checkDuplicatePartNo(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
Map<String, Object> resultMap = new HashMap<String, Object>();
|
||||
|
||||
try{
|
||||
String partNo = CommonUtils.checkNull(paramMap.get("partNo"));
|
||||
String bomReportObjid = CommonUtils.checkNull(paramMap.get("bomReportObjid"));
|
||||
|
||||
if(!"".equals(partNo)){
|
||||
paramMap.put("partNo", partNo.trim());
|
||||
paramMap.put("bomReportObjid", bomReportObjid);
|
||||
|
||||
// DB에서 중복 체크
|
||||
int count = partMngService.checkDuplicatePartNo(request, paramMap);
|
||||
|
||||
resultMap.put("isDuplicate", count > 0);
|
||||
}else{
|
||||
resultMap.put("isDuplicate", false);
|
||||
}
|
||||
|
||||
resultMap.put("result", "success");
|
||||
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
resultMap.put("result", "error");
|
||||
resultMap.put("message", "품번 중복 체크 중 오류가 발생했습니다.");
|
||||
resultMap.put("isDuplicate", false);
|
||||
}
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
@@ -1986,4 +2152,380 @@ public class PartMngController {
|
||||
|
||||
return "/ajax/ajaxResult";
|
||||
}
|
||||
|
||||
/**
|
||||
* 도면 파일 일괄 업로드
|
||||
* 파일명에 품번이 포함된 경우 자동 매칭하여 업로드
|
||||
* - stp 파일 -> 3D CAD 컬럼
|
||||
* - dwg 파일 -> 2D Drawing CAD 컬럼
|
||||
* - pdf 파일 -> 2D PDF CAD 컬럼
|
||||
*
|
||||
* @param request
|
||||
* @param session
|
||||
* @param bomObjId BOM OBJID
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/partMng/uploadDrawingFiles.do", method=RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public Map<String, Object> uploadDrawingFiles(
|
||||
HttpServletRequest request,
|
||||
HttpSession session) {
|
||||
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
MultipartRequest multi = null;
|
||||
FileRenameClass frc = null;
|
||||
|
||||
try {
|
||||
PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
|
||||
String userId = person != null ? person.getUserId() : "plmAdmin";
|
||||
|
||||
// MultipartRequest로 파일 업로드 처리
|
||||
String storagePath = Constants.FILE_STORAGE;
|
||||
int maxSize = 1024*1024*1024*9; // 9GB
|
||||
|
||||
File storage = new File(storagePath);
|
||||
if(!storage.exists()) storage.mkdirs();
|
||||
|
||||
frc = new FileRenameClass();
|
||||
multi = new MultipartRequest(request, storagePath, maxSize, "UTF-8", frc);
|
||||
java.util.List fileList = frc.getFileList();
|
||||
|
||||
// MultipartRequest에서 bomObjId 파라미터 가져오기
|
||||
String bomObjId = multi.getParameter("bomObjId");
|
||||
|
||||
if(bomObjId == null || bomObjId.isEmpty()) {
|
||||
resultMap.put("result", "fail");
|
||||
resultMap.put("message", "BOM ID가 전달되지 않았습니다.");
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
// BOM 정보 조회
|
||||
Map<String, Object> bomParam = new HashMap<>();
|
||||
bomParam.put("objId", bomObjId);
|
||||
Map bomInfo = partMngService.getBOMStructureStandardInfo(request, bomParam);
|
||||
|
||||
if(bomInfo == null) {
|
||||
resultMap.put("result", "fail");
|
||||
resultMap.put("message", "BOM 정보를 찾을 수 없습니다.");
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
// 해당 BOM의 모든 파트 정보 조회
|
||||
bomParam.put("SEARCH_BOM_OBJID", bomObjId);
|
||||
ArrayList<Map> partList = commonService.selectList("partMng.partMngListByBom", request, bomParam);
|
||||
|
||||
if(partList == null || partList.isEmpty()) {
|
||||
resultMap.put("result", "fail");
|
||||
resultMap.put("message", "BOM에 등록된 파트가 없습니다.");
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
// 품번 기준 파트 맵 생성
|
||||
Map<String, Map> partNoMap = new HashMap<>();
|
||||
for(Map part : partList) {
|
||||
String partNo = CommonUtils.checkNull((String)part.get("PART_NO"));
|
||||
if(!partNo.isEmpty()) {
|
||||
partNoMap.put(partNo, part);
|
||||
}
|
||||
}
|
||||
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
int notFoundCount = 0;
|
||||
|
||||
// 업로드된 파일 처리
|
||||
if(fileList != null && !fileList.isEmpty()) {
|
||||
for(Object fileObj : fileList) {
|
||||
Map fileInfo = (Map)fileObj;
|
||||
String originalFileName = CommonUtils.checkNull((String)fileInfo.get("realFileName"));
|
||||
String savedFileName = CommonUtils.checkNull((String)fileInfo.get("savedFileName"));
|
||||
String fileExt = CommonUtils.checkNull((String)fileInfo.get("fileExt"));
|
||||
long fileSize = Long.parseLong(CommonUtils.checkNull(fileInfo.get("fileSize"), "0"));
|
||||
|
||||
// 확장자 대문자 변환 (이미 점 없이 저장됨)
|
||||
fileExt = fileExt.toUpperCase();
|
||||
|
||||
System.out.println("========== 파일 업로드 처리 ==========");
|
||||
System.out.println("원본 파일명: " + originalFileName);
|
||||
System.out.println("저장 파일명: " + savedFileName);
|
||||
System.out.println("확장자: " + fileExt);
|
||||
System.out.println("===================================");
|
||||
|
||||
// 파일 확장자에 따른 문서 타입 결정
|
||||
String docType = "";
|
||||
String docTypeName = "";
|
||||
|
||||
if("STP".equals(fileExt) || "STEP".equals(fileExt)) {
|
||||
docType = "3D_CAD";
|
||||
docTypeName = "3D CAD 첨부파일";
|
||||
} else if("DWG".equals(fileExt)) {
|
||||
docType = "2D_DRAWING_CAD";
|
||||
docTypeName = "2D(Drawing) CAD 첨부파일";
|
||||
} else if("PDF".equals(fileExt)) {
|
||||
docType = "2D_PDF_CAD";
|
||||
docTypeName = "2D(PDF) CAD 첨부파일";
|
||||
} else {
|
||||
// 지원하지 않는 확장자는 스킵
|
||||
System.out.println("지원하지 않는 확장자: " + fileExt + ", 파일명: " + originalFileName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 파일명에서 확장자 제거하여 품번 추출
|
||||
String fileNameWithoutExt = originalFileName;
|
||||
int lastDotIndex = originalFileName.lastIndexOf('.');
|
||||
if(lastDotIndex > 0) {
|
||||
fileNameWithoutExt = originalFileName.substring(0, lastDotIndex);
|
||||
}
|
||||
|
||||
// 품번과 정확히 일치하는 경우만 매칭
|
||||
String matchedPartNo = null;
|
||||
System.out.println("품번 매칭 시작 - 파일명(확장자 제외): " + fileNameWithoutExt);
|
||||
|
||||
// 정확한 매칭 (품번과 파일명이 정확히 일치)
|
||||
if(partNoMap.containsKey(fileNameWithoutExt)) {
|
||||
matchedPartNo = fileNameWithoutExt;
|
||||
System.out.println(" ✓ 품번 정확 매칭 성공: " + matchedPartNo);
|
||||
}
|
||||
|
||||
if(matchedPartNo == null) {
|
||||
System.out.println(" ✗ 품번 매칭 실패 - 파일명과 일치하는 품번이 없음");
|
||||
notFoundCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 해당 파트에 파일 정보 저장
|
||||
Map partInfo = partNoMap.get(matchedPartNo);
|
||||
String partObjId = CommonUtils.checkNull((String)partInfo.get("OBJID"));
|
||||
|
||||
Map<String, Object> fileMap = new HashMap<>();
|
||||
fileMap.put("OBJID", CommonUtils.createObjId());
|
||||
fileMap.put("TARGET_OBJID", partObjId);
|
||||
fileMap.put("SAVED_FILE_NAME", savedFileName);
|
||||
fileMap.put("REAL_FILE_NAME", originalFileName);
|
||||
fileMap.put("DOC_TYPE", docType);
|
||||
fileMap.put("DOC_TYPE_NAME", docTypeName);
|
||||
fileMap.put("FILE_SIZE", String.valueOf(fileSize));
|
||||
fileMap.put("FILE_EXT", fileExt);
|
||||
fileMap.put("FILE_PATH", storagePath);
|
||||
fileMap.put("WRITER", userId);
|
||||
|
||||
try {
|
||||
// 파일 정보 DB 저장
|
||||
partMngService.insertDrawingFile(fileMap);
|
||||
successCount++;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultMap.put("result", "success");
|
||||
resultMap.put("successCount", successCount);
|
||||
resultMap.put("failCount", failCount);
|
||||
resultMap.put("notFoundCount", notFoundCount);
|
||||
resultMap.put("message", "업로드가 완료되었습니다.");
|
||||
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
resultMap.put("result", "error");
|
||||
resultMap.put("message", "업로드 중 오류가 발생했습니다: " + e.getMessage());
|
||||
} finally {
|
||||
if(frc != null) {
|
||||
frc.clear();
|
||||
}
|
||||
}
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* PART 목록 화면에서 도면 파일 일괄 업로드
|
||||
* BOM 정보 없이 전체 파트를 대상으로 파일명 매칭
|
||||
*
|
||||
* @param request
|
||||
* @param session
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping(value="/partMng/uploadDrawingFilesForPartList.do", method=RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public Map<String, Object> uploadDrawingFilesForPartList(
|
||||
HttpServletRequest request,
|
||||
HttpSession session) {
|
||||
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
MultipartRequest multi = null;
|
||||
FileRenameClass frc = null;
|
||||
|
||||
try {
|
||||
PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
|
||||
String userId = person != null ? person.getUserId() : "plmAdmin";
|
||||
|
||||
// MultipartRequest로 파일 업로드 처리
|
||||
String storagePath = Constants.FILE_STORAGE;
|
||||
int maxSize = 1024*1024*1024*9; // 9GB
|
||||
|
||||
File storage = new File(storagePath);
|
||||
if(!storage.exists()) storage.mkdirs();
|
||||
|
||||
frc = new FileRenameClass();
|
||||
multi = new MultipartRequest(request, storagePath, maxSize, "UTF-8", frc);
|
||||
java.util.List fileList = frc.getFileList();
|
||||
|
||||
// 화면에서 전달된 품번 목록 가져오기
|
||||
String partNoListJson = multi.getParameter("partNoList");
|
||||
if(partNoListJson == null || partNoListJson.isEmpty()) {
|
||||
resultMap.put("result", "fail");
|
||||
resultMap.put("message", "품번 목록이 전달되지 않았습니다.");
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
// JSON 파싱 (Gson 사용)
|
||||
Gson gson = new Gson();
|
||||
Type listType = new TypeToken<ArrayList<String>>(){}.getType();
|
||||
java.util.List<String> partNoList = gson.fromJson(partNoListJson, listType);
|
||||
|
||||
if(partNoList.isEmpty()) {
|
||||
resultMap.put("result", "fail");
|
||||
resultMap.put("message", "페이지에 표시된 파트가 없습니다.");
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
System.out.println("========== 화면에서 전달된 품번 목록 ==========");
|
||||
System.out.println("품번 개수: " + partNoList.size());
|
||||
System.out.println("품번 목록: " + partNoList);
|
||||
System.out.println("==========================================");
|
||||
|
||||
// 전달된 품번 목록에 해당하는 파트만 조회
|
||||
Map<String, Object> partParam = new HashMap<>();
|
||||
partParam.put("IS_LAST", "1");
|
||||
partParam.put("PART_NO_LIST", partNoList);
|
||||
ArrayList<Map> partList = commonService.selectList("partMng.partMngListByPartNos", request, partParam);
|
||||
|
||||
if(partList == null || partList.isEmpty()) {
|
||||
resultMap.put("result", "fail");
|
||||
resultMap.put("message", "조회된 파트가 없습니다.");
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
// 품번 기준 파트 맵 생성 (화면에 표시된 파트만)
|
||||
Map<String, Map> partNoMap = new HashMap<>();
|
||||
for(Map part : partList) {
|
||||
String partNo = CommonUtils.checkNull((String)part.get("PART_NO"));
|
||||
if(!partNo.isEmpty()) {
|
||||
partNoMap.put(partNo, part);
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("매칭 가능한 품번 개수: " + partNoMap.size());
|
||||
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
int notFoundCount = 0;
|
||||
|
||||
// 업로드된 파일 처리
|
||||
if(fileList != null && !fileList.isEmpty()) {
|
||||
for(Object fileObj : fileList) {
|
||||
Map fileInfo = (Map)fileObj;
|
||||
String originalFileName = CommonUtils.checkNull((String)fileInfo.get("realFileName"));
|
||||
String savedFileName = CommonUtils.checkNull((String)fileInfo.get("savedFileName"));
|
||||
String fileExt = CommonUtils.checkNull((String)fileInfo.get("fileExt"));
|
||||
long fileSize = Long.parseLong(CommonUtils.checkNull(fileInfo.get("fileSize"), "0"));
|
||||
|
||||
// 확장자 대문자 변환
|
||||
fileExt = fileExt.toUpperCase();
|
||||
|
||||
System.out.println("========== 파트 목록 파일 업로드 처리 ==========");
|
||||
System.out.println("원본 파일명: " + originalFileName);
|
||||
System.out.println("저장 파일명: " + savedFileName);
|
||||
System.out.println("확장자: " + fileExt);
|
||||
System.out.println("==========================================");
|
||||
|
||||
// 파일 확장자에 따른 문서 타입 결정
|
||||
String docType = "";
|
||||
String docTypeName = "";
|
||||
|
||||
if("STP".equals(fileExt) || "STEP".equals(fileExt)) {
|
||||
docType = "3D_CAD";
|
||||
docTypeName = "3D CAD 첨부파일";
|
||||
} else if("DWG".equals(fileExt)) {
|
||||
docType = "2D_DRAWING_CAD";
|
||||
docTypeName = "2D(Drawing) CAD 첨부파일";
|
||||
} else if("PDF".equals(fileExt)) {
|
||||
docType = "2D_PDF_CAD";
|
||||
docTypeName = "2D(PDF) CAD 첨부파일";
|
||||
} else {
|
||||
// 지원하지 않는 확장자는 스킵
|
||||
System.out.println("지원하지 않는 확장자: " + fileExt + ", 파일명: " + originalFileName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 파일명에서 확장자 제거하여 품번 추출
|
||||
String fileNameWithoutExt = originalFileName;
|
||||
int lastDotIndex = originalFileName.lastIndexOf('.');
|
||||
if(lastDotIndex > 0) {
|
||||
fileNameWithoutExt = originalFileName.substring(0, lastDotIndex);
|
||||
}
|
||||
|
||||
// 품번과 정확히 일치하는 경우만 매칭
|
||||
String matchedPartNo = null;
|
||||
System.out.println("품번 매칭 시작 - 파일명(확장자 제외): " + fileNameWithoutExt);
|
||||
|
||||
// 정확한 매칭 (품번과 파일명이 정확히 일치)
|
||||
if(partNoMap.containsKey(fileNameWithoutExt)) {
|
||||
matchedPartNo = fileNameWithoutExt;
|
||||
System.out.println(" ✓ 품번 정확 매칭 성공: " + matchedPartNo);
|
||||
}
|
||||
|
||||
if(matchedPartNo == null) {
|
||||
System.out.println(" ✗ 품번 매칭 실패 - 파일명과 일치하는 품번이 없음");
|
||||
notFoundCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 해당 파트에 파일 정보 저장
|
||||
Map partInfo = partNoMap.get(matchedPartNo);
|
||||
String partObjId = CommonUtils.checkNull((String)partInfo.get("OBJID"));
|
||||
|
||||
Map<String, Object> fileMap = new HashMap<>();
|
||||
fileMap.put("OBJID", CommonUtils.createObjId());
|
||||
fileMap.put("TARGET_OBJID", partObjId);
|
||||
fileMap.put("SAVED_FILE_NAME", savedFileName);
|
||||
fileMap.put("REAL_FILE_NAME", originalFileName);
|
||||
fileMap.put("DOC_TYPE", docType);
|
||||
fileMap.put("DOC_TYPE_NAME", docTypeName);
|
||||
fileMap.put("FILE_SIZE", String.valueOf(fileSize));
|
||||
fileMap.put("FILE_EXT", fileExt);
|
||||
fileMap.put("FILE_PATH", storagePath);
|
||||
fileMap.put("WRITER", userId);
|
||||
|
||||
try {
|
||||
// 파일 정보 DB 저장
|
||||
partMngService.insertDrawingFile(fileMap);
|
||||
successCount++;
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultMap.put("result", "success");
|
||||
resultMap.put("successCount", successCount);
|
||||
resultMap.put("failCount", failCount);
|
||||
resultMap.put("notFoundCount", notFoundCount);
|
||||
resultMap.put("message", "업로드가 완료되었습니다.");
|
||||
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
resultMap.put("result", "error");
|
||||
resultMap.put("message", "업로드 중 오류가 발생했습니다: " + e.getMessage());
|
||||
} finally {
|
||||
if(frc != null) {
|
||||
frc.clear();
|
||||
}
|
||||
}
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -862,16 +862,112 @@ public class ProductionPlanningController extends BaseService {
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 작업일지 팀장 확인
|
||||
* @param request
|
||||
* @param paramMap
|
||||
* @return
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping("/productionplanning/workDiaryConfirm.do")
|
||||
public Map workDiaryConfirm(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
Map resultMap = productionPlanningService.workDiaryConfirm(request, paramMap);
|
||||
return resultMap;
|
||||
/**
|
||||
* 작업일지 팀장 확인
|
||||
* @param request
|
||||
* @param paramMap
|
||||
* @return
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping("/productionplanning/workDiaryConfirm.do")
|
||||
public Map workDiaryConfirm(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
Map resultMap = productionPlanningService.workDiaryConfirm(request, paramMap);
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 생산관리 -> M-BOM 관리 목록
|
||||
* @param request
|
||||
* @param paramMap
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/productionplanning/mBomMgmt.do")
|
||||
public String mBomMgmt(HttpServletRequest request, @RequestParam Map paramMap){
|
||||
Map code_map = new HashMap();
|
||||
try{
|
||||
request.setAttribute("code_map", code_map);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "/productionplanning/mBomMgmtList";
|
||||
}
|
||||
|
||||
/**
|
||||
* 생산관리 -> M-BOM 관리 그리드 목록
|
||||
* @param request
|
||||
* @param paramMap
|
||||
* @return
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping("/productionplanning/mBomMgmtGridList.do")
|
||||
public Map mBomMgmtGridList(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
commonService.selectListPagingNew("productionplanning.mBomMgmtGridList", request, paramMap);
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* M-BOM E-BOM 선택 팝업
|
||||
* @param request
|
||||
* @param paramMap
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/productionplanning/mBomEbomSelectPopup.do")
|
||||
public String mBomEbomSelectPopup(HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
|
||||
try {
|
||||
// 필요한 코드 맵 설정 (structureList.jsp 참고)
|
||||
Map<String, Object> codeMap = new HashMap<>();
|
||||
// 제품구분 코드 - structureList와 동일하게 설정
|
||||
codeMap.put("product_cd", commonService.bizMakeOptionList("0000001", CommonUtils.nullToEmpty((String)paramMap.get("product_cd")), "common.getCodeselect"));
|
||||
request.setAttribute("code_map", codeMap);
|
||||
|
||||
// 현재 할당된 E-BOM 정보 조회
|
||||
String bomReportObjid = CommonUtils.checkNull(paramMap.get("bomReportObjid"));
|
||||
if(!"".equals(bomReportObjid)) {
|
||||
// E-BOM 정보 조회
|
||||
Map<String, Object> ebomInfo = productionPlanningService.getEbomInfo(bomReportObjid);
|
||||
request.setAttribute("currentEbom", ebomInfo);
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "/productionplanning/mBomEbomSelectPopup";
|
||||
}
|
||||
|
||||
/**
|
||||
* E-BOM을 M-BOM(PROJECT_MGMT)에 할당
|
||||
* @param request
|
||||
* @param paramMap
|
||||
* @return
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping("/productionplanning/assignEbomToMbom.do")
|
||||
public Map<String, Object> assignEbomToMbom(HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
try {
|
||||
String projectMgmtObjid = CommonUtils.checkNull(paramMap.get("projectMgmtObjid"));
|
||||
String bomReportObjid = CommonUtils.checkNull(paramMap.get("bomReportObjid"));
|
||||
|
||||
if(projectMgmtObjid.isEmpty() || bomReportObjid.isEmpty()) {
|
||||
resultMap.put("success", false);
|
||||
resultMap.put("message", "필수 파라미터가 누락되었습니다.");
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
// PROJECT_MGMT 테이블의 PART_OBJID 업데이트
|
||||
int updateResult = productionPlanningService.assignEbomToProject(projectMgmtObjid, bomReportObjid);
|
||||
|
||||
if(updateResult > 0) {
|
||||
resultMap.put("success", true);
|
||||
resultMap.put("message", "E-BOM이 할당되었습니다.");
|
||||
} else {
|
||||
resultMap.put("success", false);
|
||||
resultMap.put("message", "할당에 실패했습니다.");
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
resultMap.put("success", false);
|
||||
resultMap.put("message", "오류가 발생했습니다: " + e.getMessage());
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2869,4 +2869,138 @@
|
||||
</if>
|
||||
ORDER BY SUBSTRING(PROJECT_NO,POSITION('-' IN PROJECT_NO)+1) DESC
|
||||
</select>
|
||||
|
||||
<!-- M-BOM 관리 목록 조회 -->
|
||||
<select id="mBomMgmtGridList" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT
|
||||
PM.OBJID,
|
||||
PM.CONTRACT_OBJID,
|
||||
PM.PROJECT_NO,
|
||||
CM.CATEGORY_CD,
|
||||
COALESCE(
|
||||
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.CATEGORY_CD LIMIT 1),
|
||||
''
|
||||
) AS CATEGORY_NAME,
|
||||
CM.PRODUCT,
|
||||
COALESCE(
|
||||
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.PRODUCT LIMIT 1),
|
||||
''
|
||||
) AS PRODUCT_NAME,
|
||||
CM.AREA_CD,
|
||||
COALESCE(
|
||||
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.AREA_CD LIMIT 1),
|
||||
''
|
||||
) AS AREA_NAME,
|
||||
TO_CHAR(PM.REGDATE, 'YYYY-MM-DD') AS RECEIPT_DATE,
|
||||
CM.CUSTOMER_OBJID,
|
||||
COALESCE(
|
||||
(SELECT SUPPLY_NAME FROM SUPPLY_MNG WHERE OBJID = CM.CUSTOMER_OBJID::NUMERIC LIMIT 1),
|
||||
''
|
||||
) AS CUSTOMER_NAME,
|
||||
CM.PAID_TYPE,
|
||||
COALESCE(
|
||||
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.PAID_TYPE LIMIT 1),
|
||||
CASE
|
||||
WHEN CM.PAID_TYPE = 'paid' THEN '유상'
|
||||
WHEN CM.PAID_TYPE = 'free' THEN '무상'
|
||||
ELSE ''
|
||||
END
|
||||
) AS PAID_TYPE_NAME,
|
||||
COALESCE(PM.PART_NO, '') AS PART_NO,
|
||||
COALESCE(PM.PART_NAME, '') AS PART_NAME,
|
||||
-- S/N: CONTRACT_ITEM_SERIAL에서 조회
|
||||
(SELECT
|
||||
CASE
|
||||
WHEN COUNT(*) = 0 THEN ''
|
||||
WHEN COUNT(*) = 1 THEN MIN(CIS.SERIAL_NO)
|
||||
ELSE MIN(CIS.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건'
|
||||
END
|
||||
FROM CONTRACT_ITEM CI
|
||||
LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND UPPER(CIS.STATUS) = 'ACTIVE'
|
||||
WHERE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID
|
||||
AND CI.PART_OBJID = PM.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE'
|
||||
AND CIS.SERIAL_NO IS NOT NULL) AS SERIAL_NO,
|
||||
COALESCE(PM.QUANTITY::numeric, 0) AS QUANTITY,
|
||||
-- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT, 없으면 CONTRACT_MGMT
|
||||
COALESCE(
|
||||
(SELECT CI.DUE_DATE
|
||||
FROM CONTRACT_ITEM CI
|
||||
WHERE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID
|
||||
AND CI.PART_OBJID = PM.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE' LIMIT 1),
|
||||
PM.DUE_DATE,
|
||||
CM.req_del_date
|
||||
) AS REQ_DEL_DATE,
|
||||
-- 고객요청사항: CONTRACT_ITEM에서 가져옴
|
||||
COALESCE(
|
||||
(SELECT CI.CUSTOMER_REQUEST
|
||||
FROM CONTRACT_ITEM CI
|
||||
WHERE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID
|
||||
AND CI.PART_OBJID = PM.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE' LIMIT 1),
|
||||
''
|
||||
) AS CUSTOMER_REQUEST,
|
||||
-- E-BOM 정보: PM.PART_OBJID가 E-BOM OBJID를 직접 가리킴
|
||||
PM.PART_OBJID AS BOM_REPORT_OBJID,
|
||||
COALESCE(
|
||||
(SELECT PBR.STATUS
|
||||
FROM PART_BOM_REPORT PBR
|
||||
WHERE PBR.OBJID::VARCHAR = PM.PART_OBJID
|
||||
LIMIT 1),
|
||||
''
|
||||
) AS EBOM_STATUS,
|
||||
COALESCE(
|
||||
(SELECT TO_CHAR(PBR.REGDATE, 'YYYY-MM-DD')
|
||||
FROM PART_BOM_REPORT PBR
|
||||
WHERE PBR.OBJID::VARCHAR = PM.PART_OBJID
|
||||
LIMIT 1),
|
||||
''
|
||||
) AS EBOM_REGDATE,
|
||||
COALESCE(PM.MBOM_STATUS, '') AS MBOM_STATUS,
|
||||
'1.0' AS MBOM_VERSION,
|
||||
TO_CHAR(PM.REGDATE, 'YYYY-MM-DD') AS MBOM_REGDATE
|
||||
FROM
|
||||
PROJECT_MGMT PM
|
||||
LEFT JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
|
||||
WHERE 1=1
|
||||
AND PM.PROJECT_NO IS NOT NULL
|
||||
AND PM.PROJECT_NO != ''
|
||||
<!-- 품번 검색 (대소문자 구분 없음) -->
|
||||
<if test="search_part_no != null and search_part_no != ''">
|
||||
AND UPPER(PM.PART_NO) LIKE '%' || UPPER(#{search_part_no}) || '%'
|
||||
</if>
|
||||
<!-- 품명 검색 (대소문자 구분 없음) -->
|
||||
<if test="search_part_name != null and search_part_name != ''">
|
||||
AND UPPER(PM.PART_NAME) LIKE '%' || UPPER(#{search_part_name}) || '%'
|
||||
</if>
|
||||
ORDER BY PM.REGDATE DESC
|
||||
</select>
|
||||
|
||||
<!-- E-BOM을 PROJECT_MGMT에 할당 -->
|
||||
<update id="assignEbomToProject" parameterType="map">
|
||||
UPDATE PROJECT_MGMT
|
||||
SET
|
||||
PART_OBJID = #{bomReportObjid}
|
||||
WHERE OBJID = #{projectMgmtObjid}
|
||||
</update>
|
||||
|
||||
<!-- E-BOM 정보 조회 -->
|
||||
<select id="getEbomInfo" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT
|
||||
T.OBJID,
|
||||
T.PRODUCT_CD,
|
||||
CODE_NAME(T.PRODUCT_CD) as PRODUCT_NAME,
|
||||
T.PART_NO,
|
||||
T.PART_NAME,
|
||||
T.STATUS,
|
||||
T.REVISION,
|
||||
TO_CHAR(T.REGDATE, 'YYYY-MM-DD') AS REG_DATE,
|
||||
UI.USER_NAME AS WRITER_NAME,
|
||||
UI.DEPT_NAME
|
||||
FROM
|
||||
PART_BOM_REPORT T
|
||||
LEFT JOIN USER_INFO UI ON UI.USER_ID = T.WRITER
|
||||
WHERE T.OBJID::VARCHAR = #{objid}
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
@@ -3916,22 +3916,46 @@
|
||||
ELSE O.PAID_TYPE
|
||||
END
|
||||
FROM CONTRACT_MGMT AS O WHERE O.OBJID = T.CONTRACT_OBJID) AS FREE_OF_CHARGE
|
||||
,(SELECT SUM(O.QUANTITY) FROM CONTRACT_ITEM AS O WHERE O.CONTRACT_OBJID = T.CONTRACT_OBJID) AS CONTRACT_QTY
|
||||
,(SELECT STRING_AGG(O.PART_NO, ', ' ORDER BY O.SEQ) FROM CONTRACT_ITEM AS O WHERE O.CONTRACT_OBJID = T.CONTRACT_OBJID AND O.PART_NO IS NOT NULL AND O.PART_NO != '') AS PRODUCT_ITEM_CODE
|
||||
,(SELECT STRING_AGG(O.PART_NAME, ', ' ORDER BY O.SEQ) FROM CONTRACT_ITEM AS O WHERE O.CONTRACT_OBJID = T.CONTRACT_OBJID AND O.PART_NAME IS NOT NULL AND O.PART_NAME != '') AS PRODUCT_ITEM_NAME
|
||||
,(SELECT STRING_AGG(S.SERIAL_NO, ', ' ORDER BY S.SEQ)
|
||||
FROM CONTRACT_ITEM AS I
|
||||
LEFT JOIN CONTRACT_ITEM_SERIAL AS S ON S.ITEM_OBJID = I.OBJID AND S.STATUS = 'ACTIVE'
|
||||
WHERE I.CONTRACT_OBJID = T.CONTRACT_OBJID
|
||||
AND S.SERIAL_NO IS NOT NULL) AS SERIAL_NO
|
||||
-- 영업관리_주문서관리_수주등록
|
||||
,EBOM_STATUS
|
||||
,MBOM_STATUS
|
||||
,(SELECT O.PURCHASE_DATE FROM PURCHASE_ORDER_MASTER AS O WHERE O.CONTRACT_MGMT_OBJID = T.CONTRACT_OBJID LIMIT 1) AS ORDER_DATE
|
||||
,RECEIVING_RATE
|
||||
,PRODUCTION_TEAM_12
|
||||
,PRODUCTION_TEAM_3
|
||||
,(SELECT O.SHIPPING_DATE::TEXT FROM SHIPMENT_LOG AS O WHERE O.TARGET_OBJID = T.CONTRACT_OBJID LIMIT 1) AS SHIPMENT_DATE
|
||||
,COALESCE(T.QUANTITY::numeric, 0) AS CONTRACT_QTY
|
||||
,T.PART_NO AS PRODUCT_ITEM_CODE
|
||||
,T.PART_NAME AS PRODUCT_ITEM_NAME
|
||||
-- S/N: 해당 품목의 시리얼 번호 (여러 개일 경우 "S/N 외 N건" 형식)
|
||||
,(SELECT
|
||||
CASE
|
||||
WHEN COUNT(*) = 0 THEN ''
|
||||
WHEN COUNT(*) = 1 THEN MIN(S.SERIAL_NO)
|
||||
ELSE MIN(S.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건'
|
||||
END
|
||||
FROM CONTRACT_ITEM AS I
|
||||
LEFT JOIN CONTRACT_ITEM_SERIAL AS S ON S.ITEM_OBJID = I.OBJID AND S.STATUS = 'ACTIVE'
|
||||
WHERE I.CONTRACT_OBJID = T.CONTRACT_OBJID
|
||||
AND I.PART_OBJID = T.PART_OBJID
|
||||
AND S.SERIAL_NO IS NOT NULL) AS SERIAL_NO
|
||||
-- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT.DUE_DATE, 없으면 CONTRACT_MGMT.due_date
|
||||
,COALESCE(
|
||||
(SELECT CI.DUE_DATE
|
||||
FROM CONTRACT_ITEM CI
|
||||
WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID
|
||||
AND CI.PART_OBJID = T.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE'),
|
||||
T.DUE_DATE,
|
||||
(SELECT CM.due_date FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID)
|
||||
) AS REQ_DEL_DATE
|
||||
-- 영업관리_주문서관리_수주등록
|
||||
,EBOM_STATUS
|
||||
,MBOM_STATUS
|
||||
-- 발주일: CONTRACT_MGMT 테이블에서 가져오기 (영업관리_주문서관리와 동일)
|
||||
,(SELECT O.ORDER_DATE FROM CONTRACT_MGMT AS O WHERE O.OBJID = T.CONTRACT_OBJID) AS ORDER_DATE
|
||||
,RECEIVING_RATE
|
||||
,PRODUCTION_TEAM_12
|
||||
,PRODUCTION_TEAM_3
|
||||
-- 출하일: sales_registration 테이블에서 가져오기 (영업관리_판매관리와 동일)
|
||||
,COALESCE(
|
||||
(SELECT TO_CHAR(SR.shipping_date, 'YYYY-MM-DD')
|
||||
FROM sales_registration SR
|
||||
WHERE SR.project_no = T.PROJECT_NO),
|
||||
''
|
||||
) AS SHIPMENT_DATE
|
||||
,(((SELECT SUM(COALESCE(DESIGN_RATE,'0')::INTEGER) / COUNT(1) FROM PMS_WBS_TASK AS O WHERE O.CONTRACT_OBJID = T.OBJID)
|
||||
+(SELECT SUM(COALESCE(PURCHASE_RATE,'0')::INTEGER) / COUNT(1) FROM PMS_WBS_TASK AS O WHERE O.CONTRACT_OBJID = T.OBJID)
|
||||
+(SELECT SUM(COALESCE(PRODUCE_RATE,'0')::INTEGER) / COUNT(1) FROM PMS_WBS_TASK AS O WHERE O.CONTRACT_OBJID = T.OBJID)
|
||||
|
||||
@@ -2071,14 +2071,38 @@ public class ContractMgmtController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 견적서 메일 발송 (AJAX)
|
||||
* PDF 청크 업로드 (AJAX)
|
||||
* @param request
|
||||
* @param paramMap - objId (CONTRACT_OBJID)
|
||||
* @param paramMap - sessionId, chunkIndex, totalChunks, chunk
|
||||
* @return
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping(value="/contractMgmt/uploadPdfChunk.do", method=RequestMethod.POST)
|
||||
public Map uploadPdfChunk(HttpServletRequest request,
|
||||
@RequestParam Map<String, Object> paramMap){
|
||||
Map resultMap = new HashMap();
|
||||
|
||||
try {
|
||||
resultMap = contractMgmtService.uploadPdfChunk(request, paramMap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
resultMap.put("result", "error");
|
||||
resultMap.put("message", "청크 업로드 중 오류가 발생했습니다: " + e.getMessage());
|
||||
}
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 견적서 메일 발송 (AJAX) - PDF 세션 ID 방식
|
||||
* @param request
|
||||
* @param paramMap - objId (CONTRACT_OBJID), pdfSessionId (업로드된 PDF 세션 ID)
|
||||
* @return
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping(value="/contractMgmt/sendEstimateMail.do", method=RequestMethod.POST)
|
||||
public Map sendEstimateMail(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
public Map sendEstimateMail(HttpServletRequest request,
|
||||
@RequestParam Map<String, Object> paramMap){
|
||||
Map resultMap = new HashMap();
|
||||
|
||||
try {
|
||||
|
||||
@@ -816,6 +816,7 @@
|
||||
<select id="getSalesMgmtGridList" parameterType="map" resultType="map">
|
||||
/* salesNcollectMgmt.getSalesMgmtGridList - sales_registration LEFT JOIN으로 최적화 */
|
||||
SELECT
|
||||
T.OBJID,
|
||||
T.PROJECT_NO,
|
||||
T.CONTRACT_OBJID,
|
||||
CODE_NAME(T.CATEGORY_CD) AS ORDER_TYPE,
|
||||
@@ -832,7 +833,7 @@
|
||||
FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID) AS PAYMENT_TYPE,
|
||||
T.PART_NO AS PRODUCT_NO,
|
||||
T.PART_NAME AS PRODUCT_NAME,
|
||||
-- S/N: CONTRACT_ITEM_SERIAL에서만 조회 (품목별로 정확하게 매칭)
|
||||
-- S/N: 해당 품목의 시리얼 번호 (여러 개일 경우 "S/N 외 N건" 형식)
|
||||
(SELECT
|
||||
CASE
|
||||
WHEN COUNT(*) = 0 THEN ''
|
||||
@@ -840,11 +841,10 @@
|
||||
ELSE MIN(CIS.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건'
|
||||
END
|
||||
FROM CONTRACT_ITEM CI
|
||||
LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID
|
||||
LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND UPPER(CIS.STATUS) = 'ACTIVE'
|
||||
WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID
|
||||
AND CI.PART_OBJID = T.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE'
|
||||
AND UPPER(CIS.STATUS) = 'ACTIVE'
|
||||
AND CIS.SERIAL_NO IS NOT NULL) AS SERIAL_NO,
|
||||
COALESCE(T.QUANTITY::numeric, 0) AS ORDER_QUANTITY,
|
||||
-- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT, 없으면 CONTRACT_MGMT
|
||||
@@ -898,6 +898,8 @@
|
||||
FROM PROJECT_MGMT AS T
|
||||
LEFT JOIN sales_registration SR ON T.PROJECT_NO = SR.project_no
|
||||
WHERE 1 = 1
|
||||
AND T.PROJECT_NO IS NOT NULL
|
||||
AND T.PROJECT_NO != ''
|
||||
<if test="orderType != null and orderType != ''">
|
||||
AND T.CATEGORY_CD = #{orderType}
|
||||
</if>
|
||||
@@ -914,6 +916,9 @@
|
||||
AND SUPPLY_NAME LIKE CONCAT('%', #{customer}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="customer_objid != null and customer_objid != ''">
|
||||
AND T.CUSTOMER_OBJID = #{customer_objid}
|
||||
</if>
|
||||
<if test="paymentType != null and paymentType != ''">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM CONTRACT_MGMT CM
|
||||
@@ -975,7 +980,8 @@
|
||||
<if test="incoterms != null and incoterms != ''">
|
||||
/* INCOTERMS 필드 없음 - 검색 조건 무시 */
|
||||
</if>
|
||||
ORDER BY T.REGDATE DESC
|
||||
-- 등록일 기준 최신순 정렬 (프로젝트 번호는 보조 정렬)
|
||||
ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC
|
||||
</select>
|
||||
|
||||
<!-- 매출관리 그리드 목록 개수 - project_mgmt 테이블 기반 -->
|
||||
@@ -986,6 +992,8 @@
|
||||
COUNT(1)::integer AS TOTAL_CNT
|
||||
FROM PROJECT_MGMT AS T
|
||||
WHERE 1 = 1
|
||||
AND T.PROJECT_NO IS NOT NULL
|
||||
AND T.PROJECT_NO != ''
|
||||
<if test="orderType != null and orderType != ''">
|
||||
AND T.CATEGORY_CD = #{orderType}
|
||||
</if>
|
||||
@@ -1002,6 +1010,9 @@
|
||||
AND SUPPLY_NAME LIKE CONCAT('%', #{customer}, '%')
|
||||
)
|
||||
</if>
|
||||
<if test="customer_objid != null and customer_objid != ''">
|
||||
AND T.CUSTOMER_OBJID = #{customer_objid}
|
||||
</if>
|
||||
<if test="paymentType != null and paymentType != ''">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM CONTRACT_MGMT CM
|
||||
@@ -1321,16 +1332,19 @@
|
||||
FROM CONTRACT_MGMT CM WHERE CM.OBJID = T.CONTRACT_OBJID) AS PAYMENT_TYPE,
|
||||
T.PART_NO AS PRODUCT_NO,
|
||||
T.PART_NAME AS PRODUCT_NAME,
|
||||
-- S/N 정보 (CONTRACT_ITEM_SERIAL에서만 조회, 품목별로 정확하게 매칭)
|
||||
(SELECT STRING_AGG(CIS.SERIAL_NO, ',' ORDER BY CIS.SEQ)
|
||||
FROM CONTRACT_ITEM CI
|
||||
LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID
|
||||
WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID
|
||||
AND CI.PART_OBJID = T.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE'
|
||||
AND UPPER(CIS.STATUS) = 'ACTIVE'
|
||||
AND CIS.SERIAL_NO IS NOT NULL
|
||||
) AS SERIAL_NO,
|
||||
-- S/N: 해당 품목의 시리얼 번호 (여러 개일 경우 "S/N 외 N건" 형식)
|
||||
(SELECT
|
||||
CASE
|
||||
WHEN COUNT(*) = 0 THEN ''
|
||||
WHEN COUNT(*) = 1 THEN MIN(CIS.SERIAL_NO)
|
||||
ELSE MIN(CIS.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건'
|
||||
END
|
||||
FROM CONTRACT_ITEM CI
|
||||
LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND UPPER(CIS.STATUS) = 'ACTIVE'
|
||||
WHERE CI.CONTRACT_OBJID = T.CONTRACT_OBJID
|
||||
AND CI.PART_OBJID = T.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE'
|
||||
AND CIS.SERIAL_NO IS NOT NULL) AS SERIAL_NO,
|
||||
COALESCE(T.QUANTITY::numeric, 0) AS ORDER_QUANTITY,
|
||||
-- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT, 없으면 CONTRACT_MGMT
|
||||
COALESCE(
|
||||
|
||||
@@ -1551,9 +1551,9 @@ public class ContractMgmtService {
|
||||
String customerName = CommonUtils.checkNull(contractInfo.get("customer_name"));
|
||||
String subject = "[" + customerName + "] " + contractNo + " 견적서 [OBJID:" + objId + "]";
|
||||
|
||||
// 5. 메일 내용 생성
|
||||
// 5. 메일 내용 생성 (간단한 텍스트 버전)
|
||||
String contents = makeEstimateMailContents(contractInfo, estimateTemplate, estimateItems);
|
||||
|
||||
|
||||
// 6. 수신자 정보 설정
|
||||
ArrayList<String> toEmailList = new ArrayList<String>();
|
||||
String customerEmail = CommonUtils.checkNull(contractInfo.get("customer_email"));
|
||||
@@ -1571,6 +1571,26 @@ public class ContractMgmtService {
|
||||
if(!"".equals(writerEmail)){
|
||||
ccEmailList.add(writerEmail);
|
||||
}
|
||||
|
||||
// 7-1. PDF 파일 처리 (세션 ID로 업로드된 경우)
|
||||
ArrayList<HashMap> attachFileList = new ArrayList<HashMap>();
|
||||
String pdfSessionId = CommonUtils.checkNull(paramMap.get("pdfSessionId"));
|
||||
|
||||
if(!"".equals(pdfSessionId)) {
|
||||
// 세션 ID로 저장된 PDF 파일 가져오기
|
||||
File pdfFile = getPdfFromSession(pdfSessionId, estimateTemplate);
|
||||
if(pdfFile != null && pdfFile.exists()) {
|
||||
HashMap<String, String> fileMap = new HashMap<String, String>();
|
||||
fileMap.put(Constants.Db.COL_FILE_REAL_NAME, pdfFile.getName());
|
||||
fileMap.put(Constants.Db.COL_FILE_SAVED_NAME, pdfFile.getName());
|
||||
fileMap.put(Constants.Db.COL_FILE_PATH, pdfFile.getParent());
|
||||
attachFileList.add(fileMap);
|
||||
|
||||
System.out.println("PDF 파일 첨부 완료: " + pdfFile.getAbsolutePath());
|
||||
} else {
|
||||
System.out.println("PDF 파일을 찾을 수 없습니다: " + pdfSessionId);
|
||||
}
|
||||
}
|
||||
|
||||
// 8. 메일 발송
|
||||
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
|
||||
@@ -1581,11 +1601,13 @@ public class ContractMgmtService {
|
||||
System.out.println("To Email: " + toEmailList);
|
||||
System.out.println("CC Email: " + ccEmailList);
|
||||
System.out.println("Subject: " + subject);
|
||||
System.out.println("첨부파일 개수: " + attachFileList.size());
|
||||
System.out.println("========================");
|
||||
|
||||
boolean mailSent = false;
|
||||
try {
|
||||
mailSent = MailUtil.sendMailWithAttachFile(
|
||||
// UTF-8 인코딩 메서드 사용 (견적서는 한글 내용이 많음)
|
||||
mailSent = MailUtil.sendMailWithAttachFileUTF8(
|
||||
fromUserId,
|
||||
null, // fromEmail (자동으로 SMTP 설정 사용)
|
||||
new ArrayList<String>(), // toUserIdList (빈 리스트)
|
||||
@@ -1595,7 +1617,7 @@ public class ContractMgmtService {
|
||||
null, // important
|
||||
subject,
|
||||
contents,
|
||||
null, // attachFileList (TODO: PDF 첨부 구현)
|
||||
attachFileList.size() > 0 ? attachFileList : null, // PDF 첨부
|
||||
"CONTRACT_ESTIMATE"
|
||||
);
|
||||
|
||||
@@ -1643,25 +1665,27 @@ private String makeEstimateMailContents(Map contractInfo, Map estimateTemplate,
|
||||
contents.append("<meta charset='UTF-8'>");
|
||||
contents.append("<style>");
|
||||
contents.append("body { font-family: 'Malgun Gothic', '맑은 고딕', Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }");
|
||||
contents.append(".estimate-container { max-width: 1200px; background: white; margin: 0 auto; padding: 40px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }");
|
||||
contents.append(".title { text-align: center; font-size: 24pt; font-weight: bold; letter-spacing: 10px; margin-bottom: 40px; border-bottom: 3px solid #333; padding-bottom: 20px; }");
|
||||
contents.append(".estimate-container { width: 210mm; background: white; margin: 0 auto; padding: 20mm; box-shadow: 0 0 10px rgba(0,0,0,0.1); box-sizing: border-box; }");
|
||||
contents.append(".title { text-align: center; font-size: 28pt; font-weight: bold; letter-spacing: 20px; margin-bottom: 40px; padding: 10px 0; }");
|
||||
contents.append(".header-section { width: 100%; margin-bottom: 30px; overflow: hidden; }");
|
||||
contents.append(".header-left { float: left; width: 48%; }");
|
||||
contents.append(".header-right { float: right; width: 48%; }");
|
||||
contents.append(".info-table { width: 100%; border-collapse: collapse; }");
|
||||
contents.append(".info-table td { padding: 10px; border: 1px solid #000; font-size: 10pt; vertical-align: middle; }");
|
||||
contents.append(".info-table .label { background-color: #f0f0f0; font-weight: bold; width: 120px; text-align: center; }");
|
||||
contents.append(".company-info { border: 2px solid #000; padding: 20px; text-align: center; min-height: 200px; box-sizing: border-box; }");
|
||||
contents.append(".company-stamp-img { max-width: 100%; height: auto; margin: 0 auto 15px; display: block; }");
|
||||
contents.append(".manager-info { margin-top: 15px; padding-top: 15px; border-top: 1px solid #ddd; text-align: left; font-size: 9pt; line-height: 1.8; }");
|
||||
contents.append(".items-table { width: 100%; border-collapse: collapse; margin: 20px 0; clear: both; }");
|
||||
contents.append(".items-table th, .items-table td { border: 1px solid #000; padding: 8px; font-size: 9pt; }");
|
||||
contents.append(".items-table th { background-color: #e0e0e0; font-weight: bold; text-align: center; }");
|
||||
contents.append(".header-right { float: right; width: 48%; text-align: right; }");
|
||||
contents.append(".info-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }");
|
||||
contents.append(".info-table td { padding: 5px 8px; border: 1px solid #000; font-size: 9pt; }");
|
||||
contents.append(".info-table .label { background-color: #f0f0f0; font-weight: bold; width: 80px; text-align: center; }");
|
||||
contents.append(".company-info { display: inline-block; text-align: right; }");
|
||||
contents.append(".company-stamp { width: 120px; height: 120px; border: 2px solid #e74c3c; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 10px; position: relative; }");
|
||||
contents.append(".company-stamp-text { writing-mode: vertical-rl; font-size: 16pt; font-weight: bold; color: #e74c3c; letter-spacing: 3px; }");
|
||||
contents.append(".company-details { font-size: 9pt; line-height: 1.6; margin-top: 10px; }");
|
||||
contents.append(".greeting-section { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 10px; padding: 0px 5px; }");
|
||||
contents.append(".greeting-left { line-height: 1.6; font-size: 10pt; }");
|
||||
contents.append(".greeting-right { text-align: right; font-size: 9pt; line-height: 1.8; }");
|
||||
contents.append(".items-table { width: 100%; border-collapse: collapse; margin-bottom: 30px; }");
|
||||
contents.append(".items-table th, .items-table td { border: 1px solid #000; padding: 3px 5px; text-align: center; font-size: 9pt; line-height: 1.3; }");
|
||||
contents.append(".items-table th { background-color: #f0f0f0; font-weight: bold; }");
|
||||
contents.append(".text-left { text-align: left; }");
|
||||
contents.append(".text-right { text-align: right; }");
|
||||
contents.append(".text-center { text-align: center; }");
|
||||
contents.append(".note-section { margin-top: 30px; padding: 20px; background-color: #f9f9f9; border: 1px solid #ddd; clear: both; }");
|
||||
contents.append(".note-section h4 { margin-top: 0; font-size: 11pt; }");
|
||||
contents.append(".note-section div { margin: 10px 0; font-size: 9pt; line-height: 1.6; }");
|
||||
contents.append("</style>");
|
||||
contents.append("</head>");
|
||||
contents.append("<body>");
|
||||
@@ -1671,7 +1695,11 @@ private String makeEstimateMailContents(Map contractInfo, Map estimateTemplate,
|
||||
contents.append("<div class='title'>견 적 서</div>");
|
||||
|
||||
// estimateTemplate 키는 대문자/소문자 모두 체크
|
||||
String recipient = CommonUtils.checkNull(estimateTemplate.get("recipient"));
|
||||
// 수신처는 contractInfo에서 고객사명 가져오기
|
||||
String recipient = CommonUtils.checkNull(contractInfo.get("customer_name"));
|
||||
if("".equals(recipient)) recipient = CommonUtils.checkNull(contractInfo.get("CUSTOMER_NAME"));
|
||||
// 없으면 estimateTemplate에서 가져오기
|
||||
if("".equals(recipient)) recipient = CommonUtils.checkNull(estimateTemplate.get("recipient"));
|
||||
if("".equals(recipient)) recipient = CommonUtils.checkNull(estimateTemplate.get("RECIPIENT"));
|
||||
|
||||
String contactPerson = CommonUtils.checkNull(estimateTemplate.get("contact_person"));
|
||||
@@ -1680,7 +1708,9 @@ private String makeEstimateMailContents(Map contractInfo, Map estimateTemplate,
|
||||
String estimateNo = CommonUtils.checkNull(estimateTemplate.get("estimate_no"));
|
||||
if("".equals(estimateNo)) estimateNo = CommonUtils.checkNull(estimateTemplate.get("ESTIMATE_NO"));
|
||||
|
||||
String executorDate = CommonUtils.checkNull(estimateTemplate.get("executor_date"));
|
||||
String executorDate = CommonUtils.checkNull(estimateTemplate.get("executor"));
|
||||
if("".equals(executorDate)) executorDate = CommonUtils.checkNull(estimateTemplate.get("EXECUTOR"));
|
||||
if("".equals(executorDate)) executorDate = CommonUtils.checkNull(estimateTemplate.get("executor_date"));
|
||||
if("".equals(executorDate)) executorDate = CommonUtils.checkNull(estimateTemplate.get("EXECUTOR_DATE"));
|
||||
|
||||
// 담당자 정보
|
||||
@@ -1697,10 +1727,10 @@ private String makeEstimateMailContents(Map contractInfo, Map estimateTemplate,
|
||||
// 왼쪽: 기본 정보 테이블
|
||||
contents.append("<div class='header-left'>");
|
||||
contents.append("<table class='info-table'>");
|
||||
contents.append("<tr><td class='label'>시행일자</td><td>" + executorDate + "</td></tr>");
|
||||
contents.append("<tr><td class='label'>수신처</td><td>" + recipient + "</td></tr>");
|
||||
contents.append("<tr><td class='label'>수신인</td><td>" + contactPerson + "</td></tr>");
|
||||
contents.append("<tr><td class='label'>견적번호</td><td>" + estimateNo + "</td></tr>");
|
||||
contents.append("<tr><td class='label'>영업번호</td><td>" + CommonUtils.checkNull(contractInfo.get("contract_no")) + "</td></tr>");
|
||||
contents.append("</table>");
|
||||
contents.append("</div>");
|
||||
|
||||
@@ -1725,49 +1755,54 @@ private String makeEstimateMailContents(Map contractInfo, Map estimateTemplate,
|
||||
String stampBase64 = !"".equals(companyStampPath) ? encodeImageToBase64(companyStampPath) : "";
|
||||
|
||||
if(!"".equals(stampBase64)) {
|
||||
// 이미지가 있으면 Base64로 인코딩된 이미지만 표시
|
||||
contents.append("<img src='" + stampBase64 + "' alt='회사 도장' class='company-stamp-img'>");
|
||||
// 이미지가 있으면 Base64로 인코딩된 이미지 표시
|
||||
contents.append("<img src='" + stampBase64 + "' alt='회사 도장' style='max-width: 150px; height: auto; margin: 0 auto 15px; display: block;'>");
|
||||
} else {
|
||||
// 이미지가 없으면 텍스트로 표시
|
||||
contents.append("<div style='width:120px; height:120px; margin:0 auto 15px; border:2px solid #cc0000; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; background:linear-gradient(135deg, #fff 0%, #f5f5f5 100%);'>");
|
||||
contents.append("<div style='text-align:center; font-size:11pt; font-weight:bold; color:#cc0000; line-height:1.3;'>");
|
||||
contents.append("(주)알피에스<br>대표이사<br>이 종 현");
|
||||
contents.append("</div>");
|
||||
contents.append("</div>");
|
||||
contents.append("<div style='font-size:10pt; margin-bottom:5px;'>RPS CO., LTD</div>");
|
||||
contents.append("<div style='font-size:9pt; color:#666; margin-bottom:10px;'>대 표 이 사 이 종 현</div>");
|
||||
contents.append("<div style='font-size:9pt; line-height:1.8;'>");
|
||||
contents.append("<div>대전광역시 유성구 국제과학10로 8</div>");
|
||||
contents.append("<div>TEL:(042)602-3300 FAX:(042)672-3399</div>");
|
||||
contents.append("<div class='company-stamp'>");
|
||||
contents.append("<div class='company-stamp-text'>(주)알피에스<br>대표이사<br>이 종 현</div>");
|
||||
contents.append("</div>");
|
||||
}
|
||||
|
||||
// 담당자 정보는 항상 표시
|
||||
contents.append("<div class='manager-info'>");
|
||||
contents.append("<div><strong>담당자 :</strong> " + managerName + "</div>");
|
||||
if(!"".equals(managerContact)) {
|
||||
contents.append("<div><strong>연락처 :</strong> " + managerContact + "</div>");
|
||||
}
|
||||
// 회사 주소 및 연락처
|
||||
contents.append("<div class='company-details'>");
|
||||
contents.append("대전광역시 유성구 국제과학10로 8<br>");
|
||||
contents.append("TEL:(042)602-3300 FAX:(042)672-3399");
|
||||
contents.append("</div>");
|
||||
|
||||
contents.append("</div>"); // company-info 닫기
|
||||
contents.append("</div>"); // header-right 닫기
|
||||
|
||||
contents.append("</div>"); // header-section 닫기
|
||||
|
||||
// 인사말 및 담당자 정보
|
||||
contents.append("<div class='greeting-section'>");
|
||||
contents.append("<div class='greeting-left'>");
|
||||
contents.append("견적을 요청해 주셔서 대단히 감사합니다.<br>");
|
||||
contents.append("하기와 같이 견적서를 제출합니다.");
|
||||
contents.append("</div>");
|
||||
contents.append("<div class='greeting-right'>");
|
||||
contents.append("담당자 : " + managerName + "<br>");
|
||||
if(!"".equals(managerContact)) {
|
||||
contents.append("연락처 : " + managerContact + "<br>");
|
||||
}
|
||||
contents.append("<span style='font-size: 10pt; margin-top: 5px; display: inline-block;'>부가세 별도</span>");
|
||||
contents.append("</div>");
|
||||
contents.append("</div>");
|
||||
|
||||
// 견적 품목
|
||||
if(estimateItems != null && !estimateItems.isEmpty()){
|
||||
contents.append("<table class='items-table'>");
|
||||
contents.append("<thead>");
|
||||
contents.append("<tr>");
|
||||
contents.append("<th style='width:5%;'>No.</th>");
|
||||
contents.append("<th style='width:20%;'>품명</th>");
|
||||
contents.append("<th style='width:22%;'>규격</th>");
|
||||
contents.append("<th style='width:7%;'>수량</th>");
|
||||
contents.append("<th style='width:8%;'>단위</th>");
|
||||
contents.append("<th style='width:11%;'>단가</th>");
|
||||
contents.append("<th style='width:11%;'>금액</th>");
|
||||
contents.append("<th style='width:16%;'>비고</th>");
|
||||
contents.append("<th style='width:6%;'>번호<br>NO.</th>");
|
||||
contents.append("<th style='width:20%;'>품 명<br>DESCRIPTION</th>");
|
||||
contents.append("<th style='width:22%;'>규 격<br>SPECIFICATION</th>");
|
||||
contents.append("<th style='width:7%;'>수량<br>Q'TY</th>");
|
||||
contents.append("<th style='width:8%;'>단위<br>UNIT</th>");
|
||||
contents.append("<th style='width:11%;'>단 가<br>UNIT<br>PRICE</th>");
|
||||
contents.append("<th style='width:11%;'>금 액<br>AMOUNT</th>");
|
||||
contents.append("<th style='width:15%;'>비고</th>");
|
||||
contents.append("</tr>");
|
||||
contents.append("</thead>");
|
||||
contents.append("<tbody>");
|
||||
@@ -1782,16 +1817,16 @@ private String makeEstimateMailContents(Map contractInfo, Map estimateTemplate,
|
||||
} catch(Exception e){}
|
||||
}
|
||||
|
||||
contents.append("<tr>");
|
||||
contents.append("<td class='text-center'>" + (i + 1) + "</td>");
|
||||
contents.append("<td>" + CommonUtils.checkNull(item.get("description")) + "</td>");
|
||||
contents.append("<td>" + CommonUtils.checkNull(item.get("specification")) + "</td>");
|
||||
contents.append("<td class='text-center'>" + CommonUtils.checkNull(item.get("quantity")) + "</td>");
|
||||
contents.append("<td class='text-center'>" + CommonUtils.checkNull(item.get("unit")) + "</td>");
|
||||
contents.append("<td class='text-right'>" + CommonUtils.checkNull(item.get("unit_price")) + "</td>");
|
||||
contents.append("<td class='text-right'>" + CommonUtils.checkNull(item.get("amount")) + "</td>");
|
||||
contents.append("<td>" + CommonUtils.checkNull(item.get("note")) + "</td>");
|
||||
contents.append("</tr>");
|
||||
contents.append("<tr>");
|
||||
contents.append("<td class='text-center'>" + (i + 1) + "</td>");
|
||||
contents.append("<td class='text-left'>" + CommonUtils.checkNull(item.get("description")) + "</td>");
|
||||
contents.append("<td class='text-left'>" + CommonUtils.checkNull(item.get("specification")) + "</td>");
|
||||
contents.append("<td class='text-center'>" + CommonUtils.checkNull(item.get("quantity")) + "</td>");
|
||||
contents.append("<td class='text-center'>" + CommonUtils.checkNull(item.get("unit")) + "</td>");
|
||||
contents.append("<td class='text-right'>" + CommonUtils.checkNull(item.get("unit_price")) + "</td>");
|
||||
contents.append("<td class='text-right'>" + CommonUtils.checkNull(item.get("amount")) + "</td>");
|
||||
contents.append("<td class='text-left'>" + CommonUtils.checkNull(item.get("note")) + "</td>");
|
||||
contents.append("</tr>");
|
||||
}
|
||||
|
||||
// 계 (총 합계) - 통화 기호 포함
|
||||
@@ -1826,58 +1861,60 @@ private String makeEstimateMailContents(Map contractInfo, Map estimateTemplate,
|
||||
|
||||
// 원화환산 공급가액 - 제거 (화면에서도 숨김 처리)
|
||||
|
||||
// 테이블 내 비고
|
||||
// 테이블 내 비고 (항상 표시)
|
||||
String noteRemarks = CommonUtils.checkNull(estimateTemplate.get("note_remarks"));
|
||||
if("".equals(noteRemarks)) noteRemarks = CommonUtils.checkNull(estimateTemplate.get("NOTE_REMARKS"));
|
||||
|
||||
if(!"".equals(noteRemarks)){
|
||||
contents.append("<tr>");
|
||||
contents.append("<td colspan='8' style='height:100px; vertical-align:top; padding:10px; text-align:left;'>");
|
||||
contents.append("<div style='font-weight:bold; margin-bottom:10px;'><비고></div>");
|
||||
contents.append("<div style='white-space:pre-wrap;'>" + noteRemarks + "</div>");
|
||||
contents.append("</td>");
|
||||
contents.append("</tr>");
|
||||
}
|
||||
System.out.println("===== 비고 내용 확인 =====");
|
||||
System.out.println("noteRemarks: [" + noteRemarks + "]");
|
||||
System.out.println("========================");
|
||||
|
||||
contents.append("<tr>");
|
||||
contents.append("<td colspan='8' style='height:100px; vertical-align:top; padding:10px; text-align:left;'>");
|
||||
contents.append("<div style='font-weight:bold; margin-bottom:10px;'><비고></div>");
|
||||
contents.append("<div style='white-space:pre-wrap;'>" + noteRemarks + "</div>");
|
||||
contents.append("</td>");
|
||||
contents.append("</tr>");
|
||||
|
||||
// 참조사항 (NOTE1~4)
|
||||
String note1 = CommonUtils.checkNull(estimateTemplate.get("note1"));
|
||||
if("".equals(note1)) note1 = CommonUtils.checkNull(estimateTemplate.get("NOTE1"));
|
||||
if("".equals(note1)) note1 = "1. 견적유효기간: 일";
|
||||
|
||||
String note2 = CommonUtils.checkNull(estimateTemplate.get("note2"));
|
||||
if("".equals(note2)) note2 = CommonUtils.checkNull(estimateTemplate.get("NOTE2"));
|
||||
if("".equals(note2)) note2 = "2. 납품기간: 발주 후 1주 이내";
|
||||
|
||||
String note3 = CommonUtils.checkNull(estimateTemplate.get("note3"));
|
||||
if("".equals(note3)) note3 = CommonUtils.checkNull(estimateTemplate.get("NOTE3"));
|
||||
if("".equals(note3)) note3 = "3. VAT 별도";
|
||||
|
||||
String note4 = CommonUtils.checkNull(estimateTemplate.get("note4"));
|
||||
if("".equals(note4)) note4 = CommonUtils.checkNull(estimateTemplate.get("NOTE4"));
|
||||
if("".equals(note4)) note4 = "4. 결제 조건 : 기존 결제조건에 따름.";
|
||||
|
||||
// 참조사항 행 추가
|
||||
contents.append("<tr>");
|
||||
contents.append("<td colspan='8' style='vertical-align: top; padding: 10px; text-align: left;'>");
|
||||
contents.append("<div style='font-weight: bold; margin-bottom: 10px;'><참조사항></div>");
|
||||
contents.append("<div style='margin-bottom: 5px;'>" + note1 + "</div>");
|
||||
contents.append("<div style='margin-bottom: 5px;'>" + note2 + "</div>");
|
||||
contents.append("<div style='margin-bottom: 5px;'>" + note3 + "</div>");
|
||||
contents.append("<div style='margin-bottom: 5px;'>" + note4 + "</div>");
|
||||
contents.append("</td>");
|
||||
contents.append("</tr>");
|
||||
|
||||
// 하단 회사명 행 추가
|
||||
contents.append("<tr>");
|
||||
contents.append("<td colspan='8' style='text-align: right; padding: 15px; font-size: 10pt; font-weight: bold; border: none;'>");
|
||||
contents.append("㈜알피에스");
|
||||
contents.append("</td>");
|
||||
contents.append("</tr>");
|
||||
|
||||
contents.append("</tbody>");
|
||||
contents.append("</table>");
|
||||
}
|
||||
|
||||
// 참조사항 (NOTE1~4)
|
||||
String note1 = CommonUtils.checkNull(estimateTemplate.get("note1"));
|
||||
if("".equals(note1)) note1 = CommonUtils.checkNull(estimateTemplate.get("NOTE1"));
|
||||
|
||||
String note2 = CommonUtils.checkNull(estimateTemplate.get("note2"));
|
||||
if("".equals(note2)) note2 = CommonUtils.checkNull(estimateTemplate.get("NOTE2"));
|
||||
|
||||
String note3 = CommonUtils.checkNull(estimateTemplate.get("note3"));
|
||||
if("".equals(note3)) note3 = CommonUtils.checkNull(estimateTemplate.get("NOTE3"));
|
||||
|
||||
String note4 = CommonUtils.checkNull(estimateTemplate.get("note4"));
|
||||
if("".equals(note4)) note4 = CommonUtils.checkNull(estimateTemplate.get("NOTE4"));
|
||||
|
||||
if(!"".equals(note1) || !"".equals(note2) || !"".equals(note3) || !"".equals(note4)){
|
||||
contents.append("<div class='note-section'>");
|
||||
contents.append("<h4>※ 참조사항</h4>");
|
||||
int noteNum = 1;
|
||||
if(!"".equals(note1)) {
|
||||
contents.append("<div>" + noteNum + ". " + note1 + "</div>");
|
||||
noteNum++;
|
||||
}
|
||||
if(!"".equals(note2)) {
|
||||
contents.append("<div>" + noteNum + ". " + note2 + "</div>");
|
||||
noteNum++;
|
||||
}
|
||||
if(!"".equals(note3)) {
|
||||
contents.append("<div>" + noteNum + ". " + note3 + "</div>");
|
||||
noteNum++;
|
||||
}
|
||||
if(!"".equals(note4)) {
|
||||
contents.append("<div>" + noteNum + ". " + note4 + "</div>");
|
||||
}
|
||||
contents.append("</div>");
|
||||
}
|
||||
|
||||
contents.append("</div>"); // estimate-container 닫기
|
||||
contents.append("</body>");
|
||||
contents.append("</html>");
|
||||
@@ -2370,6 +2407,7 @@ private String encodeImageToBase64(String imagePath) {
|
||||
return partList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 품목 목록 조회
|
||||
* @param contractObjId 견적 OBJID
|
||||
@@ -2411,4 +2449,143 @@ private String encodeImageToBase64(String imagePath) {
|
||||
|
||||
return itemList;
|
||||
}
|
||||
|
||||
/**
|
||||
* PDF 청크 업로드 처리
|
||||
* @param request
|
||||
* @param paramMap - sessionId, chunkIndex, totalChunks, chunk
|
||||
* @return
|
||||
*/
|
||||
public Map uploadPdfChunk(HttpServletRequest request, Map<String, Object> paramMap) {
|
||||
Map resultMap = new HashMap();
|
||||
|
||||
try {
|
||||
System.out.println("===== uploadPdfChunk 호출 =====");
|
||||
System.out.println("전달된 파라미터: " + paramMap);
|
||||
System.out.println("==============================");
|
||||
|
||||
String sessionId = CommonUtils.checkNull(paramMap.get("sessionId"));
|
||||
String chunkIndexStr = CommonUtils.checkNull(paramMap.get("chunkIndex"));
|
||||
String totalChunksStr = CommonUtils.checkNull(paramMap.get("totalChunks"));
|
||||
String chunk = CommonUtils.checkNull(paramMap.get("chunk"));
|
||||
|
||||
if("".equals(sessionId) || "".equals(chunkIndexStr) || "".equals(totalChunksStr) || "".equals(chunk)) {
|
||||
System.out.println("필수 파라미터 누락");
|
||||
resultMap.put("result", "error");
|
||||
resultMap.put("message", "필수 파라미터가 누락되었습니다.");
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
int chunkIndex = Integer.parseInt(chunkIndexStr);
|
||||
int totalChunks = Integer.parseInt(totalChunksStr);
|
||||
|
||||
System.out.println("청크 업로드: " + sessionId + " [" + (chunkIndex + 1) + "/" + totalChunks + "]");
|
||||
System.out.println("청크 크기: " + chunk.length() + " bytes");
|
||||
|
||||
// 임시 디렉토리에 청크 저장
|
||||
String tempDir = System.getProperty("java.io.tmpdir");
|
||||
String chunkDir = tempDir + File.separator + "pdf_chunks" + File.separator + sessionId;
|
||||
File chunkDirFile = new File(chunkDir);
|
||||
if(!chunkDirFile.exists()) {
|
||||
chunkDirFile.mkdirs();
|
||||
}
|
||||
|
||||
// 청크 파일 저장
|
||||
String chunkFileName = "chunk_" + chunkIndex + ".txt";
|
||||
File chunkFile = new File(chunkDir + File.separator + chunkFileName);
|
||||
java.io.FileWriter fw = new java.io.FileWriter(chunkFile);
|
||||
fw.write(chunk);
|
||||
fw.close();
|
||||
|
||||
// 마지막 청크인 경우 모든 청크를 합쳐서 PDF 파일 생성
|
||||
if(chunkIndex == totalChunks - 1) {
|
||||
System.out.println("모든 청크 수신 완료, PDF 파일 생성 시작");
|
||||
|
||||
// 모든 청크 읽어서 합치기
|
||||
StringBuilder fullBase64 = new StringBuilder();
|
||||
for(int i = 0; i < totalChunks; i++) {
|
||||
File cf = new File(chunkDir + File.separator + "chunk_" + i + ".txt");
|
||||
java.io.BufferedReader br = new java.io.BufferedReader(new java.io.FileReader(cf));
|
||||
String line;
|
||||
while((line = br.readLine()) != null) {
|
||||
fullBase64.append(line);
|
||||
}
|
||||
br.close();
|
||||
}
|
||||
|
||||
// Base64 디코딩하여 PDF 파일 생성 (Apache Commons Codec 사용)
|
||||
byte[] pdfBytes = org.apache.commons.codec.binary.Base64.decodeBase64(fullBase64.toString());
|
||||
|
||||
String pdfFileName = sessionId + ".pdf";
|
||||
File pdfFile = new File(tempDir + File.separator + pdfFileName);
|
||||
java.io.FileOutputStream fos = new java.io.FileOutputStream(pdfFile);
|
||||
fos.write(pdfBytes);
|
||||
fos.close();
|
||||
|
||||
pdfFile.deleteOnExit();
|
||||
|
||||
System.out.println("PDF 파일 생성 완료: " + pdfFile.getAbsolutePath());
|
||||
|
||||
// 청크 파일들 삭제
|
||||
for(int i = 0; i < totalChunks; i++) {
|
||||
File cf = new File(chunkDir + File.separator + "chunk_" + i + ".txt");
|
||||
cf.delete();
|
||||
}
|
||||
chunkDirFile.delete();
|
||||
}
|
||||
|
||||
resultMap.put("result", "success");
|
||||
|
||||
} catch(Exception e) {
|
||||
System.out.println("청크 업로드 중 오류: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
resultMap.put("result", "error");
|
||||
resultMap.put("message", e.getMessage());
|
||||
}
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 세션 ID로 저장된 PDF 파일 가져오기
|
||||
* @param sessionId
|
||||
* @param estimateTemplate
|
||||
* @return
|
||||
*/
|
||||
private File getPdfFromSession(String sessionId, Map estimateTemplate) {
|
||||
try {
|
||||
String tempDir = System.getProperty("java.io.tmpdir");
|
||||
File pdfFile = new File(tempDir + File.separator + sessionId + ".pdf");
|
||||
|
||||
if(pdfFile.exists()) {
|
||||
// 견적번호로 파일명 변경
|
||||
String estimateNo = CommonUtils.checkNull(estimateTemplate.get("estimate_no"));
|
||||
if("".equals(estimateNo)) estimateNo = CommonUtils.checkNull(estimateTemplate.get("ESTIMATE_NO"));
|
||||
if("".equals(estimateNo)) estimateNo = "견적서";
|
||||
|
||||
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyyMMddHHmmss");
|
||||
String timestamp = sdf.format(new java.util.Date());
|
||||
String newFileName = estimateNo + "_" + timestamp + ".pdf";
|
||||
File renamedFile = new File(tempDir + File.separator + newFileName);
|
||||
|
||||
// 파일 복사
|
||||
java.nio.file.Files.copy(pdfFile.toPath(), renamedFile.toPath(),
|
||||
java.nio.file.StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
renamedFile.deleteOnExit();
|
||||
|
||||
// 원본 파일 삭제
|
||||
pdfFile.delete();
|
||||
|
||||
return renamedFile;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
} catch(Exception e) {
|
||||
System.out.println("PDF 파일 가져오기 중 오류: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,24 +341,33 @@ public class CommonService extends BaseService {
|
||||
* @param fileMap
|
||||
*/
|
||||
public void setFileDownloadLog(HttpServletRequest request, Map fileMap){
|
||||
HttpSession session = request.getSession();
|
||||
PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
|
||||
|
||||
String objId = CommonUtils.createObjId();
|
||||
String systemName = Constants.SYSTEM_NAME;
|
||||
String userId = CommonUtils.checkNull(person.getUserId());
|
||||
String fileObjId = CommonUtils.checkNull(fileMap.get("OBJID"));
|
||||
String remoteAddr = CommonUtils.checkNull(request.getRemoteAddr());
|
||||
|
||||
Map paramMap = new HashMap();
|
||||
paramMap.put("objId", objId);
|
||||
paramMap.put("systemName", systemName);
|
||||
paramMap.put("userId", userId);
|
||||
paramMap.put("fileObjId", fileObjId);
|
||||
paramMap.put("remoteAddr", remoteAddr);
|
||||
|
||||
SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession();
|
||||
sqlSession.insert("common.insertFileDownloadLog", paramMap);
|
||||
SqlSession sqlSession = null;
|
||||
try{
|
||||
HttpSession session = request.getSession();
|
||||
PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
|
||||
|
||||
String objId = CommonUtils.createObjId();
|
||||
String systemName = Constants.SYSTEM_NAME;
|
||||
String userId = CommonUtils.checkNull(person.getUserId());
|
||||
String fileObjId = CommonUtils.checkNull(fileMap.get("OBJID"));
|
||||
String remoteAddr = CommonUtils.checkNull(request.getRemoteAddr());
|
||||
|
||||
Map paramMap = new HashMap();
|
||||
paramMap.put("objId", objId);
|
||||
paramMap.put("systemName", systemName);
|
||||
paramMap.put("userId", userId);
|
||||
paramMap.put("fileObjId", fileObjId);
|
||||
paramMap.put("remoteAddr", remoteAddr);
|
||||
|
||||
sqlSession = SqlMapConfig.getInstance().getSqlSession();
|
||||
sqlSession.insert("common.insertFileDownloadLog", paramMap);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}finally{
|
||||
if(sqlSession != null){
|
||||
sqlSession.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -692,4 +692,61 @@ public class ProductionPlanningService {
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* E-BOM을 PROJECT_MGMT에 할당
|
||||
* @param projectMgmtObjid
|
||||
* @param bomReportObjid
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public int assignEbomToProject(String projectMgmtObjid, String bomReportObjid) throws Exception {
|
||||
SqlSession sqlSession = null;
|
||||
try {
|
||||
sqlSession = SqlMapConfig.getInstance().getSqlSession();
|
||||
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("projectMgmtObjid", projectMgmtObjid);
|
||||
paramMap.put("bomReportObjid", bomReportObjid);
|
||||
|
||||
int result = sqlSession.update("productionplanning.assignEbomToProject", paramMap);
|
||||
sqlSession.commit();
|
||||
|
||||
return result;
|
||||
} catch(Exception e) {
|
||||
if(sqlSession != null) {
|
||||
sqlSession.rollback();
|
||||
}
|
||||
throw e;
|
||||
} finally {
|
||||
if(sqlSession != null) {
|
||||
sqlSession.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* E-BOM 정보 조회
|
||||
* @param bomReportObjid
|
||||
* @return
|
||||
*/
|
||||
public Map<String, Object> getEbomInfo(String bomReportObjid) {
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
SqlSession sqlSession = null;
|
||||
|
||||
try {
|
||||
sqlSession = SqlMapConfig.getInstance().getSqlSession();
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("objid", bomReportObjid);
|
||||
resultMap = sqlSession.selectOne("productionplanning.getEbomInfo", paramMap);
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if(sqlSession != null) {
|
||||
sqlSession.close();
|
||||
}
|
||||
}
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,24 @@
|
||||
|
||||
<!-- PLM DataSource JNDI Resource -->
|
||||
<!-- Using environment variables for database connection -->
|
||||
<!--
|
||||
개선된 Connection Pool 설정 (커넥션 누수 방지):
|
||||
- maxTotal: 최대 커넥션 수 (기본 200 유지)
|
||||
- maxIdle: 유휴 커넥션 수 (기본 50 유지)
|
||||
- maxWaitMillis: 커넥션 획득 대기 시간 (무한 대기 -> 10초로 제한)
|
||||
- testOnBorrow: 커넥션 대여 시 유효성 검사
|
||||
- validationQuery: 유효성 검사 쿼리
|
||||
- removeAbandonedOnBorrow: 누수된 커넥션 자동 회수 활성화
|
||||
- removeAbandonedTimeout: 60초 이상 사용 중인 커넥션 회수
|
||||
- logAbandoned: 누수된 커넥션 로그 기록 (모니터링용)
|
||||
-->
|
||||
<Resource name="plm" auth="Container"
|
||||
type="javax.sql.DataSource" driverClassName="org.postgresql.Driver"
|
||||
url="${DB_URL}"
|
||||
username="${DB_USERNAME}" password="${DB_PASSWORD}"
|
||||
maxTotal="200" maxIdle="50" maxWaitMillis="-1"/>
|
||||
maxTotal="200" maxIdle="50" maxWaitMillis="10000"
|
||||
testOnBorrow="true" validationQuery="SELECT 1"
|
||||
removeAbandonedOnBorrow="true" removeAbandonedTimeout="60"
|
||||
logAbandoned="true"/>
|
||||
|
||||
</Context>
|
||||
@@ -15,11 +15,15 @@
|
||||
|
||||
<Service name="Catalina">
|
||||
<!-- URIEncoding을 UTF-8로 설정하여 한글 파라미터 처리 -->
|
||||
<!-- maxPostSize: POST 요청 최대 크기 (청크 업로드를 위해 2MB로 설정) -->
|
||||
<!-- maxHttpHeaderSize: HTTP 헤더 최대 크기 -->
|
||||
<Connector port="8080"
|
||||
protocol="HTTP/1.1"
|
||||
connectionTimeout="20000"
|
||||
connectionTimeout="60000"
|
||||
redirectPort="8443"
|
||||
URIEncoding="UTF-8" />
|
||||
URIEncoding="UTF-8"
|
||||
maxPostSize="2097152"
|
||||
maxHttpHeaderSize="65536" />
|
||||
|
||||
<Engine name="Catalina" defaultHost="localhost">
|
||||
<Realm className="org.apache.catalina.realm.LockOutRealm">
|
||||
|
||||
Reference in New Issue
Block a user