feat(backend): 出入库编码搜索双路匹配+建单填快照;finance 趋势/日期区间;partner/product 拼音搜索
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
This commit is contained in:
@@ -77,6 +77,11 @@ func (h *InventoryHandler) List(c *gin.Context) {
|
||||
like := "%" + keyword + "%"
|
||||
args = append(args, like, like, like, like)
|
||||
}
|
||||
// 商品编码独立筛选(库存屏工具栏第二个搜索框,对齐原型 codeInput)
|
||||
if code := c.Query("code"); code != "" {
|
||||
baseWhere += " AND COALESCE(NULLIF(p.code,''), NULLIF(sii.product_code,''), inv.product_code, '') LIKE ?"
|
||||
args = append(args, "%"+code+"%")
|
||||
}
|
||||
if warehouseIDStr != "" {
|
||||
baseWhere += " AND inv.warehouse_id = ?"
|
||||
args = append(args, warehouseIDStr)
|
||||
@@ -167,24 +172,41 @@ type inventorySummary struct {
|
||||
InStockQty float64 `json:"in_stock_qty"` // 在库总数量
|
||||
ShortageCount int64 `json:"shortage_count"` // 缺货数(qty<=0)
|
||||
WarningCount int64 `json:"warning_count"` // 预警数(0<qty<安全库存)
|
||||
// 月初快照(供 KPI 环比):本月净变动由 inventory_logs 反推
|
||||
LastMonthSku int64 `json:"last_month_sku"` // 月初存在的库存行数
|
||||
LastMonthQty float64 `json:"last_month_qty"` // 月初在库总数量
|
||||
LastMonthValue float64 `json:"last_month_value"` // 月初库存货值
|
||||
}
|
||||
|
||||
// Summary GET /api/v1/inventory/summary —— 全店汇总(守多租户)
|
||||
func (h *InventoryHandler) Summary(c *gin.Context) {
|
||||
shopID := middleware.GetShopID(c)
|
||||
now := time.Now()
|
||||
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
|
||||
// 月初数量 = 当前数量 − 本月净变动(product=序列号模型下 log 与库存行按 product_id+warehouse_id 一一对应)
|
||||
const sql = `
|
||||
SELECT
|
||||
COUNT(*) AS sku_count,
|
||||
COALESCE(SUM(inv.quantity * COALESCE(sii.unit_price, inv.unit_price, p.purchase_price)), 0) AS stock_value,
|
||||
COALESCE(SUM(inv.quantity), 0) AS in_stock_qty,
|
||||
COALESCE(SUM(CASE WHEN inv.quantity <= 0 THEN 1 ELSE 0 END), 0) AS shortage_count,
|
||||
COALESCE(SUM(CASE WHEN inv.quantity > 0 AND p.min_stock IS NOT NULL AND inv.quantity < p.min_stock THEN 1 ELSE 0 END), 0) AS warning_count
|
||||
COALESCE(SUM(CASE WHEN inv.quantity > 0 AND p.min_stock IS NOT NULL AND inv.quantity < p.min_stock THEN 1 ELSE 0 END), 0) AS warning_count,
|
||||
COALESCE(SUM(CASE WHEN inv.created_at < ? THEN 1 ELSE 0 END), 0) AS last_month_sku,
|
||||
COALESCE(SUM(CASE WHEN inv.created_at < ? THEN inv.quantity - COALESCE(lg.net, 0) ELSE 0 END), 0) AS last_month_qty,
|
||||
COALESCE(SUM(CASE WHEN inv.created_at < ? THEN (inv.quantity - COALESCE(lg.net, 0)) * COALESCE(sii.unit_price, inv.unit_price, p.purchase_price) ELSE 0 END), 0) AS last_month_value
|
||||
FROM inventories inv
|
||||
LEFT JOIN stock_in_items sii ON sii.id = inv.stock_in_item_id
|
||||
LEFT JOIN products p ON p.id = inv.product_id AND p.deleted_at IS NULL
|
||||
LEFT JOIN (
|
||||
SELECT product_id, warehouse_id,
|
||||
SUM(CASE WHEN direction = 'in' THEN quantity ELSE -quantity END) AS net
|
||||
FROM inventory_logs
|
||||
WHERE shop_id = ? AND created_at >= ?
|
||||
GROUP BY product_id, warehouse_id
|
||||
) lg ON lg.product_id = inv.product_id AND lg.warehouse_id = inv.warehouse_id
|
||||
WHERE inv.shop_id = ? AND inv.deleted_at IS NULL`
|
||||
var s inventorySummary
|
||||
if err := h.db.Raw(sql, shopID).Scan(&s).Error; err != nil {
|
||||
if err := h.db.Raw(sql, monthStart, monthStart, monthStart, shopID, monthStart, shopID).Scan(&s).Error; err != nil {
|
||||
util.RespondError(c, http.StatusInternalServerError, "QUERY_ERROR", "查询失败")
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user