package repository import ( "context" "database/sql" "fmt" "time" "github.com/google/uuid" "github.com/muyuqingfeng/iloom/purchaser-service/internal/model" ) type ProductRepo struct { db *sql.DB } func NewProductRepo(db *sql.DB) *ProductRepo { return &ProductRepo{db: db} } func (r *ProductRepo) List(ctx context.Context, companyID string) ([]model.Product, error) { query := `SELECT id, company_id, product_name, fabric_code, color, color_code, weight, warp_weight, weft_weight, total_weight, yarn_usage_per_meter, production_price, total_stock, raw_fabric_meters, raw_fabric_rolls, image_url, product_type, yarn_types, yarn_ratios, created_at, updated_at FROM products WHERE company_id = ? ORDER BY created_at DESC` rows, err := r.db.QueryContext(ctx, query, companyID) if err != nil { return nil, fmt.Errorf("list products: %w", err) } defer rows.Close() var products []model.Product for rows.Next() { var p model.Product if err := rows.Scan( &p.ID, &p.CompanyID, &p.ProductName, &p.FabricCode, &p.Color, &p.ColorCode, &p.Weight, &p.WarpWeight, &p.WeftWeight, &p.TotalWeight, &p.YarnUsagePerMeter, &p.ProductionPrice, &p.TotalStock, &p.RawFabricMeters, &p.RawFabricRolls, &p.ImageURL, &p.ProductType, &p.YarnTypes, &p.YarnRatios, &p.CreatedAt, &p.UpdatedAt, ); err != nil { return nil, fmt.Errorf("scan product: %w", err) } products = append(products, p) } return products, nil } func (r *ProductRepo) Create(ctx context.Context, p *model.Product) error { query := `INSERT INTO products (id, company_id, product_name, fabric_code, color, color_code, weight, warp_weight, weft_weight, total_weight, yarn_usage_per_meter, production_price, total_stock, raw_fabric_meters, raw_fabric_rolls, image_url, product_type, yarn_types, yarn_ratios, created_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)` _, err := r.db.ExecContext(ctx, query, p.ID, p.CompanyID, p.ProductName, p.FabricCode, p.Color, p.ColorCode, p.Weight, p.WarpWeight, p.WeftWeight, p.TotalWeight, p.YarnUsagePerMeter, p.ProductionPrice, p.TotalStock, p.RawFabricMeters, p.RawFabricRolls, p.ImageURL, p.ProductType, p.YarnTypes, p.YarnRatios, p.CreatedAt, p.UpdatedAt, ) if err != nil { return fmt.Errorf("insert product: %w", err) } return nil } func (r *ProductRepo) Update(ctx context.Context, p *model.Product) error { query := `UPDATE products SET product_name = ?, fabric_code = ?, color = ?, color_code = ?, weight = ?, warp_weight = ?, weft_weight = ?, total_weight = ?, yarn_usage_per_meter = ?, production_price = ?, image_url = ?, product_type = ?, yarn_types = ?, yarn_ratios = ?, updated_at = ? WHERE id = ?` _, err := r.db.ExecContext(ctx, query, p.ProductName, p.FabricCode, p.Color, p.ColorCode, p.Weight, p.WarpWeight, p.WeftWeight, p.TotalWeight, p.YarnUsagePerMeter, p.ProductionPrice, p.ImageURL, p.ProductType, p.YarnTypes, p.YarnRatios, time.Now(), p.ID, ) if err != nil { return fmt.Errorf("update product: %w", err) } return nil } func (r *ProductRepo) Delete(ctx context.Context, id, companyID string) error { _, err := r.db.ExecContext(ctx, `DELETE FROM products WHERE id = ? AND company_id = ?`, id, companyID, ) if err != nil { return fmt.Errorf("delete product: %w", err) } return nil } func (r *ProductRepo) Inbound(ctx context.Context, tx *sql.Tx, rec *model.ProductInventoryRecord) error { query := `INSERT INTO ilm_product_inventory (id, product_id, batch_no, rolls, meters, notes, operator_id, company_id, warehouse_id, created_at) VALUES (?,?,?,?,?,?,?,?,?,?)` _, err := tx.ExecContext(ctx, query, rec.ID, rec.ProductID, rec.BatchNo, rec.Rolls, rec.Meters, rec.Notes, rec.OperatorID, rec.CompanyID, rec.WarehouseID, rec.CreatedAt, ) if err != nil { return fmt.Errorf("insert inbound record: %w", err) } return nil } func (r *ProductRepo) UpdateStock(ctx context.Context, tx *sql.Tx, productID string, deltaMeters float64, deltaRolls int) error { query := `UPDATE products SET total_stock = total_stock + ?, raw_fabric_meters = raw_fabric_meters + ?, raw_fabric_rolls = raw_fabric_rolls + ?, updated_at = ? WHERE id = ?` _, err := tx.ExecContext(ctx, query, deltaMeters, deltaMeters, deltaRolls, time.Now(), productID) if err != nil { return fmt.Errorf("update stock: %w", err) } return nil } func (r *ProductRepo) Outbound(ctx context.Context, tx *sql.Tx, rec *model.ProductOutboundRecord) error { query := `INSERT INTO ilm_product_outbound (id, product_id, batch_no, rolls, meters, outbound_type, notes, operator_id, company_id, recipient_company_id, recipient_company_name, source_batch_id, washing_plan_id, created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)` _, err := tx.ExecContext(ctx, query, rec.ID, rec.ProductID, rec.BatchNo, rec.Rolls, rec.Meters, rec.OutboundType, rec.Notes, rec.OperatorID, rec.CompanyID, rec.RecipientCompanyID, rec.RecipientCompanyName, rec.SourceBatchID, rec.WashingPlanID, rec.CreatedAt, ) if err != nil { return fmt.Errorf("insert outbound record: %w", err) } return nil } func (r *ProductRepo) Records(ctx context.Context, productID string) ([]model.ProductInventoryRecord, error) { query := `SELECT id, product_id, batch_no, rolls, meters, notes, operator_id, company_id, warehouse_id, created_at FROM ilm_product_inventory WHERE product_id = ? ORDER BY created_at DESC` rows, err := r.db.QueryContext(ctx, query, productID) if err != nil { return nil, fmt.Errorf("list records: %w", err) } defer rows.Close() var records []model.ProductInventoryRecord for rows.Next() { var rec model.ProductInventoryRecord if err := rows.Scan( &rec.ID, &rec.ProductID, &rec.BatchNo, &rec.Rolls, &rec.Meters, &rec.Notes, &rec.OperatorID, &rec.CompanyID, &rec.WarehouseID, &rec.CreatedAt, ); err != nil { return nil, fmt.Errorf("scan record: %w", err) } records = append(records, rec) } return records, nil } func (r *ProductRepo) UpdatePrice(ctx context.Context, tx *sql.Tx, productID string, newPrice float64) error { _, err := tx.ExecContext(ctx, `UPDATE products SET production_price = ?, updated_at = ? WHERE id = ?`, newPrice, time.Now(), productID, ) if err != nil { return fmt.Errorf("update price: %w", err) } return nil } func (r *ProductRepo) GetPrice(ctx context.Context, productID string) (*float64, error) { var price *float64 err := r.db.QueryRowContext(ctx, `SELECT production_price FROM products WHERE id = ?`, productID, ).Scan(&price) if err != nil { return nil, fmt.Errorf("get price: %w", err) } return price, nil } func (r *ProductRepo) InsertPriceHistory(ctx context.Context, tx *sql.Tx, productID string, oldPrice *float64, newPrice float64, reason, changedBy string) error { id := uuid.New().String() var reasonPtr *string if reason != "" { reasonPtr = &reason } var changedByPtr *string if changedBy != "" { changedByPtr = &changedBy } _, err := tx.ExecContext(ctx, `INSERT INTO ilm_product_price_history (id, product_id, old_price, new_price, reason, changed_by, created_at) VALUES (?,?,?,?,?,?,?)`, id, productID, oldPrice, newPrice, reasonPtr, changedByPtr, time.Now(), ) if err != nil { return fmt.Errorf("insert price history: %w", err) } return nil } func (r *ProductRepo) PriceHistory(ctx context.Context, productID string) ([]model.ProductPriceHistory, error) { query := `SELECT id, product_id, old_price, new_price, reason, changed_by, created_at FROM ilm_product_price_history WHERE product_id = ? ORDER BY created_at DESC` rows, err := r.db.QueryContext(ctx, query, productID) if err != nil { return nil, fmt.Errorf("list price history: %w", err) } defer rows.Close() var history []model.ProductPriceHistory for rows.Next() { var h model.ProductPriceHistory if err := rows.Scan( &h.ID, &h.ProductID, &h.OldPrice, &h.NewPrice, &h.Reason, &h.ChangedBy, &h.CreatedAt, ); err != nil { return nil, fmt.Errorf("scan price history: %w", err) } history = append(history, h) } return history, nil } func (r *ProductRepo) Count(ctx context.Context, companyID string) (int, error) { var count int err := r.db.QueryRowContext(ctx, `SELECT COUNT(*) FROM products WHERE company_id = ?`, companyID, ).Scan(&count) if err != nil { return 0, fmt.Errorf("count products: %w", err) } return count, nil } func (r *ProductRepo) TotalStock(ctx context.Context, companyID string) (float64, error) { var total float64 err := r.db.QueryRowContext(ctx, `SELECT COALESCE(SUM(total_stock), 0) FROM products WHERE company_id = ?`, companyID, ).Scan(&total) if err != nil { return 0, fmt.Errorf("total stock: %w", err) } return total, nil }