iloom/washing-service/internal/service/completion_service.go
Chever John 9c39c8cbd7
init: iloom WMS monorepo with K8s deployment manifests
Go workspace (go.work) with 5 microservices + shared library:
- gateway (8080), auth-service (8081), purchaser-service (8082)
- textile-service (8083), washing-service (8084)
- shared: proto definitions, common packages

Infrastructure: docker-compose for local dev, K8s manifests for
K3s cluster deployment (mysql/redis/etcd + traefik ingress).

Frontend (iloom-flatten) added as git submodule.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-14 11:33:31 +08:00

176 lines
4.9 KiB
Go

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
}