nr 구매 제한 기능 추가

- 트리거를 이용하여 기존 프로그램 사용자도 오류가 발생하도록 함
This commit is contained in:
backuppc
2025-12-12 11:06:13 +09:00
parent 77f1ddab80
commit 890e6edab4
20 changed files with 1787 additions and 216 deletions

View File

@@ -367,6 +367,7 @@
<Compile Include="MessageWindow.cs" />
<Compile Include="MethodExtentions.cs" />
<Compile Include="Web\MachineBridge\MachineBridge.cs" />
<Compile Include="Web\MachineBridge\MachineBridge.HolidayRequest.cs" />
<Compile Include="Web\MachineBridge\MachineBridge.Login.cs" />
<Compile Include="Web\MachineBridge\MachineBridge.Dashboard.cs" />
<Compile Include="Web\MachineBridge\MachineBridge.Todo.cs" />

View File

@@ -9,7 +9,7 @@
<ErrorReportUrlHistory />
<FallbackCulture>ko-KR</FallbackCulture>
<VerifyUploadedFiles>false</VerifyUploadedFiles>
<ProjectView>ShowAllFiles</ProjectView>
<ProjectView>ProjectFiles</ProjectView>
</PropertyGroup>
<PropertyGroup>
<EnableSecurityDebugging>false</EnableSecurityDebugging>

View File

@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호가 자동으로
// 지정되도록 할 수 있습니다.
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("25.11.11.1030")]
[assembly: AssemblyFileVersion("25.11.11.1030")]
[assembly: AssemblyVersion("25.12.12.1050")]
[assembly: AssemblyFileVersion("25.12.12.1050")]

View File

@@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using Newtonsoft.Json;
using FCOMMON;
namespace Project.Web
{
public partial class MachineBridge
{
#region HolidayRequest API (/ )
/// <summary>
/// 휴가/외출 신청 목록 조회
/// </summary>
public string HolidayRequest_GetList(string startDate, string endDate, string userId, int userLevel)
{
try
{
// 권한에 따른 uid 필터링
// userLevel < 5: 본인만 조회
// userLevel >= 5: userId가 '%'이면 전체, 특정 uid면 해당 사용자만
var uidFilter = userLevel < 5 ? info.Login.no : (string.IsNullOrEmpty(userId) || userId == "%" ? "%" : userId);
var sql = @"
SELECT
hr.idx, hr.gcode, hr.uid, hr.cate, hr.sdate, hr.edate, hr.Remark, hr.wuid, hr.wdate,
u.dept, u.name, u.grade, u.tel, u.processs,
hr.Response, hr.conf, hr.HolyReason, hr.HolyBackup, hr.HolyLocation,
hr.HolyDays, hr.HolyTimes, hr.sendmail, hr.stime, hr.etime, hr.conf_id, hr.conf_time
FROM EETGW_HolydayRequest hr WITH (nolock)
LEFT OUTER JOIN vGroupUser u ON hr.uid = u.id AND hr.gcode = u.gcode
WHERE hr.gcode = @gcode
AND hr.sdate >= @startDate
AND hr.sdate <= @endDate
AND hr.uid LIKE @uid
ORDER BY hr.conf, hr.sdate DESC";
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@startDate", startDate);
cmd.Parameters.AddWithValue("@endDate", endDate);
cmd.Parameters.AddWithValue("@uid", uidFilter);
using (var da = new SqlDataAdapter(cmd))
{
var dt = new DataTable();
da.Fill(dt);
// 승인/미승인 합계 계산
decimal sumApprovedDays = 0;
decimal sumApprovedTimes = 0;
decimal sumPendingDays = 0;
decimal sumPendingTimes = 0;
foreach (DataRow row in dt.Rows)
{
var conf = Convert.ToInt32(row["conf"]);
var days = row["HolyDays"] != DBNull.Value ? Convert.ToDecimal(row["HolyDays"]) : 0;
var times = row["HolyTimes"] != DBNull.Value ? Convert.ToDecimal(row["HolyTimes"]) : 0;
if (conf == 1)
{
sumApprovedDays += days;
sumApprovedTimes += times;
}
else
{
sumPendingDays += days;
sumPendingTimes += times;
}
}
return JsonConvert.SerializeObject(new
{
Success = true,
Data = dt,
Summary = new
{
ApprovedDays = sumApprovedDays,
ApprovedTimes = sumApprovedTimes,
PendingDays = sumPendingDays,
PendingTimes = sumPendingTimes
}
});
}
}
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
/// <summary>
/// 휴가/외출 신청 저장
/// </summary>
public string HolidayRequest_Save(int idx, string uid, string cate, string sdate, string edate,
string remark, string response, int conf, string holyReason, string holyBackup,
string holyLocation, decimal holyDays, decimal holyTimes, string stime, string etime)
{
try
{
var cs = Properties.Settings.Default.gwcs;
using (var cn = new SqlConnection(cs))
{
cn.Open();
var sql = "";
if (idx == 0) // INSERT
{
sql = @"INSERT INTO EETGW_HolydayRequest
(gcode, uid, cate, sdate, edate, conf, Remark, wuid, wdate, Response,
HolyReason, HolyBackup, HolyLocation, HolyDays, HolyTimes, stime, etime)
VALUES
(@gcode, @uid, @cate, @sdate, @edate, @conf, @remark, @wuid, GETDATE(), @response,
@holyReason, @holyBackup, @holyLocation, @holyDays, @holyTimes, @stime, @etime)";
}
else // UPDATE
{
sql = @"UPDATE EETGW_HolydayRequest
SET uid = @uid, cate = @cate, sdate = @sdate, edate = @edate, conf = @conf,
Remark = @remark, Response = @response, HolyReason = @holyReason,
HolyBackup = @holyBackup, HolyLocation = @holyLocation,
HolyDays = @holyDays, HolyTimes = @holyTimes, stime = @stime, etime = @etime
WHERE idx = @idx AND gcode = @gcode";
}
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.AddWithValue("@idx", idx);
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
cmd.Parameters.AddWithValue("@uid", uid);
cmd.Parameters.AddWithValue("@cate", cate);
cmd.Parameters.AddWithValue("@sdate", sdate);
cmd.Parameters.AddWithValue("@edate", edate);
cmd.Parameters.AddWithValue("@conf", conf);
cmd.Parameters.AddWithValue("@remark", remark ?? "");
cmd.Parameters.AddWithValue("@wuid", info.Login.no); // 작성자
cmd.Parameters.AddWithValue("@response", response ?? "");
cmd.Parameters.AddWithValue("@holyReason", holyReason ?? "");
cmd.Parameters.AddWithValue("@holyBackup", holyBackup ?? "");
cmd.Parameters.AddWithValue("@holyLocation", holyLocation ?? "");
cmd.Parameters.AddWithValue("@holyDays", holyDays);
cmd.Parameters.AddWithValue("@holyTimes", holyTimes);
cmd.Parameters.AddWithValue("@stime", stime ?? "");
cmd.Parameters.AddWithValue("@etime", etime ?? "");
cmd.ExecuteNonQuery();
}
}
return JsonConvert.SerializeObject(new { Success = true, Message = "저장되었습니다." });
}
catch (Exception ex)
{
return JsonConvert.SerializeObject(new { Success = false, Message = ex.Message });
}
}
#endregion
}
}

View File

