summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/pq/service.ts14
-rw-r--r--lib/tech-vendors/service.ts198
-rw-r--r--lib/tech-vendors/table/import-button.tsx76
-rw-r--r--lib/vendor-investigation/service.ts66
-rw-r--r--lib/vendor-investigation/table/update-investigation-sheet.tsx43
5 files changed, 326 insertions, 71 deletions
diff --git a/lib/pq/service.ts b/lib/pq/service.ts
index 989f8d5c..40d81302 100644
--- a/lib/pq/service.ts
+++ b/lib/pq/service.ts
@@ -3706,13 +3706,13 @@ export async function updateInvestigationDetailsAction(input: {
try {
console.log(`๐Ÿ“ ์‹ค์‚ฌ ์ฒจ๋ถ€ํŒŒ์ผ ์ฒ˜๋ฆฌ ์ค‘: ${file.name} (${file.size} bytes)`);
- // saveFile์„ ์‚ฌ์šฉํ•˜์—ฌ ํŒŒ์ผ ์ €์žฅ
- const saveResult = await saveFile({
+ // saveDRMFile์„ ์‚ฌ์šฉํ•˜์—ฌ ํŒŒ์ผ ์ €์žฅ
+ const saveResult = await saveDRMFile(
file,
- directory: `vendor-investigation/${input.investigationId}`,
- originalName: file.name,
- userId: "investigation-update"
- });
+ decryptWithServerAction,
+ `vendor-investigation/${input.investigationId}`,
+ "investigation-update"
+ );
if (!saveResult.success) {
console.error(`โŒ ํŒŒ์ผ ์ €์žฅ ์‹คํŒจ: ${file.name}`, saveResult.error);
@@ -3738,7 +3738,7 @@ export async function updateInvestigationDetailsAction(input: {
// DB์— ์ฒจ๋ถ€ํŒŒ์ผ ๋ ˆ์ฝ”๋“œ ์ƒ์„ฑ
await tx.insert(vendorInvestigationAttachments).values({
investigationId: input.investigationId,
- fileName: saveResult.originalName!,
+ fileName: saveResult.fileName!,
originalFileName: file.name,
filePath: saveResult.publicPath!,
fileSize: file.size,
diff --git a/lib/tech-vendors/service.ts b/lib/tech-vendors/service.ts
index f5380889..e8dcb5dc 100644
--- a/lib/tech-vendors/service.ts
+++ b/lib/tech-vendors/service.ts
@@ -1423,29 +1423,138 @@ export async function importTechVendorsFromExcel(
});
continue;
}
+
// 1. ์ด๋ฉ”์ผ๋กœ ๊ธฐ์กด ๋ฒค๋” ์ค‘๋ณต ์ฒดํฌ
- const existingVendor = await tx.query.techVendors.findFirst({
+ let existingVendor = await tx.query.techVendors.findFirst({
where: eq(techVendors.email, vendor.email),
- columns: { id: true, vendorName: true, email: true }
+ columns: { id: true, vendorName: true, vendorCode: true, email: true }
});
+ // 2. ์ด๋ฉ”์ผ์ด ์ค‘๋ณต๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋ฒค๋” ์ฝ”๋“œ๋‚˜ ์ด๋ฆ„์œผ๋กœ ์ถ”๊ฐ€ ํ™•์ธ
+ if (!existingVendor && vendor.vendorCode) {
+ existingVendor = await tx.query.techVendors.findFirst({
+ where: eq(techVendors.vendorCode, vendor.vendorCode),
+ columns: { id: true, vendorName: true, vendorCode: true, email: true }
+ });
+ }
+
+ // 3. ๋ฒค๋” ์ฝ”๋“œ๋„ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ๋ฒค๋” ์ด๋ฆ„์œผ๋กœ ํ™•์ธ
+ if (!existingVendor) {
+ existingVendor = await tx.query.techVendors.findFirst({
+ where: eq(techVendors.vendorName, vendor.vendorName),
+ columns: { id: true, vendorName: true, vendorCode: true, email: true }
+ });
+ }
+
+ // 4. ์ผ์น˜ํ•˜๋Š” ๋ฒค๋”๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์ฒ˜๋ฆฌ
if (existingVendor) {
- console.log("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๋ฒค๋” ์Šคํ‚ต:", vendor.vendorName, vendor.email);
+ console.log("๊ธฐ์กด ๋ฒค๋”์— ๋‹ด๋‹น์ž ์ถ”๊ฐ€:", existingVendor.vendorName, vendor.email);
+
+ // ๊ธฐ์กด ๋ฒค๋”์˜ ๋ฒค๋” ํƒ€์ž… ์—…๋ฐ์ดํŠธ (์ƒˆ๋กœ์šด ํƒ€์ž… ์ถ”๊ฐ€)
+ const existingVendorFull = await tx.query.techVendors.findFirst({
+ where: eq(techVendors.id, existingVendor.id),
+ columns: { id: true, techVendorType: true }
+ });
+
+ if (existingVendorFull) {
+ const existingTypes = existingVendorFull.techVendorType ? existingVendorFull.techVendorType.split(',').map(t => t.trim()) : [];
+ const newType = vendor.techVendorType.trim();
+
+ // ์ƒˆ๋กœ์šด ํƒ€์ž…์ด ๊ธฐ์กด์— ์—†๋Š” ๊ฒฝ์šฐ์—๋งŒ ์ถ”๊ฐ€
+ if (!existingTypes.includes(newType)) {
+ const updatedTypes = [...existingTypes, newType];
+ const updatedTypeString = updatedTypes.join(', ');
+
+ await tx.update(techVendors)
+ .set({ techVendorType: updatedTypeString })
+ .where(eq(techVendors.id, existingVendor.id));
+
+ console.log(`๋ฒค๋” ํƒ€์ž… ์—…๋ฐ์ดํŠธ: ${existingVendorFull.techVendorType} -> ${updatedTypeString}`);
+ }
+ }
+
+ // ๋‹ด๋‹น์ž ์ •๋ณด๋ฅผ ๊ธฐ์กด ๋ฒค๋”์— ์ถ”๊ฐ€
+ let contactName = vendor.vendorName;
+ let contactEmail = vendor.email;
+
+ // vendor.contacts๊ฐ€ ์žˆ๊ณ , contactName์ด ์žˆ์œผ๋ฉด contactName ์‚ฌ์šฉ
+ if (vendor.contacts && vendor.contacts.length > 0 && vendor.contacts[0].contactName) {
+ contactName = vendor.contacts[0].contactName;
+ // ๋งŒ์•ฝ contactEmail์ด ์žˆ์œผ๋ฉด ๊ทธ๊ฑธ ์‚ฌ์šฉ, ์—†์œผ๋ฉด vendor.email ์‚ฌ์šฉ
+ if (vendor.contacts[0].contactEmail) {
+ contactEmail = vendor.contacts[0].contactEmail;
+ }
+ }
+
+ // ๋‹ด๋‹น์ž ์ด๋ฉ”์ผ ์ค‘๋ณต ์ฒดํฌ
+ const existingContact = await tx.query.techVendorContacts.findFirst({
+ where: and(
+ eq(techVendorContacts.vendorId, existingVendor.id),
+ eq(techVendorContacts.contactEmail, contactEmail)
+ ),
+ columns: { id: true, contactEmail: true }
+ });
+
+ if (existingContact) {
+ console.log("๋‹ด๋‹น์ž ์ด๋ฉ”์ผ ์ค‘๋ณต:", contactEmail);
+ errors.push({
+ vendorName: vendor.vendorName,
+ email: vendor.email,
+ error: `๋‹ด๋‹น์ž ์ด๋ฉ”์ผ '${contactEmail}'์ด(๊ฐ€) ์ด๋ฏธ ๋“ฑ๋ก๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค`
+ });
+ } else {
+ // ๋‹ด๋‹น์ž ์ƒ์„ฑ
+ await tx.insert(techVendorContacts).values({
+ vendorId: existingVendor.id,
+ contactName: contactName,
+ contactPosition: null,
+ contactEmail: contactEmail,
+ contactPhone: null,
+ contactCountry: null,
+ isPrimary: false,
+ });
+ console.log("๋‹ด๋‹น์ž ์ถ”๊ฐ€ ์„ฑ๊ณต:", contactName, contactEmail);
+ }
+
+ // ๊ธฐ์กด ๋ฒค๋”์— ๋‹ด๋‹น์ž ์ถ”๊ฐ€ํ–ˆ์œผ๋ฏ€๋กœ ๋ฒค๋” ์ƒ์„ฑ์€ ์Šคํ‚ตํ•˜๊ณ  ์œ ์ € ์ƒ์„ฑ์œผ๋กœ ๋„˜์–ด๊ฐ
skippedVendors.push({
vendorName: vendor.vendorName,
email: vendor.email,
- reason: `์ด๋ฏธ ๋“ฑ๋ก๋œ ์ด๋ฉ”์ผ์ž…๋‹ˆ๋‹ค (๊ธฐ์กด ์—…์ฒด: ${existingVendor.vendorName})`
+ reason: `๊ธฐ์กด ๋ฒค๋”์— ๋‹ด๋‹น์ž ์ถ”๊ฐ€๋จ (๊ธฐ์กด ์—…์ฒด: ${existingVendor.vendorName})`
});
- continue;
+
+ // ์œ ์ € ์ƒ์„ฑ (๊ธฐ์กด ๋ฒค๋”์˜ ๋‹ด๋‹น์ž๋กœ ์ถ”๊ฐ€๋œ ๊ฒฝ์šฐ)
+ if (contactEmail) {
+ console.log("์œ ์ € ์ƒ์„ฑ ์‹œ๋„:", contactEmail);
+
+ // ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์œ ์ €์ธ์ง€ ํ™•์ธ
+ const existingUser = await tx.query.users.findFirst({
+ where: eq(users.email, contactEmail),
+ columns: { id: true }
+ });
+
+ if (!existingUser) {
+ // ์œ ์ €๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ์ƒ์„ฑ
+ await tx.insert(users).values({
+ name: contactName,
+ email: contactEmail,
+ techCompanyId: existingVendor.id,
+ domain: "partners",
+ });
+ console.log("์œ ์ € ์ƒ์„ฑ ์„ฑ๊ณต");
+ } else {
+ // ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์œ ์ €๋ผ๋ฉด techCompanyId ์—…๋ฐ์ดํŠธ
+ await tx.update(users)
+ .set({ techCompanyId: existingVendor.id })
+ .where(eq(users.id, existingUser.id));
+ console.log("์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์œ ์ €, techCompanyId ์—…๋ฐ์ดํŠธ:", existingUser.id);
+ }
+ }
+
+ continue; // ๋ฒค๋” ์ƒ์„ฑ ๋ถ€๋ถ„์œผ๋กœ ๋„˜์–ด๊ฐ€์ง€ ์•Š์Œ
}
// 2. ๋ฒค๋” ์ƒ์„ฑ
- console.log("๋ฒค๋” ์ƒ์„ฑ ์‹œ๋„:", {
- vendorName: vendor.vendorName,
- email: vendor.email,
- techVendorType: vendor.techVendorType
- });
-
const [newVendor] = await tx.insert(techVendors).values({
vendorName: vendor.vendorName,
vendorCode: vendor.vendorCode || null,
@@ -1486,30 +1595,21 @@ export async function importTechVendorsFromExcel(
});
console.log("๋‹ด๋‹น์ž ์ƒ์„ฑ ์„ฑ๊ณต:", contact.contactName, contact.contactEmail);
}
-
- // // ๋ฒค๋” ์ด๋ฉ”์ผ์„ ์ฃผ ๋‹ด๋‹น์ž์˜ ์ด๋ฉ”์ผ๋กœ ์—…๋ฐ์ดํŠธ
- // const primaryContact = vendor.contacts.find(c => c.isPrimary) || vendor.contacts[0];
- // if (primaryContact && primaryContact.contactEmail !== vendor.email) {
- // await tx.update(techVendors)
- // .set({ email: primaryContact.contactEmail })
- // .where(eq(techVendors.id, newVendor.id));
- // console.log("๋ฒค๋” ์ด๋ฉ”์ผ ์—…๋ฐ์ดํŠธ:", primaryContact.contactEmail);
- // }
}
- // else {
- // // ๋‹ด๋‹น์ž ์ •๋ณด๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๋ฒค๋” ์ •๋ณด๋กœ ๊ธฐ๋ณธ ๋‹ด๋‹น์ž ์ƒ์„ฑ
- // console.log("๊ธฐ๋ณธ ๋‹ด๋‹น์ž ์ƒ์„ฑ");
- // await tx.insert(techVendorContacts).values({
- // vendorId: newVendor.id,
- // contactName: vendor.representativeName || vendor.vendorName || "๊ธฐ๋ณธ ๋‹ด๋‹น์ž",
- // contactPosition: null,
- // contactEmail: vendor.email,
- // contactPhone: vendor.representativePhone || vendor.phone || null,
- // contactCountry: vendor.country || null,
- // isPrimary: true,
- // });
- // console.log("๊ธฐ๋ณธ ๋‹ด๋‹น์ž ์ƒ์„ฑ ์„ฑ๊ณต:", vendor.email);
- // }
+ else {
+ // ๋‹ด๋‹น์ž ์ •๋ณด๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๋ฒค๋” ์ •๋ณด๋กœ ๊ธฐ๋ณธ ๋‹ด๋‹น์ž ์ƒ์„ฑ
+ console.log("๊ธฐ๋ณธ ๋‹ด๋‹น์ž ์ƒ์„ฑ");
+ await tx.insert(techVendorContacts).values({
+ vendorId: newVendor.id,
+ contactName: vendor.representativeName || vendor.vendorName || "๊ธฐ๋ณธ ๋‹ด๋‹น์ž",
+ contactPosition: null,
+ contactEmail: vendor.email,
+ contactPhone: vendor.representativePhone || vendor.phone || null,
+ contactCountry: vendor.country || null,
+ isPrimary: true,
+ });
+ console.log("๊ธฐ๋ณธ ๋‹ด๋‹น์ž ์ƒ์„ฑ ์„ฑ๊ณต:", vendor.email);
+ }
// 3. ์œ ์ € ์ƒ์„ฑ (์ด๋ฉ”์ผ์ด ์žˆ๋Š” ๊ฒฝ์šฐ)
if (vendor.email) {
@@ -3038,11 +3138,37 @@ export async function importPossibleItemsFromExcel(
continue
}
- const vendor = await findVendorByEmail(row.vendorEmail.trim())
+ let vendor = await findVendorByEmail(row.vendorEmail.trim())
+
+ // 2. ๋ฒค๋” ํ…Œ์ด๋ธ”์—์„œ ์ฐพ์„ ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ, ๋‹ด๋‹น์ž ํ…Œ์ด๋ธ”์—์„œ ์ฐพ๊ธฐ
+ if (!vendor) {
+ console.log(`๋ฒค๋” ํ…Œ์ด๋ธ”์—์„œ ์ฐพ์„ ์ˆ˜ ์—†์Œ, ๋‹ด๋‹น์ž ํ…Œ์ด๋ธ”์—์„œ ๊ฒ€์ƒ‰: ${row.vendorEmail}`)
+
+ // ๋‹ด๋‹น์ž ํ…Œ์ด๋ธ”์—์„œ ํ•ด๋‹น ์ด๋ฉ”์ผ๋กœ ๊ฒ€์ƒ‰
+ const contact = await db.query.techVendorContacts.findFirst({
+ where: eq(techVendorContacts.contactEmail, row.vendorEmail.trim()),
+ columns: { vendorId: true, contactEmail: true }
+ })
+
+ if (contact) {
+ console.log(`๋‹ด๋‹น์ž ํ…Œ์ด๋ธ”์—์„œ ์ฐพ์Œ, ๋ฒค๋” ID: ${contact.vendorId}`)
+
+ // ํ•ด๋‹น ๋ฒค๋” ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
+ vendor = await db.query.techVendors.findFirst({
+ where: eq(techVendors.id, contact.vendorId),
+ columns: { id: true, vendorName: true, email: true }
+ })
+
+ if (vendor) {
+ console.log(`๋‹ด๋‹น์ž๋ฅผ ํ†ตํ•ด ๋ฒค๋” ์ฐพ์Œ: ${vendor.vendorName}`)
+ }
+ }
+ }
+
if (!vendor) {
result.failedRows.push({
row: rowNumber,
- error: `๋ฒค๋” ์ด๋ฉ”์ผ '${row.vendorEmail}'์„(๋ฅผ) ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.`,
+ error: `๋ฒค๋” ์ด๋ฉ”์ผ '${row.vendorEmail}'์„(๋ฅผ) ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. (๋ฒค๋” ํ…Œ์ด๋ธ”๊ณผ ๋‹ด๋‹น์ž ํ…Œ์ด๋ธ” ๋ชจ๋‘์—์„œ ๊ฒ€์ƒ‰ ์‹คํŒจ)`,
vendorEmail: row.vendorEmail,
itemCode: row.itemCode,
itemType: row.itemType as "์กฐ์„ " | "ํ•ด์–‘TOP" | "ํ•ด์–‘HULL",
diff --git a/lib/tech-vendors/table/import-button.tsx b/lib/tech-vendors/table/import-button.tsx
index 1d3bf242..b268d29d 100644
--- a/lib/tech-vendors/table/import-button.tsx
+++ b/lib/tech-vendors/table/import-button.tsx
@@ -101,8 +101,14 @@ export function ImportTechVendorButton({ onSuccess }: ImportTechVendorButtonProp
// ํ—ค๋”๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ธ๋ฑ์Šค ๋งคํ•‘ ์ƒ์„ฑ
const headerMapping: Record<string, number> = {};
headerValues.forEach((value, index) => {
- if (typeof value === 'string') {
- headerMapping[value] = index;
+ // ๋ฆฌ์น˜ํ…์ŠคํŠธ๋ฅผ ์ผ๋ฐ˜ ํ…์ŠคํŠธ๋กœ ๋ณ€ํ™˜
+ const cleanValue = typeof value === 'string' ? value :
+ value && typeof value === 'object' && 'richText' in value ?
+ value.richText?.map((rt: any) => rt.text || '').join('') || '' :
+ String(value || '');
+
+ if (cleanValue && cleanValue.trim()) {
+ headerMapping[cleanValue] = index;
}
});
@@ -156,7 +162,19 @@ export function ImportTechVendorButton({ onSuccess }: ImportTechVendorButtonProp
// ํ—ค๋” ๋งคํ•‘์— ๋”ฐ๋ผ ๋ฐ์ดํ„ฐ ์ถ”์ถœ
Object.entries(headerMapping).forEach(([header, index]) => {
- rowData[header] = values[index] || "";
+ const rawValue = values[index];
+
+ // ๋ฆฌ์น˜ํ…์ŠคํŠธ๋ฅผ ์ผ๋ฐ˜ ํ…์ŠคํŠธ๋กœ ๋ณ€ํ™˜
+ let cleanValue = "";
+ if (typeof rawValue === 'string') {
+ cleanValue = rawValue;
+ } else if (rawValue && typeof rawValue === 'object' && 'richText' in rawValue) {
+ cleanValue = rawValue.richText?.map((rt: any) => rt.text || '').join('') || '';
+ } else if (rawValue !== null && rawValue !== undefined) {
+ cleanValue = String(rawValue);
+ }
+
+ rowData[header] = cleanValue;
});
// ๋นˆ ํ–‰์ด ์•„๋‹Œ ๊ฒฝ์šฐ๋งŒ ์ถ”๊ฐ€
@@ -271,6 +289,58 @@ export function ImportTechVendorButton({ onSuccess }: ImportTechVendorButtonProp
toast.error(result.error || "๋ฒค๋” ๊ฐ€์ ธ์˜ค๊ธฐ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.");
}
+ // ์—๋Ÿฌ๊ฐ€ ์žˆ์œผ๋ฉด ์—๋Ÿฌ ์—‘์…€ ๋‹ค์šด๋กœ๋“œ (์„ฑ๊ณต/์‹คํŒจ ์ƒ๊ด€์—†์ด)
+ if (result.details?.errors && result.details.errors.length > 0) {
+ try {
+ // ์—๋Ÿฌ ๋ฐ์ดํ„ฐ๋ฅผ Excel๋กœ ๋ณ€ํ™˜
+ const errorWorkbook = new ExcelJS.Workbook();
+ const errorWorksheet = errorWorkbook.addWorksheet("์˜ค๋ฅ˜๋‚ด์—ญ");
+
+ // ํ—ค๋” ์ถ”๊ฐ€
+ errorWorksheet.columns = [
+ { header: "์—…์ฒด๋ช…", key: "vendorName", width: 25 },
+ { header: "์ด๋ฉ”์ผ", key: "email", width: 30 },
+ { header: "์˜ค๋ฅ˜๋‚ด์šฉ", key: "error", width: 80, style: { alignment: { wrapText: true }, font: { color: { argb: "FFFF0000" } } } },
+ ];
+
+ // ํ—ค๋” ์Šคํƒ€์ผ ์„ค์ •
+ const headerRow = errorWorksheet.getRow(1);
+ headerRow.font = { bold: true };
+ headerRow.fill = {
+ type: "pattern",
+ pattern: "solid",
+ fgColor: { argb: "FFFFCCCC" },
+ };
+
+ // ์˜ค๋ฅ˜ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
+ result.details.errors.forEach(errorItem => {
+ errorWorksheet.addRow({
+ vendorName: errorItem.vendorName || "N/A",
+ email: errorItem.email || "N/A",
+ error: errorItem.error || "์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜",
+ });
+ });
+
+ const buffer = await errorWorkbook.xlsx.writeBuffer();
+ const blob = new Blob([buffer], {
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ });
+ const url = window.URL.createObjectURL(blob);
+ const link = document.createElement("a");
+ link.href = url;
+ link.download = "๋ฒค๋”_import_์—๋Ÿฌ.xlsx";
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ window.URL.revokeObjectURL(url);
+
+ toast.info("์—๋Ÿฌ ๋‚ด์—ญ ํŒŒ์ผ์ด ๋‹ค์šด๋กœ๋“œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.");
+ } catch (excelError) {
+ console.error("์—๋Ÿฌ ์—‘์…€ ์ƒ์„ฑ ์‹คํŒจ:", excelError);
+ toast.error("์—๋Ÿฌ ์—‘์…€ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.");
+ }
+ }
+
// ์ƒํƒœ ์ดˆ๊ธฐํ™” ๋ฐ ๋‹ค์ด์–ผ๋กœ๊ทธ ๋‹ซ๊ธฐ
setFile(null);
setOpen(false);
diff --git a/lib/vendor-investigation/service.ts b/lib/vendor-investigation/service.ts
index f0eb411e..c5097e75 100644
--- a/lib/vendor-investigation/service.ts
+++ b/lib/vendor-investigation/service.ts
@@ -15,6 +15,8 @@ import { v4 as uuid } from "uuid"
import { vendorsLogs } from "@/db/schema";
import { cache } from "react"
import { deleteFile } from "../file-stroage";
+import { saveDRMFile } from "../file-stroage";
+import { decryptWithServerAction } from "@/components/drm/drmUtils";
export async function getVendorsInvestigation(input: GetVendorsInvestigationSchema) {
return unstable_cache(
@@ -629,4 +631,66 @@ export const getAllItems = cache(async () => {
console.error("Error fetching all items:", error)
throw new Error("Failed to fetch items")
}
-}) \ No newline at end of file
+})
+
+/**
+ * Create vendor investigation attachment
+ */
+export async function createVendorInvestigationAttachmentAction(input: {
+ investigationId: number;
+ file: File;
+ userId?: string;
+}) {
+ unstable_noStore();
+
+ try {
+ console.log(`๐Ÿ“Ž ์‹ค์‚ฌ ์ฒจ๋ถ€ํŒŒ์ผ ์ƒ์„ฑ ์‹œ์ž‘: ${input.file.name}`);
+
+ // 1. saveDRMFile์„ ์‚ฌ์šฉํ•˜์—ฌ ํŒŒ์ผ ์ €์žฅ
+ const saveResult = await saveDRMFile(
+ input.file,
+ decryptWithServerAction,
+ `vendor-investigation/${input.investigationId}`,
+ input.userId
+ );
+
+ if (!saveResult.success) {
+ throw new Error(`ํŒŒ์ผ ์ €์žฅ ์‹คํŒจ: ${input.file.name} - ${saveResult.error}`);
+ }
+
+ console.log(`โœ… ํŒŒ์ผ ์ €์žฅ ์™„๋ฃŒ: ${input.file.name} -> ${saveResult.fileName}`);
+
+ // 2. DB์— ์ฒจ๋ถ€ํŒŒ์ผ ๋ ˆ์ฝ”๋“œ ์ƒ์„ฑ
+ const [insertedAttachment] = await db
+ .insert(vendorInvestigationAttachments)
+ .values({
+ investigationId: input.investigationId,
+ fileName: saveResult.fileName!,
+ originalFileName: input.file.name,
+ filePath: saveResult.publicPath!,
+ fileSize: input.file.size,
+ mimeType: input.file.type || 'application/octet-stream',
+ attachmentType: 'DOCUMENT', // ๋˜๋Š” ํŒŒ์ผ ํƒ€์ž…์— ๋”ฐ๋ผ ๊ฒฐ์ •
+ createdAt: new Date(),
+ updatedAt: new Date(),
+ })
+ .returning();
+
+ console.log(`โœ… ์ฒจ๋ถ€ํŒŒ์ผ DB ๋ ˆ์ฝ”๋“œ ์ƒ์„ฑ ์™„๋ฃŒ: ID ${insertedAttachment.id}`);
+
+ // 3. ์บ์‹œ ๋ฌดํšจํ™”
+ revalidateTag(`vendor-investigation-${input.investigationId}`);
+ revalidateTag("vendor-investigations");
+
+ return {
+ success: true,
+ attachment: insertedAttachment,
+ };
+ } catch (error) {
+ console.error(`โŒ ์‹ค์‚ฌ ์ฒจ๋ถ€ํŒŒ์ผ ์ƒ์„ฑ ์‹คํŒจ: ${input.file.name}`, error);
+ return {
+ success: false,
+ error: error instanceof Error ? error.message : "์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜",
+ };
+ }
+} \ No newline at end of file
diff --git a/lib/vendor-investigation/table/update-investigation-sheet.tsx b/lib/vendor-investigation/table/update-investigation-sheet.tsx
index 14350815..37d1b2cd 100644
--- a/lib/vendor-investigation/table/update-investigation-sheet.tsx
+++ b/lib/vendor-investigation/table/update-investigation-sheet.tsx
@@ -64,7 +64,7 @@ import {
updateVendorInvestigationSchema,
type UpdateVendorInvestigationSchema,
} from "../validations"
-import { updateVendorInvestigationAction, getInvestigationAttachments, deleteInvestigationAttachment } from "../service"
+import { updateVendorInvestigationAction, getInvestigationAttachments, deleteInvestigationAttachment, createVendorInvestigationAttachmentAction } from "../service"
import { VendorInvestigationsViewWithContacts } from "@/config/vendorInvestigationsColumnsConfig"
import prettyBytes from "pretty-bytes"
import { downloadFile } from "@/lib/file-download"
@@ -183,15 +183,7 @@ export function UpdateVendorInvestigationSheet({
if (!investigation) return
try {
- const response = await fetch(`/api/vendor-investigations/${investigation.investigationId}/attachments?attachmentId=${attachmentId}`, {
- method: "DELETE",
- })
-
- if (!response.ok) {
- const errorData = await response.json()
- throw new Error(errorData.error || "์ฒจ๋ถ€ํŒŒ์ผ ์‚ญ์ œ ์‹คํŒจ")
- }
-
+ await deleteInvestigationAttachment(attachmentId)
toast.success("์ฒจ๋ถ€ํŒŒ์ผ์ด ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
// ๋ชฉ๋ก ์ƒˆ๋กœ๊ณ ์นจ
loadExistingAttachments(investigation.investigationId)
@@ -409,23 +401,26 @@ export function UpdateVendorInvestigationSheet({
// ํŒŒ์ผ ์—…๋กœ๋“œ ํ•จ์ˆ˜
const uploadFiles = async (files: File[], investigationId: number) => {
const uploadPromises = files.map(async (file) => {
- const formData = new FormData()
- formData.append("file", file)
-
- const response = await fetch(`/api/vendor-investigations/${investigationId}/attachments`, {
- method: "POST",
- body: formData,
- })
+ try {
+ // ์„œ๋ฒ„ ์•ก์…˜์„ ํ˜ธ์ถœํ•˜์—ฌ ํŒŒ์ผ ์ €์žฅ ๋ฐ DB ๋ ˆ์ฝ”๋“œ ์ƒ์„ฑ
+ const result = await createVendorInvestigationAttachmentAction({
+ investigationId,
+ file,
+ userId: undefined // ํ•„์š”์‹œ ์‚ฌ์šฉ์ž ID ์ถ”๊ฐ€
+ });
+
+ if (!result.success) {
+ throw new Error(result.error || "ํŒŒ์ผ ์—…๋กœ๋“œ ์‹คํŒจ");
+ }
- if (!response.ok) {
- const errorData = await response.json()
- throw new Error(errorData.error || "ํŒŒ์ผ ์—…๋กœ๋“œ ์‹คํŒจ")
+ return result.attachment;
+ } catch (error) {
+ console.error(`ํŒŒ์ผ ์—…๋กœ๋“œ ์‹คํŒจ: ${file.name}`, error);
+ throw error;
}
+ });
- return await response.json()
- })
-
- return await Promise.all(uploadPromises)
+ return await Promise.all(uploadPromises);
}
// Submit handler