package service import ( "context" "database/sql" "fmt" "time" "github.com/google/uuid" "github.com/muyuqingfeng/iloom/shared/pkg/database" "github.com/muyuqingfeng/iloom/washing-service/internal/model" "github.com/muyuqingfeng/iloom/washing-service/internal/repository" ) type CompleteRequest struct { ActualShrinkageRate float64 `json:"actual_shrinkage_rate" binding:"required"` ActualWashedMeters float64 `json:"actual_washed_meters" binding:"required,gt=0"` Rolls int `json:"rolls" binding:"required,gt=0"` WashingDate *time.Time `json:"washing_date"` Notes string `json:"notes"` WarehouseID string `json:"warehouse_id"` WarehouseLocation string `json:"warehouse_location"` } type CompletionService struct { completionRepo *repository.CompletionRepo planRepo *repository.PlanRepo fpRepo *repository.FinishedProductRepo db *sql.DB } func NewCompletionService( cr *repository.CompletionRepo, pr *repository.PlanRepo, fpr *repository.FinishedProductRepo, db *sql.DB, ) *CompletionService { return &CompletionService{ completionRepo: cr, planRepo: pr, fpRepo: fpr, db: db, } } func (s *CompletionService) Complete( ctx context.Context, washingPlanID, companyID, userID string, req CompleteRequest, ) (*model.WashingPlanCompletion, error) { plan, err := s.planRepo.GetByID(ctx, washingPlanID) if err != nil { return nil, fmt.Errorf("get plan: %w", err) } if plan == nil { return nil, fmt.Errorf("washing plan not found") } if plan.WashingFactoryID == nil || *plan.WashingFactoryID != companyID { return nil, fmt.Errorf("washing plan not found") } if plan.Status == "completed" { return nil, fmt.Errorf("washing plan already completed") } now := time.Now() washingDate := now if req.WashingDate != nil { washingDate = *req.WashingDate } totalCost := req.ActualWashedMeters * plan.WashingPrice var unitCost float64 if req.ActualWashedMeters > 0 { unitCost = totalCost / req.ActualWashedMeters } completionID := uuid.New().String() fpID := uuid.New().String() var notes *string if req.Notes != "" { notes = &req.Notes } var warehouseID *string if req.WarehouseID != "" { warehouseID = &req.WarehouseID } var warehouseLocation *string if req.WarehouseLocation != "" { warehouseLocation = &req.WarehouseLocation } completion := &model.WashingPlanCompletion{ ID: completionID, WashingPlanID: washingPlanID, CompanyID: companyID, ActualShrinkageRate: req.ActualShrinkageRate, ActualWashedMeters: req.ActualWashedMeters, ActualWashingCost: &totalCost, UnitCost: &unitCost, Rolls: req.Rolls, WashingDate: washingDate, Notes: notes, CompletedBy: &userID, CompletedAt: now, FinishedProductID: &fpID, AutoImported: true, WarehouseID: warehouseID, WarehouseLocation: warehouseLocation, CreatedAt: now, } err = database.WithTx(ctx, s.db, func(tx *sql.Tx) error { if err := s.completionRepo.Create(ctx, tx, completion); err != nil { return fmt.Errorf("create completion: %w", err) } if err := s.planRepo.UpdateActuals(ctx, tx, washingPlanID, req.ActualShrinkageRate, req.ActualWashedMeters, totalCost); err != nil { return fmt.Errorf("update plan actuals: %w", err) } fp := &model.FinishedProduct{ ID: fpID, CompanyID: companyID, ProductName: plan.ProductName, FabricCode: "", Color: "", ShrinkageRate: req.ActualShrinkageRate, WashedMeters: req.ActualWashedMeters, StockMeters: req.ActualWashedMeters, StockRolls: req.Rolls, Rolls: &req.Rolls, WashingCost: &totalCost, UnitCost: &unitCost, Status: "in_stock", Notes: notes, ProductID: &plan.ProductID, WashingPlanID: &washingPlanID, WarehouseID: warehouseID, WarehouseLocation: warehouseLocation, CreatedBy: &userID, CreatedAt: now, UpdatedAt: now, } if err := s.fpRepo.Create(ctx, tx, fp); err != nil { return fmt.Errorf("create finished product: %w", err) } invRecord := &model.FinishedProductInventoryRecord{ ID: uuid.New().String(), FinishedProductID: fpID, CompanyID: companyID, RecordType: "inbound", Rolls: req.Rolls, Meters: req.ActualWashedMeters, Notes: notes, OperatorID: &userID, WarehouseID: warehouseID, WarehouseLocation: warehouseLocation, WashingCompletionID: &completionID, CreatedAt: now, } if err := s.fpRepo.CreateInventoryRecord(ctx, tx, invRecord); err != nil { return fmt.Errorf("create inventory record: %w", err) } return nil }) if err != nil { return nil, err } return completion, nil }