155 lines
6.9 KiB
Go
155 lines
6.9 KiB
Go
package repository
|
||
|
||
import (
|
||
"database/sql"
|
||
"errors"
|
||
"strings"
|
||
|
||
"github.com/wmms/wmms/internal/model"
|
||
"github.com/wmms/wmms/pkg/database"
|
||
)
|
||
|
||
// ErrDuplicateCodeRule 表示该分类下已经有一条编码规则。code_rules 上有
|
||
// uk_code_rules_org_category(organization_id, category_id),新建选到已占用的
|
||
// 分类、或者改分类改到已占用的分类,都会撞上它。
|
||
var ErrDuplicateCodeRule = errors.New("该分类已有编码规则")
|
||
|
||
// translateDuplicateCodeRule 把唯一键冲突翻译成可直接展示的中文错误,
|
||
// 其余错误原样返回。前端只有拿到中文消息才会照原样弹出,否则显示"请求失败"。
|
||
func translateDuplicateCodeRule(err error) error {
|
||
if err != nil && strings.Contains(err.Error(), "Duplicate entry") {
|
||
return ErrDuplicateCodeRule
|
||
}
|
||
return err
|
||
}
|
||
|
||
type MasterDataRepository struct{}
|
||
|
||
func NewMasterDataRepository() *MasterDataRepository { return &MasterDataRepository{} }
|
||
|
||
func (r *MasterDataRepository) ListCategories(organizationID int64) ([]model.MaterialCategory, error) {
|
||
rows, err := database.DB.Query(`SELECT id, organization_id, code, name, description, parent_id, status FROM material_categories WHERE organization_id = ? ORDER BY code`, organizationID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
items := make([]model.MaterialCategory, 0)
|
||
for rows.Next() {
|
||
var item model.MaterialCategory
|
||
if err := rows.Scan(&item.ID, &item.OrganizationID, &item.Code, &item.Name, &item.Description, &item.ParentID, &item.Status); err != nil {
|
||
return nil, err
|
||
}
|
||
items = append(items, item)
|
||
}
|
||
return items, rows.Err()
|
||
}
|
||
|
||
// FindCategory 按 id 在当前公司内查分类,查不到返回 (nil, nil)。用于校验编码
|
||
// 规则引用的小类、以及分类父级是否属于同一公司。
|
||
func (r *MasterDataRepository) FindCategory(organizationID, categoryID int64) (*model.MaterialCategory, error) {
|
||
query := `SELECT id, organization_id, code, name, description, parent_id, status FROM material_categories WHERE id = ? AND organization_id = ?`
|
||
item := &model.MaterialCategory{}
|
||
err := database.DB.QueryRow(query, categoryID, organizationID).Scan(&item.ID, &item.OrganizationID, &item.Code, &item.Name, &item.Description, &item.ParentID, &item.Status)
|
||
if err == sql.ErrNoRows {
|
||
return nil, nil
|
||
}
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return item, nil
|
||
}
|
||
|
||
func (r *MasterDataRepository) CreateCategory(item *model.MaterialCategory) error {
|
||
result, err := database.DB.Exec(`INSERT INTO material_categories (organization_id, code, name, description, parent_id, status) VALUES (?, ?, ?, ?, ?, ?)`, item.OrganizationID, item.Code, item.Name, item.Description, item.ParentID, item.Status)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
item.ID, err = result.LastInsertId()
|
||
return err
|
||
}
|
||
|
||
func (r *MasterDataRepository) UpdateCategory(item *model.MaterialCategory) error {
|
||
return execExpectingRow(`UPDATE material_categories SET name = ?, description = ?, parent_id = ?, status = ? WHERE id = ? AND organization_id = ?`, item.Name, item.Description, item.ParentID, item.Status, item.ID, item.OrganizationID)
|
||
}
|
||
|
||
func (r *MasterDataRepository) ListUnits(organizationID int64) ([]model.Unit, error) {
|
||
rows, err := database.DB.Query(`SELECT id, organization_id, code, name, precision_scale, status FROM units WHERE organization_id = ? ORDER BY code`, organizationID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
items := make([]model.Unit, 0)
|
||
for rows.Next() {
|
||
var item model.Unit
|
||
if err := rows.Scan(&item.ID, &item.OrganizationID, &item.Code, &item.Name, &item.PrecisionScale, &item.Status); err != nil {
|
||
return nil, err
|
||
}
|
||
items = append(items, item)
|
||
}
|
||
return items, rows.Err()
|
||
}
|
||
|
||
func (r *MasterDataRepository) CreateUnit(item *model.Unit) error {
|
||
result, err := database.DB.Exec(`INSERT INTO units (organization_id, code, name, precision_scale, status) VALUES (?, ?, ?, ?, ?)`, item.OrganizationID, item.Code, item.Name, item.PrecisionScale, item.Status)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
item.ID, err = result.LastInsertId()
|
||
return err
|
||
}
|
||
|
||
func (r *MasterDataRepository) UpdateUnit(item *model.Unit) error {
|
||
return execExpectingRow(`UPDATE units SET name = ?, precision_scale = ?, status = ? WHERE id = ? AND organization_id = ?`, item.Name, item.PrecisionScale, item.Status, item.ID, item.OrganizationID)
|
||
}
|
||
|
||
func (r *MasterDataRepository) ListCodeRules(organizationID int64) ([]model.CodeRule, error) {
|
||
rows, err := database.DB.Query(`SELECT cr.id, cr.organization_id, cr.category_id, c.code, c.name, cr.prefix, cr.sequence_length, cr.next_sequence, cr.status
|
||
FROM code_rules cr JOIN material_categories c ON c.id = cr.category_id
|
||
WHERE cr.organization_id = ? ORDER BY c.code`, organizationID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
items := make([]model.CodeRule, 0)
|
||
for rows.Next() {
|
||
var item model.CodeRule
|
||
if err := rows.Scan(&item.ID, &item.OrganizationID, &item.CategoryID, &item.CategoryCode, &item.CategoryName, &item.Prefix, &item.SequenceLength, &item.NextSequence, &item.Status); err != nil {
|
||
return nil, err
|
||
}
|
||
items = append(items, item)
|
||
}
|
||
return items, rows.Err()
|
||
}
|
||
|
||
func (r *MasterDataRepository) CreateCodeRule(item *model.CodeRule) error {
|
||
result, err := database.DB.Exec(`INSERT INTO code_rules (organization_id, category_id, prefix, sequence_length, next_sequence, status) VALUES (?, ?, ?, ?, ?, ?)`, item.OrganizationID, item.CategoryID, item.Prefix, item.SequenceLength, item.NextSequence, item.Status)
|
||
if err != nil {
|
||
return translateDuplicateCodeRule(err)
|
||
}
|
||
item.ID, err = result.LastInsertId()
|
||
return err
|
||
}
|
||
|
||
// UpdateCodeRule 更新编码规则。category_id 必须写进 SET:前端编辑弹窗里的
|
||
// 分类下拉没有禁用(与同页的编码输入框不同),而规则又只能改不能删,
|
||
// 漏掉这一列会让"改分类"提示成功却完全不落库。
|
||
func (r *MasterDataRepository) UpdateCodeRule(item *model.CodeRule) error {
|
||
return translateDuplicateCodeRule(execExpectingRow(`UPDATE code_rules SET category_id = ?, prefix = ?, sequence_length = ?, next_sequence = ?, status = ? WHERE id = ? AND organization_id = ?`, item.CategoryID, item.Prefix, item.SequenceLength, item.NextSequence, item.Status, item.ID, item.OrganizationID))
|
||
}
|
||
|
||
// FindActiveCodeRule 取小类当前启用的编码规则,没有规则时返回 (nil, nil)。
|
||
// 只读,不加锁:占号发生在创建物料的事务里。
|
||
func (r *MasterDataRepository) FindActiveCodeRule(organizationID, categoryID int64) (*model.CodeRule, error) {
|
||
query := `SELECT id, organization_id, category_id, prefix, sequence_length, next_sequence, status FROM code_rules WHERE organization_id = ? AND category_id = ? AND status = 'active'`
|
||
rule := &model.CodeRule{}
|
||
err := database.DB.QueryRow(query, organizationID, categoryID).Scan(&rule.ID, &rule.OrganizationID,
|
||
&rule.CategoryID, &rule.Prefix, &rule.SequenceLength, &rule.NextSequence, &rule.Status)
|
||
if err == sql.ErrNoRows {
|
||
return nil, nil
|
||
}
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return rule, nil
|
||
}
|