@@ -599,6 +599,7 @@ namespace Project.Web
}
break;
// ===== JobReport API (JobReport 뷰/테이블) =====
case "JOBREPORT_GET_LIST":
{
@@ -607,9 +608,10 @@ namespace Project.Web
string uid = json.uid ?? "";
string cate = json.cate ?? ""; // 사용안함 (호환성)
string searchKey = json.searchKey ?? "";
string requestId = json.requestId;
Console.WriteLine($"[WS] JOBREPORT_GET_LIST: sd={sd}, ed={ed}, uid={uid}, searchKey={searchKey}");
string result = _bridge.Jobreport_GetList(sd, ed, uid, cate, searchKey);
var response = new { type = "JOBREPORT_LIST_DATA", data = JsonConvert.DeserializeObject(result) };
var response = new { type = "JOBREPORT_LIST_DATA", data = JsonConvert.DeserializeObject(result), requestId = requestId };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
@@ -1199,6 +1201,43 @@ namespace Project.Web
}
break;
// ===== HolidayRequest API (휴가/외출 신청) =====
case "HOLIDAY_REQUEST_GET_LIST":
{
string startDate = json.startDate ?? "";
string endDate = json.endDate ?? "";
string userId = json.userId ?? "";
int userLevel = json.userLevel ?? 0;
string result = _bridge.HolidayRequest_GetList(startDate, endDate, userId, userLevel);
var response = new { type = "HOLIDAY_REQUEST_LIST_DATA", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
case "HOLIDAY_REQUEST_SAVE":
{
int idx = json.idx ?? 0;
string uid = json.uid ?? "";
string cate = json.cate ?? "";
string sdate = json.sdate ?? "";
string edate = json.edate ?? "";
string remark = json.remark ?? "";
string responseMsg = json.response ?? "";
int conf = json.conf ?? 0;
string holyReason = json.holyReason ?? "";
string holyBackup = json.holyBackup ?? "";
string holyLocation = json.holyLocation ?? "";
decimal holyDays = json.holyDays ?? 0;
decimal holyTimes = json.holyTimes ?? 0;
string stime = json.stime ?? "";
string etime = json.etime ?? "";
string result = _bridge.HolidayRequest_Save(idx, uid, cate, sdate, edate, remark, responseMsg, conf, holyReason, holyBackup, holyLocation, holyDays, holyTimes, stime, etime);
var response = new { type = "HOLIDAY_REQUEST_SAVED", data = JsonConvert.DeserializeObject(result) };
await Send(socket, JsonConvert.SerializeObject(response));
}
break;
// ===== MailForm API (메일양식) =====
case "MAILFORM_GET_LIST":
{

View File

@@ -8,6 +8,7 @@ import { MailList } from '@/pages/MailList';
import { Customs } from '@/pages/Customs';
import { LicenseList } from '@/components/license/LicenseList';
import { PartList } from '@/pages/PartList';
import HolidayRequest from '@/pages/HolidayRequest';
import { comms } from '@/communication';
import { UserInfo } from '@/types';
import { Loader2 } from 'lucide-react';
@@ -90,6 +91,7 @@ export default function App() {
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/todo" element={<Todo />} />
<Route path="/kuntae" element={<Kuntae />} />
<Route path="/holiday-request" element={<HolidayRequest />} />
<Route path="/jobreport" element={<Jobreport />} />
<Route path="/project" element={<Project />} />
<Route path="/common" element={<CommonCodePage />} />

View File

@@ -44,6 +44,7 @@ import type {
CustomItem,
LicenseItem,
PartListItem,
HolidayRequest,
} from '@/types';
// WebView2 환경 감지
@@ -137,21 +138,28 @@ class CommunicationLayer {
}, 2000);
}
const requestId = Date.now().toString() + Math.random().toString(36).substr(2, 9);
const timeoutId = setTimeout(() => {
this.listeners = this.listeners.filter(cb => cb !== handler);
reject(new Error(`${requestType} timeout`));
}, 10000);
const handler = (data: unknown) => {
const msg = data as { type: string; data?: T; Success?: boolean; Message?: string };
const msg = data as { type: string; data?: T; Success?: boolean; Message?: string; requestId?: string };
if (msg.type === responseType) {
// requestId가 있는 경우 일치 여부 확인 (백엔드가 지원하는 경우)
if (msg.requestId && msg.requestId !== requestId) {
return;
}
clearTimeout(timeoutId);
this.listeners = this.listeners.filter(cb => cb !== handler);
resolve(msg.data as T);
}
};
this.listeners.push(handler);
this.ws?.send(JSON.stringify({ ...params, type: requestType }));
this.ws?.send(JSON.stringify({ ...params, type: requestType, requestId }));
});
}
@@ -916,6 +924,39 @@ class CommunicationLayer {
}
}
// ===== HolidayRequest API (휴가/외출 신청) =====
public async getHolidayRequestList(startDate: string, endDate: string, userId: string, userLevel: number): Promise<ApiResponse<HolidayRequest[]>> {
if (isWebView && machine) {
const result = await machine.HolidayRequest_GetList(startDate, endDate, userId, userLevel);
return JSON.parse(result);
} else {
return this.wsRequest<ApiResponse<HolidayRequest[]>>('HOLIDAY_REQUEST_GET_LIST', 'HOLIDAY_REQUEST_LIST_DATA', { startDate, endDate, userId, userLevel });
}
}
public async saveHolidayRequest(data: HolidayRequest): Promise<ApiResponse> {
const { idx, uid, cate, sdate, edate, Remark, Response, conf, HolyReason, HolyBackup, HolyLocation, HolyDays, HolyTimes, stime, etime } = data;
const remark = Remark || '';
const response = Response || '';
const holyReason = HolyReason || '';
const holyBackup = HolyBackup || '';
const holyLocation = HolyLocation || '';
const holyDays = HolyDays || 0;
const holyTimes = HolyTimes || 0;
const sTime = stime || '';
const eTime = etime || '';
if (isWebView && machine) {
const result = await machine.HolidayRequest_Save(idx, uid, cate, sdate, edate, remark, response, conf, holyReason, holyBackup, holyLocation, holyDays, holyTimes, sTime, eTime);
return JSON.parse(result);
} else {
return this.wsRequest<ApiResponse>('HOLIDAY_REQUEST_SAVE', 'HOLIDAY_REQUEST_SAVED', {
idx, uid, cate, sdate, edate, remark, response, conf, holyReason, holyBackup, holyLocation, holyDays, holyTimes, stime: sTime, etime: eTime
});
}
}
// ===== MailForm API (메일양식) =====
public async getMailFormList(): Promise<ApiResponse<MailFormItem[]>> {

View File

@@ -0,0 +1,507 @@
import { useState, useEffect } from 'react';
import { X, Save, Calendar, Clock, MapPin, User, FileText, AlertCircle } from 'lucide-react';
import { comms } from '@/communication';
import { HolidayRequest, CommonCode } from '@/types';
interface HolidayRequestDialogProps {
isOpen: boolean;
onClose: () => void;
onSave: () => void;
initialData?: HolidayRequest | null;
userLevel: number;
currentUserId: string;
currentUserName: string;
}
export function HolidayRequestDialog({
isOpen,
onClose,
onSave,
initialData,
userLevel,
currentUserId,
// currentUserName
}: HolidayRequestDialogProps) {
const [loading, setLoading] = useState(false);
const [codes, setCodes] = useState<{
cate: CommonCode[];
reason: CommonCode[];
location: CommonCode[];
backup: CommonCode[];
}>({
cate: [],
reason: [],
location: [],
backup: []
});
const [users, setUsers] = useState<Array<{ id: string; name: string }>>([]);
// Form State
const [formData, setFormData] = useState<HolidayRequest>({
idx: 0,
gcode: '',
uid: currentUserId,
cate: '',
sdate: new Date().toISOString().split('T')[0],
edate: new Date().toISOString().split('T')[0],
Remark: '',
wuid: currentUserId,
wdate: '',
Response: '',
conf: 0,
HolyReason: '',
HolyBackup: '',
HolyLocation: '',
HolyDays: 0,
HolyTimes: 0,
stime: '09:00',
etime: '18:00',
sendmail: false
});
const [requestType, setRequestType] = useState<'day' | 'time' | 'out'>('day'); // day: 휴가, time: 대체, out: 외출
useEffect(() => {
if (isOpen) {
loadCodes();
if (userLevel >= 5) {
loadUsers();
}
if (initialData) {
setFormData({ ...initialData });
// Determine request type based on data
if (initialData.cate === '외출') {
setRequestType('out');
} else if (initialData.cate === '대체') {
setRequestType('time');
} else {
setRequestType('day');
}
} else {
// Reset form for new entry
setFormData({
idx: 0,
gcode: '',
uid: currentUserId,
cate: '',
sdate: new Date().toISOString().split('T')[0],
edate: new Date().toISOString().split('T')[0],
Remark: '',
wuid: currentUserId,
wdate: '',
Response: '',
conf: 0,
HolyReason: '',
HolyBackup: '',
HolyLocation: '',
HolyDays: 0,
HolyTimes: 0,
stime: '09:00',
etime: '18:00',
sendmail: false
});
setRequestType('day');
}
}
}, [isOpen, initialData, currentUserId]);
const loadCodes = async () => {
try {
const [cateRes, reasonRes, locationRes, backupRes] = await Promise.all([
comms.getCommonList('50'),
comms.getCommonList('51'),
comms.getCommonList('52'),
comms.getCommonList('53')
]);
setCodes({
cate: cateRes || [],
reason: reasonRes || [],
location: locationRes || [],
backup: backupRes || []
});
// Set default category if new
if (!initialData && cateRes && cateRes.length > 0) {
setFormData(prev => ({ ...prev, cate: cateRes[0].svalue }));
}
} catch (error) {
console.error('Failed to load codes:', error);
}
};
const loadUsers = async () => {
try {
const userList = await comms.getUserList('');
if (userList && userList.length > 0) {
const mappedUsers = userList.map((u: any) => ({
id: u.id || u.Id,
name: u.name || u.NameK || u.id
}));
setUsers(mappedUsers);
}
} catch (error) {
console.error('Failed to load users:', error);
}
};
const handleSave = async () => {
// Validation
if (!formData.cate && requestType === 'day') {
alert('구분을 선택하세요.');
return;
}
if (formData.sdate > formData.edate) {
alert('종료일이 시작일보다 빠를 수 없습니다.');
return;
}
// Prepare data based on type
const dataToSave = { ...formData };
if (requestType === 'out') {
dataToSave.cate = '외출';
dataToSave.HolyDays = 0;
// Calculate times if needed, or rely on user input?
// WinForms doesn't seem to auto-calc times for 'out', just saves stime/etime.
} else if (requestType === 'time') {
dataToSave.cate = '대체';
dataToSave.HolyDays = 0;
} else {
// Day
dataToSave.HolyTimes = 0;
dataToSave.stime = '';
dataToSave.etime = '';
// Calculate days
const start = new Date(dataToSave.sdate);
const end = new Date(dataToSave.edate);
const diffTime = Math.abs(end.getTime() - start.getTime());
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1;
dataToSave.HolyDays = diffDays;
}
setLoading(true);
try {
const response = await comms.saveHolidayRequest(dataToSave);
if (response.Success) {
onSave();
onClose();
} else {
alert('저장 실패: ' + response.Message);
}
} catch (error) {
console.error('Save error:', error);
alert('저장 중 오류가 발생했습니다.');
} finally {
setLoading(false);
}
};
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
<div className="bg-white rounded-xl shadow-2xl w-full max-w-2xl max-h-[90vh] overflow-y-auto">
{/* Header */}
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-100">
<h2 className="text-xl font-bold text-gray-800">
{formData.idx === 0 ? '휴가/외출 신청' : '신청 내역 수정'}
</h2>
<button onClick={onClose} className="p-2 hover:bg-gray-100 rounded-full transition-colors">
<X className="w-5 h-5 text-gray-500" />
</button>
</div>
{/* Body */}
<div className="p-6 space-y-6">
{/* Request Type */}
<div className="flex gap-4 p-4 bg-gray-50 rounded-lg">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="radio"
name="type"
checked={requestType === 'day'}
onChange={() => setRequestType('day')}
className="w-4 h-4 text-blue-600"
disabled={formData.idx > 0 && initialData?.cate !== '대체' && initialData?.cate !== '외출'}
/>
<span className="font-medium text-gray-700"></span>
</label>
<label className="flex items-center gap-2 cursor-pointer">
<input
type="radio"
name="type"
checked={requestType === 'time'}
onChange={() => setRequestType('time')}
className="w-4 h-4 text-blue-600"
disabled={formData.idx > 0 && initialData?.cate !== '대체'}
/>
<span className="font-medium text-gray-700">()</span>
</label>
<label className="flex items-center gap-2 cursor-pointer">
<input
type="radio"
name="type"
checked={requestType === 'out'}
onChange={() => setRequestType('out')}
className="w-4 h-4 text-blue-600"
disabled={formData.idx > 0 && initialData?.cate !== '외출'}
/>
<span className="font-medium text-gray-700"></span>
</label>
</div>
{/* User Selection (Admin only) */}
{userLevel >= 5 && (
<div className="grid grid-cols-1 gap-2">
<label className="text-sm font-medium text-gray-700 flex items-center gap-2">
<User className="w-4 h-4" />
</label>
<select
value={formData.uid}
onChange={(e) => setFormData({ ...formData, uid: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
disabled={formData.idx > 0}
>
{users.map(user => (
<option key={user.id} value={user.id}>{user.name} ({user.id})</option>
))}
</select>
</div>
)}
{/* Date & Time */}
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700 flex items-center gap-2">
<Calendar className="w-4 h-4" />
</label>
<input
type="date"
value={formData.sdate}
onChange={(e) => setFormData({ ...formData, sdate: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700 flex items-center gap-2">
<Calendar className="w-4 h-4" />
</label>
<input
type="date"
value={formData.edate}
onChange={(e) => setFormData({ ...formData, edate: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
</div>
{(requestType === 'time' || requestType === 'out') && (
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700 flex items-center gap-2">
<Clock className="w-4 h-4" />
</label>
<input
type="time"
value={formData.stime}
onChange={(e) => setFormData({ ...formData, stime: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700 flex items-center gap-2">
<Clock className="w-4 h-4" />
</label>
<input
type="time"
value={formData.etime}
onChange={(e) => setFormData({ ...formData, etime: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
</div>
)}
{/* Category & Reason */}
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700 flex items-center gap-2">
<FileText className="w-4 h-4" />
</label>
{requestType === 'day' ? (
<select
value={formData.cate}
onChange={(e) => setFormData({ ...formData, cate: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
{codes.cate.map(code => (
<option key={code.code} value={code.svalue}>{code.svalue}</option>
))}
</select>
) : (
<input
type="text"
value={requestType === 'out' ? '외출' : '대체'}
disabled
className="w-full px-3 py-2 border border-gray-300 rounded-lg bg-gray-100 text-gray-500"
/>
)}
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700 flex items-center gap-2">
<AlertCircle className="w-4 h-4" />
</label>
<select
value={formData.HolyReason}
onChange={(e) => setFormData({ ...formData, HolyReason: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value=""></option>
{codes.reason.map(code => (
<option key={code.code} value={code.svalue}>{code.svalue}</option>
))}
</select>
</div>
</div>
{/* Location & Backup */}
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700 flex items-center gap-2">
<MapPin className="w-4 h-4" />
</label>
<select
value={formData.HolyLocation}
onChange={(e) => setFormData({ ...formData, HolyLocation: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value=""></option>
{codes.location.map(code => (
<option key={code.code} value={code.svalue}>{code.svalue}</option>
))}
</select>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700 flex items-center gap-2">
<User className="w-4 h-4" />
</label>
<select
value={formData.HolyBackup}
onChange={(e) => setFormData({ ...formData, HolyBackup: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value=""></option>
{codes.backup.map(code => (
<option key={code.code} value={code.svalue}>{code.svalue}</option>
))}
</select>
</div>
</div>
{/* Days & Times (Manual Override) */}
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700"></label>
<input
type="number"
step="0.5"
value={formData.HolyDays}
onChange={(e) => setFormData({ ...formData, HolyDays: parseFloat(e.target.value) })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
disabled={requestType !== 'day'}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700"></label>
<input
type="number"
step="0.5"
value={formData.HolyTimes}
onChange={(e) => setFormData({ ...formData, HolyTimes: parseFloat(e.target.value) })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
disabled={requestType === 'day'}
/>
</div>
</div>
{/* Remark */}
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700"></label>
<textarea
value={formData.Remark}
onChange={(e) => setFormData({ ...formData, Remark: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent h-20 resize-none"
/>
</div>
{/* Admin Response & Confirmation */}
{userLevel >= 5 && (
<div className="p-4 bg-blue-50 rounded-lg space-y-4 border border-blue-100">
<h3 className="font-semibold text-blue-800"> </h3>
<div className="flex gap-4">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="radio"
name="conf"
checked={formData.conf === 0}
onChange={() => setFormData({ ...formData, conf: 0 })}
className="w-4 h-4 text-blue-600"
/>
<span className="text-gray-700"></span>
</label>
<label className="flex items-center gap-2 cursor-pointer">
<input
type="radio"
name="conf"
checked={formData.conf === 1}
onChange={() => setFormData({ ...formData, conf: 1 })}
className="w-4 h-4 text-green-600"
/>
<span className="text-gray-700"></span>
</label>
<label className="flex items-center gap-2 cursor-pointer">
<input
type="radio"
name="conf"
checked={formData.conf === 2}
onChange={() => setFormData({ ...formData, conf: 2 })}
className="w-4 h-4 text-red-600"
/>
<span className="text-gray-700"></span>
</label>
</div>
<div className="space-y-2">
<label className="text-sm font-medium text-gray-700"> </label>
<input
type="text"
value={formData.Response}
onChange={(e) => setFormData({ ...formData, Response: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
</div>
</div>
)}
</div>
{/* Footer */}
<div className="flex items-center justify-end gap-3 px-6 py-4 border-t border-gray-100 bg-gray-50 rounded-b-xl">
<button
onClick={onClose}
className="px-4 py-2 text-gray-600 hover:bg-gray-200 rounded-lg transition-colors font-medium"
>
</button>
<button
onClick={handleSave}
disabled={loading}
className="flex items-center gap-2 px-6 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg shadow-lg shadow-blue-500/30 transition-all font-medium disabled:opacity-50"
>
<Save className="w-4 h-4" />
{loading ? '저장 중...' : '저장'}
</button>
</div>
</div>
</div>
);
}

View File

@@ -319,7 +319,7 @@ export function JobReportDayDialog({ isOpen, onClose, initialMonth }: JobReportD
);
})}
<td className="px-3 py-2 text-center text-sm text-white border border-white/10 font-medium whitespace-nowrap">
{row.totalHrs}+{row.totalOt}(*{row.totalHolidayOt})
{row.totalHrs.toFixed(1)}+{row.totalOt.toFixed(1)}(*{row.totalHolidayOt.toFixed(1)})
</td>
</tr>
))

View File

@@ -95,12 +95,15 @@ export function JobreportEditModal({
try {
// WebSocket 모드에서는 같은 응답타입을 사용하므로 순차적으로 로드
const requestPart = await comms.getCommonList('13'); // 요청부서
if (requestPart) requestPart.sort((a, b) => (a.memo || a.svalue || '').localeCompare(b.memo || b.svalue || ''));
setRequestPartList(requestPart || []);
const packages = await comms.getCommonList('14'); // 패키지
if (packages) packages.sort((a, b) => (a.memo || a.svalue || '').localeCompare(b.memo || b.svalue || ''));
setPackageList(packages || []);
const processes = await comms.getCommonList('16'); // 공정(프로세스)
if (processes) processes.sort((a, b) => (a.memo || a.svalue || '').localeCompare(b.memo || b.svalue || ''));
setProcessList(processes || []);
const statuses = await comms.getCommonList('12'); // 상태

View File

@@ -15,6 +15,7 @@ import {
User,
Users,
CalendarDays,
Calendar,
Mail,
Shield,
List,
@@ -81,6 +82,7 @@ const leftDropdownMenus: DropdownMenuConfig[] = [
items: [
{ type: 'link', path: '/kuntae', icon: List, label: '목록' },
{ type: 'action', icon: AlertTriangle, label: '오류검사', action: 'kuntaeErrorCheck' },
{ type: 'link', path: '/holiday-request', icon: Calendar, label: '휴가/외출 신청' },
],
},
{

View File

@@ -61,6 +61,8 @@ export function Dashboard() {
const [purchaseCR, setPurchaseCR] = useState(0);
const [todoCount, setTodoCount] = useState(0);
const [todayWorkHrs, setTodayWorkHrs] = useState(0);
const [unregisteredJobReportCount, setUnregisteredJobReportCount] = useState(0);
const [unregisteredJobReportDays, setUnregisteredJobReportDays] = useState<{ date: string; hrs: number }[]>([]);
// 목록 데이터
const [urgentTodos, setUrgentTodos] = useState<TodoModel[]>([]);
@@ -73,6 +75,7 @@ export function Dashboard() {
// 모달 상태
const [showNRModal, setShowNRModal] = useState(false);
const [showCRModal, setShowCRModal] = useState(false);
const [showUnregisteredModal, setShowUnregisteredModal] = useState(false);
const [showNoteModal, setShowNoteModal] = useState(false);
const [showNoteEditModal, setShowNoteEditModal] = useState(false);
const [showNoteAddModal, setShowNoteAddModal] = useState(false);
@@ -125,6 +128,11 @@ export function Dashboard() {
const now = new Date();
const todayStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
// 15일 전 날짜 계산
const fifteenDaysAgoDate = new Date(now);
fifteenDaysAgoDate.setDate(now.getDate() - 15);
const fifteenDaysAgoStr = `${fifteenDaysAgoDate.getFullYear()}-${String(fifteenDaysAgoDate.getMonth() + 1).padStart(2, '0')}-${String(fifteenDaysAgoDate.getDate()).padStart(2, '0')}`;
// 현재 로그인 사용자 ID 가져오기
let currentUserId = '';
try {
@@ -142,12 +150,14 @@ export function Dashboard() {
urgentTodosResponse,
allTodosResponse,
jobreportResponse,
jobreportHistoryResponse,
notesResponse,
] = await Promise.all([
comms.getPurchaseWaitCount(),
comms.getUrgentTodos(),
comms.getTodos(),
comms.getJobReportList(todayStr, todayStr, currentUserId, ''),
comms.getJobReportList(fifteenDaysAgoStr, todayStr, currentUserId, ''),
comms.getNoteList('2000-01-01', todayStr, ''),
]);
@@ -174,6 +184,39 @@ export function Dashboard() {
setTodayWorkHrs(0);
}
// 최근 15일간 업무일지 미등록(8시간 미만) 확인
if (jobreportHistoryResponse.Success && jobreportHistoryResponse.Data) {
const dailyWork: { [key: string]: number } = {};
// 날짜별 시간 합계 계산
jobreportHistoryResponse.Data.forEach((item: JobReportItem) => {
if (item.pdate) {
const date = item.pdate.substring(0, 10);
dailyWork[date] = (dailyWork[date] || 0) + (item.hrs || 0);
}
});
const insufficientDays: { date: string; hrs: number }[] = [];
// 어제부터 15일 전까지 확인 (오늘은 제외)
for (let i = 1; i <= 15; i++) {
const d = new Date(now);
d.setDate(now.getDate() - i);
const dStr = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
// 주말(토:6, 일:0) 제외
if (d.getDay() === 0 || d.getDay() === 6) continue;
const hrs = dailyWork[dStr] || 0;
if (hrs < 8) {
insufficientDays.push({ date: dStr, hrs });
}
}
setUnregisteredJobReportCount(insufficientDays.length);
setUnregisteredJobReportDays(insufficientDays);
}
// 최근 메모 목록 (최대 10개)
if (notesResponse.Success && notesResponse.Data) {
setRecentNotes(notesResponse.Data.slice(0, 10));
@@ -525,7 +568,7 @@ export function Dashboard() {
</div>
{/* 통계 카드 */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<div className="grid grid-cols-1 md:grid-cols-5 gap-6">
<StatCard
title="구매요청 (NR)"
value={purchaseNR}
@@ -547,6 +590,13 @@ export function Dashboard() {
color="text-warning-400"
onClick={() => navigate('/todo')}
/>
<StatCard
title="업무일지 미등록"
value={`${unregisteredJobReportCount}`}
icon={<AlertTriangle className="w-6 h-6 text-danger-400" />}
color="text-danger-400"
onClick={() => setShowUnregisteredModal(true)}
/>
<StatCard
title="금일 업무일지"
value={`${todayWorkHrs}시간`}
@@ -656,6 +706,69 @@ export function Dashboard() {
)}
</div>
{/* 업무일지 미등록 상세 모달 */}
{showUnregisteredModal && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4">
<div className="bg-slate-900 border border-white/10 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-scale-in">
<div className="flex items-center justify-between px-6 py-4 border-b border-white/10 bg-white/5">
<h3 className="text-lg font-semibold text-white flex items-center gap-2">
<AlertTriangle className="w-5 h-5 text-danger-400" />
</h3>
<button
onClick={() => setShowUnregisteredModal(false)}
className="text-white/50 hover:text-white transition-colors"
>
<X className="w-5 h-5" />
</button>
</div>
<div className="p-6 max-h-[60vh] overflow-y-auto">
<p className="text-white/70 text-sm mb-4">
15( ) 8 .
</p>
{unregisteredJobReportDays.length === 0 ? (
<div className="text-center py-8 text-white/50">
.
</div>
) : (
<div className="space-y-2">
{unregisteredJobReportDays.map((day, index) => (
<div key={index} className="flex items-center justify-between p-3 bg-white/5 rounded-lg border border-white/5">
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-full bg-danger-500/20 flex items-center justify-center text-danger-400 text-xs font-bold">
{index + 1}
</div>
<span className="text-white font-medium">{day.date}</span>
</div>
<div className="flex items-center gap-2">
<span className={`font-bold ${day.hrs === 0 ? 'text-danger-400' : 'text-warning-400'}`}>
{day.hrs}
</span>
<span className="text-white/40 text-xs">/ 8</span>
</div>
</div>
))}
</div>
)}
</div>
<div className="px-6 py-4 border-t border-white/10 bg-white/5 flex justify-end">
<button
onClick={() => {
setShowUnregisteredModal(false);
navigate('/jobreport');
}}
className="px-4 py-2 bg-primary-500 hover:bg-primary-600 text-white rounded-lg transition-colors text-sm font-medium"
>
</button>
</div>
</div>
</div>
)}
{/* NR 모달 */}
{showNRModal && (
<Modal title="구매요청 (NR) 목록" onClose={() => setShowNRModal(false)}>

View File

@@ -0,0 +1,374 @@
import { useState, useEffect, useCallback } from 'react';
import { Calendar, Search, User, RefreshCw, ChevronLeft, ChevronRight, Plus } from 'lucide-react';
import { comms } from '../communication';
import { HolidayRequest, HolidayRequestSummary } from '../types';
import { HolidayRequestDialog } from '../components/holiday/HolidayRequestDialog';
export default function HolidayRequestPage() {
const [loading, setLoading] = useState(false);
const [requests, setRequests] = useState<HolidayRequest[]>([]);
const [summary, setSummary] = useState<HolidayRequestSummary>({
ApprovedDays: 0,
ApprovedTimes: 0,
PendingDays: 0,
PendingTimes: 0
});
// 필터 상태
const [startDate, setStartDate] = useState('');
const [endDate, setEndDate] = useState('');
const [selectedUserId, setSelectedUserId] = useState('%');
const [userLevel, setUserLevel] = useState(0);
const [currentUserId, setCurrentUserId] = useState('');
const [currentUserName, setCurrentUserName] = useState('');
// 사용자 목록
const [users, setUsers] = useState<Array<{id: string, name: string}>>([]);
// Dialog State
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [selectedRequest, setSelectedRequest] = useState<HolidayRequest | null>(null);
useEffect(() => {
// 초기 날짜 설정: 1개월 전 ~ 1개월 후
const today = new Date();
const oneMonthAgo = new Date(today);
oneMonthAgo.setMonth(today.getMonth() - 1);
const oneMonthLater = new Date(today);
oneMonthLater.setMonth(today.getMonth() + 1);
setStartDate(formatDate(oneMonthAgo));
setEndDate(formatDate(oneMonthLater));
// 사용자 정보 로드
loadUserInfo();
}, []);
useEffect(() => {
if (startDate && endDate && currentUserId) {
loadData();
}
}, [startDate, endDate, selectedUserId, currentUserId]);
const formatDate = (date: Date) => {
return date.toISOString().split('T')[0];
};
const formatDateShort = (dateStr?: string) => {
if (!dateStr) return '-';
const d = dateStr.substring(0, 10);
if (d.length < 10) return d;
return `${d.substring(2, 4)}-${d.substring(5, 7)}-${d.substring(8, 10)}`;
};
const loadUserInfo = async () => {
try {
// 사용자 정보 조회
const loginStatus = await comms.checkLoginStatus();
if (loginStatus.Success && loginStatus.IsLoggedIn && loginStatus.User) {
const user = loginStatus.User as { Level?: number; Id?: string; NameK?: string; Name?: string };
setCurrentUserId(user.Id || '');
setCurrentUserName(user.NameK || user.Name || '');
setUserLevel(user.Level || 0);
// 사용자 목록 로드
loadUsers(user.Level || 0);
}
} catch (error) {
console.error('Failed to load user info:', error);
}
};
const loadUsers = async (level: number) => {
try {
// 레벨 5 이상만 사용자 목록 조회 가능
if (level >= 5) {
const userList = await comms.getUserList('');
if (userList && userList.length > 0) {
const mappedUsers = userList.map((u: any) => ({
id: u.id || u.Id,
name: u.name || u.NameK || u.id
}));
setUsers([{ id: '%', name: '전체' }, ...mappedUsers]);
}
}
} catch (error) {
console.error('Failed to load users:', error);
}
};
const loadData = useCallback(async () => {
if (!startDate || !endDate) return;
setLoading(true);
try {
const userId = userLevel < 5 ? currentUserId : selectedUserId;
const response = await comms.getHolidayRequestList(startDate, endDate, userId, userLevel);
if (response.Success && response.Data) {
setRequests(response.Data);
// Summary는 별도 필드로 올 수 있음
const data = response as any;
if (data.Summary) {
setSummary(data.Summary);
}
}
} catch (error) {
console.error('Failed to load holiday requests:', error);
alert('데이터를 불러오는 중 오류가 발생했습니다.');
} finally {
setLoading(false);
}
}, [startDate, endDate, selectedUserId, userLevel, currentUserId]);
// 월 이동
const moveMonth = (offset: number) => {
const current = new Date(startDate);
current.setMonth(current.getMonth() + offset);
const year = current.getFullYear();
const month = current.getMonth();
const newStart = new Date(year, month, 1);
const newEnd = new Date(year, month + 1, 0);
setStartDate(formatDate(newStart));
setEndDate(formatDate(newEnd));
};
const getCategoryName = (cate: string) => {
const categories: { [key: string]: string } = {
'1': '연차',
'2': '반차',
'3': '병가',
'4': '경조사',
'5': '외출',
'6': '기타'
};
return categories[cate] || cate;
};
const getConfirmStatusText = (conf: number) => {
return conf === 1 ? '승인' : '미승인';
};
return (
<div className="flex flex-col h-full bg-gradient-to-br from-slate-50 via-blue-50/30 to-slate-50">
{/* 헤더 */}
<div className="glass-effect-solid border-b border-white/20">
<div className="flex items-center justify-between px-6 py-4">
<div className="flex items-center space-x-3">
<div className="p-2 bg-gradient-to-br from-blue-500 to-blue-600 rounded-xl shadow-lg">
<Calendar className="w-5 h-5 text-white" />
</div>
<div>
<h1 className="text-xl font-bold text-white">/ </h1>
<p className="text-sm text-white/70">Holiday Request Management</p>
</div>
</div>
<div className="flex items-center gap-2">
<button
onClick={() => {
setSelectedRequest(null);
setIsDialogOpen(true);
}}
className="flex items-center gap-2 px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg transition-all shadow-lg hover:shadow-blue-500/30"
>
<Plus className="w-4 h-4" />
<span className="text-sm font-medium"></span>
</button>
<button
onClick={loadData}
disabled={loading}
className="flex items-center gap-2 px-4 py-2 glass-effect-solid hover:bg-white/20 rounded-lg transition-all text-white disabled:opacity-50"
>
<RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />
<span className="text-sm font-medium"></span>
</button>
</div>
</div>
</div>
{/* 필터 영역 */}
<div className="glass-effect-solid border-b border-white/20">
<div className="px-6 py-4">
<div className="flex items-center gap-4 flex-wrap">
{/* 월 이동 버튼 */}
<div className="flex items-center gap-2">
<button
onClick={() => moveMonth(-1)}
className="p-2 glass-effect hover:bg-white/30 rounded-lg transition-all"
>
<ChevronLeft className="w-4 h-4 text-white" />
</button>
<div className="flex items-center gap-2">
<Calendar className="w-4 h-4 text-white/80" />
<input
type="date"
value={startDate}
onChange={(e) => setStartDate(e.target.value)}
className="px-3 py-2 glass-effect text-white text-sm rounded-lg focus:ring-2 focus:ring-blue-400/50 focus:outline-none"
/>
<span className="text-white/60">~</span>
<input
type="date"
value={endDate}
onChange={(e) => setEndDate(e.target.value)}
className="px-3 py-2 glass-effect text-white text-sm rounded-lg focus:ring-2 focus:ring-blue-400/50 focus:outline-none"
/>
</div>
<button
onClick={() => moveMonth(1)}
className="p-2 glass-effect hover:bg-white/30 rounded-lg transition-all"
>
<ChevronRight className="w-4 h-4 text-white" />
</button>
</div>
{/* 담당자 선택 (레벨 5 이상만) */}
{userLevel >= 5 && (
<div className="flex items-center gap-2">
<User className="w-4 h-4 text-white/80" />
<select
value={selectedUserId}
onChange={(e) => setSelectedUserId(e.target.value)}
className="px-3 py-2 glass-effect text-white text-sm rounded-lg focus:ring-2 focus:ring-blue-400/50 focus:outline-none"
>
{users.map(user => (
<option key={user.id} value={user.id}>{user.name}</option>
))}
</select>
</div>
)}
{/* 조회 버튼 */}
<button
onClick={loadData}
disabled={loading}
className="flex items-center gap-2 px-4 py-2 bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white text-sm font-medium rounded-lg shadow-lg transition-all disabled:opacity-50"
>
<Search className="w-4 h-4" />
</button>
</div>
{/* 합계 표시 */}
<div className="mt-4 flex gap-6 text-sm">
<div className="glass-effect px-4 py-2 rounded-lg">
<span className="font-medium text-white/90">() = </span>
<span className="text-blue-300 font-semibold">: {summary.ApprovedDays}</span>
<span className="mx-1 text-white/60">/</span>
<span className={summary.PendingDays === 0 ? 'text-white/90' : 'text-red-400 font-semibold'}>
: {summary.PendingDays}
</span>
</div>
<div className="glass-effect px-4 py-2 rounded-lg">
<span className="font-medium text-white/90">() = </span>
<span className="text-blue-300 font-semibold">: {summary.ApprovedTimes}</span>
<span className="mx-1 text-white/60">/</span>
<span className={summary.PendingTimes === 0 ? 'text-white/90' : 'text-red-400 font-semibold'}>
: {summary.PendingTimes}
</span>
</div>
</div>
</div>
</div>
{/* 테이블 */}
<div className="flex-1 overflow-auto px-6 py-4">
<div className="glass-effect-solid rounded-xl overflow-hidden">
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-white/10">
<th className="px-4 py-3 text-left font-semibold text-white/90">No</th>
<th className="px-4 py-3 text-left font-semibold text-white/90"></th>
<th className="px-4 py-3 text-left font-semibold text-white/90"></th>
<th className="px-4 py-3 text-left font-semibold text-white/90"></th>
<th className="px-4 py-3 text-left font-semibold text-white/90"></th>
<th className="px-4 py-3 text-left font-semibold text-white/90"></th>
<th className="px-4 py-3 text-left font-semibold text-white/90"></th>
<th className="px-4 py-3 text-center font-semibold text-white/90"></th>
<th className="px-4 py-3 text-center font-semibold text-white/90"></th>
<th className="px-4 py-3 text-left font-semibold text-white/90"></th>
<th className="px-4 py-3 text-center font-semibold text-white/90"></th>
</tr>
</thead>
<tbody>
{loading ? (
<tr>
<td colSpan={11} className="px-4 py-12 text-center">
<div className="flex items-center justify-center gap-3">
<RefreshCw className="w-5 h-5 text-blue-400 animate-spin" />
<span className="text-white/60"> ...</span>
</div>
</td>
</tr>
) : requests.length === 0 ? (
<tr>
<td colSpan={11} className="px-4 py-12 text-center">
<div className="flex flex-col items-center justify-center gap-3">
<Calendar className="w-12 h-12 text-white/30" />
<span className="text-white/60"> .</span>
</div>
</td>
</tr>
) : (
requests.map((req, index) => (
<tr
key={req.idx}
className="border-b border-white/5 hover:bg-white/5 transition-colors cursor-pointer"
onClick={() => {
setSelectedRequest(req);
setIsDialogOpen(true);
}}
>
<td className="px-4 py-3 text-white/70">{index + 1}</td>
<td className="px-4 py-3 text-white/90">{formatDateShort(req.wdate)}</td>
<td className="px-4 py-3 text-white/80">{req.dept || ''}</td>
<td className="px-4 py-3 text-white font-medium">{req.name || ''}</td>
<td className="px-4 py-3">
<span className="px-2 py-1 bg-blue-500/20 text-blue-300 rounded text-xs font-medium">
{getCategoryName(req.cate)}
</span>
</td>
<td className="px-4 py-3 text-white/90">{formatDateShort(req.sdate)}</td>
<td className="px-4 py-3 text-white/90">{formatDateShort(req.edate)}</td>
<td className="px-4 py-3 text-center text-white/90">{req.HolyDays || 0}</td>
<td className="px-4 py-3 text-center text-white/90">{req.HolyTimes || 0}</td>
<td className="px-4 py-3 text-white/70 max-w-xs truncate" title={req.HolyReason || ''}>
{req.HolyReason || '-'}
</td>
<td className="px-4 py-3 text-center">
<span className={`px-2 py-1 rounded text-xs font-semibold ${
req.conf === 1
? 'bg-green-500/20 text-green-300'
: 'bg-red-500/20 text-red-300'
}`}>
{getConfirmStatusText(req.conf)}
</span>
</td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
</div>
{/* 다이얼로그 */}
<HolidayRequestDialog
isOpen={isDialogOpen}
onClose={() => setIsDialogOpen(false)}
onSave={() => {
setIsDialogOpen(false);
loadData();
}}
initialData={selectedRequest}
currentUserName={currentUserName}
currentUserId={currentUserId}
userLevel={userLevel}
/>
</div>
);
}

View File

@@ -7,6 +7,8 @@ import {
Info,
Plus,
Calendar,
AlertTriangle,
X,
} from 'lucide-react';
import { comms } from '@/communication';
import { JobReportItem, JobReportUser } from '@/types';
@@ -43,6 +45,11 @@ export function Jobreport() {
// 오늘 근무시간 상태
const [todayWork, setTodayWork] = useState({ hrs: 0, ot: 0 });
// 미등록 업무일지 상태
const [unregisteredJobReportCount, setUnregisteredJobReportCount] = useState(0);
const [unregisteredJobReportDays, setUnregisteredJobReportDays] = useState<{ date: string; hrs: number }[]>([]);
const [showUnregisteredModal, setShowUnregisteredModal] = useState(false);
// 날짜 포맷 헬퍼 함수 (로컬 시간 기준)
const formatDateLocal = (date: Date) => {
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
@@ -70,6 +77,55 @@ export function Jobreport() {
}
}, []);
// 미등록 업무일지 로드
const loadUnregisteredJobReports = useCallback(async (userId: string) => {
try {
const now = new Date();
const todayStr = formatDateLocal(now);
// 15일 전 날짜 계산
const fifteenDaysAgoDate = new Date(now);
fifteenDaysAgoDate.setDate(now.getDate() - 15);
const fifteenDaysAgoStr = formatDateLocal(fifteenDaysAgoDate);
const response = await comms.getJobReportList(fifteenDaysAgoStr, todayStr, userId, '');
if (response.Success && response.Data) {
const dailyWork: { [key: string]: number } = {};
// 날짜별 시간 합계 계산
response.Data.forEach((item: JobReportItem) => {
if (item.pdate) {
const date = item.pdate.substring(0, 10);
dailyWork[date] = (dailyWork[date] || 0) + (item.hrs || 0);
}
});
const insufficientDays: { date: string; hrs: number }[] = [];
// 어제부터 15일 전까지 확인 (오늘은 제외)
for (let i = 1; i <= 15; i++) {
const d = new Date(now);
d.setDate(now.getDate() - i);
const dStr = formatDateLocal(d);
// 주말(토:6, 일:0) 제외
if (d.getDay() === 0 || d.getDay() === 6) continue;
const hrs = dailyWork[dStr] || 0;
if (hrs < 8) {
insufficientDays.push({ date: dStr, hrs });
}
}
setUnregisteredJobReportCount(insufficientDays.length);
setUnregisteredJobReportDays(insufficientDays);
}
} catch (error) {
console.error('미등록 업무일지 로드 오류:', error);
}
}, []);
// 초기화 완료 플래그
const [initialized, setInitialized] = useState(false);
@@ -129,6 +185,7 @@ export function Jobreport() {
const handleSearchAndLoadToday = async () => {
await handleSearch();
loadTodayWork(selectedUser);
loadUnregisteredJobReports(selectedUser);
};
// 사용자 목록 로드
@@ -182,7 +239,10 @@ export function Jobreport() {
// 새 업무일지 추가 모달
const openAddModal = () => {
setEditingItem(null);
setFormData(initialFormData);
setFormData({
...initialFormData,
pdate: formatDateLocal(new Date())
});
setShowModal(true);
};
@@ -195,7 +255,7 @@ export function Jobreport() {
const data = response.Data;
setEditingItem(null); // 새로 추가하는 것이므로 null
setFormData({
pdate: new Date().toISOString().split('T')[0], // 오늘 날짜
pdate: formatDateLocal(new Date()), // 오늘 날짜 (로컬 시간 기준)
projectName: data.projectName || '',
pidx: data.pidx ?? null, // pidx도 복사
requestpart: data.requestpart || '',
@@ -314,6 +374,7 @@ export function Jobreport() {
setShowModal(false);
loadData();
loadTodayWork(selectedUser);
loadUnregisteredJobReports(selectedUser);
} else {
alert(response.Message || '저장에 실패했습니다.');
}
@@ -336,6 +397,7 @@ export function Jobreport() {
alert('삭제되었습니다.');
loadData();
loadTodayWork(selectedUser);
loadUnregisteredJobReports(selectedUser);
} else {
alert(response.Message || '삭제에 실패했습니다.');
}
@@ -535,17 +597,34 @@ export function Jobreport() {
</button>
</div>
{/* 미등록 업무일지 카드 */}
<div className="flex-shrink-0 w-40">
<div
className="bg-white/10 rounded-xl p-4 h-full flex flex-col justify-center cursor-pointer hover:bg-white/20 transition-colors"
onClick={() => setShowUnregisteredModal(true)}
>
<div className="text-white/70 text-sm font-medium mb-2 text-center flex items-center justify-center gap-2">
<AlertTriangle className="w-4 h-4 text-danger-400" />
</div>
<div className="text-center">
<span className="text-3xl font-bold text-danger-400">{unregisteredJobReportCount}</span>
<span className="text-white/70 text-lg ml-1"></span>
</div>
</div>
</div>
{/* 우측: 오늘 근무시간 */}
<div className="flex-shrink-0 w-48">
<div className="flex-shrink-0 w-40">
<div className="bg-white/10 rounded-xl p-4 h-full flex flex-col justify-center">
<div className="text-white/70 text-sm font-medium mb-2 text-center"> </div>
<div className="text-center">
<span className="text-3xl font-bold text-white">{todayWork.hrs}</span>
<span className="text-3xl font-bold text-white">{todayWork.hrs.toFixed(1)}</span>
<span className="text-white/70 text-lg ml-1"></span>
</div>
{todayWork.ot > 0 && (
<div className="text-center mt-1">
<span className="text-warning-400 text-sm">OT: {todayWork.ot}</span>
<span className="text-warning-400 text-sm">OT: {todayWork.ot.toFixed(1)}</span>
</div>
)}
</div>
@@ -568,13 +647,13 @@ export function Jobreport() {
<thead className="bg-white/10">
<tr>
<th className="px-2 py-3 text-center text-xs font-medium text-white/70 uppercase w-10"></th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase w-24"></th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase" style={{ width: '35%' }}></th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase"></th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase"></th>
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase"></th>
{canViewOT && <th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase">OT</th>}
<th className="px-4 py-3 text-left text-xs font-medium text-white/70 uppercase"></th>
<th className="px-2 py-3 text-left text-xs font-medium text-white/70 uppercase w-24"></th>
<th className="px-2 py-3 text-left text-xs font-medium text-white/70 uppercase" style={{ width: '35%' }}></th>
<th className="px-2 py-3 text-left text-xs font-medium text-white/70 uppercase"></th>
<th className="px-2 py-3 text-left text-xs font-medium text-white/70 uppercase"></th>
<th className="px-2 py-3 text-left text-xs font-medium text-white/70 uppercase"></th>
{canViewOT && <th className="px-2 py-3 text-left text-xs font-medium text-white/70 uppercase">OT</th>}
<th className="px-2 py-3 text-left text-xs font-medium text-white/70 uppercase"></th>
</tr>
</thead>
<tbody className="divide-y divide-white/10">
@@ -722,6 +801,66 @@ export function Jobreport() {
endDate={endDate}
userId={selectedUser}
/>
{/* 업무일지 미등록 상세 모달 */}
{showUnregisteredModal && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4">
<div className="bg-slate-900 border border-white/10 rounded-2xl w-full max-w-md shadow-2xl overflow-hidden animate-scale-in">
<div className="flex items-center justify-between px-6 py-4 border-b border-white/10 bg-white/5">
<h3 className="text-lg font-semibold text-white flex items-center gap-2">
<AlertTriangle className="w-5 h-5 text-danger-400" />
</h3>
<button
onClick={() => setShowUnregisteredModal(false)}
className="text-white/50 hover:text-white transition-colors"
>
<X className="w-5 h-5" />
</button>
</div>
<div className="p-6 max-h-[60vh] overflow-y-auto">
<p className="text-white/70 text-sm mb-4">
15( ) 8 .
</p>
{unregisteredJobReportDays.length === 0 ? (
<div className="text-center py-8 text-white/50">
.
</div>
) : (
<div className="space-y-2">
{unregisteredJobReportDays.map((day, index) => (
<div key={index} className="flex items-center justify-between p-3 bg-white/5 rounded-lg border border-white/5">
<div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-full bg-danger-500/20 flex items-center justify-center text-danger-400 text-xs font-bold">
{index + 1}
</div>
<span className="text-white font-medium">{day.date}</span>
</div>
<div className="flex items-center gap-2">
<span className={`font-bold ${day.hrs === 0 ? 'text-danger-400' : 'text-warning-400'}`}>
{day.hrs}
</span>
<span className="text-white/40 text-xs">/ 8</span>
</div>
</div>
))}
</div>
)}
</div>
<div className="px-6 py-4 border-t border-white/10 bg-white/5 flex justify-end">
<button
onClick={() => setShowUnregisteredModal(false)}
className="px-4 py-2 bg-white/10 hover:bg-white/20 text-white rounded-lg transition-colors text-sm font-medium"
>
</button>
</div>
</div>
</div>
)}
</div>
);
}

View File

@@ -488,6 +488,10 @@ export interface MachineBridgeInterface {
PartList_GetList(projectIdx: number): Promise<string>;
PartList_Save(idx: number, projectIdx: number, itemgroup: string, itemname: string, item: string, itemmodel: string, itemscale: string, itemunit: string, qty: number, price: number, itemsupply: string, itemsupplyidx: number, itemmanu: string, itemsid: string, option1: string, remark: string, no: number, qtybuy: number): Promise<string>;
PartList_Delete(idx: number): Promise<string>;
// HolidayRequest API (휴가/외출 신청)
HolidayRequest_GetList(startDate: string, endDate: string, userId: string, userLevel: number): Promise<string>;
HolidayRequest_Save(idx: number, uid: string, cate: string, sdate: string, edate: string, remark: string, response: string, conf: number, holyReason: string, holyBackup: string, holyLocation: string, holyDays: number, holyTimes: number, stime: string, etime: string): Promise<string>;
}
// 사용자 권한 정보 타입
@@ -846,6 +850,44 @@ export interface JobReportTypeItem {
count: number;
}
// 휴가/외출 신청 타입
export interface HolidayRequest {
idx: number;
gcode: string;
uid: string;
cate: string;
sdate: string;
edate: string;
Remark?: string;
wuid?: string;
wdate?: string;
dept?: string;
name?: string;
grade?: string;
tel?: string;
processs?: string;
Response?: string;
conf: number;
HolyReason?: string;
HolyBackup?: string;
HolyLocation?: string;
HolyDays?: number;
HolyTimes?: number;
sendmail?: boolean;
stime?: string;
etime?: string;
conf_id?: string;
conf_time?: string;
}
// 휴가/외출 신청 합계 타입
export interface HolidayRequestSummary {
ApprovedDays: number;
ApprovedTimes: number;
PendingDays: number;
PendingTimes: number;
}
export interface JobReportDayData {
items: JobReportDayItem[];
holidays: HolidayItem[];

View File

@@ -79,7 +79,7 @@ namespace FBS0000
var sd = DateTime.Parse(ed.ToString("yyyy") + "-01-01");
int curLevel = Math.Max(FCOMMON.info.Login.level, FCOMMON.DBM.getAuth(FCOMMON.DBM.eAuthType.holyday));
var uid = curLevel > 5 ? "%" : FCOMMON.info.Login.no; // GetUIDValue();
var uid = curLevel >= 5 ? "%" : FCOMMON.info.Login.no; // GetUIDValue();
if (cmbType.SelectedIndex == 0)
{
taDay.Fill(this.dsReport.Holydata_Day, FCOMMON.info.Login.gcode, uid, sd.ToShortDateString(), ed.ToShortDateString());

View File

@@ -115,9 +115,9 @@ namespace FCOMMON
var sql = "select gcode,id,level,isnull(Process,'') as Process ,isnull(name,'') as name,isnull(useUserState,0) as useAccount,isnull(useJobReport,0) as useJobReport "+
" from vGroupUser where "+
" gcode = @gcode "+
var sql = "select gcode,id,level,isnull(Process,'') as Process ,isnull(name,'') as name,isnull(useUserState,0) as useAccount,isnull(useJobReport,0) as useJobReport " +
" from vGroupUser where " +
" gcode = @gcode " +
" and id = @uid";
var cmd = new System.Data.SqlClient.SqlCommand(sql, cn);
cmd.Parameters.Add("gcode", SqlDbType.VarChar).Value = gcode;
@@ -167,7 +167,8 @@ namespace FCOMMON
var cnt = 0;
while (rdr.Read())
{
retval.Add(new GroupUserModel {
retval.Add(new GroupUserModel
{
Gcode = rdr["gcode"].ToString(),
uid = rdr["id"].ToString(),
level = int.Parse(rdr["level"]?.ToString() ?? "0"),
@@ -1572,6 +1573,50 @@ namespace FCOMMON
return currentusername;
}
public static bool GetUserGroupUseNR()
{
var cn = getCn();
cn.Open();
int retval = 0;
var cmd2 = new SqlCommand($"select isnull(usenr,1) from UserGroup where gcode = '{FCOMMON.info.Login.gcode}'", cn);
try
{
var value = cmd2.ExecuteScalar();
retval = value.ToString().ToLower() == "true" ? 1 : 0;
//if (int.TryParse(value.ToString(), out retval))
//{
//}
//else retval = 0;
}
catch (Exception ex)
{
}
return retval > 0;
}
public static bool SetUserGroupUseNR(bool a)
{
var cn = getCn();
cn.Open();
var value = a ? 1 : 0;
int retval = 0;
var cmd2 = new SqlCommand($"update UserGroup set usenr = {value} where gcode = '{FCOMMON.info.Login.gcode}'", cn);
try
{
retval = cmd2.ExecuteNonQuery();
}
catch(Exception ex)
{
}
return retval == 1;
}
public static int addItem(string pumname, string sid, string model, decimal price, string supply, int supplyidx = -1, byte[] pic = null)
{

View File

@@ -113,6 +113,9 @@
this.ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator();
this.btMakeItemsData = new System.Windows.Forms.ToolStripMenuItem();
this.ONOFFToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.oNToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.oFFToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripButton5 = new System.Windows.Forms.ToolStripButton();
this.cm1 = new System.Windows.Forms.ContextMenuStrip(this.components);
this.columnSizeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@@ -159,9 +162,11 @@
this.btViewDel = new System.Windows.Forms.ToolStripButton();
this.toolStripButton3 = new System.Windows.Forms.ToolStripButton();
this.fpSpread1 = new FarPoint.Win.Spread.FpSpread();
this.fpSpread1_Sheet1 = new FarPoint.Win.Spread.SheetView();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.fpSpread1_Sheet1 = new FarPoint.Win.Spread.SheetView();
this.panel1 = new System.Windows.Forms.Panel();
this.lbEnableOff = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.dsPurchase)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.bs)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.bn)).BeginInit();
@@ -170,6 +175,7 @@
this.toolStrip1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.fpSpread1)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.fpSpread1_Sheet1)).BeginInit();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// dsPurchase
@@ -463,7 +469,8 @@
this.ToolStripMenuItem,
this.ToolStripMenuItem,
this.toolStripMenuItem5,
this.btMakeItemsData});
this.btMakeItemsData,
this.ONOFFToolStripMenuItem});
this.toolStripButton7.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton7.Image")));
this.toolStripButton7.ImageTransparentColor = System.Drawing.Color.Magenta;
this.toolStripButton7.Name = "toolStripButton7";
@@ -544,6 +551,29 @@
this.btMakeItemsData.Text = "구매내역에서 품목정보 생성";
this.btMakeItemsData.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
//
// 기능ONOFFToolStripMenuItem
//
this.ONOFFToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.oNToolStripMenuItem,
this.oFFToolStripMenuItem});
this.ONOFFToolStripMenuItem.Name = "기능ONOFFToolStripMenuItem";
this.ONOFFToolStripMenuItem.Size = new System.Drawing.Size(226, 22);
this.ONOFFToolStripMenuItem.Text = "기능 ON/OFF";
//
// oNToolStripMenuItem
//
this.oNToolStripMenuItem.Name = "oNToolStripMenuItem";
this.oNToolStripMenuItem.Size = new System.Drawing.Size(95, 22);
this.oNToolStripMenuItem.Text = "ON";
this.oNToolStripMenuItem.Click += new System.EventHandler(this.oNToolStripMenuItem_Click);
//
// oFFToolStripMenuItem
//
this.oFFToolStripMenuItem.Name = "oFFToolStripMenuItem";
this.oFFToolStripMenuItem.Size = new System.Drawing.Size(95, 22);
this.oFFToolStripMenuItem.Text = "OFF";
this.oFFToolStripMenuItem.Click += new System.EventHandler(this.oFFToolStripMenuItem_Click);
//
// toolStripButton5
//
this.toolStripButton5.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton5.Image")));
@@ -923,41 +953,17 @@
this.fpSpread1.Dock = System.Windows.Forms.DockStyle.Fill;
this.fpSpread1.EditModeReplace = true;
this.fpSpread1.Font = new System.Drawing.Font("맑은 고딕", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.fpSpread1.Location = new System.Drawing.Point(0, 37);
this.fpSpread1.Location = new System.Drawing.Point(0, 85);
this.fpSpread1.Name = "fpSpread1";
this.fpSpread1.Sheets.AddRange(new FarPoint.Win.Spread.SheetView[] {
this.fpSpread1_Sheet1});
this.fpSpread1.Size = new System.Drawing.Size(1334, 579);
this.fpSpread1.Size = new System.Drawing.Size(1334, 531);
this.fpSpread1.StatusBarVisible = true;
this.fpSpread1.TabIndex = 4;
this.fpSpread1.EditModeOff += new System.EventHandler(this.fpSpread1_EditModeOff);
this.fpSpread1.ClipboardPasted += new FarPoint.Win.Spread.ClipboardPastedEventHandler(this.fpSpread1_ClipboardPasted);
this.fpSpread1.CellClick += new FarPoint.Win.Spread.CellClickEventHandler(this.fpSpread1_CellClick);
//
// label1
//
this.label1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.label1.Font = new System.Drawing.Font("맑은 고딕", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label1.Location = new System.Drawing.Point(0, 639);
this.label1.Name = "label1";
this.label1.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
this.label1.Size = new System.Drawing.Size(1334, 23);
this.label1.TabIndex = 6;
this.label1.Text = "--";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// label2
//
this.label2.Dock = System.Windows.Forms.DockStyle.Bottom;
this.label2.Font = new System.Drawing.Font("맑은 고딕", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label2.Location = new System.Drawing.Point(0, 616);
this.label2.Name = "label2";
this.label2.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
this.label2.Size = new System.Drawing.Size(1334, 23);
this.label2.TabIndex = 7;
this.label2.Text = "--";
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// fpSpread1_Sheet1
//
this.fpSpread1_Sheet1.Reset();
@@ -1327,12 +1333,60 @@
this.fpSpread1_Sheet1.ShowRowSelector = true;
this.fpSpread1_Sheet1.ReferenceStyle = FarPoint.Win.Spread.Model.ReferenceStyle.A1;
//
// label1
//
this.label1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.label1.Font = new System.Drawing.Font("맑은 고딕", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label1.Location = new System.Drawing.Point(0, 639);
this.label1.Name = "label1";
this.label1.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
this.label1.Size = new System.Drawing.Size(1334, 23);
this.label1.TabIndex = 6;
this.label1.Text = "--";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// label2
//
this.label2.Dock = System.Windows.Forms.DockStyle.Bottom;
this.label2.Font = new System.Drawing.Font("맑은 고딕", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.label2.Location = new System.Drawing.Point(0, 616);
this.label2.Name = "label2";
this.label2.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
this.label2.Size = new System.Drawing.Size(1334, 23);
this.label2.TabIndex = 7;
this.label2.Text = "--";
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// panel1
//
this.panel1.Controls.Add(this.lbEnableOff);
this.panel1.Dock = System.Windows.Forms.DockStyle.Top;
this.panel1.Location = new System.Drawing.Point(0, 37);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(1334, 48);
this.panel1.TabIndex = 8;
this.panel1.Visible = false;
//
// lbEnableOff
//
this.lbEnableOff.BackColor = System.Drawing.Color.Red;
this.lbEnableOff.Dock = System.Windows.Forms.DockStyle.Fill;
this.lbEnableOff.Font = new System.Drawing.Font("궁서", 20F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(129)));
this.lbEnableOff.ForeColor = System.Drawing.Color.White;
this.lbEnableOff.Location = new System.Drawing.Point(0, 0);
this.lbEnableOff.Name = "lbEnableOff";
this.lbEnableOff.Size = new System.Drawing.Size(1334, 48);
this.lbEnableOff.TabIndex = 0;
this.lbEnableOff.Text = "신규 구매 입력이 - 구매 담당자에 의해 제한 되었습니다";
this.lbEnableOff.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// fPurchaseNR
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1334, 693);
this.Controls.Add(this.fpSpread1);
this.Controls.Add(this.panel1);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.toolStrip1);
@@ -1351,6 +1405,7 @@
this.toolStrip1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.fpSpread1)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.fpSpread1_Sheet1)).EndInit();
this.panel1.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
@@ -1450,5 +1505,10 @@
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem5;
private System.Windows.Forms.ToolStripMenuItem btMakeItemsData;
private FarPoint.Win.Spread.SheetView fpSpread1_Sheet1;
private System.Windows.Forms.ToolStripMenuItem ONOFFToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem oNToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem oFFToolStripMenuItem;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Label lbEnableOff;
}
}

View File

@@ -31,6 +31,7 @@ namespace FEQ0000
public fPurchaseNR()
{
InitializeComponent();
Properties.Settings.Default["gwcs"] = FCOMMON.info.CS;
Properties.Settings.Default["EEEntities"] = FCOMMON.info.CS;
@@ -40,7 +41,7 @@ namespace FEQ0000
this.FormClosed += __Closed;
dtSD.KeyDown += dtSD_KeyDown;
dtED.KeyDown += dtSD_KeyDown;
//this.dv1.CellFormatting += dv1_CellFormatting;
panel1.Visible = !FCOMMON.DBM.GetUserGroupUseNR();
}
private void FPurchase_FormClosing(object sender, FormClosingEventArgs e)
@@ -112,6 +113,7 @@ namespace FEQ0000
private void __Load(object sender, EventArgs e)
{
EnsureVisibleAndUsableSize();
this.cmDate.SelectedIndex = 0;
this.tbRequest.Text = string.Empty; //양진원 FCOMMON.info.Login.nameK;
@@ -169,9 +171,11 @@ namespace FEQ0000
{
btSave.Visible = true;
ToolStripMenuItem.Visible = true;
ONOFFToolStripMenuItem.Visible = true;
}
else
{
ONOFFToolStripMenuItem.Visible = false;
ToolStripMenuItem.Visible = false;
btMakeItemsData.Visible = false;
btSave.Visible = false;
@@ -445,6 +449,9 @@ namespace FEQ0000
private void btSearch_Click(object sender, EventArgs e)
{
refreshData();
if (FCOMMON.DBM.GetUserGroupUseNR() == false) this.panel1.Visible = true;
else panel1.Visible = false;
}
private void bindingNavigatorAddNewItem_Click(object sender, EventArgs e)
@@ -506,6 +513,13 @@ namespace FEQ0000
}
void func_add()
{
if (FCOMMON.DBM.GetUserGroupUseNR() == false)
{
FCOMMON.Util.MsgE("NR 구매 기능이 [구매담당자]에 의해 입력이 제한 되었습니다");
return;
}
else panel1.Visible = false;
var newdr = this.dsPurchase.Purchase.NewPurchaseRow();
if (FCOMMON.info.Login.no == "dev")
@@ -835,6 +849,13 @@ namespace FEQ0000
if (drv == null) return;
var dr = drv.Row as dsPurchase.PurchaseRow;
if (FCOMMON.DBM.GetUserGroupUseNR() == false)
{
FCOMMON.Util.MsgE("NR 구매 기능이 [구매담당자]에 의해 입력이 제한 되었습니다");
return;
}
else panel1.Visible = false;
//현재 데이터를 입력하여 신규 추가를 한다.
var newdr = this.dsPurchase.Purchase.NewPurchaseRow();
FCOMMON.Util.CopyData((System.Data.DataRow)dr, (System.Data.DataRow)newdr);
@@ -1675,5 +1696,21 @@ namespace FEQ0000
}
}
}
private void oNToolStripMenuItem_Click(object sender, EventArgs e)
{
//구매 on
//usergroup 테이블에서 usenr 을 = true 로
if(FCOMMON.DBM.SetUserGroupUseNR(true))
panel1.Visible = false;
}
private void oFFToolStripMenuItem_Click(object sender, EventArgs e)
{
//구매 off
//usergroup 테이블에서 usenr 을 = true 로
if (FCOMMON.DBM.SetUserGroupUseNR(false))
panel1.Visible = true;
}
}
}

View File

@@ -136,164 +136,164 @@
<data name="bindingNavigatorMoveFirstItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
vAAADrwBlbxySQAAASpJREFUOE9jGDygcNbz/00Lnv/PnPj4P1QIA4S3P8Apx5A789n/VUfe/8elKL77
wf/ghmu4DciY8vT/wn0fsCqK73n4f+n+///9qy/gNiCh58n/aVveYyiKaL8P1pw56/9/r9ITuA2I7Hr0
v3f1BxRFoa33wJpb1wFt7/z73yX/AG4DApsf/q+b/w6uKLjl7v9Fe///7wBqzpjz879d3c//9hnbcRvg
UXX/f/60NyiK7Ipv/0+f8/u/f9e3/zqF7/5bJKzHbYB96d3/2ZNfYyjSTzn/36ToxX+VrE//jSOX4TbA
Iu/O/9T+11gVGSSd+C+b9vW/bvA83AYYZt3+H9byEqci/dTL/zV8p+E2QCftxn+/6od4Fal4TMBtgFPu
lf8gBXgVDULAwAAA8HbAq6XlmnAAAAAASUVORK5CYII=
vAAADrwBlbxySQAAATFJREFUOE9jYBg0oHDW8/9NC57/z5z4+D+6HAyEtz/AKceQO/PZ/1VH3v/HpSi+
+8H/4IZrWOXAIGPK0/8L933Aqii+5+H/pfv///evvoAhBwcJPU/+T9vyHkNRRPt9sObMWf//e5WewG1A
ZNej/72rP6AoCm29B9bcuu7/f//Ov/9d8g/gNiCw+eH/uvnv4IqCW+7+X7T3//+Odf//Z8z5+d+u7ud/
+4ztuA3wqLr/P3/aGxRFdsW3/6fP+f3fv+vbf53Cd/8tEtbjNsC+9O7/7MmvMRTpp5z/b1L04r9K1qf/
xpHLcBtgkXfnf2r/a6yKDJJO/JdN+/pfN3gehhwcGGbd/h/W8hKnIv3Uy/81fKdhlQMDnbQb//2qH+JV
pOIxAaccg1Pulf8gBXgVDUoAAPB2wKtYlLYeAAAAAElFTkSuQmCC
</value>
</data>
<data name="bindingNavigatorMovePreviousItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
vAAADrwBlbxySQAAALZJREFUOE9jGDogvP3BfyiTdBDf/eB/cMM18gyI73n4f+n+///9qy+QbkBE+32w
5sxZ//97lZ4gzYDQ1ntgza3rgLZ3/v3vkn+AeAOCW+7+X7T3//8OoOaMOT//29X9/G+fsZ00F9gV3/6f
Puf3f/+ub/91Ct/9t0hYT3oY6Kec/29S9OK/Stan/8aRy0g3AAQMkk78l037+l83eB55BoCAfurl/xq+
08g3AARUPCZQZsBgBQwMANAUYJgEulBVAAAAAElFTkSuQmCC
vAAADrwBlbxySQAAALtJREFUOE9jYBgyILz9wX90MaJBfPeD/8EN18gzIL7n4f+l+///96++QLoBEe33
wZozZ/3/71V6gjQDQlvvgTW3rvv/37/z73+X/APEGxDccvf/or3//3es+/8/Y87P/3Z1P//bZ2wn3gAQ
sCu+/T99zu///l3f/usUvvtvkbCeNANAQD/l/H+Tohf/VbI+/TeOXEa6ASBgkHTiv2za1/+6wfPIMwAE
9FMv/9fwnUa+ASCg4jGBMgMGLwAA0BRgmCws/7cAAAAASUVORK5CYII=
</value>
</data>
<data name="bindingNavigatorMoveNextItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
vAAADrwBlbxySQAAAKNJREFUOE9jGHygcNbz/1AmeSB35rP/Cd33yDckY8rT//P2//6f0HWHPEMSep78
n73v1//OrX//u5VeJt2QyK5H/6ds+/W/ZOnf/wnT//63yT1LmiGBzQ//t659D9ZsXPLlv3T0tf/GkcuI
N8Sj6v7/krnv4JoVXXpIc4F96d3/gS3PyNMMAhZ5d/7bFFwhTzMIGGbdJl8zCOik3SBf81AEDAwAoH5f
oAc0QjgAAAAASUVORK5CYII=
vAAADrwBlbxySQAAAKRJREFUOE9jYBh0oHDW8//oYiSB3JnP/id03yPfkIwpT//P2//7f0LXHfIMSeh5
8n/2vl//O7f+/e9Wepl0QyK7Hv2fsu3X/5Klf/8nTP/73yb3LGmGBDY//N+69j1Ys3HJl//S0df+G0cu
I94Qj6r7/0vmvoNrVnTpIV4zCNiX3v0f2PKMPM0gYJF3579NwRXyNIOAYdZt8jWDgE7aDfI1D00AAKB+
X6Bjq5qXAAAAAElFTkSuQmCC
</value>
</data>
<data name="bindingNavigatorMoveLastItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
vAAADrwBlbxySQAAASxJREFUOE9jGFygcNbz/1AmBgDJNS14/j9z4mOcahhyZz77n9B9D6sCkNyqI+//
h7c/wG1AxpSn/+ft//0/oesOhiKQ3MJ9H/4HN1zDbUBCz5P/s/f9+t+59e9/t9LLKApBctO2vP/vX30B
twGRXY/+T9n263/J0r//E6b//W+TexauGCTXu/rDf6/SE7gNCGx++L917XuwZuOSL/+lo6/9N45cBtYA
kqub/+6/S/4B3AZ4VN3/XzL3HVyzoksPXDFILn/am//2GdtxG2Bfevd/YMszDM0gAJLLnvz6v0XCetwG
WOTd+W9TcAVDMwiA5FL7X8O9hBUYZt3GqhkEQHJhLS//6wbPw22ATtoNnJIgOb/qh/81fKfhNgAfcMq9
8l/FYwIYQ4UGBWBgAAC+0b+zuQxOnAAAAABJRU5ErkJggg==
vAAADrwBlbxySQAAAStJREFUOE9jYBhUoHDW8//oYjAAkmta8Px/5sTHONUw5M589j+h+x5WBSC5VUfe
/w9vf4BVHgwypjz9P2//7/8JXXcwFIHkFu778D+44RqGHBwk9Dz5P3vfr/+dW//+dyu9jKIQJDdty/v/
/tUXcBsQ2fXo/5Rtv/6XLP37P2H63/82uWfhikFyvas//PcqPYHbgMDmh/9b174HazYu+fJfOvraf+PI
ZWANILm6+e/+u+QfwG2AR9X9/yVz38E1K7r0wBWD5PKnvflvn7EdtwH2pXf/B7Y8w9AMk8ue/Pq/RcJ6
3AZY5N35b1NwBUMzTC61/zXcS1iBYdZtrJpBACQX1vLyv27wPKzyYKCTdgOnJEjOr/rhfw3faTjV4AVO
uVf+q3hMAGN0uYEFAL7Rv7NmXVYYAAAAAElFTkSuQmCC
</value>
</data>
<data name="btAdd.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
vAAADrwBlbxySQAAAUpJREFUOE9jGLzg7gL2/7fmcf6/Oofr/8UZvP+hwsSD60CNfx41/v/zsOH/yckC
pBtwfjov3ICDPSKkG3B8kiBQc93/Pw+q/u9oFydswKWZPP/PTuX7fxKo8Ui/0P993SJAzeX//94r+r++
Qeb/qhq5/0srFf/PL1X+P6tIFdPAU0B//nlYD9RUC8SV///cKwHivP9/72b+/3sn+f/f23H//92MAOKQ
/5NyNDENONQrDHbu3/ulQI0FQI3ZQI2pQI0J///digZqDPv/70bQ/3/X/f53peliGrCzXeL/lmap/+vA
zpX/v6RC8f/fWzFAjeH/p+Zp/J+QpfW/O0P3f3uq/v/mREPCYTIb6E+Qc//dCPjfk6FDWAM6APnz3w1/
IPb735qsT7oB3em6YP+CcH2cEekGtCQZ/G+IN/xfE2v8vzLahHQD6AQYGAAkI9iedfyIaQAAAABJRU5E
rkJggg==
vAAADrwBlbxySQAAAVdJREFUOE/Nz0tLAmEUBmB3kWRoCUVEISFUJGb1OywiKrDsIpZdkJAkDUvDQkij
UKSbVIvatKhNi9oERRAGEQXhjJdp7Hd83/eGs2jhLGQ20QtndTgP71Gp/m0KZ1XInlTjM6XG+4EG5fuK
yaTUIN8bIMUQ0gmtcuBtX/MLPMT0yoHnuA6kuA4iruI20lAZ+DiswWuyFum4Dk+7dbiP6kHEFVDBg+tQ
My4DLbjwG3DqbcORxygHXxJakGIQRFwDEf0gwjKI4AYtzIHmHaA5Oxg/CsYPIb7YIQced+qluvTLCyIs
gRYWQPNO0NwkWNYGxg+DcYNgGSu2Z0xy4C7SiJtwE66kuq049xlAs2Ng/AiS7nbszXci6jIh4jQjPGWR
A+U59hiluowbQMzVVfmgPKU/GdcPxlmx5TArB6KzJunf0gTtPcqBzeluhCYsCIz3wm/rUw78WX4AJCPY
nlwVm9EAAAAASUVORK5CYII=
</value>
</data>
<data name="btCopy.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAFDSURBVDhPlZK9SgNREIXzOpY+gEXQIg+QkMLOTuwsLNRU
ViKIja0SbMRCBQsLUxgQArERf1ARCaLBCErUIoWJjnyzTna4Ccs6cLLkzpzvnr17M5KiMknFQPXyQfbO
m6rNkxtZ3q9LqVyR2bVdVSKEJsbr53dpvnVUrx/RE61W2zI5v6EQqz9rVCwAYLj2+KP6VyL+bJ825LPz
pWYzpknUB7CLARgIE6HK/bfO0F86aklhZiWC8ENEA9D0iUKAKRHgE5kZHdz2VPTyUwsxYHHrWN+TIQAe
6M07F10VvVx+OgZwsgaYO3wZAHhz+ayr5zMAwGAAn8gDMA8F8Fmu7p4Uwh3wibx5vd5TNVptyeaKMQAD
p4o4HJ9oGAD42EQhBoTlE9kFYleEmZQjo+M6q5fJF4thIt4XEZudMfc/Y1i6mrpEfgGL0hchHI3KDgAA
AABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE+SURBVDhPY/hPBGDAB0AKDl55+H/9padgPP/Ijf+dG079
r12453/RhHVgjNcQkCRI4/XnH/4/ffsNjN98hNAg3Hfw/f/oqnm4XQQzAKT4+ON/YEySi0CcFafv///0
7RdYM0wjMS6CGwCyBWYASAG6i0B4z92/YDUg+ebdL/6HZndDDAERICfCDABJIrsI3QAYxmsAsotgmkF4
880/YAySC0mqRhhQs/gA2J8gRSADkA1E1rzq8m8wBsl5hGQiDACFLMyAsu0vMQxA1rzwwm9w+GAYANIA
MwDZRcgGgDRjNQAULVdvPwEbAkoDyC5C1jz11B8wvv/i/X9Hj3BUF4BCFYRBgYPsImwGgAy3dQlFGIAO
kF0ES0AgW0EYpBnkSk1DZ7BapESNSJnoLgL5F4RBzgbZDNIMj0Z0gOoWwgAAi9IXIauDIjwAAAAASUVO
RK5CYII=
</value>
</data>
<data name="btEdit.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEvSURBVDhPpZI/S0JRGIed+i6Nbk0t0iCuSkuDi1MfIKfm
+gAOQktTQkMggYtRkwgidKmgyQwMCgqz0CivvvK8cK7neG4KdeHhHs45v+c9/xLyz08Ft713OQ+6SqXV
kfLlnXJw1lSK5VrERqGkMB4JCCLpvQ7lZfDlQJ+B4EnwI9nTkbYdAZMbjxOPq4eJPH1MvXC2sD8XsOzP
0bcX/C3MXEfAfmzBsnCnP10uWBWu3IS+gJOm0w5fHCZiw0aQzu3GC0xYgm2R+poTRnh8HeqNOALu920w
9MK0F8NGkMrs+ALewqrwUXss3ed+vKB6H+rh2OT3SjpO0IBgcyvnCjgDBGCq8mcMiQ3FHAGdLB/J4vMF
KhoI83LXk6m5gCpmufbyOWlgv0BVIMx4JPj7JzIDGHRUPz2nxiQAAAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEuSURBVDhPpZKxS0JBHMff1P/S2NbUIg7RqrQ0tDj5B9jU
nH+Ag9DSlOAQhNCi6BRBBD1SaDIDg4JCLVSqpz/5HJzdz3vp4MH3vcfdfT73e3cXyJot4NHs9qUSdkxK
t20p1lsmJxc3JkfFq3m2MwUTxucCQCTd96G8DcYq9NkAnoc/kiqPzLcSMPn6eeKl8TSRl8+pB6cyx38C
yv4afXvgfzBzlYD/cQXL4HZvulywCi49RL6AnabThWv5IBa2gt10Nl5gYQn3RaobCkZ4dh+ZE1ECzvdj
MPRgvhdhK0jsHfgC7sIq+PTuVzqvvXjB5WNkNsfNYa5gxgFtEOwk01rAHiAgdlXejCFxw2JKQCflI1m8
voQVbYC5uZtbCV2BLdctn50m/C9hVQKs7sE6bQYYdFQ/+SVRqQAAAABJRU5ErkJggg==
</value>
</data>
<data name="btDel.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
vAAADrwBlbxySQAAAW9JREFUOE+1kE0ow2Ecx3dV3krt4oJaOSCTvIRkMqSxyITIzCQHDouEdnFwIOVC
DrhIDiQl5UTiNG/z2ppafy1S2gX/uDwfY6i1v7Hie3nqeb7fz+/7/FR/Ilwn0G0Exw4fV5GJlXlEZxXC
rIet9bAQvB5Ymgn2sLYAvSZEux7RUQFzE4qQt4bCXAYjPaHvnDoCkLpsRGMB2JqCTGLIijDlwqQ9bEMV
i9OIytR3EMNWcJ/BWH8A6j8/bOGFxwXNxYEvGbMQ9XnQ1/K78KfY3/VXzkMY0qFGG2H4RoLGQshJQNbG
86CNhdrsX9a/uQZTPhQl4rMY4OLofbl3aX7I8uwPC7y/g1YdjyVJuEvT8e1tfwUYteHUxCCfHChDeHmG
QQvokjlOU+PbWA0x3pZnILVVI3uvQyHsbiLnqnGmRCF1NYD8pDhpRxOH7HQoAKZGkFKjceszQbpSrumX
bO+G80MFwKUTxgfgcO/b8D9IpXoFiiMDHIQm0skAAAAASUVORK5CYII=
vAAADrwBlbxySQAAAWtJREFUOE+1kE0ow2Ecx/9X5a2UiwtKOSCTmJBMhuQlMo3IvCUHDouEXHZwIOVC
DrhIDiQl5USy07zNa2tKf2laaRf84/J8xBCetab4XL/f76fn+SnKX4DrGLqrwbHDzywkWJlHdJYjLEbY
Wg8q4eYKlma+d1hbgF4TotWIaC+FuYmAktcXCksx2HrknBOHX1KbiTDngrXhW0kMdSBM2TA5Io+/wuI0
oiz5TcRwB7hPYazfLx3rDz7+gCsXNBb4v1SdgajTQ19TaOMP2NtFmPSIilSo0v1y7FHBnAdZMWi6aO51
kVCTGZoEzzWYciA/Dl9bBZwfvh3XmxIJy7PBJdx5odnAQ2E87qJUfPbtzwGjVpxJEWjH+4ElPD/BYBsY
EjhKicW3sSoVb0vSUFsq0W6upUxhdxMtOxZnYhhqVz1oj3JJUZSdpCg0p0POmLKhJofjNqaDeikX3tFG
uuHsQM65cML4ABzY5fA/eQGKIwMcVjm2bAAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripButton4.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
TgDQASA1MVpwzwAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton8.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
TgDQASA1MVpwzwAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
</value>
</data>
<data name="btFind.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE3SURBVDhPnZIxS8NQFEb7W/wLjh0Fl9a1SxBHBekkWFd1
qYg4Ci5dndSCg2AHl4LQSaRSKDqoFUE7VAjUmvTKueWmL2mw2gunL7zmO+/mJhmZoTJusdF868vpXUfO
b5/lpPEox9f3SvnsRtk8uojxHQ7HEgSEkXS6vrz3xqtdu+xdfUiheEBsJOGCk/mz/hROUHsIIrp+qIKY
hB/a9r+CVAG4Auj5g7iA5/1NACaptgIVLHkb0wWVw13ZL60p2+uerqkCJs1mMgwUU6d1k/xJwI10RZj1
9TPUN7Wam9dgTMC75QR7TjCBkRQs5Jd1jQS8c1ewtZLTPcQW/peADpC44cudgnjZOQ1OCGjTwkwaGBon
GoSrpcVIQqmAj6LZftFBup9vWiUlUQdIDCbsQrsGZRJKBbOXyA++SlEsu6QjvQAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEwSURBVDhPpZIxSwNBEIXzW/wLKS0Fm2ib5hBLBbESjG20
UUQsBRtbKzVgIWhhIwhWIhFBtFAjgqaIcKDmLiPfwO7NXg6NZuDdLnv3vn03uyX5R5VssdB8+ZC9q5Yc
XD7K7sW9bJ9eq1b3z1WLW4eBumkvgwDADKTVjuW1k41ubrV2/CbV+Y0sCRN25uXZQ9qnk7vEqx2nCggg
PIgdfyZ95jwEAOrEXyGA//0JYCGNm0QBk9HC74CdzRVZr82q6nORjoUAOs1i3owouk50BxkIwIekwsz4
/J7qSc1UymoMAJwtO9iOO4BTHjA2MRUCOHMLWJqu6BpgZ/4TgARArPlouSrR6EgxgJj2qBBNY0cnzI3a
uId4AJeiefukjbTXt6jyEJ8AiBMdtiKuk4V4wDD1Db5KUSxr13uqAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton6.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
TgDQASA1MVpwzwAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANISURBVDhPtZBdTFN3GIdJvFu4ULMSFIiZW0JCci5EF4hU
OsZHZoEYslIgFMs6LUioQIsBZHCoKwo1ZVBqaR1DsFIrjiBoZwWDURNGsWvnGSNQoSsg/bBWqodtkhB/
I7XLZvTW5/Z9n+ef/xv2f1TTZPgZa10j+bN4ouZe2UbF7a83SoyFE0WDuQ25AwNbhCMrH4RW30ZBNbCa
LTWOm/M3MOq8DePiLTx4YoXeeglf6tjqiv7FY6U9846vtHZWSPkPue1kjuH3Xow5R9Frv4xKawMkv5AY
nLuG7D62P69X83FpzyN/zz0fJBcXwJHbckLq5svT5PZT5mp6fHEcrdMqCKaOo9h8HF0zPVDf7USGOuXY
kW6Huu2GG8bfaGjGvGA3memMqontwQA5KZEZ7ddxfvYi+FMi5E8IccLSBJ1VhwzlAVuxZib+MDm9MfLw
BRS3fBgwB1DZPYsD5WOyYODE3RKb6Y9RiCwnUbApCyfF0NkNEBlKkdSSwCxUztxvM7rwoyWAKv0KZMNe
tAwtI14wbAsGykzF68POn1BhqUcrpcSVuUGcNbVi/7ef/lDYMcvNIB/+dWVyFRK9CyXfL0GoWcJ3192I
410NBANFQ9zApGcKV+eGoLqjBOv0fn+idF/jiJj8UFV9ITKTNFOdN92o0btR1O4Er82BlkEXdrEv0MEA
tz/b1nmnHZ93sp4kyxPEJnFezEs+v/7vbK6fPniISKm+T5299hii7mVwmhdQol6CqMuO3Vldr7+QqUmT
pHYwhbaqw1HrR48q1ng8mm78Bmv5BaCTU4mkchMlNSziiNKJrLo5yAxusCvHEZXe/vqIqK3d9rK8TOsV
CNbXmmVYNejg7+/F86xDoOMTiX2CEaquz4HCVgfIzTvwzvyK6HQ5HclsZgQDr0QixhqfT7/QnsOzgX64
pPXwne/C6mdp8McSRFzeZapS+wjkJRfymh5gT64ekcwWTlD+lz9zOBxaIMAzsgE+rQreDgWeJiTBEx1L
fJLTRwnaZpAqNGFnisLBYJ7+IqS9SSApOf15fKLDx82HW1QOL7EXnohoYlemmopJazfvSJFLGSwyPLT+
brxxceG+j2Kl3h27zR5GDDxbI4jQ6H0RFvYPxczhJbhCpsgAAAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANDSURBVDhPrZBdTFMHGIZJdrdwsZnV4ISYqQkJybkYc4FA
pUN+ogViiKVgKBarFiRUoMUgIhy6FYWaMihdaR1DsFAr2iDVzgoGAyaVYm31iASq1oKzP3aV6mGbJGTv
khqNcL3n+n2e5Puioj5BNUNGn3HUN5F3xda6yYrVqluHVsvMxdYSY0FjwdDQZ0LTy88/3a9BQTWyWux1
7htPr2PUcwvmhZu498oBvWMA+3RsddXgwtHy3qfug1oXa70bJXeezDc87sOYZxR9rouodjRCcp+Ecf4q
8vrZocI+zbby3ieh3skgJBeegSN35n+UFTPkhh9ttfT4wjjaZlQQTB9Dqe0Yumd7oZ7oQrY6/ejhHre6
/boP5kc0NGMBsJttdHaNdUMkQE5JZGbXNZybuwD+tAhFViGO25uhc+iQrdzpLNXMJh4gZ1ZND99CcTOI
IVsY1T1z2Fk5JosEjk+UOS3PRyGyn8R+qxDCKTF0LgNEhnKktiYxi5Wzd9rNXlyxh1GjfwnZSACtwy+Q
KBhxRgIVltKVEc/vqLI3oI1S4tK8EWctbUj56fvfijvnuNnkw78vTS1Bovei7NdFCDWL+PmaDwm8y+FI
oGSYG57yT+Py/DBUt5VgnU4JJUt3NJnE5Feq2vMxOaSN6rrhQ53eh5IOD3jtbrQavdjCPk9HAtzBPGfX
7Q7s6mK9SpMniS3iwrh3fH7DP3ncEL1nL5Fee4c6e/UPiHpegNPyDGXqRYi6Xdia2/3+hBxNpiSjkyl0
1hzYvHLkiGKZx6PpplNYLtoPOi2DSK20UFLDAg4rPcitn4fM4AO7ehybszrePxEnTnz5rrJCGxAIVpZb
ZFgy6BAa7MOb3L2gE5OJHQITVd/vRnGbG6TeC96ZB4jNktMxzBZGJPCvSMRY5vPpt9pf8HpoEF5pA4Ln
urH0QyZC8QSRUHiRqtY+ATngRWHzPXxboEcMs5UTkT/wVz6HQwsEeE02IqhVIdCpwJ9JqfDHxhPb8/sp
QfssMoQWfJ2ucDOYp3evkT8QTk3LepOY7A5yi+ATVSJAfAf/xlhiS46aisvssG1Kl0sZLDJ6vbeGQEJC
dPCbeGlg01abnxEH/xcbifWb/53/AMXM4SV2ZbSMAAAAAElFTkSuQmCC
</value>
</data>
<data name="메일전송ToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
@@ -309,20 +309,20 @@
<data name="엑셀에서가져오기ToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m
dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAALnSURBVDhPhZJZT1NBGIa5kt+AXnhvFCEYNEJEsSiI
BhVRCRJLaUsFBQlLMGExpUZWAyKQqCEQjTcaorIJBaGFQhuKEUEUKJQ9obQs3U45PeV1zim0mpB48cxc
zHzPzPfO+ADw4RUo+FfK1fKYCvU6wU6gOCrZWUPFlA9R0aWq8ZDcgepQScsBtmYPboh6MtBvNFPGdauD
MVkdMFm8GM0sFH6tmFHweQGiF9O2EEmL7z+C6BKVwWynmaE5Gpp5JzQLTqjnnFDpt6GYduDLhBUOJ9D6
246Cj/MQPp+2n0lr5SSc4HLpoH3Lto2RRQbfFp1kdmKYk2xDqXOga8JGBC6MrDDQLNOQtS8g/FHvO05w
u/L87M2KuB1xXSySa2+gtrMJxc2VCC/yxzlCpOw0IqTBqGqt4lqxUE6sbtgQUagkd3ILPnwda0Pb1GvU
qrM5yd2aS8h+m4j0pngI669CUC9A6+gq2sYtaPmxhU2S0wXpALUn8I0tz2DUk314MyZFmeIBJ+HXRZPi
a5C8TEG/zsq10jNlh5zksGGlcaF40C1gh6gSJZXZkAStToV6TS6edmeBiMGviYN2zgYtCZYNuH/GnYmJ
CCKkfwnCCovok3mHkVgdiZGZIcj6UiFtz0T8Mx6aFJ/wfckJLQl2SE9jkGAw0wh/vCsIyjkoDso5hPTG
BNypi0JC1UVOktmeiJzmFHITHtj1kQUGw+wTE8wkyLB8lUewRCCb/BCU7YfrZTykvUpC/4QcMnkeUt8n
QVh3C1oi0JC/oSYCinYhJFfhbeFs/gC1sklhdInhUE4u42GDBCJSKK6PR1bjfc8ai22bQXBGr1cQmqeg
Fk1ewf+wkhYCU+RewanMXpt+zbaz3+b9IL/SdUzYafEIgu51zxq2HGa9kXLNrtkxa7BDt8uaheZgk7dQ
5HQH43Lt7Gwe4Xf89AhOpMrTAkRd3YFiuSlA3EV5EHVRxwn+ok43Qo6No4KOHn9BRzIAnz/8M/0zf/tL
eAAAAABJRU5ErkJggg==
dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAL1SURBVDhPhdHpTxNBHMZxX+nfoL7wvVHU1KARI8qh
KBq8EIgSOdqCoCiBEkxETMF4QA2KtIkaItH4RmNULmlBaKHQhtaIIIIgpS2ScFSg3e4sO1seswu0amJ8
8d19MTOf5DezBsCamBJj2rEKiyGh0vIzodLCJlRaiJRG/FtJQkUPib9jHogo6rq/N7t+rXhmNelz+GZX
56yXzP5kOMHDcPD4Qs16xQi+TnpR8s4FxcMRf0R2/bo/gPjb5mkvyws94zysTgqri8IyTmF2LMI4wuH9
IAOOAg1DLEreOCF/MMLuy22QEAk4eqebXfAvwu4W8NFNYXdT9ErIIkyjHPSDfnA0APukAOsPHuVNLkRd
bX8hAcma6LEzlYlLSu0pZNacRk1LHcpeaxBVGoYDpWGIK9+DWHU4qhqqpFF8hGJqzo/Y6ya6Crz60N+I
xm9PUGMplJDz1UdQ+DwVeXUpkOuOI0OXgYa+KTQO+FD/eQHzDIeD6i6yCqw7VXFZsAx34Fm/GneNlyQk
TRsPue4Esh9loXOUkUZp+8bCMMRijuFxsKx7GZBe4baJ5NemwzZqhs5ahFutBUjWRCOtOhG2cT9sTgrx
gju/L9+Jh+ERq/4NiLxeyu8q3oTU+3Gwf+9BeUcO1E35SLkXgzrjW3yaoLC5KXocPLodPKa9PKJurAAy
1QalTLUReU/P4pz2MM5WHZKQ/KZUqF5nIVkTA3Hd7hLQKz6xk8JLKCKvmYPAhEy1ATLVesgK1+Pk3Rjk
Pk5H56AB5YZi5LxMh1ybBJtLgHWcwuKkIHwAEUXG0Aj7r3WRyXmCvglByjT8A1dqs6HQJkGpS0HB04vB
NTH/ooDwy+0hYG+xkbg9IeB/MYRiR5YhBOzOb/c7ZvxLf2/8VxwNBLbKW3xBQHahdWx6gfM6ZklgbIbF
2DSL0ZVmfLyUePM+IoDhhEBgaWl+c1rzlyCwM8eQu12hb92hNHi2K/UkmEJPtin0JEzRspxcam5LRnNb
WEZzpnj2F/wz/TMpWaSxAAAAAElFTkSuQmCC
</value>
</data>
<data name="견적서폴더열기ToolStripMenuItem.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
@@ -338,30 +338,30 @@
<data name="toolStripButton7.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG8SURBVDhPjZI5SwNRFIXzY+wsLGysAgqiop2NAXHBBQSX
EGNQEHsbFdMJWphCcCHggmgaUaIhLlHjJDHuqCjqjOKWxBk95l7mjQxRyYEPZh7vfO8Obyz/RVVVCPSl
7CJKtdNBg6xFotjg3YLn9BXd/kss3Gomkb7VHHECbaLC6NEzCxyrZ/xMa4R10Jsp0TQN7rUmjATaWEAR
BdfqufHcG1JQNrljFtALlWPJJQTkcRbEHxNQUsCG/AnbzBY6g9cMCUongqAD9fqPgEISEsSUBNz7D5CT
Glp9UTj8V8zgSQrFnvXfBXfqId6+HlmwfPGEuUtASos6fHEuEiTJHZpHyFbHEv4UIbhSwxgPORFVVlAz
tY6I/A5JThjjU5HA7CKzUGA1C8aCDozu2nGjHsAT7jIKgmu7yyi/uvogFVX8CFiy0oibD4kF3tMeHjMV
iwOKAvgD2K+sxn1bD0OCvcLyzNuwT+cbsCB6iKcBNz4fFBw3t+Ou1cl89Q9j21piFlDENAQJXhZ96R9i
DqlwBGctHVwkSOLLycsUiAjJZlU9kgeRtEAyxqeiKP8pEKENoiAwri/biJME+rIei+Ub4tJbaW7QZrcA
AAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAG0SURBVDhPjZLLK0RhGMb9MXYWFjZWU5QQOxtKLrmUcpkG
EyV7G2R2ioVZKJemME3MRjRM4zKYOXNzGRoinENuM8c5PHrf+g7jIE899Z3L7/e9p+/k5PwRTdMg+v3Z
nxFQ/VzA6L9FAmxybcN58oQ+XwruKz1L9J3hfN2VgInDBxbY1pK8pntUy4jLLNF1HY71Foz7O1hAEYB9
7dRYDwQVlM/sZgvoguBYZhl+eYoFibs0FBXYlN9QM7+N7sAFlwRl0wHQhiYBhSQkiClpOA5uIWd0tHuj
sPnOuSPHKkqcGz8LrrU4nt/vWLBydo/FFCApaXR5EwxSSZI3uoRgTQNL+FOE4FwLYSrYg6iyirrZDUTk
F0hy2hifQCoWPFx3oSVbMBmwYWLPikstDGeo1wBEL6x2A36yD0IqrvwUsGS1GZevEgtcJ/08phpLAIoC
+Pw4qKrFTUc/lwT7RRXm07DOFRhlQTSO+2EH3m4VHLV24rq9h/s+NIYdS6n5fxDTUEnw6PEC7kWooQiS
bV0MUknizc03C0SEZKu6EZlwBGpIMsYnUMC/CkToBQGIGsf333z9rJ/AD+LSW2lKFCgcAAAAAElFTkSu
QmCC
</value>
</data>
<data name="toolStripButton5.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
TgDQASA1MVpwzwAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIFSURBVDhPpZLtS1NhGMbPPxJmmlYSgqHiKzGU1EDxg4iK
YKyG2WBogqMYJQOtCEVRFBGdTBCJfRnkS4VaaWNT5sqx1BUxRXxDHYxAJLvkusEeBaPAB+5z4Jzn+t3X
/aLhnEfjo8m+dCoa+7/C3O2Hqe0zDC+8KG+cRZHZhdzaaWTVTCLDMIY0vfM04Nfh77/G/sEhwpEDbO3t
I7TxE8urEVy99fT/AL5gWDLrTB/hnF4XsW0khCu5ln8DmJliT2AXrcNBsU1gj/MH4nMeKwBrPktM28xM
cX79DFKrHHD5d9D26hvicx4pABt2lpg10zYzU0zr7+e3xXGcrkEB2O2TNec9nJFwB3alZn5jZorfeDZh
6Q3g8s06BeCoKF4MRURoH1+BY2oNCbeb0TIclIYxOhzf8frTOuo7FxCbbVIAzpni0iceEc8vhzEwGkJD
lx83ymxifejdKjRNk/8PWnyIyTQqAJek0jqHwfEVscu31baIu8+90sTE4nY025dQ2/5FIPpnXlzKuK8A
HBUzHot52djqQ6HZhfR7IwK4mKpHtvEDMqvfCiQ6zaAAXM8x94aIWTNrLLG4kVUzgaTSPlzLtyJOZxbb
1wtfyg4Q+AfA3aZlButjSfxGcUJBk4g5tuP3haQKRKXcUQDOmbvNTpPOJeFFjordZmbWTNvMTHFUcpUC
nOccAdABIDXXE1nzAAAAAElFTkSuQmCC
</value>
</data>
<metadata name="cm1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
@@ -402,56 +402,56 @@
<data name="btSearch.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAE4SURBVDhPtZPPasJAEMbzPn0FwWA92JtP4NGKB1/DP7ei
QSsovkChh7ZBrCfpyR4sikopUgq9StFzM/UbZ5asSS4FfzAkO7vft5udiZMEnSBpk5dhFJmncjdHxXaG
A+9K4SbFT1luEwQBbXavVO5d0nI3ovnW5yeiMriiu+kt5asXbABEdgRigAUQDr+aHLU3lxoLl/yPJhvF
GsiYJ/vPdX5qPK3bVJ25VFukafztGQNsKHJ791I3w+8KcpNth8XDz5YxACI/gsR1J8sTYcO4UIzwv1gG
cTshgJ5IT8hChTMHsHi+v+fvffmxywVwN2FDkdsGEOK2ceu4feQ0tDqKyKMGqDfqjvprLzyswuX7Tf4E
dBo6zn/3OB7XHovRyuhQ6+hhYKA9DpL+A1keRebNAhkaJH0OHOcP031C4EjYr6wAAAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEiSURBVDhPY2DAAf6jAXRxVNVIAKYga5bH/7TpTmAMYsNA
3EQb3Ab8+/fv/71PZ/5nzXX+f+3T7v+X320H0yCct8Dz/9rTM/8HtGvDDcPQDAIgBSCNOx9NBuOOiw7/
u644/N9+ZzLYIKwGwARAkvP2dIJpGN52Y/r/9gsO/zuu2P/f+3Qq3ACQhVhtz5jlBLcBJnb43Qyw5p0P
pmB3AcwV6TNcwBLIBmLDKAbAeWQAFAPQbUG2DeYimAsxnA4z4PLnDWD/Hv2AGl0gAAobZAOxGgDSCApt
UKiDQh/ZJbDYgQGcBoDiGxTvoPiHpYUt15Gj7y9uL4BSGijFbb81FYy33pgK1gxKyqAUiuJ0ZAAyAJbG
QQBXPkDXBwfoCuA60MRpAgDTfULg/+7qPQAAAABJRU5ErkJggg==
</value>
</data>
<data name="toolStripDropDownButton1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAEcSURBVDhPpZG9SkNBEIXvW+gLiI8gpEon+ADa2liEVAHL
NHY+goWdhYViYxGEdOksUtlIQAiIIQwkIDaC4XjPycx613CJPx8My87sN7N7b1HHYvEBwtVTv4OyDU/V
xFM/pyrb/YlWL61HksuYnQHvI9igo60fWUXVCjH1e4Mqrn5NnA+7WjPZyptMu8DrFax/COvtw273FCRr
kBHypJw8OQKeD4C3u2Ww2XVDx9SAcJMmx1TKFMe7wNMOMNpSbkUO5MZbX9q5/LipXK0csMi3iphK+aH4
o8xnlLJi3IJdbKvsSg4L/MrpG8SVSzGitkGSyfQYdtNMV6YkkU3sEna+obyrS3TQ/y/lIGqEYgSRGChT
wdMZXkp4+r8UxSdk1wcO0mb/TQAAAABJRU5ErkJggg==
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAELSURBVDhPnZGvCgJBEIfvLfQFxEcQTDbBB9BqMYhJMFps
PoLBZjAoFoMINpvBZBFBOBBFBhTEIig/mTln7/b0/PeDYWFnv2927xwnIrfbFRxew72vwjDNmyIJ9z4m
CNOs8ZskCOPQAi4r0LT6XuKd9qNTw4JgLJgnHud1WS2YmsC+Dpx6oEkRNMqDhjkpI1GBFYV3VWBXAjYF
4Dz2imX9tC8wEp2sUxlm0M0C6xSwSsjeE2xJ9K3big0v47IXCWtEMil6Ep3K8ML5E+ZnLByv3DKok4yW
iGCU97+BXtktm4oUGJizr4EGGXNlhgRkCXVB7dizRA4+/i/DGu1Jvx0z9VIQjNV85Jszf+UOZNcHDvOD
dw4AAAAASUVORK5CYII=
</value>
</data>
<data name="toolStripButton2.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADlSURBVEhL7dQxCsIwFMbxnMrZWatzUXRUCo5ewSs4ewZR
DyDeQfEETsUbRL7YYNSXNC/UOiQP/kvzyE9KUcQ7y/VJtlHFvQYPjzf50xKsJsFmq81WHq538swMO9il
zhALxkWd7kAOJwsnjjPsYNeGs2B14fR5YTYq5O5c1u7sL987iAUjF+6LIjaMKJyDoiAYmVAvn8lsXHij
KBhGwDWof4APihqHqQ+OKhg2X3U/n7+9ah88CKY+pM9ndTgbplDbmQtnwS7UtmPDWfDf/jIRLnKhOuzY
UMSGmyrBauKE26jiohshHicE2B3dbRrmAAAAAElFTkSuQmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADiSURBVEhL7dVBCsIwEAXQnsq1a62ui6JLpeDSK3gF155B
1AOId1A8gSvxBiNTCcTJJM3E2i6SwN9MhnklDW2WRbvW2wu0EepW8PkBf02CE2xks9vD6f4y6jTYg720
riKCcVCvP4LxbOXEcQ97sNeGi+Bq4PwzMJ+UcLg+a3uON7NHDHODdZzu2dAgmAMQpzUXGgxTfFAsIJ+W
3uhPMAZxBaoH8EExjcPcheMSDOtHPSyWX0ftgwfB3EWitTpcDFNAf6d0z4WLYDqYu0i0x4aL4M4+mZhO
fhJNJsERw22EuvGsNycE2B33w41tAAAAAElFTkSuQmCC
</value>
</data>
<data name="btViewDel.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADuSURBVDhPY6AK+E8mgGqHGPB5/rz/X6ZP//+pq/P/5+pq
CM7N/f85Ofn/58jI/58DA/9HmknCMYYBIM1/X736//vBg/9/nj79//Py5f8/z50DGwzDJ4K8/28sc8Vu
AMhmkGawjbGxEFuhNoOxjw/cgARrGUwDQE4G2YqiOTQUxYDZriZ4DAD69+umTTg1EzYA5HSQX9E1YzEA
axiAnA4KSGwa0Q0IMZbAYgDQVlBAvgZ6BRfu1pfFbcBLU9P/95WV/9+Qlv5/ko8PjnewscExyOk4DSAG
4DSAGEBVAzBigRgAMwCGyTIAHTAwMDAAABYIANCNo26UAAAAAElFTkSuQmCC
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADuSURBVDhPY2CgBvhPJkAx4PP8ef+/TJ/+/1NX5//P1dUQ
nJv7/3Ny8v/PkZH/PwcG/o80k4RjDANAmv++evX/94MH//88ffr/5+XL/3+eOwc2GIZPBHn/31jmit0A
kM0gzWAbY2MhtkJtBmMfH7gBCdYymAaAnAyyFUVzaCiKAbNdTfAYkJv7/+umTTg1EzYA5HSQX9E1YzEA
axiAnA4KSGwa0Q0IMZbAYkBoKDggX+fm4sTd+rK4DXhpavr/vrLy/xvS0v9P8vHB8Q42NjgGOR2nAcQA
nAYQA6hqAEYsEANgBmDNC8QAWDggA5A4ABYIANBlPZqZAAAAAElFTkSuQmCC
</value>
</data>
<data name="toolStripButton3.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGSSURBVFhH7ZDdSgJBGIa9kY67hK6gdFX8g27Cm4gIYjVN
6KyMrM6qs4Kik+jXGbeO6kACQ/IwDJSCLKPJd9gVk8l1ncWNmAdeZme+l/FxfIp/j18nTCbBNH2LZ4xj
f+pyEvfNLBgTiZxxhHNR30m6gjLUX95ZoVhtR/RSc3qeTIUzF40CqbZxLoNrghZccok+YTWPpHBdEC8W
WqJ8dQPXBYHbdylBGTwT3DitsWi2xApnNfNEjGeCsWWD97AOwjNBvBzkNv/qCw6LEhSBeW8GgfnYBYEl
Z9fFXAmKsOTsupgrQRGWnF0XcyUowpKz62KuBPuxxHrzG5iNXdAJSlAWJSiLEpTlh6BsZldu2OrJI//G
in1/Z5R0BQ8rX1LZvWux5FaZX5rcLrO9zl7Uc5KuYGix9LxOm8KSk+zctvilkBXNnSTf8dEy53UuGEiR
ICRx+ajp/4P4AVFv2EBO04nGBWUJpIvxWO7qNU8b7OD+k62RBot29lqKxsyK9wR1koikrx8Cc/sf4Syt
QNocKTzG5/sG4hiki22pPhYAAAAASUVORK5CYII=
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGESURBVFhH7ZDLSsNAFIb7Iq59BJ9A27SlN/Al+hIigiS9
WHCnFaPu1J2C4ka8tTONruyiCJVil1IhwYLRiiNnwNBOJc10AiMyH/ycZOZw8uVEIor/TlRHRCTxAn7L
lqyzqHEzC/MWVqyZXMU6hXO2lzeeoAi913di1juDlN5w5pfRXLJ0bZuoM4BzEUIT/IFKFvEzVPZuGkIX
hI0lipjWMAhdEAh7lhIUQZrg9kWXpMsNYl522asRpAlm1izaB9UPaYKwOZDb+asbDIoS/A24H44fUgQB
JehHkFlK0I8gs5SgH0FmKUE/Js0alpskKUWQByUoihIURQmKMiIomsX1O7Jx/kSfocI72zNNPMGT9pdQ
Dpouye+26ND8XoscNt2xHt54gonVxssWdsYaeLN/79KhIMve8aaKHaKVrnpUMGagOEiy6+UJ+4PwAbaH
JyCn6UijgqLECvVsplLrV7FNjh8+ySaySbpS62sGzrC90ojrKJcq3D7Glo4+kmXcBmm2RyGLb+IYpIub
NYmbAAAAAElFTkSuQmCC
</value>
</data>
<metadata name="fpSpread1_Sheet1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">