package controller import ( "bytes" "testing" "time" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" "github.com/xuri/excelize/v2" ) func TestBuildPurchaseExportWorkbookHeadersAndRows(t *testing.T) { content, err := buildPurchaseExportWorkbook([]entity.Purchase{ buildPurchaseForExportTest( 1, "PR-00011", "PO-00011", time.Date(2026, time.April, 22, 0, 0, 0, 0, time.UTC), "Supplier A", "Manager Purchase", nil, "catatan", []entity.PurchaseItem{ buildPurchaseItemForExportTest(11, "Pakan Starter", 500, 2, 1000000, "Location A", "kg"), buildPurchaseItemForExportTest(12, "Vitamin A", 350, 1, 350000, "Location B", "botol"), }, ), buildPurchaseForExportTest( 2, "PR-00012", "", time.Time{}, "Supplier B", "Manager Purchase", ptrApprovalAction(entity.ApprovalActionRejected), "", []entity.PurchaseItem{ buildPurchaseItemForExportTest(21, "Obat X", 75000, 1, 75000, "", ""), }, ), }) if err != nil { t.Fatalf("buildPurchaseExportWorkbook returned error: %v", err) } file, err := excelize.OpenReader(bytes.NewReader(content)) if err != nil { t.Fatalf("failed to open workbook bytes: %v", err) } defer file.Close() // Verify all 19 headers expectedHeaders := map[string]string{ "A1": "PR Number", "B1": "PO Number", "C1": "Tanggal PO", "D1": "Tanggal Terima", "E1": "Supplier", "F1": "Lokasi", "G1": "Gudang", "H1": "Product", "I1": "Qty", "J1": "Satuan", "K1": "Price", "L1": "Total Produk", "M1": "Vendor Ekspedisi", "N1": "Qty Ekspedisi", "O1": "Price Ekspedisi", "P1": "Total Ekspedisi", "Q1": "Grand Total All", "R1": "Status", "S1": "Notes", } for cell, expected := range expectedHeaders { got, err := file.GetCellValue(purchaseExportSheetName, cell) if err != nil { t.Fatalf("GetCellValue(%s) failed: %v", cell, err) } if got != expected { t.Fatalf("expected %s=%q, got %q", cell, expected, got) } } // Row 2: Purchase 1, Item 1 (Pakan Starter) assertPurchaseCellEquals(t, file, "A2", "PR-00011") assertPurchaseCellEquals(t, file, "B2", "PO-00011") assertPurchaseCellEquals(t, file, "C2", "22-04-2026") assertPurchaseCellEquals(t, file, "E2", "Supplier A") assertPurchaseCellEquals(t, file, "F2", "Location A") assertPurchaseCellEquals(t, file, "H2", "Pakan Starter") assertPurchaseCellEquals(t, file, "J2", "kg") assertPurchaseCellEquals(t, file, "K2", "500") assertPurchaseCellEquals(t, file, "L2", "1000000") assertPurchaseCellEquals(t, file, "M2", "-") assertPurchaseCellEquals(t, file, "P2", "0") assertPurchaseCellEquals(t, file, "Q2", "1000000") assertPurchaseCellEquals(t, file, "R2", "Manager Purchase") assertPurchaseCellEquals(t, file, "S2", "catatan") // Row 3: Purchase 1, Item 2 (Vitamin A) assertPurchaseCellEquals(t, file, "A3", "PR-00011") assertPurchaseCellEquals(t, file, "H3", "Vitamin A") assertPurchaseCellEquals(t, file, "J3", "botol") assertPurchaseCellEquals(t, file, "L3", "350000") assertPurchaseCellEquals(t, file, "Q3", "350000") // Row 4: Purchase 2, Item 1 (Obat X) — no location, rejected assertPurchaseCellEquals(t, file, "A4", "PR-00012") assertPurchaseCellEquals(t, file, "B4", "-") assertPurchaseCellEquals(t, file, "C4", "-") assertPurchaseCellEquals(t, file, "F4", "-") assertPurchaseCellEquals(t, file, "H4", "Obat X") assertPurchaseCellEquals(t, file, "J4", "-") assertPurchaseCellEquals(t, file, "L4", "75000") assertPurchaseCellEquals(t, file, "Q4", "75000") assertPurchaseCellEquals(t, file, "R4", "Ditolak") assertPurchaseCellEquals(t, file, "S4", "-") // Row 5: SUM row — total produk=1425000, ekspedisi=0, grand total all=1425000 assertPurchaseCellEquals(t, file, "A5", "TOTAL") assertPurchaseCellEquals(t, file, "L5", "1425000") assertPurchaseCellEquals(t, file, "P5", "0") assertPurchaseCellEquals(t, file, "Q5", "1425000") } func assertPurchaseCellEquals(t *testing.T, file *excelize.File, cell, expected string) { t.Helper() got, err := file.GetCellValue(purchaseExportSheetName, cell) if err != nil { t.Fatalf("GetCellValue(%s) failed: %v", cell, err) } if got != expected { t.Fatalf("expected %s=%q, got %q", cell, expected, got) } } func buildPurchaseForExportTest( id uint, prNumber, poNumber string, poDate time.Time, supplierName, stepName string, action *entity.ApprovalAction, notes string, items []entity.PurchaseItem, ) entity.Purchase { var poNumberRef *string if poNumber != "" { poNumberRef = &poNumber } var poDateRef *time.Time if !poDate.IsZero() { poDateRef = &poDate } var notesRef *string if notes != "" { notesRef = ¬es } return entity.Purchase{ Id: id, PrNumber: prNumber, PoNumber: poNumberRef, PoDate: poDateRef, Notes: notesRef, Supplier: entity.Supplier{ Id: id + 100, Name: supplierName, }, LatestApproval: &entity.Approval{ Id: id + 1000, StepName: stepName, Action: action, }, Items: items, } } func buildPurchaseItemForExportTest(productID uint, productName string, price, totalQty, totalPrice float64, locationName, uomName string) entity.PurchaseItem { uomID := uint(0) if uomName != "" { uomID = productID + 2000 } item := entity.PurchaseItem{ ProductId: productID, Price: price, TotalQty: totalQty, TotalPrice: totalPrice, Product: &entity.Product{ Id: productID, Name: productName, Uom: entity.Uom{Id: uomID, Name: uomName}, }, } if locationName != "" { locationID := productID + 1000 warehouseID := productID + 500 item.Warehouse = &entity.Warehouse{ Id: warehouseID, Location: &entity.Location{ Id: locationID, Name: locationName, }, } } return item } func ptrApprovalAction(value entity.ApprovalAction) *entity.ApprovalAction { return &value }