quoteFixturePath = storage_path('framework/testing/return-pickup-page-quote-' . uniqid('', true) . '.json'); file_put_contents($this->quoteFixturePath, json_encode([ 'sourcePath' => 'C:/private/逆向物流报价.xlsx', 'sheets' => [ [ 'index' => 0, 'name' => '中通快运逆向', 'address' => 'A1:F3', 'values' => [ ['中通快运逆向2026.4.21起', null, null, null, null, null], ['省份', '面单费', '省份', '0-30kg', '31-60kg', '备注'], ['河南省', 9, '河南省', 1.7, 1.7, '1、目前该报价仅支持常州仓收件地址。'], ], 'formulas' => [[], [], []], ], ], ], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)); $this->app->instance( ReverseLogisticsQuoteSnapshot::class, new ReverseLogisticsQuoteSnapshot($this->quoteFixturePath) ); } protected function tearDown(): void { if (isset($this->quoteFixturePath) && is_file($this->quoteFixturePath)) { unlink($this->quoteFixturePath); } parent::tearDown(); } public function test_page_is_registered_in_courier_payables_navigation(): void { $courierPayables = collect(config('business_navigation')) ->firstWhere('slug', 'courier-payables'); $this->assertIsArray($courierPayables); $this->assertContains('return-pickup-management', array_column($courierPayables['pages'], 'slug')); $this->assertSame( '退货取件管理', collect($courierPayables['pages'])->firstWhere('slug', 'return-pickup-management')['title'] ); } public function test_page_explains_when_return_pickup_mirror_is_not_available(): void { $this->user->attachPermission( Permission::where('name', 'read-admin-panel')->firstOrFail() ); $repository = Mockery::mock(ReturnPickupManagementMirrorRepository::class); $repository->shouldReceive('available')->once()->andReturnFalse(); $repository->shouldNotReceive('state'); $repository->shouldNotReceive('syncStatus'); $repository->shouldNotReceive('ready'); $repository->shouldNotReceive('paginate'); $this->app->instance(ReturnPickupManagementMirrorRepository::class, $repository); $this->loginAs() ->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'return-pickup-management', ])) ->assertOk() ->assertSeeText('退货取件管理') ->assertSeeText('共享业务镜像尚未建立,退货取件管理暂时无法读取。') ->assertSeeText('创建时间从') ->assertSeeText('快递公司') ->assertSeeText('订单状态') ->assertSeeText('实退快递单号'); } public function test_page_renders_mirrored_rows_filters_and_detail(): void { $this->user->attachPermission( Permission::where('name', 'read-admin-panel')->firstOrFail() ); $repository = Mockery::mock(ReturnPickupManagementMirrorRepository::class); $repository->shouldReceive('available')->once()->andReturnTrue(); $repository->shouldReceive('state')->once()->andReturn([ 'status' => 'succeeded', 'initial_completed_at' => '2026-10-02 16:49:22', 'last_succeeded_at' => '2026-10-02 16:49:22', 'mirror_row_count' => 296707, ]); $repository->shouldReceive('syncStatus')->once()->andReturn(['code' => 'healthy', 'lag_minutes' => 5]); $repository->shouldReceive('ready')->once()->andReturnTrue(); $repository->shouldReceive('paginate') ->once() ->withArgs(function (array $filters, int $page, int $limit): bool { return $filters['carrier'] === '中通快运' && $filters['keyword'] === 'E202610020090' && $filters['created_from'] === '2026-10-01' && $page === 1 && $limit > 0; }) ->andReturn([ 'data' => [(object) [ 'Id' => 307390, 'orderId' => 'E202610020090', 'express_company' => '中通快运', 'urgencyLevel' => '正常', 'expectedPickupTime' => '2026-10-02 18:00:00', 'orderStatus' => 'WaitExpOrder', 'returnExpressNo' => '', 'paymentMethod' => '月结', 'pickupType' => '有主取件', 'shopName' => '天猫-欧意朗旗舰店', 'transactionNo' => '3316448031005001855', 'totalReturnQuantity' => 1, 'senderName' => '张三', 'province' => '河南省', 'expressFollowStatus' => '待跟进', 'expressFollowResult' => '', 'createTime' => '2026-10-02 16:48:07', ]], 'total' => 1, ]); $repository->shouldReceive('carriers')->once()->andReturn(['中通快运', '速寄递']); $repository->shouldReceive('statuses')->once()->andReturn(['WaitExpOrder', 'ExpCanceled']); $repository->shouldReceive('warehouseDirectory')->once()->andReturn(['15444120' => '简尚-POP仓']); $repository->shouldReceive('detail')->once()->withArgs( static fn (int $id): bool => $id === 307390 )->andReturn([ 'main' => (object) [ 'Id' => 307390, 'orderId' => 'E202610020090', 'express_company' => '中通快运', 'orderStatus' => 'WaitExpOrder', 'urgencyLevel' => '正常', 'shopName' => '天猫-欧意朗旗舰店', 'transactionNo' => '3316448031005001855', 'totalReturnQuantity' => 1, 'returnExpressNo' => '', 'paymentMethod' => '月结', 'pickupType' => '有主取件', 'expectedPickupTime' => '2026-10-02 18:00:00', 'expressOrderTime' => '2026-10-02 17:00:00', 'expressPickupTime' => null, 'senderName' => '张三', 'senderPhone' => '13800000000', 'province' => '河南省', 'city' => '洛阳市', 'district' => '栾川县', 'street' => '', 'address' => '庙子镇某处', 'issueType' => '', 'issueDescription' => '', 'expressFollowStatus' => '待跟进', 'expressFollowResult' => '', 'expressFollowDescription' => '已与客户确认上门时间', 'note' => '客户要求下午上门', 'createTime' => '2026-10-02 16:48:07', 'updateTime' => '2026-10-02 16:48:07', 'creator' => '白沙', 'canceler' => '', ], 'items' => [(object) [ 'id' => 388432, 'returnProcode' => 'SG007027', 'actualReturnProcode' => 'SF-RMA-0001', 'barcode' => 'SG007027N223V01', 'productName' => '住宅家具 - 书柜', 'platformProductId' => '3316448031005001855', 'platformSkuId' => '4200000000999', 'returnQuantity' => 1, 'actualReturnQuantity' => 0, 'unverifiedQuantity' => 1, 'returnProgress' => '在途', 'warehouse' => '15444120', 'receiverName' => '逆向物流萧十一郎', 'receiverPhone' => '15370596225', 'receiverAddress' => '江苏省宿迁市沭阳县某处', 'length' => 93, 'width' => 13, 'height' => 13, 'grossWeight' => 18.2, 'estimatedFreight' => 39.94, 'actualReturnOrderNo' => '', ]], 'truth_numbers' => ['SF1234567890'], ]); $this->app->instance(ReturnPickupManagementMirrorRepository::class, $repository); $this->loginAs() ->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'return-pickup-management', 'carrier' => '中通快运', 'keyword' => 'E202610020090', 'created_from' => '2026-10-01', 'pickup_id' => 307390, ])) ->assertOk() ->assertSeeText('同步正常') ->assertSeeText('待快递下单') ->assertSeeText('E202610020090') ->assertSeeText('天猫-欧意朗旗舰店') ->assertSeeText('第 1 / 1 页,本页 1 条,共 1 条') ->assertSeeText('退货取件单详情') ->assertSeeText('SG007027') ->assertSeeText('SF-RMA-0001') ->assertSeeText('3316448031005001855') ->assertSeeText('4200000000999') ->assertSeeText('平台商品id') ->assertSeeText('平台skuid') ->assertSeeText('实退货号') ->assertSeeText('简尚-POP仓') ->assertSeeText('总件数') ->assertSeeText('已与客户确认上门时间') ->assertSeeText('客户要求下午上门') ->assertSeeText('长') ->assertSeeText('93.00') ->assertSeeText('13.00') ->assertSeeText('明细退货进度') ->assertSeeText('住宅家具 - 书柜') ->assertSeeText('39.94') ->assertSeeText('实际运费(元)') ->assertSeeText('未算出原因') ->assertSeeText('加收(元)') ->assertSeeText('差异(元)') ->assertSeeText('41.30') ->assertSeeText('-1.36') ->assertSeeText('重量取大(kg)') ->assertSeeText('计费重量(kg)') ->assertSeeText('面单(元)') ->assertSeeText('费率(元/kg)') ->assertSeeText('18.20') ->assertSeeText('19.00') ->assertSeeText('9.00') ->assertSeeText('1.70') ->assertSeeText('SF1234567890') ->assertSee('value="中通快运" selected', false) ->assertSee('value="2026-10-01"', false); } public function test_detail_shows_why_actual_freight_cannot_be_calculated(): void { $this->user->attachPermission( Permission::where('name', 'read-admin-panel')->firstOrFail() ); $repository = Mockery::mock(ReturnPickupManagementMirrorRepository::class); $repository->shouldReceive('available')->once()->andReturnTrue(); $repository->shouldReceive('state')->once()->andReturn([ 'status' => 'succeeded', 'initial_completed_at' => '2026-10-02 16:49:22', 'last_succeeded_at' => '2026-10-02 16:49:22', ]); $repository->shouldReceive('syncStatus')->once()->andReturn(['code' => 'healthy', 'lag_minutes' => 5]); $repository->shouldReceive('ready')->once()->andReturnTrue(); $repository->shouldReceive('paginate')->once()->andReturn(['data' => [], 'total' => 0]); $repository->shouldReceive('carriers')->once()->andReturn([]); $repository->shouldReceive('statuses')->once()->andReturn([]); $repository->shouldReceive('warehouseDirectory')->once()->andReturn([]); $repository->shouldReceive('detail')->once()->andReturn([ 'main' => (object) [ 'Id' => 900001, 'orderId' => 'E202609130088', 'express_company' => '百世', 'orderStatus' => 'WaitExpOrder', 'urgencyLevel' => '正常', 'shopName' => '天猫-欧意朗旗舰店', 'transactionNo' => '3316448031005001999', 'returnExpressNo' => '', 'paymentMethod' => '月结', 'pickupType' => '有主取件', 'expectedPickupTime' => null, 'expressOrderTime' => null, 'expressPickupTime' => null, 'senderName' => '张三', 'senderPhone' => '13800000000', 'province' => '福建省', 'city' => '泉州市', 'district' => '', 'street' => '', 'address' => '某处', 'issueType' => '', 'issueDescription' => '', 'expressFollowStatus' => '', 'expressFollowResult' => '', 'expressFollowDescription' => '', 'note' => '', 'totalReturnQuantity' => 1, 'createTime' => '2026-09-13 10:00:00', 'updateTime' => '2026-09-13 10:00:00', 'creator' => '白沙', 'canceler' => '', ], 'items' => [(object) [ 'id' => 1, 'returnProcode' => 'CJ002318', 'actualReturnProcode' => '', 'barcode' => 'CJ002318N1', 'productName' => '住宅家具 - 床', 'platformProductId' => '3316448031005001999', 'platformSkuId' => '4200000001001', 'returnQuantity' => 1, 'actualReturnQuantity' => null, 'unverifiedQuantity' => null, 'returnProgress' => '在途', 'warehouse' => '', 'receiverName' => '逆向物流萧十一郎', 'receiverPhone' => '15370596225', 'receiverAddress' => '江苏省常州市某处', 'grossWeight' => 3.0, 'length' => 87, 'width' => 46, 'height' => 6, 'estimatedFreight' => null, 'actualReturnOrderNo' => '', ]], 'truth_numbers' => [], ]); $this->app->instance(ReturnPickupManagementMirrorRepository::class, $repository); $this->loginAs() ->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'return-pickup-management', 'pickup_id' => 900001, ])) ->assertOk() ->assertSeeText('未算出原因') ->assertSeeText('该快递公司不在逆向物流报价范围内'); } public function test_export_streams_filtered_rows_with_actual_freight_and_difference(): void { $this->user->attachPermission( Permission::where('name', 'read-admin-panel')->firstOrFail() ); $repository = Mockery::mock(ReturnPickupManagementMirrorRepository::class); $repository->shouldReceive('exportRows') ->once() ->withArgs(static function (array $filters): bool { return ($filters['carrier'] ?? '') === '中通快运'; }) ->andReturn((function (): \Generator { yield (object) [ 'orderId' => 'E202610020090', 'express_company' => '中通快运', 'orderStatus' => 'WaitExpOrder', 'urgencyLevel' => '正常', 'shopName' => '天猫-欧意朗旗舰店', 'transactionNo' => '3316448031005001855', 'province' => '河南省', 'createTime' => '2026-10-02 16:48:07', 'updateTime' => '2026-10-02 17:30:00.000000', 'expectedPickupTime' => '2026-10-02 18:00:00.000000', 'paymentMethod' => '月结', 'pickupType' => '有主取件', 'totalReturnQuantity' => 1, 'note' => '客户要求下午上门', 'issueType' => '退货', 'issueDescription' => '少发配件', 'expressFollowStatus' => '待跟进', 'expressFollowResult' => '已联系', 'expressFollowDescription' => '已与客户确认上门时间', 'expressOrderTime' => '2026-10-02 17:00:00.000000', 'expressPickupTime' => '2026-10-02 18:10:00.000000', 'creator' => '白沙', 'canceler' => '张三', 'returnProcode' => 'SG007027', 'actualReturnProcode' => 'SF-RMA-0001', 'barcode' => 'SG007027N223V01', 'productName' => '住宅家具 - 书柜', 'platformProductId' => '3316448031005001855', 'platformSkuId' => '4200000000999', 'returnQuantity' => 1, 'actualReturnQuantity' => 0, 'unverifiedQuantity' => 1, 'returnProgress' => '在途', 'warehouse' => '15444120', 'receiverName' => '逆向物流萧十一郎', 'receiverPhone' => '15370596225', 'receiverAddress' => '江苏省常州市某处', 'actualReturnOrderNo' => '', 'grossWeight' => 18.2, 'length' => 210, 'width' => 13, 'height' => 13, 'estimatedFreight' => 39.94, 'returnExpressNo' => '', ]; })()); $repository->shouldReceive('warehouseDirectory')->once()->andReturn(['15444120' => '简尚-POP仓']); $this->app->instance(ReturnPickupManagementMirrorRepository::class, $repository); $response = $this->loginAs()->get(route('business.courier-payables.return-pickup-management.export', [ 'carrier' => '中通快运', ])); $response->assertOk(); $this->assertStringContainsString( 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', (string) $response->headers->get('content-type') ); $this->assertStringContainsString('.xlsx', (string) $response->headers->get('content-disposition')); $path = tempnam(sys_get_temp_dir(), 'return_pickup_export_'); file_put_contents($path, $response->streamedContent()); $sheet = IOFactory::load($path)->getActiveSheet(); $headers = []; $plain = static fn (mixed $cellValue): mixed => $cellValue instanceof \PhpOffice\PhpSpreadsheet\RichText\RichText ? $cellValue->getPlainText() : $cellValue; foreach ($sheet->getRowIterator(1, 1) as $row) { foreach ($row->getCellIterator() as $cell) { $headers[$cell->getColumn()] = $plain($cell->getValue()); } } $column = static fn (string $label): string => (string) array_search($label, $headers, true); $value = static fn (string $label): mixed => $plain($sheet->getCell($column($label) . '2')->getValue()); $type = static fn (string $label): string => $sheet->getCell($column($label) . '2')->getDataType(); try { $this->assertSame('订单号', $headers['A']); $this->assertSame('E202610020090', $value('订单号')); $this->assertSame('待快递下单', $value('订单状态')); $this->assertSame('3316448031005001855', $value('交易单号')); $this->assertSame('SG007027', $value('应退货号')); $this->assertSame('SF-RMA-0001', $value('实退货号')); $this->assertSame('3316448031005001855', $value('平台商品id')); $this->assertSame('4200000000999', $value('平台skuid')); $this->assertSame(1, $value('应退数量')); $this->assertSame(0, $value('实退数量')); $this->assertSame(1, $value('待核销数量')); $this->assertSame(1, $value('总件数')); $this->assertSame('简尚-POP仓', $value('发货仓')); $this->assertEquals(210.0, $value('长')); $this->assertEquals(13.0, $value('宽')); $this->assertEquals(13.0, $value('高')); $this->assertSame('2026-10-02 18:00:00', $value('预计取件时间')); $this->assertSame('待跟进(已联系)', $value('退货进度')); $this->assertSame('月结', $value('付款方式')); $this->assertSame('有主取件', $value('取件类型')); $this->assertSame('客户要求下午上门', $value('备注')); $this->assertSame('退货', $value('问题类型')); $this->assertSame('少发配件', $value('问题描述')); $this->assertSame('待跟进', $value('问题跟进状态')); $this->assertSame('已联系', $value('问题跟进结果')); $this->assertSame('已与客户确认上门时间', $value('问题跟进说明')); $this->assertSame('2026-10-02 17:00:00', $value('快递下单时间')); $this->assertSame('2026-10-02 18:10:00', $value('快递取件时间')); $this->assertSame('白沙', $value('创建人')); $this->assertSame('张三', $value('取消人')); $this->assertSame('2026-10-02 16:48:07', $value('创建时间')); $this->assertSame('2026-10-02 17:30:00', $value('更新时间')); $this->assertContains('明细退货进度', $headers); $this->assertContains('平台skuid', $headers); $this->assertEquals(39.94, $value('预估运费(元)')); $this->assertEquals(41.3, $value('实际运费(元)')); $this->assertEquals(20.0, $value('加收(元)')); $this->assertEquals(-1.36, $value('差异(元)')); $this->assertEquals(18.2, $value('毛重(kg)')); $this->assertEquals(9.0, $value('面单(元)')); $this->assertEquals(1.7, $value('费率(元/kg)')); $this->assertSame('inlineStr', $type('交易单号')); $this->assertSame('inlineStr', $type('平台商品id')); } finally { unlink($path); } } }