user->attachPermission( Permission::where('name', 'read-admin-panel')->firstOrFail() ); Storage::fake('uploads'); } public function test_final_directory_places_summary_pages_after_courier_payables_report(): void { $response = $this->loginAs()->get(route('business.pages.show', [ 'section' => 'courier-payables-final', ])); $response->assertOk()->assertSeeInOrder([ route('business.pages.show', ['section' => 'courier-payables-final', 'page' => 'courier-payables-report']), route('business.pages.show', ['section' => 'courier-payables-final', 'page' => 'freight-difference']), route('business.pages.show', ['section' => 'courier-payables-final', 'page' => 'other-surcharge-summary']), route('business.pages.show', ['section' => 'courier-payables-final', 'page' => 'return-fee']), ], false); } public function test_summary_pages_render_import_and_export_buttons(): void { foreach (CourierPayableSummaryStore::PAGES as $page => $title) { $this->loginAs()->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => $page, ])) ->assertOk() ->assertSee("data-courier-payable-summary=\"{$page}\"", false) ->assertSeeText($title) ->assertSeeText('导入') ->assertSeeText('导出') ->assertSee('name="date"', false) ->assertSee('name="person"', false) ->assertSee('name="courier"', false) ->assertSee('name="keyword"', false) ->assertSeeText('查询') ->assertSeeText('重置') ->assertSeeText('暂无数据,请先点击“导入”上传汇总表。'); } } public function test_each_summary_page_can_query_by_date_person_courier_and_long_identifier(): void { $pages = [ 'freight-difference' => ['结算月份', '快递公司', 'SF000000000000000001'], 'other-surcharge-summary' => ['日期', '快递公司', 'ORDER000000000000000002'], 'return-fee' => ['日期', '快递名称', '7732543323146123456'], ]; foreach ($pages as $page => [$dateHeader, $courierHeader, $longIdentifier]) { $this->storeSnapshot($page, [ $dateHeader, '负责人', $courierHeader, '运单号码', '备注', ], [ ['2026-07', '苏木', '测试中通', $longIdentifier, '需要复核'], ['2026-08', '周生辰', '测试圆通', 'OTHER000000000000000009', '无需复核'], ]); $url = route('business.pages.show', [ 'company_id' => $this->company->id, 'section' => 'courier-payables', 'page' => $page, ]) . '?' . http_build_query([ 'date' => '2026-07', 'person' => '苏木', 'courier' => '中通', 'keyword' => $longIdentifier, ]); $this->loginAs()->get($url) ->assertOk() ->assertSeeText($longIdentifier) ->assertDontSeeText('OTHER000000000000000009') ->assertSeeText('查询到 1 条 / 共 2 条') ->assertSee('value="2026-07"', false) ->assertSee('value="苏木"', false) ->assertSee('value="中通"', false) ->assertSee('value="' . $longIdentifier . '"', false); } } public function test_summary_query_preserves_filters_during_pagination_and_has_a_clear_empty_state(): void { $rows = []; for ($index = 1; $index <= 51; $index++) { $rows[] = [ '2026-07', '苏木', '测试中通', sprintf('SF%018d', $index), sprintf('ORDER%015d', $index), ]; } $this->storeSnapshot('return-fee', ['日期', '负责人', '快递名称', '运单号码', '交易订单'], $rows); $pageRoute = route('business.pages.show', [ 'company_id' => $this->company->id, 'section' => 'courier-payables', 'page' => 'return-fee', ]); $filters = ['courier' => '中通', 'keyword' => 'ORDER']; $this->loginAs()->get($pageRoute . '?' . http_build_query($filters)) ->assertOk() ->assertSeeText('第 1 / 2 页') ->assertSee('courier=' . rawurlencode('中通') . '&keyword=ORDER&page=2', false); $lastWaybill = sprintf('SF%018d', 51); $this->loginAs()->get($pageRoute . '?' . http_build_query([...$filters, 'page' => 2])) ->assertOk() ->assertSeeText($lastWaybill) ->assertSeeText('第 2 / 2 页'); $this->loginAs()->get($pageRoute . '?keyword=' . rawurlencode('完全不存在的数据')) ->assertOk() ->assertSeeText('当前查询条件下没有匹配数据。') ->assertDontSeeText('暂无数据,请先点击“导入”上传汇总表。') ->assertSeeText('查询到 0 条 / 共 51 条'); } public function test_each_summary_page_exports_only_filtered_rows_and_preserves_year_month_display(): void { $pages = [ 'freight-difference' => ['结算月份', '快递公司'], 'other-surcharge-summary' => ['日期', '快递公司'], 'return-fee' => ['日期', '快递名称'], ]; foreach ($pages as $page => [$dateHeader, $courierHeader]) { $this->storeSnapshot($page, [ $dateHeader, '负责人', $courierHeader, '运单号码', '备注', ], [ ['2026-07', '苏木', '测试中通', 'SF000000000000000001', '需要导出'], ['2026-08', '苏木', '测试中通', 'SF000000000000000002', '其他月份'], ['2026-07', '周生辰', '测试圆通', 'SF000000000000000003', '其他负责人'], ]); $filters = [ 'date' => '2026-07', 'person' => '苏木', 'courier' => '中通', 'keyword' => '需要导出', ]; $exportUrl = route('business.courier-payable-summaries.export', [ 'company_id' => $this->company->id, 'summaryPage' => $page, ...$filters, ]); $this->loginAs()->get(route('business.pages.show', [ 'company_id' => $this->company->id, 'section' => 'courier-payables', 'page' => $page, ...$filters, ])) ->assertOk() ->assertSee(htmlspecialchars($exportUrl, ENT_QUOTES, 'UTF-8'), false); $response = $this->loginAs()->get($exportUrl); $content = $response->streamedContent(); $lines = preg_split('/\r\n|\r|\n/', trim(substr($content, 3))); $response->assertOk()->assertHeader('content-type', 'text/csv; charset=UTF-8'); $this->assertCount(2, $lines); $this->assertSame('="2026-07"', str_getcsv($lines[1])[0]); $this->assertStringContainsString('SF000000000000000001', $content); $this->assertStringNotContainsString('SF000000000000000002', $content); $this->assertStringNotContainsString('SF000000000000000003', $content); } } public function test_freight_difference_import_dialog_downloads_the_supplied_xlsx_template(): void { $pageResponse = $this->loginAs()->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'freight-difference', ])); $pageResponse ->assertOk() ->assertSee('data-courier-summary-import-dialog', false) ->assertSeeText('下载模板') ->assertSee(route('business.courier-payable-summaries.template', [ 'summaryPage' => 'freight-difference', ]), false); $templateResponse = $this->loginAs()->get(route('business.courier-payable-summaries.template', [ 'summaryPage' => 'freight-difference', ])); $templateResponse ->assertOk() ->assertDownload('freight_difference_import_template.xlsx') ->assertHeader('content-type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); $this->assertStringContainsString( "filename*=utf-8''" . rawurlencode('运费差导入模板.xlsx'), (string) $templateResponse->headers->get('Content-Disposition'), ); $spreadsheet = IOFactory::load(resource_path('templates/courier-payables/freight-difference-import-template.xlsx')); $worksheet = $spreadsheet->getActiveSheet(); $this->assertSame([ '日期', '负责人', '快递公司', '快递单号', '店铺', 'id', '条码', '交易单号', '差异(结算运费-提供成本运费)', '是否配件', '配件成品条码', ], $worksheet->rangeToArray('A1:K1', null, false, false)[0]); $this->assertSame('FFFFC000', $worksheet->getStyle('E1')->getFill()->getStartColor()->getARGB()); $this->assertSame('FFFFC000', $worksheet->getStyle('F1')->getFill()->getStartColor()->getARGB()); $this->assertSame('FFFFC000', $worksheet->getStyle('H1')->getFill()->getStartColor()->getARGB()); $this->assertSame('@', $worksheet->getStyle('A2')->getNumberFormat()->getFormatCode()); $this->assertSame('@', $worksheet->getStyle('F2')->getNumberFormat()->getFormatCode()); $this->assertSame('@', $worksheet->getStyle('H2')->getNumberFormat()->getFormatCode()); $this->assertSame('', (string) $worksheet->getCell('A2')->getValue()); $spreadsheet->disconnectWorksheets(); } public function test_other_surcharge_import_dialog_downloads_the_supplied_xlsx_template(): void { $pageResponse = $this->loginAs()->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'other-surcharge-summary', ])); $pageResponse ->assertOk() ->assertSee('data-courier-summary-import-dialog', false) ->assertSeeText('导入其他附加费汇总') ->assertSeeText('其他附加费导入模板') ->assertSeeText('.xlsx · 11 个字段') ->assertSeeText('下载模板') ->assertSee(route('business.courier-payable-summaries.template', [ 'summaryPage' => 'other-surcharge-summary', ]), false); $templateResponse = $this->loginAs()->get(route('business.courier-payable-summaries.template', [ 'summaryPage' => 'other-surcharge-summary', ])); $templateResponse ->assertOk() ->assertDownload('other_surcharge_import_template.xlsx') ->assertHeader('content-type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); $this->assertStringContainsString( "filename*=utf-8''" . rawurlencode('其他附加费导入模板.xlsx'), (string) $templateResponse->headers->get('Content-Disposition'), ); $spreadsheet = IOFactory::load(resource_path('templates/courier-payables/other-surcharge-import-template.xlsx')); $worksheet = $spreadsheet->getActiveSheet(); $this->assertSame([ '日期', '负责人', '快递公司', '快递单号', '商品ID', '条码', '店铺', '交易单号', '金额', '备注', '成品条码', ], $worksheet->rangeToArray('A1:K1', null, false, false)[0]); $this->assertSame('@', $worksheet->getStyle('A2')->getNumberFormat()->getFormatCode()); $this->assertSame('@', $worksheet->getStyle('D2')->getNumberFormat()->getFormatCode()); $this->assertSame('@', $worksheet->getStyle('E2')->getNumberFormat()->getFormatCode()); $this->assertSame('@', $worksheet->getStyle('F2')->getNumberFormat()->getFormatCode()); $this->assertSame('@', $worksheet->getStyle('H2')->getNumberFormat()->getFormatCode()); $this->assertSame('0.00########', $worksheet->getStyle('I2')->getNumberFormat()->getFormatCode()); $this->assertSame('@', $worksheet->getStyle('K2')->getNumberFormat()->getFormatCode()); $this->assertSame('', (string) $worksheet->getCell('A2')->getValue()); $this->assertSame('', (string) $worksheet->getCell('K18')->getValue()); $spreadsheet->disconnectWorksheets(); } public function test_return_fee_import_dialog_downloads_the_supplied_xlsx_template(): void { $pageResponse = $this->loginAs()->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'return-fee', ])); $pageResponse ->assertOk() ->assertSee('data-courier-summary-import-dialog', false) ->assertSeeText('导入退件费') ->assertSeeText('退件费导入模板') ->assertSeeText('.xlsx · 11 个字段') ->assertSeeText('下载模板') ->assertSee(route('business.courier-payable-summaries.template', [ 'summaryPage' => 'return-fee', ]), false); $templateResponse = $this->loginAs()->get(route('business.courier-payable-summaries.template', [ 'summaryPage' => 'return-fee', ])); $templateResponse ->assertOk() ->assertDownload('return_fee_import_template.xlsx') ->assertHeader('content-type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); $this->assertStringContainsString( "filename*=utf-8''" . rawurlencode('退件费导入模板.xlsx'), (string) $templateResponse->headers->get('Content-Disposition'), ); $spreadsheet = IOFactory::load(resource_path('templates/courier-payables/return-fee-import-template.xlsx')); $worksheet = $spreadsheet->getActiveSheet(); $this->assertSame([ '日期', '负责人', '快递名称', '运单号码', 'ID', '条码', '店铺', '交易订单', '结算金额', '备注', '成品条码', ], $worksheet->rangeToArray('A1:K1', null, false, false)[0]); foreach (['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K'] as $column) { $this->assertSame('@', $worksheet->getStyle($column . '2')->getNumberFormat()->getFormatCode()); } $this->assertSame('0.00########', $worksheet->getStyle('I2')->getNumberFormat()->getFormatCode()); foreach ($worksheet->rangeToArray('A2:K15', null, false, false) as $row) { $this->assertSame([], array_values(array_filter( $row, static fn (mixed $value): bool => $value !== null && $value !== '', ))); } $spreadsheet->disconnectWorksheets(); } public function test_completed_freight_difference_template_preserves_long_identifiers_on_import(): void { $spreadsheet = IOFactory::load(resource_path('templates/courier-payables/freight-difference-import-template.xlsx')); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray([ '2026-07', '苏木', '测试快递', 'YT7632543323146', '测试店铺', '855226246304', 'DNZ0117123N1210V01', '3312228147193110069', 0.1827, '成品', '', ], null, 'A2'); $worksheet->getCell('F2')->setValueExplicit('855226246304', DataType::TYPE_STRING); $worksheet->getCell('H2')->setValueExplicit('3312228147193110069', DataType::TYPE_STRING); $path = tempnam(sys_get_temp_dir(), 'freight-template-'); $this->assertNotFalse($path); $xlsxPath = $path . '.xlsx'; try { IOFactory::createWriter($spreadsheet, 'Xlsx')->save($xlsxPath); $spreadsheet->disconnectWorksheets(); $file = new UploadedFile( $xlsxPath, '运费差导入模板.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', null, true, ); $this->loginAs()->post(route('business.courier-payable-summaries.import', [ 'summaryPage' => 'freight-difference', ]), ['import' => $file]) ->assertRedirect(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'freight-difference', ])) ->assertSessionHas('success', '导入完成:1 条数据,11 个字段。'); $snapshot = app(CourierPayableSummaryStore::class)->snapshot( (int) $this->company->id, 'freight-difference', ); $this->assertSame('2026-07', $snapshot['rows'][0][0]); $this->assertSame('855226246304', $snapshot['rows'][0][5]); $this->assertSame('3312228147193110069', $snapshot['rows'][0][7]); $this->assertSame('0.1827', $snapshot['rows'][0][8]); } finally { if (isset($spreadsheet)) { $spreadsheet->disconnectWorksheets(); } if ($path !== false && is_file($path)) { unlink($path); } if (isset($xlsxPath) && is_file($xlsxPath)) { unlink($xlsxPath); } } } public function test_completed_other_surcharge_template_preserves_long_identifiers_on_import(): void { $spreadsheet = IOFactory::load(resource_path('templates/courier-payables/other-surcharge-import-template.xlsx')); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray([ '2026-07', '苏木', '测试快递', 'SF5199125637478', '6041543429417', 'SJ038017N1410V01', '测试店铺', '5110751415866048404', -60, '安装费', 'CP001', ], null, 'A2'); $worksheet->getCell('E2')->setValueExplicit('6041543429417', DataType::TYPE_STRING); $worksheet->getCell('H2')->setValueExplicit('5110751415866048404', DataType::TYPE_STRING); $path = tempnam(sys_get_temp_dir(), 'other-surcharge-template-'); $this->assertNotFalse($path); $xlsxPath = $path . '.xlsx'; try { IOFactory::createWriter($spreadsheet, 'Xlsx')->save($xlsxPath); $spreadsheet->disconnectWorksheets(); $file = new UploadedFile( $xlsxPath, '其他附加费导入模板.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', null, true, ); $this->loginAs()->post(route('business.courier-payable-summaries.import', [ 'summaryPage' => 'other-surcharge-summary', ]), ['import' => $file]) ->assertRedirect(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'other-surcharge-summary', ])) ->assertSessionHas('success', '导入完成:1 条数据,11 个字段。'); $snapshot = app(CourierPayableSummaryStore::class)->snapshot( (int) $this->company->id, 'other-surcharge-summary', ); $this->assertSame('2026-07', $snapshot['rows'][0][0]); $this->assertSame('6041543429417', $snapshot['rows'][0][4]); $this->assertSame('5110751415866048404', $snapshot['rows'][0][7]); $this->assertSame('-60', $snapshot['rows'][0][8]); $this->assertSame('安装费', $snapshot['rows'][0][9]); } finally { if (isset($spreadsheet)) { $spreadsheet->disconnectWorksheets(); } if ($path !== false && is_file($path)) { unlink($path); } if (isset($xlsxPath) && is_file($xlsxPath)) { unlink($xlsxPath); } } } public function test_completed_return_fee_template_preserves_long_identifiers_and_amount_precision(): void { $spreadsheet = IOFactory::load(resource_path('templates/courier-payables/return-fee-import-template.xlsx')); $worksheet = $spreadsheet->getActiveSheet(); $worksheet->fromArray([ '2026-09-20', '苏木', '测试快递', '7732543323146123456', '6041543429417123456', 'SJ038017N1410V01', '测试店铺', '5110751415866048404', 12.3456, '退件费', 'CP000000000000001', ], null, 'A2'); $worksheet->getCell('D2')->setValueExplicit('7732543323146123456', DataType::TYPE_STRING); $worksheet->getCell('E2')->setValueExplicit('6041543429417123456', DataType::TYPE_STRING); $worksheet->getCell('F2')->setValueExplicit('SJ038017N1410V01', DataType::TYPE_STRING); $worksheet->getCell('H2')->setValueExplicit('5110751415866048404', DataType::TYPE_STRING); $worksheet->getCell('K2')->setValueExplicit('CP000000000000001', DataType::TYPE_STRING); $path = tempnam(sys_get_temp_dir(), 'return-fee-template-'); $this->assertNotFalse($path); $xlsxPath = $path . '.xlsx'; try { IOFactory::createWriter($spreadsheet, 'Xlsx')->save($xlsxPath); $spreadsheet->disconnectWorksheets(); $file = new UploadedFile( $xlsxPath, '退件费导入模板.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', null, true, ); $this->loginAs()->post(route('business.courier-payable-summaries.import', [ 'summaryPage' => 'return-fee', ]), ['import' => $file]) ->assertRedirect(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'return-fee', ])) ->assertSessionHas('success', '导入完成:1 条数据,11 个字段。'); $snapshot = app(CourierPayableSummaryStore::class)->snapshot( (int) $this->company->id, 'return-fee', ); $this->assertSame('2026-09-20', $snapshot['rows'][0][0]); $this->assertSame('7732543323146123456', $snapshot['rows'][0][3]); $this->assertSame('6041543429417123456', $snapshot['rows'][0][4]); $this->assertSame('SJ038017N1410V01', $snapshot['rows'][0][5]); $this->assertSame('5110751415866048404', $snapshot['rows'][0][7]); $this->assertSame('12.3456', $snapshot['rows'][0][8]); $this->assertSame('CP000000000000001', $snapshot['rows'][0][10]); } finally { if (isset($spreadsheet)) { $spreadsheet->disconnectWorksheets(); } if ($path !== false && is_file($path)) { unlink($path); } if (isset($xlsxPath) && is_file($xlsxPath)) { unlink($xlsxPath); } } } public function test_import_replaces_the_page_snapshot_and_export_returns_all_rows(): void { $file = UploadedFile::fake()->createWithContent( '运费差.csv', "快递公司,结算月份,运费差,备注\n测试快递,2026-08,-18.50,复核\n第二快递,2026-08,20,=待确认\n", ); $this->loginAs()->post(route('business.courier-payable-summaries.import', [ 'summaryPage' => 'freight-difference', ]), ['import' => $file]) ->assertRedirect(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'freight-difference', ])) ->assertSessionHas('success', '导入完成:2 条数据,4 个字段。'); Storage::disk('uploads')->assertExists( app(CourierPayableSummaryStore::class)->path((int) $this->company->id, 'freight-difference') ); $this->loginAs()->get(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'freight-difference', ])) ->assertOk() ->assertSeeText('导入完成:2 条数据,4 个字段。') ->assertSeeText('测试快递') ->assertSeeText('-18.50') ->assertSeeText('第二快递'); $response = $this->loginAs()->get(route('business.courier-payable-summaries.export', [ 'summaryPage' => 'freight-difference', ])); $content = $response->streamedContent(); $response->assertOk()->assertHeader('content-type', 'text/csv; charset=UTF-8'); $this->assertStringContainsString('快递公司,结算月份,运费差,备注', $content); $this->assertStringContainsString('测试快递,"=""2026-08""",-18.50,复核', $content); $this->assertStringContainsString("\t=待确认", $content); } public function test_return_fee_page_paginates_and_export_includes_all_imported_rows(): void { $rows = ["运单号码,交易订单,结算金额\n"]; for ($index = 1; $index <= 51; $index++) { $rows[] = sprintf("SF%018d,ORDER%015d,%d.%04d\n", $index, $index, $index, $index); } $file = UploadedFile::fake()->createWithContent('退件费.csv', implode('', $rows)); $this->loginAs()->post(route('business.courier-payable-summaries.import', [ 'summaryPage' => 'return-fee', ]), ['import' => $file]) ->assertRedirect(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'return-fee', ])) ->assertSessionHas('success', '导入完成:51 条数据,3 个字段。'); $firstWaybill = sprintf('SF%018d', 1); $lastWaybill = sprintf('SF%018d', 51); $pageRoute = route('business.pages.show', [ 'section' => 'courier-payables', 'page' => 'return-fee', ]); $this->loginAs()->get($pageRoute) ->assertOk() ->assertSeeText($firstWaybill) ->assertDontSeeText($lastWaybill) ->assertSeeText('第 1 / 2 页'); $this->loginAs()->get($pageRoute . '?page=2') ->assertOk() ->assertSeeText($lastWaybill) ->assertSeeText('第 2 / 2 页'); $response = $this->loginAs()->get(route('business.courier-payable-summaries.export', [ 'summaryPage' => 'return-fee', ])); $content = $response->streamedContent(); $response->assertOk()->assertHeader('content-type', 'text/csv; charset=UTF-8'); $this->assertStringContainsString($firstWaybill, $content); $this->assertStringContainsString($lastWaybill, $content); $this->assertStringContainsString('51.0051', $content); } public function test_summary_imports_accept_more_than_ten_thousand_rows(): void { $store = app(CourierPayableSummaryStore::class); $this->assertSame(500000, $store->maxRows('freight-difference')); $this->assertSame(500000, $store->maxRows('other-surcharge-summary')); $this->assertSame(500000, $store->maxRows('return-fee')); $rows = ["快递公司,结算月份\n"]; for ($index = 1; $index <= 10001; $index++) { $rows[] = "测试快递,2026-08\n"; } foreach (['freight-difference', 'other-surcharge-summary', 'return-fee'] as $page) { $file = UploadedFile::fake()->createWithContent($page . '.csv', implode('', $rows)); $this->loginAs()->post(route('business.courier-payable-summaries.import', [ 'summaryPage' => $page, ]), ['import' => $file]) ->assertRedirect(route('business.pages.show', [ 'section' => 'courier-payables', 'page' => $page, ])) ->assertSessionHas('success', '导入完成:10001 条数据,2 个字段。') ->assertSessionDoesntHaveErrors('import'); $snapshot = $store->snapshot((int) $this->company->id, $page); $this->assertCount(10001, $snapshot['rows']); } } public function test_summary_actions_require_the_matching_page_permission(): void { $this->withExceptionHandling(); $this->user->roles()->sync([ \App\Models\Auth\Role::where('name', 'admin')->firstOrFail()->id, ]); setting()->set('system.sidebar_permissions', [ 'configured' => ['user_' . $this->user->id], 'subjects' => ['user_' . $this->user->id => ['courier-payables/courier-information']], ]); $this->loginAs()->get(route('business.courier-payable-summaries.export', [ 'summaryPage' => 'freight-difference', ]))->assertForbidden(); $this->loginAs()->get(route('business.courier-payable-summaries.template', [ 'summaryPage' => 'freight-difference', ]))->assertForbidden(); $this->loginAs()->get(route('business.courier-payable-summaries.template', [ 'summaryPage' => 'other-surcharge-summary', ]))->assertForbidden(); $this->loginAs()->post(route('business.courier-payable-summaries.import', [ 'summaryPage' => 'return-fee', ]))->assertForbidden(); $this->loginAs()->get(route('business.courier-payable-summaries.export', [ 'summaryPage' => 'return-fee', ]))->assertForbidden(); $this->loginAs()->get(route('business.courier-payable-summaries.template', [ 'summaryPage' => 'return-fee', ]))->assertForbidden(); } /** @param list $headers @param list> $rows */ private function storeSnapshot(string $page, array $headers, array $rows): void { $store = app(CourierPayableSummaryStore::class); Storage::disk('uploads')->put( $store->path((int) $this->company->id, $page), json_encode([ 'version' => 1, 'headers' => $headers, 'rows' => $rows, 'source_name' => $page . '.xlsx', 'imported_at' => now()->toIso8601String(), 'imported_by' => $this->user->id, ], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR), ); } }