245 lines
6.9 KiB
Go
245 lines
6.9 KiB
Go
|
|
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"
|
||
|
|
)
|
||
|
|
|
||
|
|
type ProductService struct {
|
||
|
|
repo *repository.ProductRepo
|
||
|
|
db *sql.DB
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewProductService(repo *repository.ProductRepo, db *sql.DB) *ProductService {
|
||
|
|
return &ProductService{repo: repo, db: db}
|
||
|
|
}
|
||
|
|
|
||
|
|
type CreateProductRequest struct {
|
||
|
|
ProductName string `json:"product_name"`
|
||
|
|
FabricCode string `json:"fabric_code"`
|
||
|
|
Color string `json:"color"`
|
||
|
|
ColorCode string `json:"color_code"`
|
||
|
|
Weight float64 `json:"weight"`
|
||
|
|
YarnUsagePerMeter float64 `json:"yarn_usage_per_meter"`
|
||
|
|
ProductionPrice *float64 `json:"production_price"`
|
||
|
|
YarnTypes []string `json:"yarn_types"`
|
||
|
|
YarnRatios []float64 `json:"yarn_ratios"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type UpdateProductRequest struct {
|
||
|
|
ProductName string `json:"product_name"`
|
||
|
|
FabricCode string `json:"fabric_code"`
|
||
|
|
Color string `json:"color"`
|
||
|
|
ColorCode string `json:"color_code"`
|
||
|
|
Weight float64 `json:"weight"`
|
||
|
|
YarnUsagePerMeter float64 `json:"yarn_usage_per_meter"`
|
||
|
|
ProductionPrice *float64 `json:"production_price"`
|
||
|
|
YarnTypes []string `json:"yarn_types"`
|
||
|
|
YarnRatios []float64 `json:"yarn_ratios"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type InboundRequest struct {
|
||
|
|
BatchNo string `json:"batch_no"`
|
||
|
|
Rolls int `json:"rolls"`
|
||
|
|
Meters float64 `json:"meters"`
|
||
|
|
Notes string `json:"notes"`
|
||
|
|
WarehouseID string `json:"warehouse_id"`
|
||
|
|
}
|
||
|
|
|
||
|
|
type OutboundRequest struct {
|
||
|
|
BatchNo string `json:"batch_no"`
|
||
|
|
Rolls int `json:"rolls"`
|
||
|
|
Meters float64 `json:"meters"`
|
||
|
|
OutboundType string `json:"outbound_type"`
|
||
|
|
Notes string `json:"notes"`
|
||
|
|
RecipientCompanyID string `json:"recipient_company_id"`
|
||
|
|
RecipientCompanyName string `json:"recipient_company_name"`
|
||
|
|
SourceBatchID string `json:"source_batch_id"`
|
||
|
|
WashingPlanID string `json:"washing_plan_id"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *ProductService) List(ctx context.Context, companyID string) ([]model.Product, error) {
|
||
|
|
return s.repo.List(ctx, companyID)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *ProductService) Create(ctx context.Context, companyID string, req CreateProductRequest) (*model.Product, error) {
|
||
|
|
now := time.Now()
|
||
|
|
p := &model.Product{
|
||
|
|
ID: uuid.New().String(),
|
||
|
|
CompanyID: companyID,
|
||
|
|
ProductName: req.ProductName,
|
||
|
|
FabricCode: req.FabricCode,
|
||
|
|
Color: req.Color,
|
||
|
|
ColorCode: req.ColorCode,
|
||
|
|
Weight: req.Weight,
|
||
|
|
YarnUsagePerMeter: req.YarnUsagePerMeter,
|
||
|
|
ProductionPrice: req.ProductionPrice,
|
||
|
|
TotalStock: 0,
|
||
|
|
RawFabricMeters: 0,
|
||
|
|
RawFabricRolls: 0,
|
||
|
|
YarnTypes: req.YarnTypes,
|
||
|
|
YarnRatios: req.YarnRatios,
|
||
|
|
CreatedAt: now,
|
||
|
|
UpdatedAt: now,
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := s.repo.Create(ctx, p); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
return p, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *ProductService) Update(ctx context.Context, id, companyID string, req UpdateProductRequest) error {
|
||
|
|
p := &model.Product{
|
||
|
|
ID: id,
|
||
|
|
ProductName: req.ProductName,
|
||
|
|
FabricCode: req.FabricCode,
|
||
|
|
Color: req.Color,
|
||
|
|
ColorCode: req.ColorCode,
|
||
|
|
Weight: req.Weight,
|
||
|
|
YarnUsagePerMeter: req.YarnUsagePerMeter,
|
||
|
|
ProductionPrice: req.ProductionPrice,
|
||
|
|
YarnTypes: req.YarnTypes,
|
||
|
|
YarnRatios: req.YarnRatios,
|
||
|
|
}
|
||
|
|
return s.repo.Update(ctx, p)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *ProductService) Delete(ctx context.Context, id, companyID string) error {
|
||
|
|
return s.repo.Delete(ctx, id, companyID)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *ProductService) Inbound(ctx context.Context, productID, companyID, userID string, req InboundRequest) error {
|
||
|
|
now := time.Now()
|
||
|
|
var notesPtr *string
|
||
|
|
if req.Notes != "" {
|
||
|
|
notesPtr = &req.Notes
|
||
|
|
}
|
||
|
|
var warehousePtr *string
|
||
|
|
if req.WarehouseID != "" {
|
||
|
|
warehousePtr = &req.WarehouseID
|
||
|
|
}
|
||
|
|
|
||
|
|
rec := &model.ProductInventoryRecord{
|
||
|
|
ID: uuid.New().String(),
|
||
|
|
ProductID: productID,
|
||
|
|
BatchNo: req.BatchNo,
|
||
|
|
Rolls: req.Rolls,
|
||
|
|
Meters: req.Meters,
|
||
|
|
Notes: notesPtr,
|
||
|
|
OperatorID: &userID,
|
||
|
|
CompanyID: &companyID,
|
||
|
|
WarehouseID: warehousePtr,
|
||
|
|
CreatedAt: now,
|
||
|
|
}
|
||
|
|
|
||
|
|
tx, err := s.db.BeginTx(ctx, nil)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("begin tx: %w", err)
|
||
|
|
}
|
||
|
|
defer tx.Rollback()
|
||
|
|
|
||
|
|
if err := s.repo.Inbound(ctx, tx, rec); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if err := s.repo.UpdateStock(ctx, tx, productID, req.Meters, req.Rolls); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
return tx.Commit()
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *ProductService) Outbound(ctx context.Context, productID, companyID, userID string, req OutboundRequest) error {
|
||
|
|
now := time.Now()
|
||
|
|
var notesPtr *string
|
||
|
|
if req.Notes != "" {
|
||
|
|
notesPtr = &req.Notes
|
||
|
|
}
|
||
|
|
var recipientIDPtr, recipientNamePtr, sourceBatchPtr, washingPlanPtr *string
|
||
|
|
if req.RecipientCompanyID != "" {
|
||
|
|
recipientIDPtr = &req.RecipientCompanyID
|
||
|
|
}
|
||
|
|
if req.RecipientCompanyName != "" {
|
||
|
|
recipientNamePtr = &req.RecipientCompanyName
|
||
|
|
}
|
||
|
|
if req.SourceBatchID != "" {
|
||
|
|
sourceBatchPtr = &req.SourceBatchID
|
||
|
|
}
|
||
|
|
if req.WashingPlanID != "" {
|
||
|
|
washingPlanPtr = &req.WashingPlanID
|
||
|
|
}
|
||
|
|
|
||
|
|
outboundType := req.OutboundType
|
||
|
|
if outboundType == "" {
|
||
|
|
outboundType = "normal"
|
||
|
|
}
|
||
|
|
|
||
|
|
rec := &model.ProductOutboundRecord{
|
||
|
|
ID: uuid.New().String(),
|
||
|
|
ProductID: productID,
|
||
|
|
BatchNo: req.BatchNo,
|
||
|
|
Rolls: req.Rolls,
|
||
|
|
Meters: req.Meters,
|
||
|
|
OutboundType: outboundType,
|
||
|
|
Notes: notesPtr,
|
||
|
|
OperatorID: &userID,
|
||
|
|
CompanyID: &companyID,
|
||
|
|
RecipientCompanyID: recipientIDPtr,
|
||
|
|
RecipientCompanyName: recipientNamePtr,
|
||
|
|
SourceBatchID: sourceBatchPtr,
|
||
|
|
WashingPlanID: washingPlanPtr,
|
||
|
|
CreatedAt: now,
|
||
|
|
}
|
||
|
|
|
||
|
|
tx, err := s.db.BeginTx(ctx, nil)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("begin tx: %w", err)
|
||
|
|
}
|
||
|
|
defer tx.Rollback()
|
||
|
|
|
||
|
|
if err := s.repo.Outbound(ctx, tx, rec); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if err := s.repo.UpdateStock(ctx, tx, productID, -req.Meters, -req.Rolls); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
return tx.Commit()
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *ProductService) Records(ctx context.Context, productID string) ([]model.ProductInventoryRecord, error) {
|
||
|
|
return s.repo.Records(ctx, productID)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *ProductService) UpdatePrice(ctx context.Context, productID, companyID, userID string, newPrice float64, reason string) error {
|
||
|
|
oldPrice, err := s.repo.GetPrice(ctx, productID)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
tx, err := s.db.BeginTx(ctx, nil)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("begin tx: %w", err)
|
||
|
|
}
|
||
|
|
defer tx.Rollback()
|
||
|
|
|
||
|
|
if err := s.repo.UpdatePrice(ctx, tx, productID, newPrice); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if err := s.repo.InsertPriceHistory(ctx, tx, productID, oldPrice, newPrice, reason, userID); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
return tx.Commit()
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *ProductService) PriceHistory(ctx context.Context, productID string) ([]model.ProductPriceHistory, error) {
|
||
|
|
return s.repo.PriceHistory(ctx, productID)
|
||
|
|
}
|