package model import ( "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"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at,omitempty"` 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"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at,omitempty"` 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"` 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 { 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"` } // 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"` 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"` }