- 搜索框右侧新增书签按钮,点开显示 config/sidebar_links.php 中配置的网址,新标签页打开 - 未配置时给出去哪里加的提示 - 补充导航测试
431 lines
21 KiB
PHP
431 lines
21 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Settings;
|
|
|
|
use App\Models\Auth\Permission;
|
|
use App\Models\Auth\Role;
|
|
use App\Utilities\BusinessNavigation;
|
|
use Tests\Feature\FeatureTestCase;
|
|
|
|
class NavigationTest extends FeatureTestCase
|
|
{
|
|
public function testGroupedNavigationRendersExistingPageLinks(): void
|
|
{
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', ['section' => 'basic-data']))
|
|
->assertOk()
|
|
->assertSeeText('基础数据')
|
|
->assertSeeText('商品价格管理')
|
|
->assertSee('/workspace/order-data/raw-data', false)
|
|
->assertSee('/workspace/refunds/non-jingxi-refund-data', false)
|
|
->assertSee('/workspace/product-inventory/normal-products', false)
|
|
->assertSee('/workspace/product-inventory/monthly-weighted-average-price', false)
|
|
->assertSee('/workspace/product-inventory/inventory-report', false);
|
|
}
|
|
|
|
public function testInventoryReportMenuPageIsAvailable(): void
|
|
{
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'product-inventory',
|
|
'page' => 'inventory-report',
|
|
]))
|
|
->assertOk()
|
|
->assertSeeText('进销存报表')
|
|
->assertSeeTextInOrder([
|
|
'货主', '月份', '仓储方', '仓库', '商品编码', '分类',
|
|
'期初数量', '期末数量', '加工进仓', '加工出仓', '采购进仓',
|
|
'采购退货', '其它进仓', '其它退货', '销售出仓', '销售退货',
|
|
'盘点', '调拨出', '调拨入', '期初', '其它出库',
|
|
])
|
|
->assertSeeText('进销存总表')
|
|
->assertSeeText('仓库名称映射表')
|
|
->assertSee('data-inventory-report-tabs', false)
|
|
->assertSee('data-inventory-report-tab="mapping"', false)
|
|
->assertSee('id="inventory-report-mapping-panel"', false)
|
|
->assertDontSeeText('复用“应付款项 → 快递应付 → 三方仓进销存”')
|
|
->assertSeeText('进销存共享镜像尚未建立')
|
|
->assertDontSeeText('已按附件模板导入 20 条演示数据')
|
|
->assertDontSeeText('进销存报表字段和数据来源尚未定义。');
|
|
}
|
|
|
|
public function testMonthlyWeightedAveragePricePageIsAvailable(): void
|
|
{
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'product-inventory',
|
|
'page' => 'monthly-weighted-average-price',
|
|
]))
|
|
->assertOk()
|
|
->assertSeeText('商品月加权平均单价')
|
|
->assertSeeText('确认取数规则后再接入经营分析副本库数据')
|
|
// 第一层:未分组的列标题跨两行,期初数据与入库数据各自合并子列。
|
|
->assertSeeTextInOrder([
|
|
'月份', '条码+仓库', '仓库简称', '供应商', '商品编码', '商品名称',
|
|
'期初数据', '入库数据', '加权平均单价', '定价状态',
|
|
// 第二层:只有分组列出现在这一行,顺序固定。
|
|
'原始数量', '原始价格', '原始期初金额', '更改价格', '期初金额',
|
|
'采购数量', '采购单价', '入库金额',
|
|
])
|
|
->assertSee('data-grouped-header="true"', false)
|
|
->assertSee('data-finance-table-native', false)
|
|
->assertSee('rowspan="2"', false)
|
|
->assertSee('colspan="5"', false)
|
|
->assertSee('colspan="3"', false)
|
|
// 两层表头里第二行的最后一个单元格不在表格最右侧,需要保留右边框,
|
|
// 否则右侧跨行一级标题列会和左侧分组列断格。
|
|
->assertSee('data-directory-last-column', false)
|
|
// 工作表没有序号列,页面也不再渲染固定序号列。
|
|
->assertDontSee('<th scope="col" data-directory-fixed="start"', false)
|
|
->assertDontSee('<td data-directory-fixed="start"', false)
|
|
->assertSeeText('确认取数规则后再接入经营分析副本库数据');
|
|
}
|
|
|
|
public function testPerformanceDetailsPageIsAvailable(): void
|
|
{
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'financial-operations',
|
|
'page' => 'performance-details',
|
|
]))
|
|
->assertOk()
|
|
->assertSeeText('业绩明细')
|
|
// 列顺序:发生时间 → 店铺名称 → 线上单号 → 收入金额 → 商品金额 → 差额 → 匹配状态 → 异常标注
|
|
->assertSee('data-column-key="platform"', false)
|
|
->assertSee('data-column-key="occurred_date"', false)
|
|
->assertSee('data-column-key="store_name"', false)
|
|
->assertSee('data-column-key="online_order_no"', false)
|
|
->assertSee('data-column-key="income_amount"', false)
|
|
->assertSee('data-column-key="discount_amount"', false)
|
|
->assertSee('data-column-key="offset_amount"', false)
|
|
->assertSee('data-column-key="planting_amount"', false)
|
|
->assertSee('data-column-key="product_amount"', false)
|
|
->assertSee('data-column-key="difference"', false)
|
|
->assertSee('data-column-key="match_status"', false)
|
|
->assertSee('data-column-key="remark"', false)
|
|
// 子订单号已按业务要求移除。
|
|
->assertDontSeeText('子订单号')
|
|
// 展示粒度=一笔账单收款,不再展开发货明细列。
|
|
->assertDontSee('data-column-key="warehouse"', false)
|
|
->assertDontSee('data-column-key="product_code"', false)
|
|
->assertDontSee('data-column-key="quantity"', false)
|
|
// 筛选栏:月份选择器 + 店铺名称 + 线上单号 + 匹配状态。
|
|
->assertSee('data-inventory-month-picker', false)
|
|
->assertSee('name="store_name"', false)
|
|
->assertSee('name="online_order_no"', false)
|
|
->assertSee('name="match_status"', false)
|
|
->assertSee('/workspace/financial-operations/performance-details/export', false)
|
|
->assertSeeText('店铺收款匹配订单明细尚未同步,请先执行 store-receipt:order-match:sync。');
|
|
}
|
|
|
|
public function testFinancialRiskControlPageIsAvailableBetweenFinancialReports(): void
|
|
{
|
|
$navigation = BusinessNavigation::all();
|
|
$slugs = array_column($navigation, 'slug');
|
|
$riskControlIndex = array_search('financial-risk-control', $slugs, true);
|
|
|
|
$this->assertNotFalse($riskControlIndex);
|
|
$this->assertSame('financial-operations', $slugs[$riskControlIndex - 1]);
|
|
$this->assertSame('financial-sw', $slugs[$riskControlIndex + 1]);
|
|
$this->assertSame('财务经营风险管控', $navigation[$riskControlIndex]['title']);
|
|
$this->assertSame('security', $navigation[$riskControlIndex]['icon']);
|
|
$this->assertSame([
|
|
['slug' => 'store-violation-alert', 'title' => '店铺违规信息预警'],
|
|
['slug' => 'preview-statistics', 'title' => '预览统计'],
|
|
[
|
|
'slug' => 'process-statistics',
|
|
'title' => '流程统计',
|
|
'directory' => [
|
|
'icon' => 'timeline',
|
|
'status' => '共享镜像',
|
|
'description' => '店铺申请流程(开店 / 变更 / 关店)明细,数据来自共享业务镜像,只读展示。',
|
|
'empty' => '共享镜像中暂未读取到店铺申请流程数据,请检查镜像同步状态。',
|
|
'fields' => [
|
|
['name' => 'flow_id', 'label' => '单据编号'],
|
|
['name' => 'flow_type', 'label' => '流程类型'],
|
|
['name' => 'approval_status', 'label' => '审批状态'],
|
|
['name' => 'current_approver', 'label' => '当前审批人'],
|
|
['name' => 'shop_name', 'label' => '店铺名称'],
|
|
['name' => 'platform', 'label' => '平台'],
|
|
['name' => 'shop_head', 'label' => '店铺运营负责人'],
|
|
['name' => 'customer_manager', 'label' => '店铺客服负责人'],
|
|
['name' => 'responsible_department', 'label' => '利润中心'],
|
|
['name' => 'erp_shop_name', 'label' => 'ERP店铺名称'],
|
|
['name' => 'apply_user', 'label' => '申请人'],
|
|
['name' => 'apply_time', 'label' => '申请日期', 'kind' => 'date'],
|
|
['name' => 'trademark_number', 'label' => '商标号'],
|
|
['name' => 'change_reason', 'label' => '变更原因'],
|
|
['name' => 'close_reason', 'label' => '关店原因'],
|
|
['name' => 'expected_closing_time', 'label' => '预计关店时间', 'kind' => 'date'],
|
|
['name' => 'refund_type', 'label' => '退回类型'],
|
|
['name' => 'refund_amount', 'label' => '退回金额', 'kind' => 'number'],
|
|
['name' => 'change_successful', 'label' => '是否完成', 'kind' => 'boolean'],
|
|
],
|
|
],
|
|
],
|
|
], $navigation[$riskControlIndex]['pages']);
|
|
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', ['section' => 'financial-risk-control']))
|
|
->assertRedirect(route('business.pages.show', [
|
|
'section' => 'financial-risk-control',
|
|
'page' => 'store-violation-alert',
|
|
]));
|
|
|
|
$this->get(route('business.pages.show', [
|
|
'section' => 'financial-risk-control',
|
|
'page' => 'store-violation-alert',
|
|
]) . '?platform=jingxi')
|
|
->assertOk()
|
|
->assertSeeText('财务经营风险管控')
|
|
->assertSeeText('店铺违规信息预警')
|
|
->assertSeeTextInOrder(['淘系', '阿里巴巴', '京东', '京喜', '拼多多', '抖音'])
|
|
->assertSeeText('京喜店铺违规信息预警')
|
|
->assertSeeText('统一表头:')
|
|
->assertSeeText('处理状态')
|
|
->assertSee('data-active-platform="jingxi"', false)
|
|
->assertSee('id="financial-risk-control-panel-jingxi"', false)
|
|
->assertSee('data-platform-tab="jingxi"', false)
|
|
->assertSee('/workspace/financial-risk-control/store-violation-alert', false);
|
|
}
|
|
|
|
public function test_business_pages_render_menu_filter_and_open_page_tabs(): void
|
|
{
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'financial-risk-control',
|
|
'page' => 'store-violation-alert',
|
|
]))
|
|
->assertOk()
|
|
// 侧边栏菜单搜索框(方便按名称找页面)。
|
|
->assertSee('id="prototype-menu-filter"', false)
|
|
->assertSee('prototype-menu-filter', false)
|
|
// 已打开页面标签栏(固定在页面上方,点开的页面并排显示)。
|
|
->assertSee('data-open-tabs', false)
|
|
->assertSee('data-open-tabs-list', false)
|
|
->assertSee('caiwu.open-pages.v1.', false)
|
|
// 目录 iframe 里的子页面不再重复渲染这条标签栏。
|
|
->assertSee('window.self !== window.top', false)
|
|
// 搜索框右侧的常用网址入口。
|
|
->assertSee('data-sidebar-links-toggle', false)
|
|
->assertSee('data-sidebar-links', false);
|
|
}
|
|
|
|
public function test_sidebar_quick_links_render_from_config(): void
|
|
{
|
|
config(['sidebar_links' => [
|
|
['title' => '聚水潭 ERP', 'url' => 'https://example.com/jst'],
|
|
['title' => '京麦工作台', 'url' => 'https://example.com/jd'],
|
|
]]);
|
|
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'financial-risk-control',
|
|
'page' => 'store-violation-alert',
|
|
]))
|
|
->assertOk()
|
|
->assertSee('href="https://example.com/jst"', false)
|
|
->assertSeeText('聚水潭 ERP')
|
|
->assertSee('target="_blank"', false);
|
|
}
|
|
|
|
public function testProcessStatisticsPageIsAvailableUnderRiskControl(): void
|
|
{
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', [
|
|
'section' => 'financial-risk-control',
|
|
'page' => 'process-statistics',
|
|
]))
|
|
->assertOk()
|
|
->assertSeeText('流程统计')
|
|
// 店铺申请流程镜像:列头与查询条件都按外部店铺申请页对齐。
|
|
->assertSeeText('单据编号')
|
|
->assertSeeText('审批状态')
|
|
->assertSeeText('ERP店铺名称')
|
|
->assertSee('data-directory-multi-select="flow_type"', false)
|
|
->assertSee('data-directory-multi-select="approval_status"', false)
|
|
// 测试环境没有业务镜像,页面给出同步提示而不是报错。
|
|
->assertSeeText('店铺申请流程共享镜像尚未完成首次同步。')
|
|
// 目录内页签:两个页面可以互相切换。
|
|
->assertSee('/workspace/financial-risk-control/store-violation-alert', false)
|
|
->assertSee('/workspace/financial-risk-control/process-statistics', false);
|
|
}
|
|
|
|
public function testGroupedNavigationKeepsRestrictedPagePermissions(): void
|
|
{
|
|
$this->user->roles()->sync([Role::where('name', 'manager')->firstOrFail()->id]);
|
|
$subject = 'user_' . $this->user->id;
|
|
setting()->set('system.sidebar_permissions', [
|
|
'configured' => [$subject],
|
|
'subjects' => [$subject => ['basic-data', 'refunds/non-jingxi-refund-data']],
|
|
]);
|
|
setting()->save();
|
|
|
|
$this->loginAs()
|
|
->get(route('business.pages.show', ['section' => 'basic-data']))
|
|
->assertOk()
|
|
->assertSee('/workspace/refunds/non-jingxi-refund-data', false)
|
|
->assertDontSee('/workspace/order-data/raw-data', false)
|
|
->assertDontSee('/workspace/product-inventory/normal-products', false);
|
|
|
|
$this->withExceptionHandling()
|
|
->get(route('business.pages.show', ['section' => 'order-data']))
|
|
->assertForbidden();
|
|
}
|
|
|
|
public function testAdminCanUpdateSidebarLabels()
|
|
{
|
|
$this->user->roles()->sync([
|
|
Role::where('name', 'admin')->firstOrFail()->id,
|
|
]);
|
|
|
|
$this->user->attachPermission(
|
|
Permission::where('name', 'read-admin-panel')->firstOrFail()
|
|
);
|
|
|
|
$this->loginAs()
|
|
->get(route('settings.navigation.manage'))
|
|
->assertOk()
|
|
->assertSeeText('侧边栏功能名称')
|
|
->assertSee('name="page_titles[courier-payables][quote-management]"', false)
|
|
->assertSee('name="page_titles[courier-payables][courier-surcharge]"', false);
|
|
|
|
$this->patch(route('settings.navigation.save'), [
|
|
'section_titles' => [
|
|
'company-management' => '企业档案',
|
|
],
|
|
'page_titles' => [
|
|
'company-management' => [
|
|
'subject-operating-report' => '主体经营',
|
|
],
|
|
'courier-payables' => [
|
|
'quote-management' => '快递报价单',
|
|
'unknown-page' => '不应保存',
|
|
],
|
|
],
|
|
])->assertOk();
|
|
|
|
$navigation = BusinessNavigation::all();
|
|
$companyRegistration = collect($navigation[0]['pages'])->firstWhere('slug', 'subject-operating-report');
|
|
|
|
$this->assertSame('企业档案', $navigation[0]['title']);
|
|
$this->assertSame('主体经营', $companyRegistration['title']);
|
|
$courier = collect($navigation)->firstWhere('slug', 'courier-payables');
|
|
$quotes = collect($courier['pages'])->firstWhere('slug', 'courier-quote');
|
|
$this->assertSame('快递报价单', collect($quotes['children'])->firstWhere('slug', 'quote-management')['title']);
|
|
$this->assertArrayNotHasKey('unknown-page', setting('navigation.titles')['pages']['courier-payables']);
|
|
}
|
|
|
|
public function testOrderDataAndRefundAreConfiguredBetweenInvoiceAndReceivablesMenus()
|
|
{
|
|
$navigation = BusinessNavigation::all();
|
|
$slugs = array_column($navigation, 'slug');
|
|
|
|
$invoiceIndex = array_search('invoice-management', $slugs, true);
|
|
$fundIndex = array_search('fund-management', $slugs, true);
|
|
$orderIndex = array_search('order-data', $slugs, true);
|
|
$refundIndex = array_search('refunds', $slugs, true);
|
|
$billingIndex = array_search('billing-transactions', $slugs, true);
|
|
$balanceIndex = array_search('account-balance-data', $slugs, true);
|
|
$productIndex = array_search('product-inventory', $slugs, true);
|
|
$receivablesIndex = array_search('store-receivables', $slugs, true);
|
|
$orderData = $navigation[$orderIndex] ?? null;
|
|
$refund = $navigation[$refundIndex] ?? null;
|
|
$productInventory = $navigation[$productIndex] ?? null;
|
|
$financialOperations = collect($navigation)->firstWhere('slug', 'financial-operations');
|
|
|
|
$this->assertNotFalse($invoiceIndex);
|
|
$this->assertNotFalse($fundIndex);
|
|
$this->assertNotFalse($orderIndex);
|
|
$this->assertNotFalse($refundIndex);
|
|
$this->assertNotFalse($billingIndex);
|
|
$this->assertNotFalse($balanceIndex);
|
|
$this->assertNotFalse($productIndex);
|
|
$this->assertNotFalse($receivablesIndex);
|
|
$this->assertSame(
|
|
[
|
|
'invoice-management',
|
|
'fund-management',
|
|
'order-data',
|
|
'refunds',
|
|
'billing-transactions',
|
|
'planting-data',
|
|
'account-balance-data',
|
|
'product-inventory',
|
|
],
|
|
array_slice($slugs, $invoiceIndex, 8)
|
|
);
|
|
$this->assertSame(
|
|
[
|
|
'票务管理',
|
|
'资金管理',
|
|
'订单数据',
|
|
'退款数据',
|
|
'账单流水数据',
|
|
'种菜数据汇总',
|
|
'账单余额数据',
|
|
'商品价格管理',
|
|
],
|
|
array_column(array_slice($navigation, $invoiceIndex, 8), 'title')
|
|
);
|
|
$this->assertGreaterThan($invoiceIndex, $orderIndex);
|
|
$this->assertGreaterThan($orderIndex, $refundIndex);
|
|
$this->assertGreaterThan($refundIndex, $billingIndex);
|
|
$this->assertGreaterThan($billingIndex, $balanceIndex);
|
|
$this->assertGreaterThan($balanceIndex, $productIndex);
|
|
$this->assertLessThan($receivablesIndex, $productIndex);
|
|
$this->assertSame('订单数据', $orderData['title']);
|
|
$this->assertSame(
|
|
['raw-data', 'bill-offset'],
|
|
array_column($orderData['pages'], 'slug')
|
|
);
|
|
$this->assertSame(
|
|
['原始数据', '账单冲减'],
|
|
array_column($orderData['pages'], 'title')
|
|
);
|
|
$this->assertSame(
|
|
['after-sales-expenses', 'promotion-expenses'],
|
|
array_column($orderData['pages'][0]['tabs'], 'slug')
|
|
);
|
|
$this->assertSame(
|
|
['订单售后费用明细', '商品推广费用明细'],
|
|
array_column($orderData['pages'][0]['tabs'], 'title')
|
|
);
|
|
$this->assertSame(
|
|
['normal-products', 'online-daifa-price', 'monthly-weighted-average-price', 'inventory-report'],
|
|
array_column($productInventory['pages'], 'slug')
|
|
);
|
|
$this->assertSame(
|
|
['普通商品资料', '线上代发价格', '商品月加权平均单价', '进销存报表'],
|
|
array_column($productInventory['pages'], 'title')
|
|
);
|
|
$this->assertArrayNotHasKey('children', $orderData['pages'][0]);
|
|
$this->assertSame(
|
|
['bill-offset'],
|
|
array_column(array_slice($orderData['pages'], 1), 'slug')
|
|
);
|
|
$this->assertSame(
|
|
['账单冲减'],
|
|
array_column(array_slice($orderData['pages'], 1), 'title')
|
|
);
|
|
$this->assertSame('退款数据', $refund['title']);
|
|
$this->assertSame('assignment_return', $refund['icon']);
|
|
$this->assertSame(
|
|
['jingxi-refund-data', 'non-jingxi-refund-data'],
|
|
array_column($refund['pages'], 'slug')
|
|
);
|
|
$this->assertSame(
|
|
['京喜退款数据', '售后(退货退款)'],
|
|
array_column($refund['pages'], 'title')
|
|
);
|
|
$this->assertNotNull($financialOperations);
|
|
$smallPayment = collect($financialOperations['pages'])->firstWhere('slug', 'small-payment-work-orders');
|
|
$this->assertSame('小额打款', $smallPayment['title'] ?? null);
|
|
$this->assertGreaterThan(
|
|
array_search('after-sales-expenses', array_column($financialOperations['pages'], 'slug'), true),
|
|
array_search('small-payment-work-orders', array_column($financialOperations['pages'], 'slug'), true)
|
|
);
|
|
}
|
|
|
|
}
|