From 7af78d04dde33043cb1ca3798e3502d8b08622d9 Mon Sep 17 00:00:00 2001 From: aguhh18 Date: Thu, 8 Jan 2026 18:52:12 +0700 Subject: [PATCH] feat(BE): add file size validation and improve document indexing for transfer creation --- .../controllers/transfer.controller.go | 10 +++++-- .../transfers/services/transfer.service.go | 30 ++++++++++++------- .../validations/transfer.validation.go | 2 +- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/internal/modules/inventory/transfers/controllers/transfer.controller.go b/internal/modules/inventory/transfers/controllers/transfer.controller.go index 4f060dc2..530d70dc 100644 --- a/internal/modules/inventory/transfers/controllers/transfer.controller.go +++ b/internal/modules/inventory/transfers/controllers/transfer.controller.go @@ -75,6 +75,8 @@ func (u *TransferController) GetOne(c *fiber.Ctx) error { func (u *TransferController) CreateOne(c *fiber.Ctx) error { data := c.FormValue("data") + const maxFileSize = 5 * 1024 * 1024 + var req validation.TransferRequest if err := json.Unmarshal([]byte(data), &req); err != nil { return fiber.NewError(fiber.StatusBadRequest, "Invalid request body") @@ -87,9 +89,11 @@ func (u *TransferController) CreateOne(c *fiber.Ctx) error { files := form.File["documents"] - if len(files) != len(req.Deliveries) { - return fiber.NewError(fiber.StatusBadRequest, - fiber.NewError(fiber.StatusBadRequest, "Jumlah dokumen harus sama dengan jumlah deliveries").Message) + for i, file := range files { + if file.Size > maxFileSize { + return fiber.NewError(fiber.StatusBadRequest, + "Dokumen ke-"+strconv.Itoa(i+1)+" melebihi ukuran maksimal 5MB") + } } result, err := u.TransferService.CreateOne(c, &req, files) diff --git a/internal/modules/inventory/transfers/services/transfer.service.go b/internal/modules/inventory/transfers/services/transfer.service.go index 86fdc69a..d14125ed 100644 --- a/internal/modules/inventory/transfers/services/transfer.service.go +++ b/internal/modules/inventory/transfers/services/transfer.service.go @@ -128,7 +128,6 @@ func (s transferService) GetOne(c *fiber.Ctx, id uint) (*entity.StockTransfer, e func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferRequest, files []*multipart.FileHeader) (*entity.StockTransfer, error) { - // === VALIDASI SOURCE WAREHOUSE === pwIDs := make([]uint, 0, len(req.Products)) for _, product := range req.Products { @@ -201,7 +200,6 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Supplier dengan ID %d bukan kategori BOP", delivery.SupplierID)) } } - seqNum, err := s.StockTransferRepo.GetNextMovementNumber(c.Context()) if err != nil { return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to generate movement number") @@ -226,12 +224,11 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques return err } - // Prepare details and fetch product warehouses details := make([]*entity.StockTransferDetail, 0, len(req.Products)) detailMap := make(map[uint64]*entity.StockTransferDetail) for _, product := range req.Products { - // Get source product warehouse + sourcePW, err := s.ProductWarehouseRepo.GetProductWarehouseByProductAndWarehouseID( c.Context(), uint(product.ProductID), uint(req.SourceWarehouseID), ) @@ -242,7 +239,6 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques return fiber.NewError(fiber.StatusInternalServerError, "Gagal mengambil data product warehouse source") } - // Get or create destination product warehouse destPW, err := s.ProductWarehouseRepo.GetProductWarehouseByProductAndWarehouseID( c.Context(), uint(product.ProductID), uint(req.DestinationWarehouseID), ) @@ -323,24 +319,38 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques if s.DocumentSvc != nil && len(files) > 0 { - for idx, file := range files { + for deliveryIdx, delivery := range deliveries { + reqDelivery := req.Deliveries[deliveryIdx] + + if reqDelivery.DocumentIndex < 0 { + continue + } + + if reqDelivery.DocumentIndex >= len(files) { + return fiber.NewError(fiber.StatusBadRequest, + fmt.Sprintf("DocumentIndex %d untuk delivery %d melebihi jumlah file yang diupload (%d)", + reqDelivery.DocumentIndex, deliveryIdx+1, len(files))) + } + + file := files[reqDelivery.DocumentIndex] + documentFiles := []commonSvc.DocumentFile{ { File: file, Type: string(utils.DocumentTypeTransfer), - Index: &idx, + Index: &reqDelivery.DocumentIndex, }, } _, err := s.DocumentSvc.UploadDocuments(c.Context(), commonSvc.DocumentUploadRequest{ DocumentableType: string(utils.DocumentableTypeTransfer), - DocumentableID: deliveries[idx].Id, + DocumentableID: delivery.Id, CreatedBy: &actorID, Files: documentFiles, }) if err != nil { s.Log.WithError(err).Errorf("Failed to upload document for delivery %d (delivery_id: %d, filename: %s)", - idx+1, deliveries[idx].Id, file.Filename) - return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to upload document for delivery %d: %v", idx+1, err)) + deliveryIdx+1, delivery.Id, file.Filename) + return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to upload document for delivery %d: %v", deliveryIdx+1, err)) } } } diff --git a/internal/modules/inventory/transfers/validations/transfer.validation.go b/internal/modules/inventory/transfers/validations/transfer.validation.go index c64077ff..785295e2 100644 --- a/internal/modules/inventory/transfers/validations/transfer.validation.go +++ b/internal/modules/inventory/transfers/validations/transfer.validation.go @@ -23,7 +23,7 @@ type TransferDeliveryProduct struct { type TransferDelivery struct { DeliveryCost float64 `json:"delivery_cost" validate:"required"` DeliveryCostPerItem float64 `json:"delivery_cost_per_item" validate:"required"` - DocumentIndex int `json:"document_index" validate:"min=0"` + DocumentIndex int `json:"document_index" validate:"omitempty,min=-1" default:"-1"` DriverName string `json:"driver_name" validate:"required"` VehiclePlate string `json:"vehicle_plate" validate:"required"` SupplierID uint `json:"supplier_id" validate:"required"`