325 lines
10 KiB
Go
325 lines
10 KiB
Go
|
|
package repository
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"database/sql"
|
||
|
|
"fmt"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/google/uuid"
|
||
|
|
"github.com/muyuqingfeng/iloom/purchaser-service/internal/model"
|
||
|
|
"github.com/muyuqingfeng/iloom/shared/pkg/types"
|
||
|
|
)
|
||
|
|
|
||
|
|
type PlanRepo struct {
|
||
|
|
db *sql.DB
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewPlanRepo(db *sql.DB) *PlanRepo {
|
||
|
|
return &PlanRepo{db: db}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *PlanRepo) List(ctx context.Context, companyID string, p types.Pagination, status string) ([]model.ProductionPlan, int, error) {
|
||
|
|
countQuery := `SELECT COUNT(*) FROM ilm_production_plan WHERE purchaser_id = ?`
|
||
|
|
args := []interface{}{companyID}
|
||
|
|
|
||
|
|
if status != "" {
|
||
|
|
countQuery += " AND status = ?"
|
||
|
|
args = append(args, status)
|
||
|
|
}
|
||
|
|
|
||
|
|
var total int
|
||
|
|
if err := r.db.QueryRowContext(ctx, countQuery, args...).Scan(&total); err != nil {
|
||
|
|
return nil, 0, fmt.Errorf("count plans: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
query := `SELECT id, plan_code, purchaser_id, product_name, fabric_code, color, color_code,
|
||
|
|
target_quantity, completed_quantity, production_price, yarn_usage_per_meter,
|
||
|
|
status, remark, start_time, created_by, created_at, updated_at
|
||
|
|
FROM ilm_production_plan WHERE purchaser_id = ?`
|
||
|
|
|
||
|
|
queryArgs := []interface{}{companyID}
|
||
|
|
|
||
|
|
if status != "" {
|
||
|
|
query += " AND status = ?"
|
||
|
|
queryArgs = append(queryArgs, status)
|
||
|
|
}
|
||
|
|
|
||
|
|
query += " ORDER BY created_at DESC"
|
||
|
|
query += " LIMIT ? OFFSET ?"
|
||
|
|
queryArgs = append(queryArgs, p.PageSize, p.Offset())
|
||
|
|
|
||
|
|
rows, err := r.db.QueryContext(ctx, query, queryArgs...)
|
||
|
|
if err != nil {
|
||
|
|
return nil, 0, fmt.Errorf("list plans: %w", err)
|
||
|
|
}
|
||
|
|
defer rows.Close()
|
||
|
|
|
||
|
|
var plans []model.ProductionPlan
|
||
|
|
for rows.Next() {
|
||
|
|
var plan model.ProductionPlan
|
||
|
|
if err := rows.Scan(
|
||
|
|
&plan.ID, &plan.PlanCode, &plan.PurchaserID, &plan.ProductName,
|
||
|
|
&plan.FabricCode, &plan.Color, &plan.ColorCode,
|
||
|
|
&plan.TargetQuantity, &plan.CompletedQuantity,
|
||
|
|
&plan.ProductionPrice, &plan.YarnUsagePerMeter,
|
||
|
|
&plan.Status, &plan.Remark, &plan.StartTime,
|
||
|
|
&plan.CreatedBy, &plan.CreatedAt, &plan.UpdatedAt,
|
||
|
|
); err != nil {
|
||
|
|
return nil, 0, fmt.Errorf("scan plan: %w", err)
|
||
|
|
}
|
||
|
|
plans = append(plans, plan)
|
||
|
|
}
|
||
|
|
return plans, total, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *PlanRepo) Create(ctx context.Context, tx *sql.Tx, plan *model.ProductionPlan) error {
|
||
|
|
query := `INSERT INTO ilm_production_plan
|
||
|
|
(id, plan_code, purchaser_id, product_name, fabric_code, color, color_code,
|
||
|
|
target_quantity, completed_quantity, production_price, yarn_usage_per_meter,
|
||
|
|
status, remark, start_time, created_by, created_at, updated_at)
|
||
|
|
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
|
||
|
|
_, err := tx.ExecContext(ctx, query,
|
||
|
|
plan.ID, plan.PlanCode, plan.PurchaserID, plan.ProductName,
|
||
|
|
plan.FabricCode, plan.Color, plan.ColorCode,
|
||
|
|
plan.TargetQuantity, plan.CompletedQuantity,
|
||
|
|
plan.ProductionPrice, plan.YarnUsagePerMeter,
|
||
|
|
plan.Status, plan.Remark, plan.StartTime,
|
||
|
|
plan.CreatedBy, plan.CreatedAt, plan.UpdatedAt,
|
||
|
|
)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("insert plan: %w", err)
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *PlanRepo) CreateYarnRatios(ctx context.Context, tx *sql.Tx, ratios []model.YarnRatio) error {
|
||
|
|
for _, ratio := range ratios {
|
||
|
|
query := `INSERT INTO ilm_yarn_ratio
|
||
|
|
(id, plan_id, yarn_name, yarn_type, ratio, amount_per_meter, total_amount, company_id, created_at)
|
||
|
|
VALUES (?,?,?,?,?,?,?,?,?)`
|
||
|
|
_, err := tx.ExecContext(ctx, query,
|
||
|
|
ratio.ID, ratio.PlanID, ratio.YarnName, ratio.YarnType,
|
||
|
|
ratio.Ratio, ratio.AmountPerMeter, ratio.TotalAmount,
|
||
|
|
ratio.CompanyID, ratio.CreatedAt,
|
||
|
|
)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("insert yarn ratio: %w", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *PlanRepo) GetByID(ctx context.Context, id string) (*model.ProductionPlan, error) {
|
||
|
|
query := `SELECT id, plan_code, purchaser_id, product_name, fabric_code, color, color_code,
|
||
|
|
target_quantity, completed_quantity, production_price, yarn_usage_per_meter,
|
||
|
|
status, remark, start_time, created_by, created_at, updated_at
|
||
|
|
FROM ilm_production_plan WHERE id = ?`
|
||
|
|
|
||
|
|
plan := &model.ProductionPlan{}
|
||
|
|
err := r.db.QueryRowContext(ctx, query, id).Scan(
|
||
|
|
&plan.ID, &plan.PlanCode, &plan.PurchaserID, &plan.ProductName,
|
||
|
|
&plan.FabricCode, &plan.Color, &plan.ColorCode,
|
||
|
|
&plan.TargetQuantity, &plan.CompletedQuantity,
|
||
|
|
&plan.ProductionPrice, &plan.YarnUsagePerMeter,
|
||
|
|
&plan.Status, &plan.Remark, &plan.StartTime,
|
||
|
|
&plan.CreatedBy, &plan.CreatedAt, &plan.UpdatedAt,
|
||
|
|
)
|
||
|
|
if err != nil {
|
||
|
|
if err == sql.ErrNoRows {
|
||
|
|
return nil, nil
|
||
|
|
}
|
||
|
|
return nil, fmt.Errorf("get plan by id: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Load yarn ratios
|
||
|
|
yarnRows, err := r.db.QueryContext(ctx,
|
||
|
|
`SELECT id, plan_id, yarn_name, yarn_type, ratio, amount_per_meter, total_amount, company_id, created_at
|
||
|
|
FROM ilm_yarn_ratio WHERE plan_id = ? ORDER BY created_at`, id)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("get yarn ratios: %w", err)
|
||
|
|
}
|
||
|
|
defer yarnRows.Close()
|
||
|
|
|
||
|
|
for yarnRows.Next() {
|
||
|
|
var yr model.YarnRatio
|
||
|
|
if err := yarnRows.Scan(
|
||
|
|
&yr.ID, &yr.PlanID, &yr.YarnName, &yr.YarnType,
|
||
|
|
&yr.Ratio, &yr.AmountPerMeter, &yr.TotalAmount,
|
||
|
|
&yr.CompanyID, &yr.CreatedAt,
|
||
|
|
); err != nil {
|
||
|
|
return nil, fmt.Errorf("scan yarn ratio: %w", err)
|
||
|
|
}
|
||
|
|
plan.YarnRatios = append(plan.YarnRatios, yr)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Load factories
|
||
|
|
factoryRows, err := r.db.QueryContext(ctx,
|
||
|
|
`SELECT pf.id, pf.plan_id, pf.factory_id, pf.factory_type, pf.created_at,
|
||
|
|
COALESCE(c.name, '') as factory_name
|
||
|
|
FROM ilm_plan_factory pf
|
||
|
|
LEFT JOIN ilm_company c ON c.id = pf.factory_id
|
||
|
|
WHERE pf.plan_id = ? ORDER BY pf.created_at`, id)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("get plan factories: %w", err)
|
||
|
|
}
|
||
|
|
defer factoryRows.Close()
|
||
|
|
|
||
|
|
for factoryRows.Next() {
|
||
|
|
var pf model.PlanFactory
|
||
|
|
if err := factoryRows.Scan(
|
||
|
|
&pf.ID, &pf.PlanID, &pf.FactoryID, &pf.FactoryType,
|
||
|
|
&pf.CreatedAt, &pf.FactoryName,
|
||
|
|
); err != nil {
|
||
|
|
return nil, fmt.Errorf("scan plan factory: %w", err)
|
||
|
|
}
|
||
|
|
plan.Factories = append(plan.Factories, pf)
|
||
|
|
}
|
||
|
|
|
||
|
|
return plan, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *PlanRepo) Update(ctx context.Context, plan *model.ProductionPlan) error {
|
||
|
|
query := `UPDATE ilm_production_plan SET
|
||
|
|
product_name = ?, fabric_code = ?, color = ?, color_code = ?,
|
||
|
|
target_quantity = ?, production_price = ?, yarn_usage_per_meter = ?,
|
||
|
|
status = ?, remark = ?, updated_at = ?
|
||
|
|
WHERE id = ?`
|
||
|
|
_, err := r.db.ExecContext(ctx, query,
|
||
|
|
plan.ProductName, plan.FabricCode, plan.Color, plan.ColorCode,
|
||
|
|
plan.TargetQuantity, plan.ProductionPrice, plan.YarnUsagePerMeter,
|
||
|
|
plan.Status, plan.Remark, time.Now(),
|
||
|
|
plan.ID,
|
||
|
|
)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("update plan: %w", err)
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *PlanRepo) Delete(ctx context.Context, id, companyID string) error {
|
||
|
|
_, err := r.db.ExecContext(ctx,
|
||
|
|
`DELETE FROM ilm_production_plan WHERE id = ? AND purchaser_id = ?`,
|
||
|
|
id, companyID,
|
||
|
|
)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("delete plan: %w", err)
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *PlanRepo) AssignFactory(ctx context.Context, planID, factoryID, factoryType string) error {
|
||
|
|
id := uuid.New().String()
|
||
|
|
_, err := r.db.ExecContext(ctx,
|
||
|
|
`INSERT INTO ilm_plan_factory (id, plan_id, factory_id, factory_type, created_at)
|
||
|
|
VALUES (?, ?, ?, ?, ?)`,
|
||
|
|
id, planID, factoryID, factoryType, time.Now(),
|
||
|
|
)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("assign factory: %w", err)
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *PlanRepo) GetSteps(ctx context.Context, planID string) ([]model.ProcessStep, error) {
|
||
|
|
rows, err := r.db.QueryContext(ctx,
|
||
|
|
`SELECT ps.id, ps.plan_id, ps.step_type, ps.status, ps.notes,
|
||
|
|
ps.operator_id, ps.company_id, ps.timestamp, ps.created_at,
|
||
|
|
COALESCE(su.real_name, '') as operator_name
|
||
|
|
FROM ilm_process_step ps
|
||
|
|
LEFT JOIN sys_user su ON su.user_id = ps.operator_id
|
||
|
|
WHERE ps.plan_id = ? ORDER BY ps.created_at`, planID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("get steps: %w", err)
|
||
|
|
}
|
||
|
|
defer rows.Close()
|
||
|
|
|
||
|
|
var steps []model.ProcessStep
|
||
|
|
for rows.Next() {
|
||
|
|
var s model.ProcessStep
|
||
|
|
if err := rows.Scan(
|
||
|
|
&s.ID, &s.PlanID, &s.StepType, &s.Status, &s.Notes,
|
||
|
|
&s.OperatorID, &s.CompanyID, &s.Timestamp, &s.CreatedAt,
|
||
|
|
&s.OperatorName,
|
||
|
|
); err != nil {
|
||
|
|
return nil, fmt.Errorf("scan step: %w", err)
|
||
|
|
}
|
||
|
|
steps = append(steps, s)
|
||
|
|
}
|
||
|
|
return steps, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *PlanRepo) GetInventory(ctx context.Context, planID string) ([]model.InventoryRecord, error) {
|
||
|
|
rows, err := r.db.QueryContext(ctx,
|
||
|
|
`SELECT ir.id, ir.plan_id, ir.warehouse_id, ir.quantity, ir.rolls,
|
||
|
|
ir.price_per_meter, ir.price_note, ir.warehouse_location,
|
||
|
|
ir.operator_id, ir.company_id, ir.created_at,
|
||
|
|
COALESCE(su.real_name, '') as operator_name
|
||
|
|
FROM ilm_inventory_record ir
|
||
|
|
LEFT JOIN sys_user su ON su.user_id = ir.operator_id
|
||
|
|
WHERE ir.plan_id = ? ORDER BY ir.created_at DESC`, planID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("get inventory: %w", err)
|
||
|
|
}
|
||
|
|
defer rows.Close()
|
||
|
|
|
||
|
|
var records []model.InventoryRecord
|
||
|
|
for rows.Next() {
|
||
|
|
var rec model.InventoryRecord
|
||
|
|
if err := rows.Scan(
|
||
|
|
&rec.ID, &rec.PlanID, &rec.WarehouseID, &rec.Quantity, &rec.Rolls,
|
||
|
|
&rec.PricePerMeter, &rec.PriceNote, &rec.WarehouseLocation,
|
||
|
|
&rec.OperatorID, &rec.CompanyID, &rec.CreatedAt,
|
||
|
|
&rec.OperatorName,
|
||
|
|
); err != nil {
|
||
|
|
return nil, fmt.Errorf("scan inventory record: %w", err)
|
||
|
|
}
|
||
|
|
records = append(records, rec)
|
||
|
|
}
|
||
|
|
return records, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *PlanRepo) CountByStatus(ctx context.Context, companyID string) (total, producing, completed int, err error) {
|
||
|
|
query := `SELECT
|
||
|
|
COUNT(*),
|
||
|
|
SUM(CASE WHEN status = 'producing' THEN 1 ELSE 0 END),
|
||
|
|
SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END)
|
||
|
|
FROM ilm_production_plan WHERE purchaser_id = ?`
|
||
|
|
err = r.db.QueryRowContext(ctx, query, companyID).Scan(&total, &producing, &completed)
|
||
|
|
if err != nil {
|
||
|
|
return 0, 0, 0, fmt.Errorf("count by status: %w", err)
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *PlanRepo) Recent(ctx context.Context, companyID string, limit int) ([]model.ProductionPlan, error) {
|
||
|
|
query := `SELECT id, plan_code, purchaser_id, product_name, fabric_code, color, color_code,
|
||
|
|
target_quantity, completed_quantity, production_price, yarn_usage_per_meter,
|
||
|
|
status, remark, start_time, created_by, created_at, updated_at
|
||
|
|
FROM ilm_production_plan WHERE purchaser_id = ?
|
||
|
|
ORDER BY created_at DESC LIMIT ?`
|
||
|
|
|
||
|
|
rows, err := r.db.QueryContext(ctx, query, companyID, limit)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("recent plans: %w", err)
|
||
|
|
}
|
||
|
|
defer rows.Close()
|
||
|
|
|
||
|
|
var plans []model.ProductionPlan
|
||
|
|
for rows.Next() {
|
||
|
|
var plan model.ProductionPlan
|
||
|
|
if err := rows.Scan(
|
||
|
|
&plan.ID, &plan.PlanCode, &plan.PurchaserID, &plan.ProductName,
|
||
|
|
&plan.FabricCode, &plan.Color, &plan.ColorCode,
|
||
|
|
&plan.TargetQuantity, &plan.CompletedQuantity,
|
||
|
|
&plan.ProductionPrice, &plan.YarnUsagePerMeter,
|
||
|
|
&plan.Status, &plan.Remark, &plan.StartTime,
|
||
|
|
&plan.CreatedBy, &plan.CreatedAt, &plan.UpdatedAt,
|
||
|
|
); err != nil {
|
||
|
|
return nil, fmt.Errorf("scan recent plan: %w", err)
|
||
|
|
}
|
||
|
|
plans = append(plans, plan)
|
||
|
|
}
|
||
|
|
return plans, nil
|
||
|
|
}
|