Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2c8e622
feat(cost-erp): add ERP item management page + routing metadata field…
ilramdhan Jun 23, 2026
2e0fd69
revert(cost-erp): remove ERP items management page; add legacy fields…
ilramdhan Jun 24, 2026
c3fba69
feat(product-master): remove ERP linkage modal; add Oracle Sys ID / C…
ilramdhan Jun 24, 2026
b203240
chore(cost-erp): remove ERP items/grades/shades from routing and fron…
ilramdhan Jun 24, 2026
737a993
chore(gen): regenerate finance/v1/yarn_master TypeScript types (T1)
ilramdhan Jun 24, 2026
c79c4d5
feat(frontend/finance): add 9 fill-group columns to machine form
ilramdhan Jun 24, 2026
fdcb4a8
feat(frontend/finance): add mbs_cc and mbs_cost_rate_mkt to MB_SPIN form
ilramdhan Jun 24, 2026
d66ecc0
feat(frontend/finance): add grade lookup columns to product-grade form
ilramdhan Jun 24, 2026
c7002bb
fix(frontend/finance): send undefined not empty string for optional g…
ilramdhan Jun 24, 2026
36d2426
feat(bulk-import): params-only import dialog + template + BFF routes
ilramdhan Jun 25, 2026
aa17d93
refactor(params-import-dialog): match ImportDialog style — dashed dro…
ilramdhan Jun 25, 2026
81f0c14
fix(bulk-import-template): sync frontend template columns with backen…
ilramdhan Jun 25, 2026
804bcf5
fix(params-import): fix 429 large file error + add background job pol…
ilramdhan Jun 25, 2026
a50548d
chore(gen): regenerate from proto — BBC + PG new fields
ilramdhan Jun 26, 2026
d20e7db
feat(frontend): add missing columns to all 6 costing master tables an…
ilramdhan Jun 26, 2026
a18833d
chore(gen): regenerate — Machine/BBC/MBSpin/MBHead Oracle columns
ilramdhan Jun 26, 2026
e82d33e
feat(frontend): expose Oracle columns in Machine/BBC/MBSpin/MBHead ta…
ilramdhan Jun 26, 2026
a20c21b
feat(frontend/formula): add missing FormulaType options to formula fo…
ilramdhan Jun 27, 2026
b7265b7
fix(frontend/formula): result param dropdown includes INPUT category …
ilramdhan Jun 27, 2026
0a30e6a
fix(frontend/formula): add self-reference validation in formula form
ilramdhan Jun 27, 2026
2089044
feat(frontend/cost-results): upgrade UI/UX — list, detail, breakdown …
ilramdhan Jun 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ export default function ProductMasterDetailClient({ productSysId }: Props) {
</div>
<Field label="Shade" value={product.shadeCode || "—"} />
<Field label="Grade" value={product.gradeCode || "—"} />
<Field label="ERP item" value={product.erpItemCode || "—"} mono />
<Field label="ERP grade 1" value={product.erpGradeCode1 || "—"} mono />
<Field label="ERP grade 2" value={product.erpGradeCode2 || "—"} mono />
<Field label="Oracle Sys ID" value={product.flex02 || "—"} mono />
<Field label="ERP Compound Key" value={product.flex01 || "—"} mono />
<Field label="Type Label" value={product.flex03 || "—"} />
{product.description && (
<div className="col-span-full">
<Field label="Description" value={product.description} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ import {
} from "@/components/ui/select"
import {
DeactivateProductMasterDialog,
ErpLinkageDialog,
ProductMasterFormDialog,
ProductMasterTable,
} from "@/components/finance/cost-product-master"
import { BulkImportDialog } from "@/components/finance/costing/bulk-import-dialog"
import { ParamsOnlyImportDialog } from "@/components/finance/costing/params-only-import-dialog"
import { ImportDialog } from "@/components/finance/costing/import-dialog"
import { ProductTypeCombobox } from "@/components/finance/comboboxes"
import { DataTablePagination } from "@/components/shared"
Expand All @@ -51,10 +51,10 @@ export default function ProductMasterPageClient() {
const router = useRouter()

const [formOpen, setFormOpen] = useState(false)
const [erpOpen, setErpOpen] = useState(false)
const [deactivateOpen, setDeactivateOpen] = useState(false)
const [importOpen, setImportOpen] = useState(false)
const [bulkImportOpen, setBulkImportOpen] = useState(false)
const [paramsImportOpen, setParamsImportOpen] = useState(false)
const [editing, setEditing] = useState<CostProductMaster | null>(null)
const [bulkExportLoading, setBulkExportLoading] = useState(false)

Expand All @@ -68,10 +68,6 @@ export default function ProductMasterPageClient() {
setEditing(p)
setFormOpen(true)
}
function openErp(p: CostProductMaster) {
setEditing(p)
setErpOpen(true)
}
function openDeactivate(p: CostProductMaster) {
setEditing(p)
setDeactivateOpen(true)
Expand Down Expand Up @@ -129,6 +125,9 @@ export default function ProductMasterPageClient() {
<DropdownMenuItem onSelect={() => setBulkImportOpen(true)}>
Import Produk + Routing (Bulk)
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => setParamsImportOpen(true)}>
Import Params Saja (Bulk)
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>

Expand Down Expand Up @@ -205,7 +204,6 @@ export default function ProductMasterPageClient() {
items={items}
isLoading={isLoading}
onEdit={openEdit}
onLinkErp={openErp}
onDeactivate={openDeactivate}
/>

Expand All @@ -229,13 +227,13 @@ export default function ProductMasterPageClient() {
}
/>
<ProductMasterFormDialog open={formOpen} onOpenChange={setFormOpen} product={editing} />
<ErpLinkageDialog open={erpOpen} onOpenChange={setErpOpen} product={editing} />
<DeactivateProductMasterDialog
open={deactivateOpen}
onOpenChange={setDeactivateOpen}
product={editing}
/>
<BulkImportDialog open={bulkImportOpen} onOpenChange={setBulkImportOpen} />
<ParamsOnlyImportDialog open={paramsImportOpen} onOpenChange={setParamsImportOpen} />
</div>
)
}
25 changes: 0 additions & 25 deletions src/app/api/v1/finance/cost-erp/grades/route.ts

This file was deleted.

27 changes: 0 additions & 27 deletions src/app/api/v1/finance/cost-erp/items/route.ts

This file was deleted.

25 changes: 0 additions & 25 deletions src/app/api/v1/finance/cost-erp/shades/route.ts

This file was deleted.

This file was deleted.

73 changes: 73 additions & 0 deletions src/app/api/v1/finance/costing/import/bulk_params_only/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// POST /api/v1/finance/costing/import/bulk_params_only
// Accepts multipart/form-data ("file" field) to avoid JSON number-array inflation:
// a 50 MB binary file as JSON becomes ~200 MB — this sends the actual binary bytes.
// Falls back to JSON body (small files only) for backward compatibility.
// Returns: { base, data: { jobId: number } }
import { NextRequest, NextResponse } from "next/server"
import {
getCostDataImportClient,
createMetadataFromRequest,
isGrpcError,
handleGrpcError,
} from "@/lib/grpc"

export async function POST(request: NextRequest) {
try {
const metadata = createMetadataFromRequest(request)
const client = getCostDataImportClient()

let fileContent: Uint8Array
let fileName: string

const contentType = request.headers.get("content-type") ?? ""

if (contentType.includes("multipart/form-data")) {
const formData = await request.formData()
const file = formData.get("file") as File | null
if (!file) {
return NextResponse.json(
{
base: {
isSuccess: false,
statusCode: "400",
message: "Missing 'file' field in form data",
validationErrors: [],
},
},
{ status: 400 },
)
}
fileContent = new Uint8Array(await file.arrayBuffer())
fileName = file.name
} else {
// Legacy JSON fallback
const body = await request.json()
fileContent = new Uint8Array(body.fileContent as number[])
fileName = body.fileName ?? ""
}

const res = await client.importBulkParamsOnly(
{ fileContent, fileName },
metadata,
)

return NextResponse.json({
base: res.base,
data: { jobId: res.jobId, status: res.status },
})
} catch (error) {
if (isGrpcError(error)) return handleGrpcError(error)
console.error("Error importing bulk params:", error)
return NextResponse.json(
{
base: {
isSuccess: false,
statusCode: "500",
message: "Failed to import params",
validationErrors: [],
},
},
{ status: 500 },
)
}
}
57 changes: 57 additions & 0 deletions src/app/api/v1/finance/costing/template/bulk_params_only/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// GET /api/v1/finance/costing/template/bulk_params_only
// Returns a 2-sheet Excel template: product_parameters + product_applicable_params.
// Use this when importing params separately from product master + routing.
// Supports split part-sheets (e.g. product_parameters_p1, _p2) on import.
import ExcelJS from "exceljs"
import { NextResponse } from "next/server"

const SHEETS: { name: string; headers: string[]; sample: string[] }[] = [
{
name: "product_parameters",
headers: [
"legacy_oracle_sys_id",
"param_code",
"data_type",
"value_numeric",
"value_text",
"value_flag",
],
sample: ["2512", "PARAM_CODE", "NUMERIC", "100.5", "", ""],
},
{
name: "product_applicable_params",
headers: ["legacy_oracle_sys_id", "param_code", "is_required", "display_order"],
sample: ["2512", "PARAM_CODE", "true", "1"],
},
]

export async function GET() {
const wb = new ExcelJS.Workbook()

for (const sheet of SHEETS) {
const ws = wb.addWorksheet(sheet.name)
ws.addRow(sheet.headers)
ws.addRow(sheet.sample)

// Bold header row
const headerRow = ws.getRow(1)
headerRow.font = { bold: true }
headerRow.commit()

// Auto-width columns
sheet.headers.forEach((h, i) => {
const col = ws.getColumn(i + 1)
col.width = Math.max(h.length + 4, 14)
})
}

const buffer = await wb.xlsx.writeBuffer()

return new NextResponse(buffer, {
status: 200,
headers: {
"Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"Content-Disposition": 'attachment; filename="bulk_params_only_template.xlsx"',
},
})
}
Loading
Loading