Files
jiu/client/lib/models/inventory.dart
T
wangjia 65c672b4ad feat(client): 库存三态 UI——抽屉挂售 switch + 三态徽章/筛选/已售 KPI 卡
- 新组件 DsSwitch(原型 atoms .switch 对照,L1 已登记:38×22 pill,on=primary)
- 商品抽屉状态行:三态徽章后跟挂售 switch(在售⇄库存 即时生效,已售只读,
  readonly 禁用),桌面 drawer / 移动 sheet 同组件
- 列表状态列/卡片徽章三态化(在售绿/库存蓝/已售灰,icons BADGE_ICON 补两词);
  状态筛选改「全部/在售/库存/已售」走服务端;预警缺货退出状态维度由数量染色承担
- KPI 第四卡「缺货预警」→「已售」(sold_count,点击筛选已售)
- 原型同步:atoms/index 登记 switch 与 b-库存/b-已售、inventory 两屏三态数据、
  抽屉状态行 switch;12 道 ds 闸 + L1 同源闸全过
- golden 重录 ×9(三态徽章 + 已售卡形态)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
2026-07-07 13:35:19 +08:00

163 lines
5.4 KiB
Dart
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.
String? _parseDate(String? s) {
if (s == null || s.isEmpty) return null;
return s.length >= 10 ? s.substring(0, 10) : s;
}
class Inventory {
final int id;
final int? warehouseId;
final int? productId;
final int? stockInItemId;
final double quantity;
/// 库存三态(2026-07-07):stock=库存 / on_sale=在售(公开酒单) / sold=已售(卖光留痕)
final String status;
final String productCode;
final String productName;
final String series;
final String spec;
final String unit;
final String warehouseName;
final double? unitPrice; // 成本价(入库进价)
final double? salePrice; // 单价(product 售价)
final String? productionDate;
final String batchNo;
final String supplierName;
final String remark;
final int? minStock;
final String brand;
final String? createdAt;
const Inventory({
required this.id,
this.warehouseId,
this.productId,
this.stockInItemId,
required this.quantity,
this.status = 'stock',
this.productCode = '',
this.productName = '',
this.series = '',
this.spec = '',
this.unit = '',
this.warehouseName = '',
this.unitPrice,
this.salePrice,
this.productionDate,
this.batchNo = '',
this.supplierName = '',
this.remark = '',
this.minStock,
this.brand = '',
this.createdAt,
});
factory Inventory.fromJson(Map<String, dynamic> json) => Inventory(
id: (json['id'] as num).toInt(),
warehouseId: (json['warehouse_id'] as num?)?.toInt(),
productId: (json['product_id'] as num?)?.toInt(),
stockInItemId: (json['stock_in_item_id'] as num?)?.toInt(),
quantity: (json['quantity'] as num).toDouble(),
status: json['status'] as String? ?? 'stock',
productCode: json['product_code'] as String? ?? '',
productName: json['product_name'] as String? ?? '',
series: json['series'] as String? ?? '',
spec: json['spec'] as String? ?? '',
unit: json['unit'] as String? ?? '',
warehouseName: json['warehouse_name'] as String? ?? '',
unitPrice: (json['unit_price'] as num?)?.toDouble(),
salePrice: (json['sale_price'] as num?)?.toDouble(),
productionDate: _parseDate(json['production_date'] as String?),
batchNo: json['batch_no'] as String? ?? '',
supplierName: json['supplier_name'] as String? ?? '',
remark: json['remark'] as String? ?? '',
minStock: (json['min_stock'] as num?)?.toInt(),
brand: json['brand'] as String? ?? '',
createdAt: json['created_at'] as String?,
);
}
/// 全店库存 KPI 汇总(GET /inventory/summary,不随分页)。
class InventorySummary {
final int skuCount;
final double stockValue;
final double inStockQty;
final int soldCount; // 已售留痕行数(status=sold
final int shortageCount;
final int warningCount;
// 月初快照(KPI 环比):由 inventory_logs 反推
final int lastMonthSku;
final double lastMonthQty;
final double lastMonthValue;
const InventorySummary({
this.skuCount = 0,
this.stockValue = 0,
this.inStockQty = 0,
this.soldCount = 0,
this.shortageCount = 0,
this.warningCount = 0,
this.lastMonthSku = 0,
this.lastMonthQty = 0,
this.lastMonthValue = 0,
});
factory InventorySummary.fromJson(Map<String, dynamic> j) => InventorySummary(
skuCount: (j['sku_count'] as num?)?.toInt() ?? 0,
stockValue: (j['stock_value'] as num?)?.toDouble() ?? 0,
inStockQty: (j['in_stock_qty'] as num?)?.toDouble() ?? 0,
soldCount: (j['sold_count'] as num?)?.toInt() ?? 0,
shortageCount: (j['shortage_count'] as num?)?.toInt() ?? 0,
warningCount: (j['warning_count'] as num?)?.toInt() ?? 0,
lastMonthSku: (j['last_month_sku'] as num?)?.toInt() ?? 0,
lastMonthQty: (j['last_month_qty'] as num?)?.toDouble() ?? 0,
lastMonthValue: (j['last_month_value'] as num?)?.toDouble() ?? 0,
);
}
class InventoryLog {
final int warehouseId;
final String? warehouseName;
final int productId;
final String? productName;
final String direction; // in | out
final double quantity;
final double? qtyBefore;
final double? qtyAfter;
final String? refType;
final int? refId;
final String? createdAt;
const InventoryLog({
required this.warehouseId,
this.warehouseName,
required this.productId,
this.productName,
required this.direction,
required this.quantity,
this.qtyBefore,
this.qtyAfter,
this.refType,
this.refId,
this.createdAt,
});
factory InventoryLog.fromJson(Map<String, dynamic> json) => InventoryLog(
warehouseId: (json['warehouse_id'] as num).toInt(),
warehouseName:
(json['warehouse'] as Map<String, dynamic>?)?['name'] as String?,
productId: (json['product_id'] as num).toInt(),
productName:
(json['product'] as Map<String, dynamic>?)?['name'] as String?,
direction: json['direction'] as String,
quantity: (json['quantity'] as num).toDouble(),
qtyBefore: json['qty_before'] != null
? (json['qty_before'] as num).toDouble()
: null,
qtyAfter: json['qty_after'] != null
? (json['qty_after'] as num).toDouble()
: null,
refType: json['ref_type'] as String?,
refId: json['ref_id'] != null ? (json['ref_id'] as num).toInt() : null,
createdAt: json['created_at'] as String?,
);
}