class StockOutItem { final int? id; final int? orderId; final int productId; final double quantity; final double costPrice; // 成本单价(入库成本快照,仅管理员可见——operator 响应被服务端抹零) final double salePrice; // 销售单价 final double costAmount; // 成本小计 = quantity × costPrice final double saleAmount; // 售价小计 = quantity × salePrice(待定价=0) final double returnedQuantity; final String? productName; final String? productCode; final String? productSeries; final String? productSpec; final String? productUnit; final String? batchNo; final String? productionDate; bool get isReturned => returnedQuantity >= quantity && quantity > 0; const StockOutItem({ this.id, this.orderId, required this.productId, required this.quantity, this.costPrice = 0, this.salePrice = 0, this.costAmount = 0, this.saleAmount = 0, this.returnedQuantity = 0, this.productName, this.productCode, this.productSeries, this.productSpec, this.productUnit, this.batchNo, this.productionDate, }); factory StockOutItem.fromJson(Map json) { final product = json['product'] as Map?; // 历史导入行 product_id=0、无 product 关联,商品信息存在明细行自身列上。 // 优先取明细行快照列,回退到 product 关联(App 新建单据走 product)。 String? lineOrProduct(String lineKey, String productKey) { final line = json[lineKey] as String?; if (line != null && line.isNotEmpty) return line; return product?[productKey] as String?; } return StockOutItem( id: json['id'] != null ? (json['id'] as num).toInt() : null, orderId: json['order_id'] != null ? (json['order_id'] as num).toInt() : null, productId: (json['product_id'] as num).toInt(), quantity: (json['quantity'] as num).toDouble(), costPrice: (json['cost_price'] as num?)?.toDouble() ?? 0, salePrice: (json['sale_price'] as num?)?.toDouble() ?? 0, costAmount: (json['cost_amount'] as num?)?.toDouble() ?? 0, saleAmount: (json['sale_amount'] as num?)?.toDouble() ?? 0, returnedQuantity: (json['returned_quantity'] as num?)?.toDouble() ?? 0, productName: lineOrProduct('product_name', 'name'), productCode: lineOrProduct('product_code', 'code'), productSeries: lineOrProduct('series', 'series'), productSpec: lineOrProduct('spec', 'spec'), productUnit: (json['product'] as Map?)?['unit'] as String?, // 批次号/生产日期:优先明细行快照,回退 product 关联(App 新建单据快照列常为空) batchNo: lineOrProduct('batch_no', 'batch_no'), productionDate: lineOrProduct('production_date', 'production_date'), ); } } class StockOutOrder { final int id; final String orderNo; final String? type; final int warehouseId; final String? warehouseName; final int? partnerId; final String? partnerName; final int? operatorId; final String? operatorName; final int? reviewerId; final String? reviewerName; final String status; // draft | pending | approved | rejected final String returnState; // none | partial | full final String? orderDate; final String? reviewedAt; final String? createdAt; final double? saleTotal; // 应收合计 = Σ 售价小计 final double profitTotal; // 总利润(仅管理员可见,operator 被抹零) final String? remark; final List items; const StockOutOrder({ required this.id, required this.orderNo, this.type, required this.warehouseId, this.warehouseName, this.partnerId, this.partnerName, this.operatorId, this.operatorName, this.reviewerId, this.reviewerName, required this.status, this.returnState = 'none', this.orderDate, this.reviewedAt, this.createdAt, this.saleTotal, this.profitTotal = 0, this.remark, this.items = const [], }); factory StockOutOrder.fromJson(Map json) => StockOutOrder( id: (json['id'] as num).toInt(), orderNo: json['order_no'] as String, type: json['type'] as String?, warehouseId: (json['warehouse_id'] as num).toInt(), warehouseName: (json['warehouse'] as Map?)?['name'] as String?, partnerId: json['partner_id'] != null ? (json['partner_id'] as num).toInt() : null, partnerName: (json['partner'] as Map?)?['name'] as String?, operatorId: json['operator_id'] != null ? (json['operator_id'] as num).toInt() : null, operatorName: (json['operator'] as Map?)?['real_name'] as String?, reviewerId: json['reviewer_id'] != null ? (json['reviewer_id'] as num).toInt() : null, reviewerName: (json['reviewer'] as Map?)?['real_name'] as String?, status: json['status'] as String, returnState: json['return_state'] as String? ?? 'none', orderDate: json['order_date'] as String?, reviewedAt: json['reviewed_at'] as String?, createdAt: json['created_at'] as String?, saleTotal: json['sale_total'] != null ? (json['sale_total'] as num).toDouble() : null, profitTotal: (json['profit_total'] as num?)?.toDouble() ?? 0, remark: json['remark'] as String?, items: json['items'] != null ? (json['items'] as List) .map((e) => StockOutItem.fromJson(e as Map)) .toList() : [], ); }