230 lines
6.7 KiB
Go
230 lines
6.7 KiB
Go
package repository
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/wmms/wmms/internal/model"
|
|
"github.com/wmms/wmms/pkg/database"
|
|
)
|
|
|
|
// BeginTx starts a new database transaction
|
|
func BeginTx() (*sql.Tx, error) {
|
|
return database.DB.Begin()
|
|
}
|
|
|
|
type MaterialRepo struct{}
|
|
|
|
func NewMaterialRepo() *MaterialRepo {
|
|
return &MaterialRepo{}
|
|
}
|
|
|
|
func (r *MaterialRepo) Create(m *model.Material) error {
|
|
query := `INSERT INTO materials (material_code, name, material_image, spec, category, unit, warning_stock) VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
result, err := database.DB.Exec(query, m.MaterialCode, m.Name, m.MaterialImage, m.Spec, m.Category, m.Unit, m.WarningStock)
|
|
if err != nil {
|
|
if strings.Contains(err.Error(), "Duplicate entry") {
|
|
return fmt.Errorf("material code already exists")
|
|
}
|
|
return err
|
|
}
|
|
id, err := result.LastInsertId()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.ID = id
|
|
return r.GetByID(m.ID, m)
|
|
}
|
|
|
|
func (r *MaterialRepo) GetByID(id int64, m *model.Material) error {
|
|
query := `SELECT id, material_code, name, material_image, spec, category, unit, warning_stock, current_stock, created_at, updated_at FROM materials WHERE id = ?`
|
|
var materialImage sql.NullString
|
|
err := database.DB.QueryRow(query, id).Scan(&m.ID, &m.MaterialCode, &m.Name, &materialImage, &m.Spec, &m.Category, &m.Unit, &m.WarningStock, &m.CurrentStock, &m.CreatedAt, &m.UpdatedAt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if materialImage.Valid {
|
|
m.MaterialImage = &materialImage.String
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *MaterialRepo) GetByMaterialCode(code string) (*model.Material, error) {
|
|
m := &model.Material{}
|
|
query := `SELECT id, material_code, name, material_image, spec, category, unit, warning_stock, current_stock, created_at, updated_at FROM materials WHERE material_code = ?`
|
|
var materialImage sql.NullString
|
|
err := database.DB.QueryRow(query, code).Scan(&m.ID, &m.MaterialCode, &m.Name, &materialImage, &m.Spec, &m.Category, &m.Unit, &m.WarningStock, &m.CurrentStock, &m.CreatedAt, &m.UpdatedAt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if materialImage.Valid {
|
|
m.MaterialImage = &materialImage.String
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func (r *MaterialRepo) List(search, category string, limit, offset int) ([]model.Material, int64, error) {
|
|
countQuery := `SELECT COUNT(*) FROM materials WHERE 1=1`
|
|
var countArgs []interface{}
|
|
|
|
if search != "" {
|
|
countQuery += ` AND (material_code LIKE ? OR name LIKE ?)`
|
|
searchPattern := "%" + search + "%"
|
|
countArgs = append(countArgs, searchPattern, searchPattern)
|
|
}
|
|
if category != "" {
|
|
countQuery += ` AND category = ?`
|
|
countArgs = append(countArgs, category)
|
|
}
|
|
|
|
var total int64
|
|
err := database.DB.QueryRow(countQuery, countArgs...).Scan(&total)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
query := `SELECT id, material_code, name, material_image, spec, category, unit, warning_stock, current_stock, created_at, updated_at FROM materials WHERE 1=1`
|
|
var args []interface{}
|
|
|
|
if search != "" {
|
|
query += ` AND (material_code LIKE ? OR name LIKE ?)`
|
|
searchPattern := "%" + search + "%"
|
|
args = append(args, searchPattern, searchPattern)
|
|
}
|
|
if category != "" {
|
|
query += ` AND category = ?`
|
|
args = append(args, category)
|
|
}
|
|
query += ` ORDER BY id DESC LIMIT ? OFFSET ?`
|
|
args = append(args, limit, offset)
|
|
|
|
rows, err := database.DB.Query(query, args...)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var materials []model.Material
|
|
for rows.Next() {
|
|
var m model.Material
|
|
var materialImage sql.NullString
|
|
err := rows.Scan(&m.ID, &m.MaterialCode, &m.Name, &materialImage, &m.Spec, &m.Category, &m.Unit, &m.WarningStock, &m.CurrentStock, &m.CreatedAt, &m.UpdatedAt)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
if materialImage.Valid {
|
|
m.MaterialImage = &materialImage.String
|
|
}
|
|
materials = append(materials, m)
|
|
}
|
|
return materials, total, nil
|
|
}
|
|
|
|
func (r *MaterialRepo) Update(id int64, req *model.UpdateMaterialRequest) error {
|
|
var sets []string
|
|
var args []interface{}
|
|
|
|
if req.MaterialCode != "" {
|
|
sets = append(sets, "material_code = ?")
|
|
args = append(args, req.MaterialCode)
|
|
}
|
|
if req.Name != "" {
|
|
sets = append(sets, "name = ?")
|
|
args = append(args, req.Name)
|
|
}
|
|
if req.MaterialImage != "" {
|
|
sets = append(sets, "material_image = ?")
|
|
args = append(args, req.MaterialImage)
|
|
}
|
|
if req.Spec != "" {
|
|
sets = append(sets, "spec = ?")
|
|
args = append(args, req.Spec)
|
|
}
|
|
if req.Category != "" {
|
|
sets = append(sets, "category = ?")
|
|
args = append(args, req.Category)
|
|
}
|
|
if req.Unit != "" {
|
|
sets = append(sets, "unit = ?")
|
|
args = append(args, req.Unit)
|
|
}
|
|
if req.WarningStock > 0 {
|
|
sets = append(sets, "warning_stock = ?")
|
|
args = append(args, req.WarningStock)
|
|
}
|
|
|
|
if len(sets) == 0 {
|
|
return nil
|
|
}
|
|
|
|
query := fmt.Sprintf("UPDATE materials SET %s WHERE id = ?", strings.Join(sets, ", "))
|
|
args = append(args, id)
|
|
_, err := database.DB.Exec(query, args...)
|
|
return err
|
|
}
|
|
|
|
func (r *MaterialRepo) Delete(id int64) error {
|
|
// Check if material has related records
|
|
var count int
|
|
|
|
// Check inbound_items
|
|
database.DB.QueryRow(`SELECT COUNT(*) FROM inbound_items WHERE material_id = ?`, id).Scan(&count)
|
|
if count > 0 {
|
|
return fmt.Errorf("该物料有入库记录,无法删除")
|
|
}
|
|
|
|
// Check outbound_items
|
|
database.DB.QueryRow(`SELECT COUNT(*) FROM outbound_items WHERE material_id = ?`, id).Scan(&count)
|
|
if count > 0 {
|
|
return fmt.Errorf("该物料有出库记录,无法删除")
|
|
}
|
|
|
|
// Check stock_logs
|
|
database.DB.QueryRow(`SELECT COUNT(*) FROM stock_logs WHERE material_id = ?`, id).Scan(&count)
|
|
if count > 0 {
|
|
return fmt.Errorf("该物料有库存流水记录,无法删除")
|
|
}
|
|
|
|
_, err := database.DB.Exec(`DELETE FROM materials WHERE id = ?`, id)
|
|
return err
|
|
}
|
|
|
|
func (r *MaterialRepo) UpdateStock(id int64, delta int) error {
|
|
query := `UPDATE materials SET current_stock = current_stock + ? WHERE id = ?`
|
|
result, err := database.DB.Exec(query, delta, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
affected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if affected == 0 {
|
|
return sql.ErrNoRows
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *MaterialRepo) GetStock(id int64) (int, error) {
|
|
var stock int
|
|
query := `SELECT current_stock FROM materials WHERE id = ?`
|
|
err := database.DB.QueryRow(query, id).Scan(&stock)
|
|
return stock, err
|
|
}
|
|
|
|
// GetStockForUpdateTx gets stock with row lock within a transaction (prevents race condition)
|
|
func (r *MaterialRepo) GetStockForUpdateTx(tx *sql.Tx, id int64) (int, error) {
|
|
query := `SELECT current_stock FROM materials WHERE id = ? FOR UPDATE`
|
|
var stock int
|
|
err := tx.QueryRow(query, id).Scan(&stock)
|
|
return stock, err
|
|
}
|
|
|
|
// UpdateStockTx updates stock within a transaction
|
|
func (r *MaterialRepo) UpdateStockTx(tx *sql.Tx, id int64, delta int) error {
|
|
query := `UPDATE materials SET current_stock = current_stock + ? WHERE id = ?`
|
|
_, err := tx.Exec(query, delta, id)
|
|
return err
|
|
}
|