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:
wangjia
2026-07-03 12:44:22 +08:00
parent 4fd0bb8b83
commit 979ca3c83b
20 changed files with 400 additions and 145 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 130 KiB

@@ -26,7 +26,7 @@ const _orders = [
reviewerName: '张管理',
status: 'approved',
orderDate: '2026-06-20',
totalAmount: 34320),
costTotal: 34320),
StockInOrder(
id: 2,
orderNo: 'RK-20260618-009',
@@ -36,7 +36,7 @@ const _orders = [
operatorName: '李销售',
status: 'pending',
orderDate: '2026-06-18',
totalAmount: 6700),
costTotal: 6700),
StockInOrder(
id: 3,
orderNo: 'RK-20260615-003',
@@ -46,7 +46,7 @@ const _orders = [
operatorName: '王经理',
status: 'draft',
orderDate: '2026-06-15',
totalAmount: 5000),
costTotal: 5000),
StockInOrder(
id: 4,
orderNo: 'RK-20260612-001',
@@ -57,7 +57,7 @@ const _orders = [
reviewerName: '张管理',
status: 'rejected',
orderDate: '2026-06-12',
totalAmount: 3500),
costTotal: 3500),
];
class _FakeStockInNotifier extends StockInListNotifier {
@@ -26,7 +26,7 @@ const _orders = [
reviewerName: '张管理',
status: 'approved',
orderDate: '2026-06-20',
totalAmount: 12800),
saleTotal: 12800),
StockOutOrder(
id: 2,
orderNo: 'CK-20260618-022',
@@ -36,7 +36,7 @@ const _orders = [
operatorName: '张前台',
status: 'pending',
orderDate: '2026-06-18',
totalAmount: 5400),
saleTotal: 5400),
StockOutOrder(
id: 3,
orderNo: 'CK-20260615-017',
@@ -46,7 +46,7 @@ const _orders = [
operatorName: '李销售',
status: 'draft',
orderDate: '2026-06-15',
totalAmount: 3200),
saleTotal: 3200),
StockOutOrder(
id: 4,
orderNo: 'CK-20260612-009',
@@ -57,7 +57,7 @@ const _orders = [
reviewerName: '张管理',
status: 'rejected',
orderDate: '2026-06-12',
totalAmount: 1960),
saleTotal: 1960),
];
class _FakeStockOutNotifier extends StockOutListNotifier {
@@ -0,0 +1,74 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jiu_client/core/theme/themes.dart';
import 'package:jiu_client/widgets/order_detail_drawer.dart';
/// 2026-07 定价重设计:出库详情按角色两种形态——
/// 管理员(行带 cost/profit)= 成本价/售价/利润 三数值列 + 合计利润;
/// operator(不带)= 售价/小计两列,无成本无利润。
void main() {
Widget host(OrderDetailDrawer drawer) => MaterialApp(
theme: buildTheme('a'),
home: Scaffold(body: drawer),
);
const line6 = OrderLine(
name: '汾酒青花20年',
code: 'ZXZ027232',
series: '普通/53度',
spec: '500ml*6/件',
qty: '2',
price: '¥400.00',
cost: '¥355.00',
profit: '¥90.00',
);
const line4 = OrderLine(
name: '汾酒青花20年',
code: 'ZXZ027232',
series: '普通/53度',
spec: '500ml*6/件',
qty: '2',
price: '¥400.00',
amount: '¥800.00',
);
testWidgets('管理员形态:成本价/售价/利润列 + 合计利润', (tester) async {
await tester.pumpWidget(host(OrderDetailDrawer(
title: 'CK001',
infoRows: const [],
linesLabel: '出库明细',
lines: const [line6],
totalText: '¥800.00',
profitText: '¥90.00',
priceLabel: '售价',
amountLabel: '小计',
actionGroups: const [],
)));
expect(find.text('成本价'), findsOneWidget);
expect(find.text('售价'), findsOneWidget);
expect(find.text('利润'), findsOneWidget);
expect(find.text('¥355.00'), findsOneWidget);
expect(find.text('¥90.00'), findsNWidgets(2)); // 行利润 + 合计利润
expect(find.text('合计利润 '), findsOneWidget);
});
testWidgets('operator 形态:售价/小计,无成本无利润', (tester) async {
await tester.pumpWidget(host(OrderDetailDrawer(
title: 'CK001',
infoRows: const [],
linesLabel: '出库明细',
lines: const [line4],
totalText: '¥800.00',
priceLabel: '售价',
amountLabel: '小计',
actionGroups: const [],
)));
expect(find.text('售价'), findsOneWidget);
expect(find.text('小计'), findsOneWidget);
expect(find.text('成本价'), findsNothing);
expect(find.text('利润'), findsNothing);
expect(find.text('合计利润 '), findsNothing);
expect(find.text('¥800.00'), findsNWidgets(2)); // 行小计 + 合计金额
});
}
+1 -1
View File
@@ -136,7 +136,7 @@ StockInOrder _makeOrder({
warehouseName: warehouseName,
partnerName: partnerName,
status: status,
totalAmount: 1000.0,
costTotal: 1000.0,
orderDate: '2024-01-10',
);