'GD' . $orderNo, 'online_order_no' => $orderNo, 'status' => '待处理', 'paid_at' => '2026-11-11 10:00:00', 'started_at' => '2026-11-11 12:00:00', 'completed_at' => '', 'initiator' => '白菜', 'executor' => '李斯', 'completer' => '', 'platform' => '天猫', 'shop_name' => '天猫-欧意朗旗舰店', 'internal_order_no' => '1', 'task_remark' => '', 'seller_remark' => 'CCDD', 'actual_paid_amount' => $amount, 'task_payment' => $amount, 'task_freight' => '0.00', 'task_commission' => '0.00', 'jingxi_price_diff' => '0.00', 'task_deduction' => '0.00', 'task_empty_package_fee' => '0.00', 'reimbursement_amount' => $amount, 'payee_name' => '姚佳炜', 'payee_account' => '6228760405006454377', 'payee_bank' => '农业银行', 'payee_mobile' => '13800000000', 'erp_order_label' => '工单同步', 'erp_order_status' => '已发货', 'platform_order_status' => '', 'product_code' => '', 'product_name' => '', 'product_quantity' => '', 'platform_product_id' => '', ], $patch); } private function mockRepository(array $rows): void { $repository = \Mockery::mock(PlantingReimbursementRepository::class); $repository->shouldReceive('syncStatus')->andReturn(['code' => 'ready', 'last_succeeded_at' => '2026-11-12 12:57:00']); $repository->shouldReceive('pendingRows')->with('2026-11-05 00:00:00', '2026-11-11 23:59:59', ['WAIT_CONFIRM'])->andReturn($rows); $repository->shouldReceive('reimbursedOrders')->andReturn(['900' => '2026-11-03']); $this->app->instance(PlantingReimbursementRepository::class, $repository); Storage::fake('local'); $this->user->attachPermission(Permission::where('name', 'read-admin-panel')->firstOrFail()); } private function upload(string $kind, string $name, string $content) { return $this->loginAs()->post(route('business.planting-data.planting-reimbursement-workbench.upload', [ 'company_id' => $this->company->id, 'kind' => $kind, ]), ['date' => '2026-11-12', 'file' => UploadedFile::fake()->createWithContent($name, $content)]); } private function url(array $query = []): string { return route('business.pages.show', ['company_id' => $this->company->id, 'section' => 'planting-data', 'page' => 'planting-reimbursement-workbench']) . ($query ? '?' . http_build_query($query) : ''); } public function test_workbench_audits_pending_rows_and_exports(): void { $this->mockRepository([ $this->row('101', '60.00'), $this->row('102', '40.50', ['shop_name' => '京喜-星光闪闪京喜自营工厂店', 'initiator' => '星璃', 'payee_name' => '王敏佳']), $this->row('900', '10.00', ['initiator' => '星璃']), $this->row('103', '20.00', ['payee_name' => '离职的人']), $this->row('104', '30.00'), ]); $this->upload('commission', '报销佣金人员.csv', "平台,人员,佣金,人员,平台\n天猫,王也,10元,白菜,天猫\n")->assertRedirect(); $this->upload('roster', '花名册.csv', "序号,花名,姓名\n1,白,姚佳炜\n2,星,王敏佳\n")->assertRedirect(); $this->upload('shop', '店铺信息.csv', "开店时间,ERP系统店铺名称,店铺所属利润中心\n2021-01-01,天猫-欧意朗旗舰店,运营中心A\n2021-01-01,京喜-星光闪闪京喜自营工厂店,运营中心A\n")->assertRedirect(); // 看板是 GBK 编码导出;104 没匹配到,应待复查。 $monitor = mb_convert_encoding("订单号,风险,提示,风险类型,ID负责人,日限额,支付时间\n101,正常,,,白菜,,2026/11/11 10:00\n102,正常,,,星璃,,2026/11/11 10:00\n900,正常,,,星璃,,2026/11/11 10:00\n103,正常,,,白菜,,2026/11/11 10:00\n", 'GB18030', 'UTF-8'); $this->upload('monitor', '种菜报销明细.csv', $monitor)->assertRedirect()->assertSessionHas('success'); $this->assertSame(['白菜' => true], app(PlantingReimbursementReferences::class)->nameSet($this->company->id, 'commission')); $this->assertSame(['姚佳炜' => true, '王敏佳' => true], app(PlantingReimbursementReferences::class)->nameSet($this->company->id, 'roster')); $this->loginAs()->get($this->url(['date' => '2026-11-12'])) ->assertOk() ->assertSee('种菜报销工作台', false) ->assertSee('2026-11-05 00:00:00') ->assertSee('100.50') ->assertSee('11.11种菜报销') ->assertSee('欧意朗') ->assertSee('重复报销') ->assertSee('不在在职花名册') ->assertSee('@星璃 以上订单本次不予报销', false) ->assertSee('待复查') ->assertSee('应为') ->assertSee('5 条', false) ->assertSee('钉钉明细合计 = 可报销合计'); $response = $this->loginAs()->get(route('business.planting-data.planting-reimbursement-workbench.export', [ 'company_id' => $this->company->id, 'type' => 'reconciliation', 'date' => '2026-11-12', 'verified' => '1', ])); $response->assertOk(); $this->assertStringContainsString('11.11.xlsx', (string) $response->headers->get('content-disposition')); $this->assertStringStartsWith('PK', $response->streamedContent()); foreach (['details', 'import-template'] as $type) { $this->loginAs()->get(route('business.planting-data.planting-reimbursement-workbench.export', [ 'company_id' => $this->company->id, 'type' => $type, 'date' => '2026-11-12', 'verified' => '1', ]))->assertOk(); } // 没勾「已核对」不给导出。 $this->loginAs()->get(route('business.planting-data.planting-reimbursement-workbench.export', [ 'company_id' => $this->company->id, 'type' => 'details', 'date' => '2026-11-12', ]))->assertRedirect()->assertSessionHas('error'); $this->withExceptionHandling()->loginAs()->get($this->url(['date' => '2026-13-40']))->assertStatus(422); $this->withExceptionHandling()->loginAs()->get($this->url(['date' => '2026-11-12', 'paid_from' => '2026-11-12']))->assertStatus(422); $this->withExceptionHandling()->loginAs()->get($this->url(['date' => '2026-11-12', 'scope' => 'x']))->assertStatus(422); } public function test_bad_upload_reports_error(): void { $this->mockRepository([]); $this->upload('monitor', 'x.csv', "a,b\n1,2\n")->assertRedirect()->assertSessionHas('error'); $this->upload('roster', 'x.txt', "姓名\n张三\n")->assertRedirect()->assertSessionHas('error'); } }