CaiWuXiTong/tests/Feature/Business/ReturnPickupManagementPageTest.php

574 lines
26 KiB
PHP

<?php
namespace Tests\Feature\Business;
use App\Models\Auth\Permission;
use App\Support\ReverseLogisticsQuoteSnapshot;
use Mockery;
use Modules\EcommerceFinance\Repositories\ReturnPickupManagementMirrorRepository;
use PhpOffice\PhpSpreadsheet\IOFactory;
use Tests\Feature\FeatureTestCase;
class ReturnPickupManagementPageTest extends FeatureTestCase
{
private string $quoteFixturePath;
protected function setUp(): void
{
parent::setUp();
$this->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()
->assertSee('id="pickupCarrierFilter"', false)
->assertSee('data-name="carrier"', false)
->assertSee('is-clickable', false)
->assertSee('data-href=', false)
->assertSee('data-detail-url=', false)
->assertSeeText('同步正常')
->assertSeeText('待快递下单')
->assertSeeText('E202610020090')
->assertSeeText('天猫-欧意朗旗舰店')
->assertSeeText('第 1 / 1 页,本页 1 条,共 1 条')
->assertSeeText('退货取件单详情')
->assertSee('id="return-pickup-detail"', false)
->assertSee('is-active', false)
->assertSee('return-pickup-table-wrap--scroll', false)
->assertSeeText('SG007027')
->assertSeeText('SF-RMA-0001')
->assertSeeText('3316448031005001855')
->assertSeeText('4200000000999')
->assertSeeText('平台商品id')
->assertSeeText('平台skuid')
->assertSeeText('实退货号')
->assertSeeText('简尚-POP仓')
->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')
->assertSee('data-initial="[&quot;中通快运&quot;,&quot;速寄递&quot;]"', 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_detail_endpoint_returns_partial_for_inline_loading(): void
{
$this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail());
$repository = Mockery::mock(ReturnPickupManagementMirrorRepository::class);
$repository->shouldReceive('detail')->once()->withArgs(
static fn (int $id): bool => $id === 900002
)->andReturn([
'main' => (object) [
'Id' => 900002,
'orderId' => 'E202609130089',
'express_company' => '百世',
'orderStatus' => 'WaitExpOrder',
'urgencyLevel' => '正常',
'shopName' => '天猫-欧意朗旗舰店',
'transactionNo' => '3316448031005001998',
'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' => 2,
'returnProcode' => 'CJ002319',
'actualReturnProcode' => '',
'barcode' => 'CJ002319N1',
'productName' => '住宅家具 - 床',
'platformProductId' => '3316448031005001998',
'platformSkuId' => '4200000001002',
'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' => [],
]);
$repository->shouldReceive('warehouseDirectory')->once()->andReturn([]);
$this->app->instance(ReturnPickupManagementMirrorRepository::class, $repository);
$this->loginAs()
->get(route('business.courier-payables.return-pickup-management.detail', ['pickup_id' => 900002]))
->assertOk()
->assertSee('id="return-pickup-detail"', false)
->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);
}
}
}