package service import ( "context" "database/sql" "fmt" "time" "github.com/google/uuid" "github.com/muyuqingfeng/iloom/purchaser-service/internal/model" "github.com/muyuqingfeng/iloom/purchaser-service/internal/repository" "github.com/muyuqingfeng/iloom/shared/pkg/types" ) type PlanService struct { repo *repository.PlanRepo db *sql.DB } func NewPlanService(repo *repository.PlanRepo, db *sql.DB) *PlanService { return &PlanService{repo: repo, db: db} } type CreatePlanRequest struct { ProductName string `json:"product_name"` FabricCode string `json:"fabric_code"` Color string `json:"color"` ColorCode string `json:"color_code"` TargetQuantity float64 `json:"target_quantity"` ProductionPrice *float64 `json:"production_price"` YarnUsagePerMeter *float64 `json:"yarn_usage_per_meter"` Remark string `json:"remark"` YarnRatios []CreateYarnRatio `json:"yarn_ratios"` } type CreateYarnRatio struct { YarnName string `json:"yarn_name"` YarnType string `json:"yarn_type"` Ratio float64 `json:"ratio"` AmountPerMeter float64 `json:"amount_per_meter"` TotalAmount float64 `json:"total_amount"` } type UpdatePlanRequest struct { ProductName string `json:"product_name"` FabricCode string `json:"fabric_code"` Color string `json:"color"` ColorCode string `json:"color_code"` TargetQuantity float64 `json:"target_quantity"` ProductionPrice *float64 `json:"production_price"` YarnUsagePerMeter *float64 `json:"yarn_usage_per_meter"` Status string `json:"status"` Remark string `json:"remark"` } func (s *PlanService) List(ctx context.Context, companyID string, page types.Pagination, status string) ([]model.ProductionPlan, int, error) { page.Normalize() return s.repo.List(ctx, companyID, page, status) } func (s *PlanService) Create(ctx context.Context, companyID, userID string, req CreatePlanRequest) (*model.ProductionPlan, error) { now := time.Now() planCode := fmt.Sprintf("PP%s%03d", now.Format("20060102"), now.UnixMilli()%1000) var remarkPtr *string if req.Remark != "" { remarkPtr = &req.Remark } plan := &model.ProductionPlan{ ID: uuid.New().String(), PlanCode: planCode, PurchaserID: companyID, ProductName: req.ProductName, FabricCode: req.FabricCode, Color: req.Color, ColorCode: req.ColorCode, TargetQuantity: req.TargetQuantity, CompletedQuantity: 0, ProductionPrice: req.ProductionPrice, YarnUsagePerMeter: req.YarnUsagePerMeter, Status: string(types.PlanPending), Remark: remarkPtr, CreatedBy: &userID, CreatedAt: now, UpdatedAt: now, } var yarnRatios []model.YarnRatio for _, yr := range req.YarnRatios { var yarnTypePtr *string if yr.YarnType != "" { yarnTypePtr = &yr.YarnType } yarnRatios = append(yarnRatios, model.YarnRatio{ ID: uuid.New().String(), PlanID: plan.ID, YarnName: yr.YarnName, YarnType: yarnTypePtr, Ratio: yr.Ratio, AmountPerMeter: yr.AmountPerMeter, TotalAmount: yr.TotalAmount, CompanyID: &companyID, CreatedAt: now, }) } tx, err := s.db.BeginTx(ctx, nil) if err != nil { return nil, fmt.Errorf("begin tx: %w", err) } defer tx.Rollback() if err := s.repo.Create(ctx, tx, plan); err != nil { return nil, err } if len(yarnRatios) > 0 { if err := s.repo.CreateYarnRatios(ctx, tx, yarnRatios); err != nil { return nil, err } } if err := tx.Commit(); err != nil { return nil, fmt.Errorf("commit tx: %w", err) } plan.YarnRatios = yarnRatios return plan, nil } func (s *PlanService) GetByID(ctx context.Context, id, companyID string) (*model.ProductionPlan, error) { plan, err := s.repo.GetByID(ctx, id) if err != nil { return nil, err } if plan == nil { return nil, fmt.Errorf("plan not found") } if plan.PurchaserID != companyID { return nil, fmt.Errorf("plan not found") } steps, err := s.repo.GetSteps(ctx, id) if err != nil { return nil, err } plan.ProcessSteps = steps inventory, err := s.repo.GetInventory(ctx, id) if err != nil { return nil, err } plan.InventoryRecords = inventory return plan, nil } func (s *PlanService) Update(ctx context.Context, id, companyID string, req UpdatePlanRequest) error { plan, err := s.repo.GetByID(ctx, id) if err != nil { return err } if plan == nil || plan.PurchaserID != companyID { return fmt.Errorf("plan not found") } if req.ProductName != "" { plan.ProductName = req.ProductName } if req.FabricCode != "" { plan.FabricCode = req.FabricCode } if req.Color != "" { plan.Color = req.Color } if req.ColorCode != "" { plan.ColorCode = req.ColorCode } if req.TargetQuantity > 0 { plan.TargetQuantity = req.TargetQuantity } if req.ProductionPrice != nil { plan.ProductionPrice = req.ProductionPrice } if req.YarnUsagePerMeter != nil { plan.YarnUsagePerMeter = req.YarnUsagePerMeter } if req.Status != "" { plan.Status = req.Status } if req.Remark != "" { plan.Remark = &req.Remark } return s.repo.Update(ctx, plan) } func (s *PlanService) Delete(ctx context.Context, id, companyID string) error { return s.repo.Delete(ctx, id, companyID) } func (s *PlanService) AssignFactory(ctx context.Context, planID, factoryID, factoryType string) error { return s.repo.AssignFactory(ctx, planID, factoryID, factoryType) } func (s *PlanService) GetSteps(ctx context.Context, planID string) ([]model.ProcessStep, error) { return s.repo.GetSteps(ctx, planID) } func (s *PlanService) GetInventory(ctx context.Context, planID string) ([]model.InventoryRecord, error) { return s.repo.GetInventory(ctx, planID) }