diff --git a/internal/entities/stock_transfer_delivery.go b/internal/entities/stock_transfer_delivery.go index 69324b65..0eeccc04 100644 --- a/internal/entities/stock_transfer_delivery.go +++ b/internal/entities/stock_transfer_delivery.go @@ -20,4 +20,5 @@ type StockTransferDelivery struct { StockTransfer *StockTransfer `gorm:"foreignKey:StockTransferId"` Supplier *Supplier `gorm:"foreignKey:SupplierId"` Items []StockTransferDeliveryItem `gorm:"foreignKey:StockTransferDeliveryId"` + Documents []Document `gorm:"foreignKey:DocumentableId;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"` } diff --git a/internal/modules/inventory/transfers/controllers/transfer.controller.go b/internal/modules/inventory/transfers/controllers/transfer.controller.go index c21e5286..4f060dc2 100644 --- a/internal/modules/inventory/transfers/controllers/transfer.controller.go +++ b/internal/modules/inventory/transfers/controllers/transfer.controller.go @@ -68,7 +68,7 @@ func (u *TransferController) GetOne(c *fiber.Ctx) error { Code: fiber.StatusOK, Status: "success", Message: "Get transfer successfully", - Data: dto.ToTransferListDTO(*result), + Data: dto.ToTransferDetailDTO(*result), }) } @@ -87,6 +87,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) + } + result, err := u.TransferService.CreateOne(c, &req, files) if err != nil { return err @@ -97,6 +102,6 @@ func (u *TransferController) CreateOne(c *fiber.Ctx) error { Code: fiber.StatusCreated, Status: "success", Message: "Create transfer successfully", - Data: dto.ToTransferListDTO(*result), + Data: dto.ToTransferDetailDTO(*result), }) } diff --git a/internal/modules/inventory/transfers/dto/transfer.dto.go b/internal/modules/inventory/transfers/dto/transfer.dto.go index d38fb78d..14ca04d2 100644 --- a/internal/modules/inventory/transfers/dto/transfer.dto.go +++ b/internal/modules/inventory/transfers/dto/transfer.dto.go @@ -7,8 +7,6 @@ import ( userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto" ) -// === DTO Structs === - type TransferRelationDTO struct { Id uint64 `json:"id"` TransferReason string `json:"transfer_reason"` @@ -17,7 +15,6 @@ type TransferRelationDTO struct { DestinationWarehouse *WarehouseDetailDTO `json:"destination_warehouse,omitempty"` } -// Only id and name for warehouse simple view type WarehouseSimpleDTO struct { Id uint `json:"id"` Name string `json:"name"` @@ -65,7 +62,6 @@ type TransferListDTO struct { UpdatedAt time.Time `json:"updated_at"` Details []TransferDetailItemDTO `json:"details"` Deliveries []TransferDeliveryDTO `json:"deliveries"` - Documents []DocumentDTO `json:"documents"` } type TransferDetailDTO struct { @@ -74,14 +70,12 @@ type TransferDetailDTO struct { Deliveries []TransferDeliveryDTO `json:"deliveries"` } -// Detail produk type TransferDetailItemDTO struct { Id uint64 `json:"id"` - Proudct ProductSimpleDTO `json:"product"` + Product ProductSimpleDTO `json:"product"` Quantity float64 `json:"quantity"` } -// Delivery ekspedisi type TransferDeliveryDTO struct { Id uint64 `json:"id"` Supplier SupplierSimpleDTO `json:"supplier"` @@ -91,6 +85,7 @@ type TransferDeliveryDTO struct { ShippingCostItem float64 `json:"shipping_cost_item"` ShippingCostTotal float64 `json:"shipping_cost_total"` Items []TransferDeliveryItemDTO `json:"items"` + Documents []DocumentDTO `json:"documents"` } type TransferDeliveryItemDTO struct { @@ -99,10 +94,7 @@ type TransferDeliveryItemDTO struct { Quantity float64 `json:"quantity"` } -// === Mapper Functions === - func ToTransferRelationDTO(e entity.StockTransfer) TransferRelationDTO { - var sourceWarehouse *WarehouseDetailDTO if e.FromWarehouse != nil && e.FromWarehouse.Id != 0 { sourceWarehouse = toWarehouseDetailDTO(e.FromWarehouse) @@ -148,7 +140,7 @@ func toWarehouseDetailDTO(w *entity.Warehouse) *WarehouseDetailDTO { Id: w.Id, Name: w.Name, Location: toLocationDTO(w.Location), - Area: toAreaDTO(&w.Area), // Ambil area langsung dari warehouse (area_id) + Area: toAreaDTO(&w.Area), } } @@ -158,22 +150,21 @@ func ToTransferListDTO(e entity.StockTransfer) TransferListDTO { mapped := userDTO.ToUserRelationDTO(*e.CreatedUser) createdUser = &mapped } - // Map details + var details []TransferDetailItemDTO for _, d := range e.Details { details = append(details, TransferDetailItemDTO{ Id: d.Id, - Proudct: ProductSimpleDTO{ + Product: ProductSimpleDTO{ Id: d.Product.Id, Name: d.Product.Name, }, Quantity: d.Quantity, }) } - // Map deliveries + var deliveries []TransferDeliveryDTO for _, del := range e.Deliveries { - // Map delivery items var items []TransferDeliveryItemDTO for _, item := range del.Items { items = append(items, TransferDeliveryItemDTO{ @@ -183,6 +174,17 @@ func ToTransferListDTO(e entity.StockTransfer) TransferListDTO { }) } + var documents []DocumentDTO + for _, doc := range del.Documents { + documents = append(documents, DocumentDTO{ + Id: doc.Id, + Path: doc.Path, + Name: doc.Name, + Ext: doc.Ext, + Size: doc.Size, + }) + } + deliveries = append(deliveries, TransferDeliveryDTO{ Id: del.Id, Supplier: SupplierSimpleDTO{ @@ -195,16 +197,7 @@ func ToTransferListDTO(e entity.StockTransfer) TransferListDTO { ShippingCostItem: del.ShippingCostItem, ShippingCostTotal: del.ShippingCostTotal, Items: items, - }) - } - var documents []DocumentDTO - for _, doc := range e.Documents { - documents = append(documents, DocumentDTO{ - Id: doc.Id, - Path: doc.Path, - Name: doc.Name, - Ext: doc.Ext, - Size: doc.Size, + Documents: documents, }) } @@ -215,7 +208,6 @@ func ToTransferListDTO(e entity.StockTransfer) TransferListDTO { UpdatedAt: e.UpdatedAt, Details: details, Deliveries: deliveries, - Documents: documents, } } @@ -228,21 +220,31 @@ func ToTransferListDTOs(e []entity.StockTransfer) []TransferListDTO { } func ToTransferDetailDTO(e entity.StockTransfer) TransferDetailDTO { - // Map details var details []TransferDetailItemDTO for _, d := range e.Details { details = append(details, TransferDetailItemDTO{ Id: d.Id, - Proudct: ProductSimpleDTO{ + Product: ProductSimpleDTO{ Id: d.Product.Id, Name: d.Product.Name, }, Quantity: d.Quantity, }) } - // Map deliveries + var deliveries []TransferDeliveryDTO for _, del := range e.Deliveries { + var documents []DocumentDTO + for _, doc := range del.Documents { + documents = append(documents, DocumentDTO{ + Id: doc.Id, + Path: doc.Path, + Name: doc.Name, + Ext: doc.Ext, + Size: doc.Size, + }) + } + deliveries = append(deliveries, TransferDeliveryDTO{ Id: del.Id, Supplier: SupplierSimpleDTO{ @@ -254,8 +256,10 @@ func ToTransferDetailDTO(e entity.StockTransfer) TransferDetailDTO { DocumentNumber: del.DocumentNumber, ShippingCostItem: del.ShippingCostItem, ShippingCostTotal: del.ShippingCostTotal, + Documents: documents, }) } + return TransferDetailDTO{ TransferListDTO: ToTransferListDTO(e), Details: details, diff --git a/internal/modules/inventory/transfers/services/transfer.service.go b/internal/modules/inventory/transfers/services/transfer.service.go index 33ca77ff..89e7b271 100644 --- a/internal/modules/inventory/transfers/services/transfer.service.go +++ b/internal/modules/inventory/transfers/services/transfer.service.go @@ -76,8 +76,8 @@ func (s transferService) withRelations(db *gorm.DB) *gorm.DB { Preload("Details.Product"). Preload("Deliveries.Items"). Preload("Deliveries.Supplier"). - Preload("Documents", func(db *gorm.DB) *gorm.DB { - return db.Where("documentable_type = ?", "STOCK_TRANSFER") + Preload("Deliveries.Documents", func(db *gorm.DB) *gorm.DB { + return db.Where("documentable_type = ?", string(utils.DocumentableTypeTransfer)) }) } @@ -258,29 +258,26 @@ func (s *transferService) CreateOne(c *fiber.Ctx, req *validation.TransferReques return err } - actorIDCopy := actorID if s.DocumentSvc != nil && len(files) > 0 { - s.Log.Infof("Starting document upload for %d files", len(files)) - documentFiles := make([]commonSvc.DocumentFile, 0, len(files)) + // Upload documents for each delivery for idx, file := range files { - docIndex := idx - documentFiles = append(documentFiles, commonSvc.DocumentFile{ - File: file, - Type: "STOCK_TRANSFER_DOCUMENT", - Index: &docIndex, + documentFiles := []commonSvc.DocumentFile{ + { + File: file, + Type: string(utils.DocumentTypeTransfer), + Index: &idx, + }, + } + _, err := s.DocumentSvc.UploadDocuments(c.Context(), commonSvc.DocumentUploadRequest{ + DocumentableType: string(utils.DocumentableTypeTransfer), + DocumentableID: deliveries[idx].Id, + CreatedBy: &actorID, + Files: documentFiles, }) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("Failed to upload document for delivery %d", idx+1)) + } } - _, err := s.DocumentSvc.UploadDocuments(c.Context(), commonSvc.DocumentUploadRequest{ - DocumentableType: "STOCK_TRANSFER", - DocumentableID: entityTransfer.Id, - CreatedBy: &actorIDCopy, - Files: documentFiles, - }) - if err != nil { - s.Log.Errorf("Failed to upload documents for transfer %d: %+v", entityTransfer.Id, err) - return fiber.NewError(fiber.StatusInternalServerError, "Failed to upload transfer documents") - } - s.Log.Infof("Successfully uploaded documents for transfer ID %d", entityTransfer.Id) } for _, product := range req.Products { diff --git a/internal/utils/constant.go b/internal/utils/constant.go index 85b33f9b..02b15102 100644 --- a/internal/utils/constant.go +++ b/internal/utils/constant.go @@ -314,6 +314,19 @@ var ExpenseApprovalSteps = map[approvalutils.ApprovalStep]string{ ExpenseStepSelesai: "Selesai", } +// ------------------------------------------------------------------- +// Document +// ------------------------------------------------------------------- + +type DocumentType string +type DocumentableType string + +const ( + DocumentTypeTransfer DocumentType = "STOCK_TRANSFER_DOCUMENT" + + DocumentableTypeTransfer DocumentableType = "STOCK_TRANSFER" +) + // ------------------------------------------------------------------- // Validators // ------------------------------------------------------------------- @@ -448,7 +461,7 @@ func IsValidExpenseCategory(v string) bool { return false } -// example use +// e xample use // Recording helper