summaryrefslogtreecommitdiff
path: root/lib/vendor-document-list/plant/upload/service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vendor-document-list/plant/upload/service.ts')
-rw-r--r--lib/vendor-document-list/plant/upload/service.ts228
1 files changed, 228 insertions, 0 deletions
diff --git a/lib/vendor-document-list/plant/upload/service.ts b/lib/vendor-document-list/plant/upload/service.ts
new file mode 100644
index 00000000..18e6c132
--- /dev/null
+++ b/lib/vendor-document-list/plant/upload/service.ts
@@ -0,0 +1,228 @@
+import db from "@/db/db"
+import { stageSubmissionView, StageSubmissionView } from "@/db/schema"
+import { and, asc, desc, eq, or, ilike, isTrue, sql, isNotNull, count } from "drizzle-orm"
+import { filterColumns } from "@/lib/filter-columns"
+import { GetStageSubmissionsSchema } from "./validation"
+import { getServerSession } from 'next-auth/next'
+import { authOptions } from '@/app/api/auth/[...nextauth]/route'
+import { redirect } from "next/navigation"
+
+// Repository functions (동일)
+async function selectStageSubmissions(
+ tx: typeof db,
+ params: {
+ where?: any
+ orderBy?: any
+ offset?: number
+ limit?: number
+ }
+) {
+ const { where, orderBy = [desc(stageSubmissionView.isOverdue)], offset = 0, limit = 10 } = params
+
+ const query = tx
+ .select()
+ .from(stageSubmissionView)
+ .$dynamic()
+
+ if (where) query.where(where)
+ if (orderBy) query.orderBy(...(Array.isArray(orderBy) ? orderBy : [orderBy]))
+ query.limit(limit).offset(offset)
+
+ return await query
+}
+
+async function countStageSubmissions(tx: typeof db, where?: any) {
+ const query = tx
+ .select({ count: count() })
+ .from(stageSubmissionView)
+ .$dynamic()
+
+ if (where) query.where(where)
+
+ const result = await query
+ return result[0]?.count ?? 0
+}
+
+// Service function with session check
+export async function getStageSubmissions(input: GetStageSubmissionsSchema) {
+ // Session 체크
+ const session = await getServerSession(authOptions)
+ if (!session?.user?.id) {
+ return {
+ success: false,
+ error: '로그인이 필요합니다.'
+ }
+ }
+ const vendorId = session.user.companyId // companyId가 vendorId
+
+ try {
+ const offset = (input.page - 1) * input.perPage
+
+ // Advanced filters
+ const advancedWhere = filterColumns({
+ table: stageSubmissionView,
+ filters: input.filters,
+ joinOperator: input.joinOperator,
+ })
+
+ // Global search
+ let globalWhere
+ if (input.search) {
+ const s = `%${input.search}%`
+ globalWhere = or(
+ ilike(stageSubmissionView.documentTitle, s),
+ ilike(stageSubmissionView.docNumber, s),
+ ilike(stageSubmissionView.vendorDocNumber, s),
+ ilike(stageSubmissionView.stageName, s)
+ // vendorName 검색 제거 (자기 회사만 보므로)
+ )
+ }
+
+ // Status filters
+ let statusWhere
+ if (input.submissionStatus && input.submissionStatus !== "all") {
+ switch (input.submissionStatus) {
+ case "required":
+ statusWhere = eq(stageSubmissionView.requiresSubmission, true)
+ break
+ case "submitted":
+ statusWhere = eq(stageSubmissionView.latestSubmissionStatus, "SUBMITTED")
+ break
+ case "approved":
+ statusWhere = eq(stageSubmissionView.latestReviewStatus, "APPROVED")
+ break
+ case "rejected":
+ statusWhere = eq(stageSubmissionView.latestReviewStatus, "REJECTED")
+ break
+ }
+ }
+
+ // Sync status filter
+ let syncWhere
+ if (input.syncStatus && input.syncStatus !== "all") {
+ if (input.syncStatus === "pending") {
+ syncWhere = or(
+ eq(stageSubmissionView.latestSyncStatus, "pending"),
+ eq(stageSubmissionView.requiresSync, true)
+ )
+ } else {
+ syncWhere = eq(stageSubmissionView.latestSyncStatus, input.syncStatus)
+ }
+ }
+
+ // Project filter
+ let projectWhere = input.projectId ? eq(stageSubmissionView.projectId, input.projectId) : undefined
+
+ // ✅ 벤더 필터 - session의 companyId 사용
+ const vendorWhere = eq(stageSubmissionView.vendorId, vendorId)
+
+ const finalWhere = and(
+ vendorWhere, // 항상 벤더 필터 적용
+ advancedWhere,
+ globalWhere,
+ statusWhere,
+ syncWhere,
+ projectWhere
+ )
+
+ const orderBy =
+ input.sort.length > 0
+ ? input.sort.map((item) =>
+ item.desc
+ ? desc(stageSubmissionView[item.id])
+ : asc(stageSubmissionView[item.id])
+ )
+ : [desc(stageSubmissionView.isOverdue), asc(stageSubmissionView.daysUntilDue)]
+
+ // Transaction
+ const { data, total } = await db.transaction(async (tx) => {
+ const data = await selectStageSubmissions(tx, {
+ where: finalWhere,
+ orderBy,
+ offset,
+ limit: input.perPage,
+ })
+ const total = await countStageSubmissions(tx, finalWhere)
+ return { data, total }
+ })
+
+ const pageCount = Math.ceil(total / input.perPage)
+
+ return { data, pageCount }
+ } catch (err) {
+ console.error("Error fetching stage submissions:", err)
+ return { data: [], pageCount: 0 }
+ }
+}
+
+// 프로젝트 목록 조회 - 벤더 필터 적용
+export async function getProjects() {
+ const session = await getServerSession(authOptions)
+ if (!session?.user?.id) {
+ return {
+ success: false,
+ error: '로그인이 필요합니다.'
+ }
+ }
+ if (!session?.user?.companyId) {
+ return []
+ }
+
+ const vendorId = session.user.companyId
+
+ const projects = await db
+ .selectDistinct({
+ id: stageSubmissionView.projectId,
+ code: stageSubmissionView.projectCode,
+ })
+ .from(stageSubmissionView)
+ .where(
+ and(
+ eq(stageSubmissionView.vendorId, vendorId),
+ isNotNull(stageSubmissionView.projectId)
+ )
+ )
+ .orderBy(asc(stageSubmissionView.projectCode))
+
+ return projects
+}
+
+// 통계 조회 - 벤더별
+export async function getSubmissionStats() {
+ const session = await getServerSession(authOptions)
+ if (!session?.user?.id) {
+ return {
+ success: false,
+ error: '로그인이 필요합니다.'
+ }
+ }
+
+
+ if (!session?.user?.companyId) {
+ return {
+ pending: 0,
+ overdue: 0,
+ awaitingSync: 0,
+ completed: 0,
+ }
+ }
+
+ const vendorId = session.user.companyId
+
+ const stats = await db
+ .select({
+ pending: sql<number>`count(*) filter (where ${stageSubmissionView.requiresSubmission} = true)::int`,
+ overdue: sql<number>`count(*) filter (where ${stageSubmissionView.isOverdue} = true)::int`,
+ awaitingSync: sql<number>`count(*) filter (where ${stageSubmissionView.requiresSync} = true)::int`,
+ completed: sql<number>`count(*) filter (where ${stageSubmissionView.latestReviewStatus} = 'APPROVED')::int`,
+ })
+ .from(stageSubmissionView)
+ .where(eq(stageSubmissionView.vendorId, vendorId))
+
+ return stats[0] || {
+ pending: 0,
+ overdue: 0,
+ awaitingSync: 0,
+ completed: 0,
+ }
+} \ No newline at end of file