feat: 품목정보 상세 패널 추가 및 프로젝트/근태/권한 기능 확장
- Items: 우측에 이미지, 담당자, 입고/발주내역 패널 추가 (fItems 윈폼 동일) - Project: 목록 및 상세 다이얼로그 구현 - Kuntae: 오류검사/수정 기능 추가 - UserAuth: 사용자 권한 관리 페이지 추가 - UserGroup: 그룹정보 다이얼로그로 전환 - Header: 사용자 메뉴 서브메뉴 방향 수정, 즐겨찾기 기능 - Backend API: Items 상세/담당자/구매내역, 근태 오류검사, 프로젝트 목록 등 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -228,6 +228,50 @@ namespace Project.Web
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 즐겨찾기 목록 조회 (grp=17)
|
||||
/// memo가 표시명, svalue가 URL
|
||||
/// </summary>
|
||||
public string Favorite_GetList()
|
||||
{
|
||||
try
|
||||
{
|
||||
var sql = "select isnull(code,'') as code, isnull(memo,'') as name, isnull(svalue,'') as url " +
|
||||
"from common WITH (nolock) " +
|
||||
"where gcode = @gcode and grp = '17' and isnull(code,'') <> '' " +
|
||||
"order by code";
|
||||
|
||||
var cs = Properties.Settings.Default.gwcs;
|
||||
var cn = new SqlConnection(cs);
|
||||
var cmd = new SqlCommand(sql, cn);
|
||||
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||
|
||||
var da = new SqlDataAdapter(cmd);
|
||||
var dt = new DataTable();
|
||||
da.Fill(dt);
|
||||
da.Dispose();
|
||||
cmd.Dispose();
|
||||
cn.Dispose();
|
||||
|
||||
var result = new System.Collections.Generic.List<object>();
|
||||
foreach (DataRow dr in dt.Rows)
|
||||
{
|
||||
result.Add(new
|
||||
{
|
||||
name = dr["name"]?.ToString() ?? "",
|
||||
url = dr["url"]?.ToString() ?? ""
|
||||
});
|
||||
}
|
||||
|
||||
return JsonConvert.SerializeObject(new { Success = true, Data = result });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Favorite_GetList 오류: {ex.Message}");
|
||||
return JsonConvert.SerializeObject(new { Success = false, Data = new object[] { }, Message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Items API
|
||||
@@ -421,6 +465,349 @@ namespace Project.Web
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 품목 이미지 조회 (Base64 반환)
|
||||
/// </summary>
|
||||
public string Items_GetImage(int idx)
|
||||
{
|
||||
try
|
||||
{
|
||||
var cs = Properties.Settings.Default.gwcs;
|
||||
using (var cn = new SqlConnection(cs))
|
||||
using (var cmd = new SqlCommand("SELECT image FROM Items WHERE idx = @idx AND gcode = @gcode", cn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@idx", idx);
|
||||
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||
|
||||
cn.Open();
|
||||
var data = cmd.ExecuteScalar() as byte[];
|
||||
|
||||
if (data != null && data.Length > 0)
|
||||
{
|
||||
var base64 = Convert.ToBase64String(data);
|
||||
return JsonConvert.SerializeObject(new { Success = true, Data = base64 });
|
||||
}
|
||||
|
||||
return JsonConvert.SerializeObject(new { Success = true, Data = (string)null });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return JsonConvert.SerializeObject(new { Success = false, Message = "이미지 조회 실패: " + ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 품목 이미지 저장 (Base64 입력)
|
||||
/// </summary>
|
||||
public string Items_SaveImage(int idx, string base64Image)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] imageData = null;
|
||||
|
||||
if (!string.IsNullOrEmpty(base64Image))
|
||||
{
|
||||
// data:image/png;base64, 형식 제거
|
||||
if (base64Image.Contains(","))
|
||||
{
|
||||
base64Image = base64Image.Substring(base64Image.IndexOf(",") + 1);
|
||||
}
|
||||
imageData = Convert.FromBase64String(base64Image);
|
||||
|
||||
// 이미지 크기 조정 (640x480 제한, WinForms과 동일)
|
||||
using (var ms = new System.IO.MemoryStream(imageData))
|
||||
using (var img = System.Drawing.Image.FromStream(ms))
|
||||
{
|
||||
System.Drawing.Image resized = img;
|
||||
bool needResize = false;
|
||||
|
||||
if (img.Width > 640)
|
||||
{
|
||||
var newRate = 640.0 / img.Width;
|
||||
var newHeight = (int)(img.Height * newRate);
|
||||
resized = new System.Drawing.Bitmap(640, newHeight);
|
||||
using (var g = System.Drawing.Graphics.FromImage(resized))
|
||||
{
|
||||
g.DrawImage(img, new System.Drawing.Rectangle(0, 0, 640, newHeight));
|
||||
}
|
||||
needResize = true;
|
||||
}
|
||||
else if (img.Height > 480)
|
||||
{
|
||||
var newRate = 480.0 / img.Height;
|
||||
var newWidth = (int)(img.Width * newRate);
|
||||
resized = new System.Drawing.Bitmap(newWidth, 480);
|
||||
using (var g = System.Drawing.Graphics.FromImage(resized))
|
||||
{
|
||||
g.DrawImage(img, new System.Drawing.Rectangle(0, 0, newWidth, 480));
|
||||
}
|
||||
needResize = true;
|
||||
}
|
||||
|
||||
using (var outMs = new System.IO.MemoryStream())
|
||||
{
|
||||
resized.Save(outMs, System.Drawing.Imaging.ImageFormat.Jpeg);
|
||||
imageData = outMs.ToArray();
|
||||
}
|
||||
|
||||
if (needResize)
|
||||
{
|
||||
resized.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var cs = Properties.Settings.Default.gwcs;
|
||||
using (var cn = new SqlConnection(cs))
|
||||
using (var cmd = new SqlCommand("UPDATE Items SET image = @image, wuid = @wuid, wdate = GETDATE() WHERE idx = @idx AND gcode = @gcode", cn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@idx", idx);
|
||||
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||
cmd.Parameters.AddWithValue("@wuid", info.Login.no);
|
||||
|
||||
if (imageData != null)
|
||||
{
|
||||
cmd.Parameters.Add("@image", SqlDbType.VarBinary, -1).Value = imageData;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd.Parameters.Add("@image", SqlDbType.VarBinary, -1).Value = DBNull.Value;
|
||||
}
|
||||
|
||||
cn.Open();
|
||||
var result = cmd.ExecuteNonQuery();
|
||||
|
||||
return JsonConvert.SerializeObject(new { Success = result > 0, Message = result > 0 ? "이미지가 저장되었습니다." : "이미지 저장에 실패했습니다." });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return JsonConvert.SerializeObject(new { Success = false, Message = "이미지 저장 실패: " + ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 품목 이미지 삭제
|
||||
/// </summary>
|
||||
public string Items_DeleteImage(int idx)
|
||||
{
|
||||
return Items_SaveImage(idx, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 품목의 공급처 담당자 조회
|
||||
/// </summary>
|
||||
public string Items_GetSupplierStaff(int supplyIdx)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (supplyIdx <= 0)
|
||||
{
|
||||
return JsonConvert.SerializeObject(new { Success = true, Data = new object[] { } });
|
||||
}
|
||||
|
||||
var sql = @"SELECT idx, name, grade, dept, tel, email, memo
|
||||
FROM Staff WITH (NOLOCK)
|
||||
WHERE gcode = @gcode AND cid = @cid";
|
||||
|
||||
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("@cid", supplyIdx);
|
||||
|
||||
var da = new SqlDataAdapter(cmd);
|
||||
var dt = new DataTable();
|
||||
da.Fill(dt);
|
||||
|
||||
var result = new System.Collections.Generic.List<object>();
|
||||
foreach (DataRow dr in dt.Rows)
|
||||
{
|
||||
var name = dr["name"]?.ToString() ?? "";
|
||||
var grade = dr["grade"]?.ToString() ?? "";
|
||||
if (!string.IsNullOrEmpty(grade))
|
||||
{
|
||||
name += $"({grade})";
|
||||
}
|
||||
result.Add(new
|
||||
{
|
||||
idx = Convert.ToInt32(dr["idx"]),
|
||||
name = name,
|
||||
tel = dr["tel"]?.ToString() ?? "",
|
||||
email = dr["email"]?.ToString() ?? "",
|
||||
dept = dr["dept"]?.ToString() ?? ""
|
||||
});
|
||||
}
|
||||
|
||||
return JsonConvert.SerializeObject(new { Success = true, Data = result });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return JsonConvert.SerializeObject(new { Success = false, Message = "담당자 조회 실패: " + ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 품목의 최근 입고내역 조회 (indate 기준)
|
||||
/// </summary>
|
||||
public string Items_GetIncomingHistory(int itemIdx)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sql = @"SELECT TOP 10 idx, indate, request, pumqty, pumprice, state
|
||||
FROM Purchase WITH (NOLOCK)
|
||||
WHERE pumidx = @pumidx
|
||||
AND ISNULL(indate, '') <> ''
|
||||
AND ISNULL(isdel, 0) = 0
|
||||
ORDER BY indate DESC";
|
||||
|
||||
var cs = Properties.Settings.Default.gwcs;
|
||||
using (var cn = new SqlConnection(cs))
|
||||
using (var cmd = new SqlCommand(sql, cn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@pumidx", itemIdx);
|
||||
|
||||
var da = new SqlDataAdapter(cmd);
|
||||
var dt = new DataTable();
|
||||
da.Fill(dt);
|
||||
|
||||
var result = new System.Collections.Generic.List<object>();
|
||||
foreach (DataRow dr in dt.Rows)
|
||||
{
|
||||
var date = dr["indate"]?.ToString() ?? "";
|
||||
// 년도 2자리로 표시 (2024-01-01 -> 24-01-01)
|
||||
if (date.Length > 9) date = date.Substring(2);
|
||||
|
||||
result.Add(new
|
||||
{
|
||||
idx = Convert.ToInt32(dr["idx"]),
|
||||
date = date,
|
||||
request = dr["request"]?.ToString() ?? "",
|
||||
qty = dr["pumqty"] == DBNull.Value ? 0 : Convert.ToInt32(dr["pumqty"]),
|
||||
price = dr["pumprice"] == DBNull.Value ? 0m : Convert.ToDecimal(dr["pumprice"]),
|
||||
state = dr["state"]?.ToString() ?? ""
|
||||
});
|
||||
}
|
||||
|
||||
return JsonConvert.SerializeObject(new { Success = true, Data = result });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return JsonConvert.SerializeObject(new { Success = false, Message = "입고내역 조회 실패: " + ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 품목의 발주내역 조회 (pdate 기준)
|
||||
/// </summary>
|
||||
public string Items_GetOrderHistory(int itemIdx)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sql = @"SELECT TOP 10 idx, pdate, request, pumqty, pumprice, state
|
||||
FROM Purchase WITH (NOLOCK)
|
||||
WHERE pumidx = @pumidx
|
||||
AND state <> 'Cancled'
|
||||
AND ISNULL(isdel, 0) = 0
|
||||
ORDER BY pdate DESC";
|
||||
|
||||
var cs = Properties.Settings.Default.gwcs;
|
||||
using (var cn = new SqlConnection(cs))
|
||||
using (var cmd = new SqlCommand(sql, cn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@pumidx", itemIdx);
|
||||
|
||||
var da = new SqlDataAdapter(cmd);
|
||||
var dt = new DataTable();
|
||||
da.Fill(dt);
|
||||
|
||||
var result = new System.Collections.Generic.List<object>();
|
||||
foreach (DataRow dr in dt.Rows)
|
||||
{
|
||||
var date = dr["pdate"]?.ToString() ?? "";
|
||||
// 년도 2자리로 표시 (2024-01-01 -> 24-01-01)
|
||||
if (date.Length > 9) date = date.Substring(2);
|
||||
|
||||
result.Add(new
|
||||
{
|
||||
idx = Convert.ToInt32(dr["idx"]),
|
||||
date = date,
|
||||
request = dr["request"]?.ToString() ?? "",
|
||||
qty = dr["pumqty"] == DBNull.Value ? 0 : Convert.ToInt32(dr["pumqty"]),
|
||||
price = dr["pumprice"] == DBNull.Value ? 0m : Convert.ToDecimal(dr["pumprice"]),
|
||||
state = dr["state"]?.ToString() ?? ""
|
||||
});
|
||||
}
|
||||
|
||||
return JsonConvert.SerializeObject(new { Success = true, Data = result });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return JsonConvert.SerializeObject(new { Success = false, Message = "발주내역 조회 실패: " + ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 품목 상세 정보 조회 (supplyidx 포함)
|
||||
/// </summary>
|
||||
public string Items_GetDetail(int idx)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sql = @"SELECT idx, sid, cate, name, model, scale, unit, price, supply, supplyidx, manu, storage, disable, memo
|
||||
FROM Items WITH (NOLOCK)
|
||||
WHERE idx = @idx AND gcode = @gcode";
|
||||
|
||||
var cs = Properties.Settings.Default.gwcs;
|
||||
using (var cn = new SqlConnection(cs))
|
||||
using (var cmd = new SqlCommand(sql, cn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@idx", idx);
|
||||
cmd.Parameters.AddWithValue("@gcode", info.Login.gcode);
|
||||
|
||||
cn.Open();
|
||||
using (var reader = cmd.ExecuteReader())
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
var item = new
|
||||
{
|
||||
idx = reader.GetInt32(reader.GetOrdinal("idx")),
|
||||
sid = reader["sid"]?.ToString() ?? "",
|
||||
cate = reader["cate"]?.ToString() ?? "",
|
||||
name = reader["name"]?.ToString() ?? "",
|
||||
model = reader["model"]?.ToString() ?? "",
|
||||
scale = reader["scale"]?.ToString() ?? "",
|
||||
unit = reader["unit"]?.ToString() ?? "",
|
||||
price = reader["price"] == DBNull.Value ? 0m : Convert.ToDecimal(reader["price"]),
|
||||
supply = reader["supply"]?.ToString() ?? "",
|
||||
supplyidx = reader["supplyidx"] == DBNull.Value ? -1 : Convert.ToInt32(reader["supplyidx"]),
|
||||
manu = reader["manu"]?.ToString() ?? "",
|
||||
storage = reader["storage"]?.ToString() ?? "",
|
||||
disable = reader["disable"] != DBNull.Value && Convert.ToBoolean(reader["disable"]),
|
||||
memo = reader["memo"]?.ToString() ?? ""
|
||||
};
|
||||
|
||||
return JsonConvert.SerializeObject(new { Success = true, Data = item });
|
||||
}
|
||||
else
|
||||
{
|
||||
return JsonConvert.SerializeObject(new { Success = false, Message = "품목을 찾을 수 없습니다." });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return JsonConvert.SerializeObject(new { Success = false, Message = "품목 상세 조회 실패: " + ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user