summaryrefslogtreecommitdiff
path: root/lib/po/vendor-table/vendor-po-items-dialog.tsx
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-11-20 19:36:01 +0900
committerjoonhoekim <26rote@gmail.com>2025-11-20 19:36:01 +0900
commit935fd22e17afc034a472bc2d159de7b9f5e5dcae (patch)
tree6beea33ab38750be17632dffca5e05e644647365 /lib/po/vendor-table/vendor-po-items-dialog.tsx
parentb75b1cd920efd61923f7b2dbc4c49987b7b0c4e1 (diff)
(김준회) PO, POS, swp
- PO: 발주서출력기능 초안 - 벤더측 POS 다운로드 기능 추가 - Contract 생성시 Status 설정 (mapper) - swp document registration table 로직 리팩터링 - swp: 입력가능 문서번호 validation 추가 (리스트 메뉴에서 Completed 된 건)
Diffstat (limited to 'lib/po/vendor-table/vendor-po-items-dialog.tsx')
-rw-r--r--lib/po/vendor-table/vendor-po-items-dialog.tsx131
1 files changed, 130 insertions, 1 deletions
diff --git a/lib/po/vendor-table/vendor-po-items-dialog.tsx b/lib/po/vendor-table/vendor-po-items-dialog.tsx
index d88d88d1..8b984812 100644
--- a/lib/po/vendor-table/vendor-po-items-dialog.tsx
+++ b/lib/po/vendor-table/vendor-po-items-dialog.tsx
@@ -16,22 +16,122 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table"
+import { Button } from "@/components/ui/button"
import { Skeleton } from "@/components/ui/skeleton"
+import { Download, FileText } from "lucide-react"
+import { toast } from "sonner"
import { VendorPO, VendorPOItem } from "./types"
import { getVendorPOItemsByContractNo } from "./service"
import { formatNumber } from "@/lib/utils"
+import { getDownloadUrlByMaterialCode, checkPosFileExists } from "@/lib/pos"
+import { PosFileSelectionDialog } from "@/lib/pos/components/pos-file-selection-dialog"
interface VendorPOItemsDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
po: VendorPO | null
+ /**
+ * 뷰어 타입
+ * - 'evcp': EVCP 사용자 (암호화된 파일 직접 다운로드)
+ * - 'partners': 협력사 사용자 (복호화된 파일 다운로드)
+ */
+ viewerType?: 'evcp' | 'partners'
}
-export function VendorPOItemsDialog({ open, onOpenChange, po }: VendorPOItemsDialogProps) {
+export function VendorPOItemsDialog({ open, onOpenChange, po, viewerType = 'partners' }: VendorPOItemsDialogProps) {
const [items, setItems] = React.useState<VendorPOItem[]>([])
const [loading, setLoading] = React.useState(false)
const [error, setError] = React.useState<string | null>(null)
+ // POS 파일 선택 다이얼로그 상태
+ const [posDialogOpen, setPosDialogOpen] = React.useState(false)
+ const [selectedMaterialCode, setSelectedMaterialCode] = React.useState<string>("")
+ const [posFiles, setPosFiles] = React.useState<Array<{
+ fileName: string
+ dcmtmId: string
+ projNo: string
+ posNo: string
+ posRevNo: string
+ fileSer: string
+ }>>([])
+ const [loadingPosFiles, setLoadingPosFiles] = React.useState(false)
+ const [downloadingFileIndex, setDownloadingFileIndex] = React.useState<number | null>(null)
+
+ // POS 파일 목록 조회 및 다이얼로그 열기
+ const handleOpenPosDialog = async (materialCode: string) => {
+ if (!materialCode) {
+ toast.error("자재코드가 없습니다")
+ return
+ }
+
+ setLoadingPosFiles(true)
+ setSelectedMaterialCode(materialCode)
+
+ try {
+ toast.loading(`POS 파일 목록 조회 중... (${materialCode})`, { id: `pos-check-${materialCode}` })
+
+ const result = await checkPosFileExists(materialCode)
+
+ if (result.exists && result.files && result.files.length > 0) {
+ // 파일 정보를 상세하게 가져오기 위해 getDownloadUrlByMaterialCode 사용
+ const detailResult = await getDownloadUrlByMaterialCode(materialCode)
+
+ if (detailResult.success && detailResult.availableFiles) {
+ setPosFiles(detailResult.availableFiles)
+ setPosDialogOpen(true)
+ toast.success(`${result.fileCount}개의 POS 파일을 찾았습니다`, { id: `pos-check-${materialCode}` })
+ } else {
+ toast.error('POS 파일 정보를 가져올 수 없습니다', { id: `pos-check-${materialCode}` })
+ }
+ } else {
+ toast.error(result.error || 'POS 파일을 찾을 수 없습니다', { id: `pos-check-${materialCode}` })
+ }
+ } catch (error) {
+ console.error("POS 파일 조회 오류:", error)
+ toast.error("POS 파일 조회에 실패했습니다", { id: `pos-check-${materialCode}` })
+ } finally {
+ setLoadingPosFiles(false)
+ }
+ }
+
+ // POS 파일 다운로드 실행
+ const handleDownloadPosFile = async (fileIndex: number, fileName: string) => {
+ if (!selectedMaterialCode) return
+
+ setDownloadingFileIndex(fileIndex)
+
+ try {
+ toast.loading(`POS 파일 다운로드 준비 중...`, { id: `download-${fileIndex}` })
+
+ // viewerType에 따라 다른 엔드포인트 사용
+ const endpoint = viewerType === 'partners'
+ ? `/api/pos/download-on-demand-partners` // 복호화 포함
+ : `/api/pos/download-on-demand` // 암호화 파일 그대로
+
+ const downloadUrl = `${endpoint}?materialCode=${encodeURIComponent(selectedMaterialCode)}&fileIndex=${fileIndex}`
+
+ toast.success(`POS 파일 다운로드 시작: ${fileName}`, { id: `download-${fileIndex}` })
+ window.open(downloadUrl, '_blank', 'noopener,noreferrer')
+
+ // 다운로드 시작 후 잠시 대기 후 상태 초기화
+ setTimeout(() => {
+ setDownloadingFileIndex(null)
+ }, 1000)
+ } catch (error) {
+ console.error("POS 파일 다운로드 오류:", error)
+ toast.error("POS 파일 다운로드에 실패했습니다", { id: `download-${fileIndex}` })
+ setDownloadingFileIndex(null)
+ }
+ }
+
+ // POS 다이얼로그 닫기
+ const handleClosePosDialog = () => {
+ setPosDialogOpen(false)
+ setSelectedMaterialCode("")
+ setPosFiles([])
+ setDownloadingFileIndex(null)
+ }
+
// 상세품목 데이터 로드
React.useEffect(() => {
if (!open || !po) {
@@ -123,6 +223,7 @@ export function VendorPOItemsDialog({ open, onOpenChange, po }: VendorPOItemsDia
<TableHead className="min-w-[120px] text-right whitespace-nowrap">기본총액(BRTWR)</TableHead>
<TableHead className="min-w-[120px] text-right whitespace-nowrap">조정금액(ZPDT_EXDS_AMT)</TableHead>
<TableHead className="min-w-[120px] text-right whitespace-nowrap">최종정가(NETWR)</TableHead>
+ <TableHead className="min-w-[100px] text-center whitespace-nowrap">POS</TableHead>
<TableHead className="min-w-[100px] whitespace-nowrap">예정시작일자</TableHead>
<TableHead className="min-w-[100px] whitespace-nowrap">납기일자</TableHead>
<TableHead className="min-w-[100px] whitespace-nowrap">예정종료일자</TableHead>
@@ -223,6 +324,24 @@ export function VendorPOItemsDialog({ open, onOpenChange, po }: VendorPOItemsDia
return item.NETWR ? formatNumber(item.NETWR, decimals) : '-'
})()}
</TableCell>
+ <TableCell className="text-center">
+ {item.materialNo ? (
+ <Button
+ variant="ghost"
+ size="sm"
+ className="h-8 px-2 text-xs text-green-600 hover:text-green-800"
+ onClick={() => handleOpenPosDialog(item.materialNo!)}
+ disabled={loadingPosFiles && selectedMaterialCode === item.materialNo}
+ title={`POS 파일 다운로드 (자재코드: ${item.materialNo})`}
+ >
+ <FileText className="h-3 w-3 mr-1" />
+ <Download className="h-3 w-3 mr-1" />
+ {loadingPosFiles && selectedMaterialCode === item.materialNo ? '조회중...' : 'POS'}
+ </Button>
+ ) : (
+ <span className="text-muted-foreground">-</span>
+ )}
+ </TableCell>
<TableCell>{item.ZPLN_ST_DT || '-'}</TableCell>
<TableCell>{item.ZPO_DLV_DT || item.deliveryDate || '-'}</TableCell>
<TableCell>{item.ZPLN_ED_DT || '-'}</TableCell>
@@ -277,6 +396,16 @@ export function VendorPOItemsDialog({ open, onOpenChange, po }: VendorPOItemsDia
</div>
</div>
)}
+
+ {/* POS 파일 선택 다이얼로그 */}
+ <PosFileSelectionDialog
+ isOpen={posDialogOpen}
+ onClose={handleClosePosDialog}
+ materialCode={selectedMaterialCode}
+ files={posFiles}
+ onDownload={handleDownloadPosFile}
+ downloadingIndex={downloadingFileIndex}
+ />
</DialogContent>
</Dialog>
)