feat(client): 出库明细成本/售价/利润三列重设计 + 建单利润列 + 入库列头统一
- 出库详情抽屉(管理员):数量/成本价/售价/利润 + 合计金额/合计利润 双行; operator 见 售价/小计(服务端抹零+前端隐藏双保险);利润负数 danger - 出库建单:商品名称下带编码(字体同系列列);金额列→利润列(实时); 底栏合计利润(仅管理员);进价列仅管理员;提交发新 key(cost_price/sale_price) - 入库建单列头:进价(单瓶)/ 参考售价 / 总进价 - 退单弹窗金额改售价口径(与冲应收一致);打印保留待定价回退成本兼容 - models 字段随后端消歧改名;golden 重打;新增抽屉双形态 widget 测试 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JJ1g8XV1YhhmHRzhwWEW7o
This commit is contained in:
@@ -153,7 +153,7 @@ class _StockInFormScreenState extends ConsumerState<StockInFormScreen> {
|
||||
final row = _ItemRow();
|
||||
row.productId = item.productId;
|
||||
row.qtyCtrl.text = item.quantity.toStringAsFixed(0);
|
||||
row.priceCtrl.text = item.unitPrice.toStringAsFixed(2);
|
||||
row.priceCtrl.text = item.costPrice.toStringAsFixed(2);
|
||||
row.selectedNameId =
|
||||
nameOpts.where((o) => o.name == item.productName).firstOrNull?.id;
|
||||
row.selectedSeriesId = seriesOpts
|
||||
@@ -375,9 +375,8 @@ class _StockInFormScreenState extends ConsumerState<StockInFormScreen> {
|
||||
'series': optName(seriesOpts, it.selectedSeriesId),
|
||||
'spec': optName(specOpts, it.selectedSpecId),
|
||||
'quantity': qty,
|
||||
'unit_price': price,
|
||||
'cost_price': price,
|
||||
if (sale > 0) 'sale_price': sale,
|
||||
'total_price': qty * price,
|
||||
if (batchNo.isNotEmpty) 'batch_no': batchNo,
|
||||
if (productionDate.isNotEmpty) 'production_date': productionDate,
|
||||
};
|
||||
@@ -649,9 +648,9 @@ class _StockInFormScreenState extends ConsumerState<StockInFormScreen> {
|
||||
GridCol('prodDate', '生产日期', width: 150, req: true),
|
||||
GridCol('batch', '批次号', width: 116, req: true),
|
||||
GridCol('qty', '数量', width: 82, num: true, req: true),
|
||||
GridCol('price', '进价', width: 100, num: true),
|
||||
GridCol('sale', '售价', width: 108, num: true, req: true),
|
||||
GridCol('amount', '金额', width: 110, num: true),
|
||||
GridCol('price', '进价(单瓶)', width: 108, num: true),
|
||||
GridCol('sale', '参考售价', width: 108, num: true, req: true),
|
||||
GridCol('amount', '总进价', width: 110, num: true),
|
||||
GridCol('act', '', width: 78),
|
||||
];
|
||||
|
||||
@@ -912,14 +911,14 @@ class _StockInFormScreenState extends ConsumerState<StockInFormScreen> {
|
||||
num: true,
|
||||
hint: '0',
|
||||
onChanged: (_) => setState(() {}))),
|
||||
MobileCardField('进价(可留空/0)', null,
|
||||
MobileCardField('进价·单瓶(可留空/0)', null,
|
||||
valueWidget: GciField(
|
||||
controller: item.priceCtrl,
|
||||
num: true,
|
||||
money: true,
|
||||
hint: '留空=待定价',
|
||||
onChanged: (_) => setState(() {}))),
|
||||
MobileCardField('售价', null,
|
||||
MobileCardField('参考售价', null,
|
||||
valueWidget: GciField(
|
||||
controller: item.saleCtrl,
|
||||
num: true,
|
||||
@@ -927,7 +926,7 @@ class _StockInFormScreenState extends ConsumerState<StockInFormScreen> {
|
||||
hint: '0.00',
|
||||
onChanged: (_) => setState(() {}))),
|
||||
MobileCardField(
|
||||
'金额', item.pending ? '待定价' : '¥${item.amount.toStringAsFixed(2)}'),
|
||||
'总进价', item.pending ? '待定价' : '¥${item.amount.toStringAsFixed(2)}'),
|
||||
MobileCardField('', null,
|
||||
valueWidget: TextButton.icon(
|
||||
onPressed: () => setState(() => item.expanded = !item.expanded),
|
||||
|
||||
@@ -688,7 +688,7 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
||||
o.partnerName ?? '',
|
||||
o.status,
|
||||
o.orderDate,
|
||||
o.totalAmount,
|
||||
o.costTotal,
|
||||
])
|
||||
.toList(),
|
||||
);
|
||||
@@ -712,8 +712,8 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
||||
return Text(o.warehouseName ?? '-');
|
||||
case 'amount':
|
||||
return Text(
|
||||
o.totalAmount != null
|
||||
? '¥${NumberFormat('#,##0').format(o.totalAmount)}'
|
||||
o.costTotal != null
|
||||
? '¥${NumberFormat('#,##0').format(o.costTotal)}'
|
||||
: '—',
|
||||
style: const TextStyle(
|
||||
fontFamily: AppFonts.mono,
|
||||
@@ -828,11 +828,8 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
||||
fields: [
|
||||
MobileCardField('供应商', o.partnerName ?? '-'),
|
||||
MobileCardField('仓库', o.warehouseName ?? '-'),
|
||||
MobileCardField(
|
||||
'合计金额',
|
||||
o.totalAmount != null
|
||||
? '¥${o.totalAmount!.toStringAsFixed(2)}'
|
||||
: '-'),
|
||||
MobileCardField('合计金额',
|
||||
o.costTotal != null ? '¥${o.costTotal!.toStringAsFixed(2)}' : '-'),
|
||||
MobileCardField('入库员', o.operatorName ?? '-'),
|
||||
MobileCardField('审核员', o.reviewerName ?? '-'),
|
||||
],
|
||||
@@ -899,13 +896,13 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
||||
series: it.productSeries ?? '—',
|
||||
spec: it.productSpec ?? '',
|
||||
qty: _fmtQty(it.quantity),
|
||||
price: it.unitPrice == 0 ? '待定价' : _money.format(it.unitPrice),
|
||||
price: it.costPrice == 0 ? '待定价' : _money.format(it.costPrice),
|
||||
amount:
|
||||
it.totalPrice == 0 ? '待定价' : _money.format(it.totalPrice),
|
||||
pending: it.unitPrice == 0,
|
||||
it.costAmount == 0 ? '待定价' : _money.format(it.costAmount),
|
||||
pending: it.costPrice == 0,
|
||||
))
|
||||
.toList(),
|
||||
totalText: o.totalAmount != null ? _money.format(o.totalAmount) : '—',
|
||||
totalText: o.costTotal != null ? _money.format(o.costTotal) : '—',
|
||||
actionGroups: _detailActionGroups(o),
|
||||
);
|
||||
}
|
||||
@@ -971,7 +968,7 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
||||
}
|
||||
if (audit.isNotEmpty) groups.add(DrawerActionGroup('单据审核', audit));
|
||||
|
||||
final pending = o.items.where((it) => it.unitPrice == 0).length;
|
||||
final pending = o.items.where((it) => it.costPrice == 0).length;
|
||||
if (o.status == 'approved' && isAdmin && pending > 0) {
|
||||
groups.add(DrawerActionGroup('成本 · $pending 项待定价', [
|
||||
b('确认进价', () {
|
||||
@@ -1025,7 +1022,7 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
||||
/// 确认进价:为 0 价(待定)明细补填真实进价,调后端前向补偿后刷新列表。
|
||||
Future<void> _confirmCost(StockInOrder o) async {
|
||||
final pending =
|
||||
o.items.where((it) => it.unitPrice == 0 && it.id != null).toList();
|
||||
o.items.where((it) => it.costPrice == 0 && it.id != null).toList();
|
||||
if (pending.isEmpty) return;
|
||||
final controllers = {
|
||||
for (final it in pending) it.id!: TextEditingController()
|
||||
@@ -1303,8 +1300,8 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
||||
series: it.productSeries ?? '',
|
||||
spec: it.productSpec ?? '',
|
||||
quantity: it.quantity,
|
||||
unitPrice: it.unitPrice,
|
||||
totalPrice: it.totalPrice,
|
||||
unitPrice: it.costPrice,
|
||||
totalPrice: it.costAmount,
|
||||
alreadyReturned: it.isReturned,
|
||||
))
|
||||
.toList();
|
||||
|
||||
Reference in New Issue
Block a user