[CLAUDE] Supplier: Excel-import Phase B (upload NCC preview/confirm) + Mig 63
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m25s

Import Excel 'Database NCC': upload→preview classify (New/Update/Skip/Error)→confirm all-or-nothing upsert-by-Code (OrdinalIgnoreCase dedup, fill-nulls-safe). Mig 63 +SourceUpdatedAt/By (2 nullable). Parser layout-locked header-fingerprint NFC-normalized + absolute-cell-index + #REF!-null + NAS-backslash-raw. Type-lạ→NhaCungCap. FE dialog fe-admin+fe-user byte-identical. 10 test SupplierExcelImportServiceTests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-07-12 16:13:46 +07:00
parent a829d0df99
commit e100ef065b
20 changed files with 8268 additions and 14 deletions

View File

@ -1,6 +1,6 @@
import { useState, type FormEvent } from 'react'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { Pencil, Plus, Trash2 } from 'lucide-react'
import { Pencil, Plus, Trash2, Upload } from 'lucide-react'
import { toast } from 'sonner'
import { PageHeader } from '@/components/PageHeader'
import { DataTable, Pagination, type Column } from '@/components/DataTable'
@ -12,6 +12,7 @@ import { Select } from '@/components/ui/Select'
import { Textarea } from '@/components/ui/Textarea'
import { Dialog } from '@/components/ui/Dialog'
import { PathLink } from '@/components/ui/PathLink'
import { SupplierImportDialog } from '@/components/master/SupplierImportDialog'
import { api } from '@/lib/api'
import { getErrorMessage } from '@/lib/apiError'
import { MenuKeys } from '@/lib/menuKeys'
@ -72,6 +73,7 @@ export function SuppliersPage() {
const [sortDesc, setSortDesc] = useState(true)
const [open, setOpen] = useState(false)
const [importOpen, setImportOpen] = useState(false)
const [form, setForm] = useState<FormState>(emptyForm)
const isEdit = !!form.id
@ -209,12 +211,20 @@ export function SuppliersPage() {
title="Nhà cung cấp"
description="Quản lý NCC / Thầu phụ / Tổ đội / Đơn vị dịch vụ / Chủ đầu tư"
actions={
<PermissionGuard menuKey={MenuKeys.Suppliers} action="Create">
<Button onClick={openNew}>
<Plus className="h-4 w-4" />
Thêm NCC
</Button>
</PermissionGuard>
<div className="flex items-center gap-2">
<PermissionGuard menuKey={MenuKeys.Suppliers} action="Create">
<Button variant="outline" onClick={() => setImportOpen(true)}>
<Upload className="h-4 w-4" />
Import Excel NCC
</Button>
</PermissionGuard>
<PermissionGuard menuKey={MenuKeys.Suppliers} action="Create">
<Button onClick={openNew}>
<Plus className="h-4 w-4" />
Thêm NCC
</Button>
</PermissionGuard>
</div>
}
/>
@ -413,6 +423,8 @@ export function SuppliersPage() {
</div>
</form>
</Dialog>
<SupplierImportDialog open={importOpen} onClose={() => setImportOpen(false)} />
</div>
)
}