material_manage_system/internal/model/model.go
2026-10-09 12:21:53 +08:00

405 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package model
import (
"encoding/json"
"fmt"
"time"
"github.com/shopspring/decimal"
)
// Material represents a material in the warehouse
type Material struct {
ID int64 `json:"id"`
OrganizationID int64 `json:"organization_id"`
MaterialCode string `json:"material_code"`
Name string `json:"name"`
MaterialImage *string `json:"material_image,omitempty"`
Spec string `json:"spec,omitempty"`
ManufacturerModel *string `json:"manufacturer_model,omitempty"`
CategoryID *int64 `json:"category_id,omitempty"`
BaseUnitID *int64 `json:"base_unit_id,omitempty"`
Unit string `json:"unit,omitempty"`
Status string `json:"status"`
Version int `json:"version"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// Tag represents a tag associated with a material
type Tag struct {
ID int64 `json:"id"`
MaterialID int64 `json:"material_id"`
TagCode string `json:"tag_code"`
CreatedAt time.Time `json:"created_at"`
}
// ApplyOrder represents a material application/requisition order
type ApplyOrder struct {
ID int64 `json:"id"`
Applicant string `json:"applicant"`
ApplyDate time.Time `json:"apply_date"`
Status string `json:"status"`
ApprovalComment string `json:"approval_comment,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// ApplyItem represents an item in an application order
type ApplyItem struct {
ID int64 `json:"id"`
ApplyOrderID int64 `json:"apply_order_id"`
MaterialID int64 `json:"material_id"`
Quantity int `json:"quantity"`
Purpose string `json:"purpose,omitempty"`
}
// InboundOrder represents an inbound order
type InboundOrder struct {
ID int64 `json:"id"`
InboundNo string `json:"inbound_no"`
Operator string `json:"operator"`
InboundDate time.Time `json:"inbound_date"`
Handler string `json:"handler"`
Supplier string `json:"supplier"`
Remark string `json:"remark"`
// OrganizationID/WarehouseID/LocationID 是单据落库的库存维度,由服务端按当前
// 公司解析后填写,不接受前端伪造的归属。
OrganizationID int64 `json:"organization_id"`
WarehouseID int64 `json:"warehouse_id"`
LocationID int64 `json:"location_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
// Status 不落库:inbound_orders 表没有 status 列,状态由查询层统一写死为 "completed"
// (单据创建即完成,当前没有中间态)。保留字段是为了稳定 JSON 契约,
// 将来引入状态机时再改为真实读写。
Status string `json:"status,omitempty"`
Items []InboundItem `json:"items,omitempty"`
}
// InboundItem represents an item in an inbound order
type InboundItem struct {
ID int64 `json:"id"`
InboundOrderID int64 `json:"inbound_order_id"`
MaterialID int64 `json:"material_id"`
Quantity int `json:"quantity"`
MaterialCode string `json:"material_code,omitempty"`
MaterialName string `json:"material_name,omitempty"`
MaterialStatus string `json:"material_status,omitempty"`
}
// OutboundOrder represents an outbound order
type OutboundOrder struct {
ID int64 `json:"id"`
OutboundNo string `json:"outbound_no"`
Operator string `json:"operator"`
OutboundDate time.Time `json:"outbound_date"`
Handler string `json:"handler"`
Receiver string `json:"receiver"`
Remark string `json:"remark"`
// OrganizationID/WarehouseID/LocationID 是单据落库的库存维度,由服务端按当前
// 公司解析后填写,不接受前端伪造的归属。
OrganizationID int64 `json:"organization_id"`
WarehouseID int64 `json:"warehouse_id"`
LocationID int64 `json:"location_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at,omitempty"`
// Status 不落库:outbound_orders 表没有 status 列,状态由查询层统一写死为 "completed"
// (单据创建即完成,当前没有中间态)。保留字段是为了稳定 JSON 契约,
// 将来引入状态机时再改为真实读写。
Status string `json:"status,omitempty"`
Items []OutboundItem `json:"items,omitempty"`
}
// OutboundItem represents an item in an outbound order
type OutboundItem struct {
ID int64 `json:"id"`
OutboundOrderID int64 `json:"outbound_order_id"`
MaterialID int64 `json:"material_id"`
Quantity int `json:"quantity"`
MaterialCode string `json:"material_code,omitempty"`
MaterialName string `json:"material_name,omitempty"`
MaterialStatus string `json:"material_status,omitempty"`
}
// OrderNo returns the stable display number used by inbound and outbound
// documents. It derives from the database ID, so it remains unique and does
// not change when the document is edited.
func OrderNo(prefix string, id int64) string {
return fmt.Sprintf("%s-%08d", prefix, id)
}
// StockLog represents a stock change log
type StockLog struct {
ID int64 `json:"id"`
OrganizationID int64 `json:"organization_id"`
WarehouseID int64 `json:"warehouse_id"`
LocationID int64 `json:"location_id"`
MaterialID int64 `json:"material_id"`
Type string `json:"type"`
Quantity int `json:"quantity"`
RelatedOrderType string `json:"related_order_type"`
RelatedOrderID int64 `json:"related_order_id"`
CreatedAt time.Time `json:"created_at"`
RunningBalance decimal.Decimal `json:"running_balance,omitempty"`
}
// Stock represents stock information with material details
type Stock struct {
MaterialID int64 `json:"material_id"`
MaterialCode string `json:"material_code"`
Name string `json:"name"`
Spec string `json:"spec,omitempty"`
Unit string `json:"unit,omitempty"`
MajorCategory string `json:"major_category"`
MinorCategory string `json:"minor_category"`
TotalOnHand decimal.Decimal `json:"total_on_hand"`
WarningStock decimal.Decimal `json:"warning_stock"`
WarningEnabled bool `json:"warning_enabled"`
// WarningConfigured 表示该物料到底配没配过预警规则。"从没配过"和"配过但被手动
// 关闭"都会被 WarningEnabled 报成 false,仅靠它无法区分;前端预填时需要知道是哪
// 一种,才能决定默认勾选还是默认不勾选。它只服务于这个用途,不参与任何判定。
WarningConfigured bool `json:"warning_configured"`
StockStatus string `json:"stock_status"`
}
// Stock.StockStatus 的取值。只要预警处于启用状态、并且现存量已经触达阈值就是 low,
// 其余一律为 normal。阈值为 0 是合法配置,含义是"库存降到 0 时预警",不会被排除。
const (
StockStatusLow = "low"
StockStatusNormal = "normal"
)
// InventoryBalanceDetail represents detailed inventory balance
type InventoryBalanceDetail struct {
ID int64 `json:"id"`
OrganizationID int64 `json:"organization_id"`
Organization string `json:"organization"`
WarehouseID int64 `json:"warehouse_id"`
Warehouse string `json:"warehouse"`
LocationID int64 `json:"location_id"`
Location string `json:"location"`
MaterialID int64 `json:"material_id"`
OnHand decimal.Decimal `json:"on_hand"`
Reserved decimal.Decimal `json:"reserved"`
Locked decimal.Decimal `json:"locked"`
InTransit decimal.Decimal `json:"in_transit"`
UpdatedAt string `json:"updated_at"`
}
// StockLogResponse represents a stock log with computed balance
type StockLogResponse struct {
ID int64 `json:"id"`
MaterialID int64 `json:"material_id"`
ChangeType string `json:"change_type"`
ChangeQuantity decimal.Decimal `json:"change_quantity"`
BeforeQuantity decimal.Decimal `json:"before_quantity"`
AfterQuantity decimal.Decimal `json:"after_quantity"`
ReferenceID int64 `json:"reference_id"`
ReferenceType string `json:"reference_type"`
CreatedAt time.Time `json:"created_at"`
}
// InboundRecord represents inbound records for a specific material
type InboundRecord struct {
ID int64 `json:"id"`
InboundID int64 `json:"inbound_id"`
Handler string `json:"handler"`
Supplier string `json:"supplier"`
Quantity int `json:"quantity"`
CreatedAt time.Time `json:"created_at"`
}
// OutboundRecord represents outbound records for a specific material
type OutboundRecord struct {
ID int64 `json:"id"`
OutboundID int64 `json:"outbound_id"`
Handler string `json:"handler"`
Receiver string `json:"receiver"`
Quantity int `json:"quantity"`
CreatedAt time.Time `json:"created_at"`
}
// ============= Request DTOs =============
// CreateMaterialRequest represents the request to create a material
type CreateMaterialRequest struct {
OrganizationID int64 `json:"organization_id"`
MaterialCode string `json:"material_code" binding:"required"`
Name string `json:"name" binding:"required"`
MaterialImage *string `json:"material_image"`
Spec string `json:"spec"`
ManufacturerModel *string `json:"manufacturer_model"`
CategoryID *int64 `json:"category_id" binding:"required,gt=0"`
BaseUnitID *int64 `json:"base_unit_id"`
Unit string `json:"unit"`
}
// UpdateMaterialRequest represents the request to update a material
type UpdateMaterialRequest struct {
MaterialCode string `json:"material_code"`
Name string `json:"name"`
MaterialImage *string `json:"material_image"`
Spec string `json:"spec"`
ManufacturerModel *string `json:"manufacturer_model"`
CategoryID *int64 `json:"category_id" binding:"required,gt=0"`
BaseUnitID *int64 `json:"base_unit_id"`
Unit string `json:"unit"`
Status string `json:"status"`
}
// CreateInboundRequest represents the request to create an inbound order
type CreateInboundRequest struct {
Handler string `json:"handler" binding:"required"`
Supplier string `json:"supplier" binding:"required"`
Remark string `json:"remark"`
Items []struct {
MaterialID int64 `json:"material_id" binding:"required"`
Quantity int `json:"quantity" binding:"required,gt=0"`
// WarningStock 可选,为 nil 表示本次不改动已存的预警阈值。
WarningStock *decimal.Decimal `json:"warning_stock"`
// WarningEnabled 可选,为 nil 表示本次不改动预警的启用状态。仅在 WarningStock
// 非 nil 时生效,用来表达"启用还是关闭预警"。
WarningEnabled *bool `json:"warning_enabled"`
} `json:"items" binding:"required,min=1"`
}
// CreateOutboundRequest represents the request to create an outbound order
type CreateOutboundRequest struct {
// Handler is retained for API compatibility but is ignored. The server
// always records the authenticated user's username as the operator.
Handler string `json:"handler"`
Receiver string `json:"receiver" binding:"required"`
Remark string `json:"remark"`
Items []struct {
MaterialID int64 `json:"material_id" binding:"required"`
Quantity int `json:"quantity" binding:"required,gt=0"`
} `json:"items" binding:"required,min=1"`
}
// UpdateInboundRequest represents the request to update an inbound order
type UpdateInboundRequest struct {
Handler string `json:"handler"`
Supplier string `json:"supplier"`
Remark string `json:"remark"`
Items []struct {
MaterialID int64 `json:"material_id" binding:"required"`
Quantity int `json:"quantity" binding:"required,gt=0"`
// WarningStock 可选,为 nil 表示本次不改动已存的预警阈值。
WarningStock *decimal.Decimal `json:"warning_stock"`
// WarningEnabled 可选,为 nil 表示本次不改动预警的启用状态。仅在 WarningStock
// 非 nil 时生效,用来表达"启用还是关闭预警"。
WarningEnabled *bool `json:"warning_enabled"`
} `json:"items" binding:"required,min=1"`
}
// UpdateOutboundRequest represents the request to update an outbound order
type UpdateOutboundRequest struct {
Handler string `json:"handler"`
Operator string `json:"-"`
Receiver string `json:"receiver"`
Remark string `json:"remark"`
Items []struct {
MaterialID int64 `json:"material_id" binding:"required"`
Quantity int `json:"quantity" binding:"required,gt=0"`
} `json:"items" binding:"required,min=1"`
}
// MaterialLog represents a material change log
type MaterialLog struct {
ID int64 `json:"id"`
OperationType string `json:"operation_type"`
MaterialID int64 `json:"material_id"`
OldValue string `json:"old_value,omitempty"`
NewValue string `json:"new_value,omitempty"`
Operator string `json:"operator,omitempty"`
Description string `json:"description,omitempty"`
CreatedAt time.Time `json:"created_at"`
}
// User represents a system user
type User struct {
OrganizationIDs []int64 `json:"organization_ids"`
DefaultOrganizationID *int64 `json:"default_organization_id"`
ID int64 `json:"id"`
Username string `json:"username"`
Password string `json:"-"`
Role string `json:"role"`
Roles []Role `json:"roles"`
Description string `json:"description"`
CreatedAt time.Time `json:"created_at"`
}
// RawMessage distinguishes an omitted default from an explicit null on edits.
type UserOrganizationInput struct {
OrganizationIDs *[]int64 `json:"organization_ids"`
DefaultOrganizationID json.RawMessage `json:"default_organization_id"`
ActorUserID int64 `json:"-"`
}
// LoginRequest represents login request
type LoginRequest struct {
Username string `json:"username" binding:"required"`
Password string `json:"password" binding:"required"`
}
// LoginResponse represents login response
type LoginResponse struct {
Token string `json:"token"`
User User `json:"user"`
}
// ApplicationStatus constants
const (
ApplicationStatusPending = "pending"
ApplicationStatusApproved = "approved"
ApplicationStatusRejected = "rejected"
ApplicationStatusCancelled = "cancelled"
)
// MaterialApplication represents a material application record
type MaterialApplication struct {
ID int64 `json:"id"`
// ApplicationNo 不落库,由主键派生(同入库单/出库单的单号规则),查询时统一补齐。
ApplicationNo string `json:"application_no,omitempty"`
UserID int64 `json:"user_id"`
Username string `json:"username"`
Purpose string `json:"purpose,omitempty"`
Status string `json:"status"`
RejectionReason string `json:"rejection_reason,omitempty"`
ApprovedBy int64 `json:"approved_by,omitempty"`
ApproverName string `json:"approver_name,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Items []ApplicationItem `json:"items,omitempty"`
}
// CreateApplicationRequest represents request to create application
type CreateApplicationRequest struct {
Purpose string `json:"purpose"`
Items []struct {
MaterialID int64 `json:"material_id" binding:"required"`
Quantity int `json:"quantity" binding:"required,gt=0"`
} `json:"items" binding:"required,min=1"`
}
// ApplicationItem represents an item in a material application
type ApplicationItem struct {
ID int64 `json:"id"`
ApplicationID int64 `json:"application_id"`
MaterialID int64 `json:"material_id"`
MaterialCode string `json:"material_code"`
MaterialName string `json:"material_name"`
Quantity int `json:"quantity"`
}
// ApproveApplicationRequest represents request to approve application
type ApproveApplicationRequest struct {
AutoOutbound bool `json:"auto_outbound"`
}
// RejectApplicationRequest represents request to reject application
type RejectApplicationRequest struct {
Reason string `json:"reason" binding:"required"`
}