From 8de33a0f24e86331955aba70d4377853262c20de Mon Sep 17 00:00:00 2001 From: ragilap Date: Fri, 2 Jan 2026 20:43:57 +0700 Subject: [PATCH] feat(BE): fix delete project flock budget and uniformity, and fix uniformity with update purchase document --- .../common/service/common.document.service.go | 51 +++++++++++ .../common/service/common.fifo.service.go | 68 -------------- ...uniformity_project_budget_cascade.down.sql | 86 ++++++++++++++++++ ...e_uniformity_project_budget_cascade.up.sql | 90 +++++++++++++++++++ .../project_flock_kandang_uniformity.go | 9 +- .../services/projectflock.service.go | 9 ++ .../recordings/services/recording.service.go | 45 ---------- .../uniformities/dto/uniformity.dto.go | 2 - .../repositories/uniformity.repository.go | 13 +++ .../services/uniformity.service.go | 2 +- .../controllers/purchase.controller.go | 5 +- .../purchases/services/purchase.service.go | 64 ++++++++++++- internal/utils/constant.go | 4 +- 13 files changed, 320 insertions(+), 128 deletions(-) create mode 100644 internal/database/migrations/20260102092243_update_uniformity_project_budget_cascade.down.sql create mode 100644 internal/database/migrations/20260102092243_update_uniformity_project_budget_cascade.up.sql diff --git a/internal/common/service/common.document.service.go b/internal/common/service/common.document.service.go index 079e3eba..44f2c116 100644 --- a/internal/common/service/common.document.service.go +++ b/internal/common/service/common.document.service.go @@ -6,6 +6,7 @@ import ( "fmt" "mime" "mime/multipart" + "net/url" "path/filepath" "strings" "time" @@ -305,6 +306,56 @@ func (s *documentService) PresignURL(ctx context.Context, document entity.Docume return s.storage.PresignURL(ctx, document.Path, expires) } +// ResolveDocumentURL normalizes a stored path or URL into a presigned URL. +func ResolveDocumentURL( + ctx context.Context, + svc DocumentService, + rawPath string, + expires time.Duration, +) (string, error) { + if svc == nil { + return "", nil + } + + rawPath = strings.TrimSpace(rawPath) + if rawPath == "" { + return "", nil + } + + key := rawPath + lower := strings.ToLower(rawPath) + if strings.HasPrefix(lower, "http://") || strings.HasPrefix(lower, "https://") { + key = extractS3KeyFromURL(rawPath) + if key == "" { + return "", nil + } + } + + return svc.PresignURL(ctx, entity.Document{Path: key}, expires) +} + +func extractS3KeyFromURL(raw string) string { + parsed, err := url.Parse(strings.TrimSpace(raw)) + if err != nil { + return "" + } + path := strings.TrimPrefix(parsed.Path, "/") + if path == "" { + return "" + } + + host := strings.ToLower(strings.TrimSpace(parsed.Host)) + if strings.HasPrefix(host, "s3.") || strings.HasPrefix(host, "s3-") { + parts := strings.SplitN(path, "/", 2) + if len(parts) == 2 { + return parts[1] + } + return "" + } + + return path +} + func (s *documentService) generateObjectKey(ext string) (string, error) { normalizedExt := strings.TrimSpace(ext) if normalizedExt != "" && !strings.HasPrefix(normalizedExt, ".") { diff --git a/internal/common/service/common.fifo.service.go b/internal/common/service/common.fifo.service.go index 5b7adc2e..2a65c1b4 100644 --- a/internal/common/service/common.fifo.service.go +++ b/internal/common/service/common.fifo.service.go @@ -192,17 +192,6 @@ func (s *fifoService) Consume(ctx context.Context, req StockConsumeRequest) (*St if req.Quantity < 0 { return nil, errors.New("quantity must be zero or greater") } - if s.logger.IsLevelEnabled(logrus.DebugLevel) { - s.logger.WithFields(logrus.Fields{ - "usable_key": req.UsableKey.String(), - "usable_id": req.UsableID, - "requested_quantity": req.Quantity, - "allow_pending": req.AllowPending, - "product_warehouse_id": req.ProductWarehouseID, - }).Debug("fifo consume request") - } - - cfg, ok := fifo.Usable(req.UsableKey) if !ok { return nil, fmt.Errorf("usable %q is not registered", req.UsableKey) @@ -230,20 +219,6 @@ func (s *fifoService) Consume(ctx context.Context, req StockConsumeRequest) (*St currentPending := ctxRow.PendingQty currentTotal := currentUsage + currentPending delta := req.Quantity - currentTotal - if s.logger.IsLevelEnabled(logrus.DebugLevel) { - s.logger.WithFields(logrus.Fields{ - "usable_key": req.UsableKey.String(), - "usable_id": req.UsableID, - "product_warehouse_id": productWarehouseID, - "current_usage_qty": currentUsage, - "current_pending_qty": currentPending, - "current_total_qty": currentTotal, - "requested_quantity": req.Quantity, - "calculated_delta": delta, - "input_warehouse_match": req.ProductWarehouseID == 0 || req.ProductWarehouseID == productWarehouseID, - }).Debug("fifo consume context") - } - var ( usageDelta float64 pendingDelta float64 @@ -308,21 +283,6 @@ func (s *fifoService) Consume(ctx context.Context, req StockConsumeRequest) (*St result.ReleasedQuantity = releasedAmount result.UsageQuantity = currentUsage + usageDelta result.PendingQuantity = currentPending + pendingDelta - if s.logger.IsLevelEnabled(logrus.DebugLevel) { - s.logger.WithFields(logrus.Fields{ - "usable_key": req.UsableKey.String(), - "usable_id": req.UsableID, - "product_warehouse_id": productWarehouseID, - "usage_delta": usageDelta, - "pending_delta": pendingDelta, - "released_quantity": releasedAmount, - "added_allocations": len(addedAlloc), - "final_usage_qty": result.UsageQuantity, - "final_pending_qty": result.PendingQuantity, - "final_requested_qty": result.RequestedQuantity, - }).Debug("fifo consume result") - } - return nil }) if err != nil { @@ -336,14 +296,6 @@ func (s *fifoService) ReleaseUsage(ctx context.Context, req StockReleaseRequest) if req.UsableID == 0 || strings.TrimSpace(req.UsableKey.String()) == "" { return errors.New("usable key and id are required") } - if s.logger.IsLevelEnabled(logrus.DebugLevel) { - s.logger.WithFields(logrus.Fields{ - "usable_key": req.UsableKey.String(), - "usable_id": req.UsableID, - "reason": req.Reason, - }).Debug("fifo release request") - } - return s.withTransaction(ctx, req.Tx, func(tx *gorm.DB) error { cfg, ok := fifo.Usable(req.UsableKey) if !ok { @@ -354,17 +306,6 @@ func (s *fifoService) ReleaseUsage(ctx context.Context, req StockReleaseRequest) if err != nil { return err } - if s.logger.IsLevelEnabled(logrus.DebugLevel) { - s.logger.WithFields(logrus.Fields{ - "usable_key": req.UsableKey.String(), - "usable_id": req.UsableID, - "product_warehouse_id": ctxRow.ProductWarehouseID, - "current_usage_qty": ctxRow.UsageQty, - "current_pending_qty": ctxRow.PendingQty, - "current_total_qty": ctxRow.UsageQty + ctxRow.PendingQty, - }).Debug("fifo release context") - } - var usageDelta, pendingDelta float64 if ctxRow.UsageQty > 0 { if _, err := s.releaseUsagePortion(ctx, tx, req.UsableKey, req.UsableID, ctxRow.UsageQty); err != nil { @@ -380,15 +321,6 @@ func (s *fifoService) ReleaseUsage(ctx context.Context, req StockReleaseRequest) return err } - if s.logger.IsLevelEnabled(logrus.DebugLevel) { - s.logger.WithFields(logrus.Fields{ - "usable_key": req.UsableKey.String(), - "usable_id": req.UsableID, - "usage_delta": usageDelta, - "pending_delta": pendingDelta, - }).Debug("fifo release applied") - } - return s.allocations.ReleaseByUsable(ctx, req.UsableKey.String(), req.UsableID, req.Reason, func(db *gorm.DB) *gorm.DB { return s.txOrDB(tx, db) }) diff --git a/internal/database/migrations/20260102092243_update_uniformity_project_budget_cascade.down.sql b/internal/database/migrations/20260102092243_update_uniformity_project_budget_cascade.down.sql new file mode 100644 index 00000000..b702016c --- /dev/null +++ b/internal/database/migrations/20260102092243_update_uniformity_project_budget_cascade.down.sql @@ -0,0 +1,86 @@ +BEGIN; + +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_constraint + WHERE conname = 'fk_project_flock_kandang_uniformity_project_flock_kandang' + ) THEN + ALTER TABLE project_flock_kandang_uniformity + DROP CONSTRAINT fk_project_flock_kandang_uniformity_project_flock_kandang; + END IF; +END $$; + +ALTER TABLE project_flock_kandang_uniformity + ADD CONSTRAINT fk_project_flock_kandang_uniformity_project_flock_kandang + FOREIGN KEY (project_flock_kandang_id) + REFERENCES project_flock_kandangs (id) + ON DELETE RESTRICT ON UPDATE CASCADE; + +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_tables + WHERE tablename = 'project_budgets' + ) THEN + IF EXISTS ( + SELECT 1 + FROM pg_constraint + WHERE conname = 'fk_project_budgets_project_flock_id' + ) THEN + ALTER TABLE project_budgets + DROP CONSTRAINT fk_project_budgets_project_flock_id; + END IF; + + ALTER TABLE project_budgets + ADD CONSTRAINT fk_project_budgets_project_flock_id + FOREIGN KEY (project_flock_id) + REFERENCES project_flocks(id); + END IF; +END $$; + +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_tables + WHERE tablename = 'project_flock_kandang_uniformity' + ) THEN + IF NOT EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'project_flock_kandang_uniformity' + AND column_name = 'created_at' + ) THEN + ALTER TABLE project_flock_kandang_uniformity + ADD COLUMN created_at TIMESTAMPTZ DEFAULT NOW(); + END IF; + + IF NOT EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'project_flock_kandang_uniformity' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE project_flock_kandang_uniformity + ADD COLUMN updated_at TIMESTAMPTZ DEFAULT NOW(); + END IF; + + IF NOT EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'project_flock_kandang_uniformity' + AND column_name = 'deleted_at' + ) THEN + ALTER TABLE project_flock_kandang_uniformity + ADD COLUMN deleted_at TIMESTAMPTZ; + END IF; + END IF; +END $$; + +COMMIT; diff --git a/internal/database/migrations/20260102092243_update_uniformity_project_budget_cascade.up.sql b/internal/database/migrations/20260102092243_update_uniformity_project_budget_cascade.up.sql new file mode 100644 index 00000000..7a092012 --- /dev/null +++ b/internal/database/migrations/20260102092243_update_uniformity_project_budget_cascade.up.sql @@ -0,0 +1,90 @@ +BEGIN; + +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_constraint + WHERE conname = 'fk_project_flock_kandang_uniformity_project_flock_kandang' + ) THEN + ALTER TABLE project_flock_kandang_uniformity + DROP CONSTRAINT fk_project_flock_kandang_uniformity_project_flock_kandang; + END IF; +END $$; + +ALTER TABLE project_flock_kandang_uniformity + ADD CONSTRAINT fk_project_flock_kandang_uniformity_project_flock_kandang + FOREIGN KEY (project_flock_kandang_id) + REFERENCES project_flock_kandangs (id) + ON DELETE CASCADE ON UPDATE CASCADE; + +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_tables + WHERE tablename = 'project_budgets' + ) THEN + IF EXISTS ( + SELECT 1 + FROM pg_constraint + WHERE conname = 'fk_project_budgets_project_flock_id' + ) THEN + ALTER TABLE project_budgets + DROP CONSTRAINT fk_project_budgets_project_flock_id; + END IF; + + ALTER TABLE project_budgets + ADD CONSTRAINT fk_project_budgets_project_flock_id + FOREIGN KEY (project_flock_id) + REFERENCES project_flocks(id) + ON DELETE CASCADE ON UPDATE CASCADE; + END IF; +END $$; + +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM pg_trigger + WHERE tgname = 'trg_soft_delete_fk_project_flock_kandang_uniformity' + ) THEN + DROP TRIGGER trg_soft_delete_fk_project_flock_kandang_uniformity + ON project_flock_kandang_uniformity; + END IF; + + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'project_flock_kandang_uniformity' + AND column_name = 'created_at' + ) THEN + ALTER TABLE project_flock_kandang_uniformity + DROP COLUMN created_at; + END IF; + + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'project_flock_kandang_uniformity' + AND column_name = 'updated_at' + ) THEN + ALTER TABLE project_flock_kandang_uniformity + DROP COLUMN updated_at; + END IF; + + IF EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_schema = 'public' + AND table_name = 'project_flock_kandang_uniformity' + AND column_name = 'deleted_at' + ) THEN + ALTER TABLE project_flock_kandang_uniformity + DROP COLUMN deleted_at; + END IF; +END $$; + +COMMIT; diff --git a/internal/entities/project_flock_kandang_uniformity.go b/internal/entities/project_flock_kandang_uniformity.go index ecf90d19..bf320c72 100644 --- a/internal/entities/project_flock_kandang_uniformity.go +++ b/internal/entities/project_flock_kandang_uniformity.go @@ -1,10 +1,6 @@ package entities -import ( - "time" - - "gorm.io/gorm" -) +import "time" type ProjectFlockKandangUniformity struct { Id uint `gorm:"primaryKey"` @@ -18,9 +14,6 @@ type ProjectFlockKandangUniformity struct { UniformQty float64 `gorm:"type:numeric(15,3)"` NotUniformQty float64 `gorm:"type:numeric(15,3)"` UniformDate *time.Time `gorm:"type:timestamptz"` - CreatedAt time.Time `gorm:"autoCreateTime"` - UpdatedAt time.Time `gorm:"autoUpdateTime"` - DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` CreatedBy uint `gorm:"not null"` ProjectFlockKandang ProjectFlockKandang `gorm:"foreignKey:ProjectFlockKandangId;references:Id"` diff --git a/internal/modules/production/project_flocks/services/projectflock.service.go b/internal/modules/production/project_flocks/services/projectflock.service.go index 1e859e47..ec887eea 100644 --- a/internal/modules/production/project_flocks/services/projectflock.service.go +++ b/internal/modules/production/project_flocks/services/projectflock.service.go @@ -22,6 +22,7 @@ import ( pfutils "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/utils" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/validations" recordingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories" + uniformityRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/uniformities/repositories" utils "gitlab.com/mbugroup/lti-api.git/internal/utils" approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals" @@ -866,6 +867,14 @@ func (s projectflockService) detachKandangs(ctx context.Context, dbTransaction * } if len(pfkIDs) > 0 { + uniformityRepo := uniformityRepository.NewUniformityRepository(s.Repository.DB()) + if dbTransaction != nil { + uniformityRepo = uniformityRepository.NewUniformityRepository(dbTransaction) + } + if err := uniformityRepo.DeleteByProjectFlockKandangIDs(ctx, pfkIDs); err != nil { + return fiber.NewError(fiber.StatusInternalServerError, "Failed to remove uniformity data for project flock kandang") + } + pwRepo := s.ProductWarehouseRepo if dbTransaction != nil { pwRepo = productWarehouseRepository.NewProductWarehouseRepository(dbTransaction) diff --git a/internal/modules/production/recordings/services/recording.service.go b/internal/modules/production/recordings/services/recording.service.go index c9ca74f5..54052518 100644 --- a/internal/modules/production/recordings/services/recording.service.go +++ b/internal/modules/production/recordings/services/recording.service.go @@ -333,13 +333,6 @@ func (s recordingService) UpdateOne(c *fiber.Ctx, req *validation.Update, id uin s.Log.Errorf("Failed to list existing stocks: %+v", err) return err } - if s.Log != nil && s.Log.IsLevelEnabled(logrus.DebugLevel) { - s.Log.WithFields(logrus.Fields{ - "recording_id": recordingEntity.Id, - "existing": summarizeExistingStocks(existingStocks), - "incoming": summarizeIncomingStocks(req.Stocks), - }).Debug("recording update stock comparison") - } if stocksMatch(existingStocks, req.Stocks) { hasStockChanges = false } @@ -698,16 +691,6 @@ func (s *recordingService) consumeRecordingStocks(ctx context.Context, tx *gorm. } desiredTotal := desired + pending - if s.Log != nil && s.Log.IsLevelEnabled(logrus.DebugLevel) { - s.Log.WithFields(logrus.Fields{ - "recording_stock_id": stock.Id, - "product_warehouse_id": stock.ProductWarehouseId, - "desired_usage_qty": desired, - "desired_pending_qty": pending, - "desired_total_qty": desiredTotal, - }).Debug("recording fifo consume start") - } - result, err := s.FifoSvc.Consume(ctx, commonSvc.StockConsumeRequest{ UsableKey: recordingStockUsableKey, UsableID: stock.Id, @@ -721,17 +704,6 @@ func (s *recordingService) consumeRecordingStocks(ctx context.Context, tx *gorm. return err } - if s.Log != nil && s.Log.IsLevelEnabled(logrus.DebugLevel) { - s.Log.WithFields(logrus.Fields{ - "recording_stock_id": stock.Id, - "product_warehouse_id": stock.ProductWarehouseId, - "result_usage_qty": result.UsageQuantity, - "result_pending_qty": result.PendingQuantity, - "released_qty": result.ReleasedQuantity, - "added_allocations": len(result.AddedAllocations), - }).Debug("recording fifo consume result") - } - if err := s.Repository.UpdateStockUsage(tx, stock.Id, result.UsageQuantity, result.PendingQuantity); err != nil { return err } @@ -754,23 +726,6 @@ func (s *recordingService) releaseRecordingStocks(ctx context.Context, tx *gorm. continue } - var usage float64 - var pending float64 - if stock.UsageQty != nil { - usage = *stock.UsageQty - } - if stock.PendingQty != nil { - pending = *stock.PendingQty - } - if s.Log != nil && s.Log.IsLevelEnabled(logrus.DebugLevel) { - s.Log.WithFields(logrus.Fields{ - "recording_stock_id": stock.Id, - "product_warehouse_id": stock.ProductWarehouseId, - "current_usage_qty": usage, - "current_pending_qty": pending, - }).Debug("recording fifo release start") - } - if err := s.FifoSvc.ReleaseUsage(ctx, commonSvc.StockReleaseRequest{ UsableKey: recordingStockUsableKey, UsableID: stock.Id, diff --git a/internal/modules/production/uniformities/dto/uniformity.dto.go b/internal/modules/production/uniformities/dto/uniformity.dto.go index 4a813b98..0c38d81b 100644 --- a/internal/modules/production/uniformities/dto/uniformity.dto.go +++ b/internal/modules/production/uniformities/dto/uniformity.dto.go @@ -74,7 +74,6 @@ type UniformityListDTO struct { MeanDown float64 `json:"mean_down"` StandardMeanWeight *float64 `json:"standard_mean_weight"` StandardUniformity *float64 `json:"standard_uniformity"` - CreatedAt time.Time `json:"created_at"` CreatedBy uint `json:"created_by"` LatestApproval *approvalDTO.ApprovalRelationDTO `json:"latest_approval"` } @@ -154,7 +153,6 @@ func ToUniformityListDTOs(items []entity.ProjectFlockKandangUniformity) []Unifor UniformQty: item.UniformQty, MeanUp: item.MeanUp, MeanDown: item.MeanDown, - CreatedAt: item.CreatedAt, CreatedBy: item.CreatedBy, LatestApproval: latestApproval, } diff --git a/internal/modules/production/uniformities/repositories/uniformity.repository.go b/internal/modules/production/uniformities/repositories/uniformity.repository.go index 3bc66f4f..241dea49 100644 --- a/internal/modules/production/uniformities/repositories/uniformity.repository.go +++ b/internal/modules/production/uniformities/repositories/uniformity.repository.go @@ -1,6 +1,8 @@ package repository import ( + "context" + "gitlab.com/mbugroup/lti-api.git/internal/common/repository" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" "gorm.io/gorm" @@ -8,6 +10,7 @@ import ( type UniformityRepository interface { repository.BaseRepository[entity.ProjectFlockKandangUniformity] + DeleteByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) error } type UniformityRepositoryImpl struct { @@ -19,3 +22,13 @@ func NewUniformityRepository(db *gorm.DB) UniformityRepository { BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectFlockKandangUniformity](db), } } + +func (r *UniformityRepositoryImpl) DeleteByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) error { + if len(projectFlockKandangIDs) == 0 { + return nil + } + return r.DB().WithContext(ctx). + Unscoped(). + Where("project_flock_kandang_id IN ?", projectFlockKandangIDs). + Delete(&entity.ProjectFlockKandangUniformity{}).Error +} diff --git a/internal/modules/production/uniformities/services/uniformity.service.go b/internal/modules/production/uniformities/services/uniformity.service.go index 318fabc0..fb7ed9ed 100644 --- a/internal/modules/production/uniformities/services/uniformity.service.go +++ b/internal/modules/production/uniformities/services/uniformity.service.go @@ -99,7 +99,7 @@ func (s uniformityService) GetAll(c *fiber.Ctx, params *validation.Query) ([]ent if params.Week != 0 { db = db.Where("week = ?", params.Week) } - return db.Order("uniform_date DESC").Order("created_at DESC") + return db.Order("uniform_date DESC").Order("id DESC") }) if err != nil { diff --git a/internal/modules/purchases/controllers/purchase.controller.go b/internal/modules/purchases/controllers/purchase.controller.go index d9b32cd1..977b4ac1 100644 --- a/internal/modules/purchases/controllers/purchase.controller.go +++ b/internal/modules/purchases/controllers/purchase.controller.go @@ -180,7 +180,10 @@ func (ctrl *PurchaseController) ReceiveProducts(c *fiber.Ctx) error { req.Items = []validation.ReceivePurchaseItemRequest{singleItem} } } - req.TravelDocuments = form.File["documents"] + req.TravelDocuments = form.File["travel_documents"] + if len(req.TravelDocuments) == 0 { + req.TravelDocuments = form.File["documents"] + } result, err := ctrl.service.ReceiveProducts(c, uint(id), req) if err != nil { return err diff --git a/internal/modules/purchases/services/purchase.service.go b/internal/modules/purchases/services/purchase.service.go index 7dac0e19..68b21d6a 100644 --- a/internal/modules/purchases/services/purchase.service.go +++ b/internal/modules/purchases/services/purchase.service.go @@ -999,6 +999,22 @@ func (s *purchaseService) uploadTravelDocument( return "", errors.New("document service not available") } + documents, err := s.DocumentSvc.ListByTarget(ctx, string(utils.DocumentableTypePurchaseItem), uint64(itemID)) + if err != nil { + return "", err + } + if len(documents) > 0 { + var ids []uint + for _, doc := range documents { + if doc.Type == string(utils.DocumentTypePurchaseTravel) { + ids = append(ids, doc.Id) + } + } + if err := s.DocumentSvc.DeleteDocuments(ctx, ids, true); err != nil { + return "", err + } + } + documentFiles := []commonSvc.DocumentFile{{ File: file, Type: string(utils.DocumentTypePurchaseTravel), @@ -1015,7 +1031,7 @@ func (s *purchaseService) uploadTravelDocument( if len(results) == 0 { return "", errors.New("upload result is empty") } - return results[0].URL, nil + return results[0].Document.Path, nil } func (s *purchaseService) DeleteItems(c *fiber.Ctx, id uint, req *validation.DeletePurchaseItemsRequest) (*entity.Purchase, error) { @@ -1499,10 +1515,56 @@ func (s *purchaseService) loadPurchase( if err := s.attachLatestApproval(ctx, purchase); err != nil { s.Log.Warnf("Unable to attach latest approval for purchase %d: %+v", id, err) } + s.applyTravelDocumentURLs(ctx, purchase) return purchase, nil } +func (s *purchaseService) applyTravelDocumentURLs(ctx context.Context, purchase *entity.Purchase) { + if purchase == nil || s.DocumentSvc == nil { + return + } + + for i := range purchase.Items { + item := &purchase.Items[i] + documents, err := s.DocumentSvc.ListByTarget(ctx, string(utils.DocumentableTypePurchaseItem), uint64(item.Id)) + if err != nil { + s.Log.Warnf("Unable to load travel documents for purchase item %d: %+v", item.Id, err) + } else { + var targetDoc *entity.Document + for j := len(documents) - 1; j >= 0; j-- { + if documents[j].Type == string(utils.DocumentTypePurchaseTravel) { + targetDoc = &documents[j] + break + } + } + if targetDoc != nil { + url, err := s.DocumentSvc.PresignURL(ctx, *targetDoc, 15*time.Minute) + if err != nil { + s.Log.Warnf("Unable to presign travel document for purchase item %d: %+v", item.Id, err) + } else if url != "" { + item.TravelNumberDocs = &url + continue + } + } + } + + path := item.TravelNumberDocs + if path == nil || strings.TrimSpace(*path) == "" { + continue + } + url, err := commonSvc.ResolveDocumentURL(ctx, s.DocumentSvc, *path, 15*time.Minute) + if err != nil { + s.Log.Warnf("Unable to presign travel document for purchase item %d: %+v", item.Id, err) + continue + } + if url == "" { + continue + } + item.TravelNumberDocs = &url + } +} + func collectPFKIDsFromPurchase(p *entity.Purchase) []uint { seen := make(map[uint]struct{}) ids := make([]uint, 0) diff --git a/internal/utils/constant.go b/internal/utils/constant.go index 34334166..6ec50447 100644 --- a/internal/utils/constant.go +++ b/internal/utils/constant.go @@ -426,12 +426,12 @@ const ( DocumentTypeTransfer DocumentType = "STOCK_TRANSFER_DOCUMENT" DocumentTypeExpense DocumentType = "EXPENSE_DOCUMENT" DocumentTypeExpenseRealization DocumentType = "EXPENSE_REALIZATION_DOCUMENT" - DocumentTypePurchaseTravel DocumentType = "PURCHASE_TRAVEL_DOCUMENT" + DocumentTypePurchaseTravel DocumentType = "PURCHASE_TRAVEL_DOCUMENT" DocumentableTypeTransfer DocumentableType = "STOCK_TRANSFER" DocumentableTypeExpense DocumentableType = "EXPENSE" DocumentableTypeExpenseRealization DocumentableType = "EXPENSE_REALIZATION" - DocumentableTypePurchaseItem DocumentableType = "PURCHASE_ITEM" + DocumentableTypePurchaseItem DocumentableType = "PURCHASE_ITEM" ) // -------------------------------------------------------------------