一次点击按采购拆分任务,并用一条钉钉群消息发送全部采购独立处理链接。 Co-authored-by: 苏木 <594326199@qq.com> Co-committed-by: 苏木 <594326199@qq.com>
227 lines
9.5 KiB
PHP
227 lines
9.5 KiB
PHP
<?php
|
||
|
||
namespace Tests\Feature\Business;
|
||
|
||
use App\Http\Controllers\Business\PayablesFinalActions;
|
||
use App\Listeners\Menu\ShowInAdmin;
|
||
use App\Models\Auth\Permission;
|
||
use App\Utilities\BusinessNavigation;
|
||
use Modules\EcommerceFinance\Repositories\SupplierManagementMirrorRepository;
|
||
use Modules\EcommerceFinance\Services\PayablesFinalRegistry;
|
||
use Modules\EcommerceFinance\Services\SupplierNegativeAlertDingTalkWorkflow;
|
||
use Modules\EcommerceFinance\Services\SupplierPayableOverrideStore;
|
||
use Tests\Feature\FeatureTestCase;
|
||
|
||
class NegativeAlertPagesTest extends FeatureTestCase
|
||
{
|
||
protected function setUp(): void
|
||
{
|
||
parent::setUp();
|
||
|
||
$this->user->attachPermission(
|
||
Permission::where('name', 'read-admin-panel')->firstOrFail()
|
||
);
|
||
}
|
||
|
||
public function test_negative_alerts_is_a_payables_section_with_two_children(): void
|
||
{
|
||
$navigation = collect(BusinessNavigation::all())->keyBy('slug');
|
||
$alertSection = $navigation->get('negative-alerts');
|
||
|
||
$this->assertNotNull($alertSection);
|
||
$this->assertSame('负数预警', $alertSection['title']);
|
||
$this->assertSame(
|
||
['supplier-negative-alert', 'courier-negative-alert'],
|
||
array_column($alertSection['pages'], 'slug'),
|
||
);
|
||
$this->assertNotContains(
|
||
'supplier-negative-alert',
|
||
array_column($navigation->get('supplier-reconciliation')['pages'], 'slug'),
|
||
);
|
||
|
||
$grouped = ShowInAdmin::groupPayables(BusinessNavigation::all());
|
||
$payables = collect($grouped)->firstWhere('slug', 'payable-items');
|
||
$this->assertSame('negative-alerts', $payables['sections'][0]['slug']);
|
||
}
|
||
|
||
public function test_supplier_and_courier_negative_alert_pages_render_as_tabs(): void
|
||
{
|
||
$this->saveSupplierPayable('negative:1', '负余额供应商', '-128.50', '2026-09', [
|
||
'company' => '测试供应商有限公司',
|
||
'negative_reason' => '历史多付款',
|
||
]);
|
||
$this->saveSupplierPayable('positive:1', '正余额供应商', '88.00', '2026-09');
|
||
$this->saveSupplierPayable('formula:1', '公式命中供应商', '100.00', '2026-09', [
|
||
'should_pay' => '180.00',
|
||
'estimated_price' => '50.00',
|
||
]);
|
||
$this->saveSupplierPayable('formula:exclude', '负余额但公式为正供应商', '-100.00', '2026-09', [
|
||
'should_pay' => '-500.00',
|
||
'estimated_price' => '50.00',
|
||
]);
|
||
$supplierManagement = \Mockery::mock(SupplierManagementMirrorRepository::class);
|
||
$supplierManagement->shouldReceive('suppliersByNames')
|
||
->once()
|
||
->withArgs(static fn (array $names): bool => count($names) === 2
|
||
&& in_array('负余额供应商', $names, true)
|
||
&& in_array('公式命中供应商', $names, true))
|
||
->andReturn([
|
||
(object) ['scmname' => '负余额供应商', 'buyer' => '采购甲'],
|
||
(object) ['scmname' => '公式命中供应商', 'buyer' => '采购乙'],
|
||
]);
|
||
$this->app->instance(SupplierManagementMirrorRepository::class, $supplierManagement);
|
||
|
||
$response = $this->loginAs()
|
||
->get(route('business.pages.show', [
|
||
'section' => 'negative-alerts',
|
||
'page' => 'supplier-negative-alert',
|
||
]))
|
||
->assertOk()
|
||
->assertSeeText('供应商负数预警')
|
||
->assertSeeText('快递负数预警')
|
||
->assertSeeText('按负数预警公式筛出的终版数据')
|
||
->assertSeeText('供应商余额 - 应付余额 + 预估金额 < 0')
|
||
->assertSeeText('负余额供应商')
|
||
->assertSeeText('公式命中供应商')
|
||
->assertSeeText('-128.50')
|
||
->assertSeeText('历史多付款')
|
||
->assertSeeText('采购甲')
|
||
->assertSeeText('采购乙')
|
||
->assertDontSeeText('正余额供应商')
|
||
->assertDontSeeText('负余额但公式为正供应商');
|
||
|
||
$fields = app(PayablesFinalRegistry::class)->fields(
|
||
PayablesFinalRegistry::SECTION_SUPPLIER,
|
||
'supplier-payables-report',
|
||
);
|
||
$html = (string) $response->getContent();
|
||
preg_match_all('/<th(?:\s|>)/', $html, $headers);
|
||
// 序号 + 隐藏主体名称后的原字段 + 动态匹配的采购 + 处理结果。
|
||
$this->assertCount(count($fields) + 2, $headers[0]);
|
||
preg_match_all('/<th[^>]*>(.*?)<\/th>/s', $html, $headerMatches);
|
||
$labels = array_map(
|
||
static fn (string $label): string => trim(html_entity_decode(strip_tags($label), ENT_QUOTES | ENT_HTML5, 'UTF-8')),
|
||
$headerMatches[1],
|
||
);
|
||
$negativeReasonIndex = array_search('负数原因', $labels, true);
|
||
$this->assertNotFalse($negativeReasonIndex);
|
||
$this->assertSame('采购', $labels[$negativeReasonIndex + 1]);
|
||
$this->assertSame('处理结果', $labels[$negativeReasonIndex + 2]);
|
||
$this->assertStringContainsString('待采购处理', $html);
|
||
$this->assertStringNotContainsString('供应商公司主体名称', $html);
|
||
$this->assertStringNotContainsString('测试供应商有限公司', $html);
|
||
|
||
preg_match_all('/<th[^>]*data-fixed-column="([^"]+)"/', $html, $fixedHeaders);
|
||
$this->assertSame(
|
||
['index', 'summary_month', 'checker', 'settlement_method', 'supplier'],
|
||
$fixedHeaders[1],
|
||
);
|
||
|
||
$this->loginAs()
|
||
->get(route('business.pages.show', [
|
||
'section' => 'negative-alerts',
|
||
'page' => 'courier-negative-alert',
|
||
]))
|
||
->assertOk()
|
||
->assertSeeText('供应商负数预警')
|
||
->assertSeeText('快递负数预警')
|
||
->assertSeeText('快递负数判定口径待确认')
|
||
->assertSeeText('待接入');
|
||
}
|
||
|
||
public function test_supplier_negative_alert_filters_by_month_and_supplier(): void
|
||
{
|
||
$this->saveSupplierPayable('negative:sep', '九月负余额供应商', '-99.00', '2026-09');
|
||
$this->saveSupplierPayable('negative:aug', '八月负余额供应商', '-66.00', '2026-08');
|
||
|
||
$this->loginAs()
|
||
->get(route('business.pages.show', [
|
||
'section' => 'negative-alerts',
|
||
'page' => 'supplier-negative-alert',
|
||
'month' => '2026-08',
|
||
'supplier' => '八月负余额',
|
||
]))
|
||
->assertOk()
|
||
->assertSeeText('八月负余额供应商')
|
||
->assertSeeText('-66.00')
|
||
->assertDontSeeText('九月负余额供应商');
|
||
}
|
||
|
||
public function test_supplier_negative_alert_filters_by_checker_settlement_and_purchaser(): void
|
||
{
|
||
$this->saveSupplierPayable('filter:target', '目标供应商', '-50.00', '2026-09', [
|
||
'checker' => '李萌',
|
||
'settlement_method' => '订单合同',
|
||
]);
|
||
$this->saveSupplierPayable('filter:other', '其他供应商', '-60.00', '2026-09', [
|
||
'checker' => '陈亲园',
|
||
'settlement_method' => '终止合作',
|
||
]);
|
||
$supplierManagement = \Mockery::mock(SupplierManagementMirrorRepository::class);
|
||
$supplierManagement->shouldReceive('suppliersByNames')
|
||
->andReturn([
|
||
(object) ['scmname' => '目标供应商', 'buyer' => '采购甲'],
|
||
(object) ['scmname' => '其他供应商', 'buyer' => '采购乙'],
|
||
]);
|
||
$this->app->instance(SupplierManagementMirrorRepository::class, $supplierManagement);
|
||
app(SupplierNegativeAlertDingTalkWorkflow::class)->saveConfiguration(
|
||
'https://oapi.dingtalk.com/robot/send?access_token=test-token',
|
||
'SEC-test',
|
||
'财务&采购沟通群',
|
||
);
|
||
|
||
$this->loginAs()
|
||
->get(route('business.pages.show', [
|
||
'section' => 'negative-alerts',
|
||
'page' => 'supplier-negative-alert',
|
||
]))
|
||
->assertOk()
|
||
->assertSeeText('一键发送全部采购(2 人)');
|
||
|
||
$this->loginAs()
|
||
->get(route('business.pages.show', [
|
||
'section' => 'negative-alerts',
|
||
'page' => 'supplier-negative-alert',
|
||
'checker' => '李萌',
|
||
'settlement_method' => '订单合同',
|
||
'purchaser' => '采购甲',
|
||
]))
|
||
->assertOk()
|
||
->assertSeeText('目标供应商')
|
||
->assertSeeText('采购甲')
|
||
->assertSeeText('发送给 采购甲(1 家)')
|
||
->assertDontSeeText('其他供应商');
|
||
}
|
||
|
||
public function test_supplier_negative_alert_route_no_longer_renders_under_supplier_reconciliation(): void
|
||
{
|
||
$this->withExceptionHandling()
|
||
->loginAs()
|
||
->get(route('business.pages.show', [
|
||
'section' => 'supplier-reconciliation',
|
||
'page' => 'supplier-negative-alert',
|
||
]))
|
||
->assertRedirect();
|
||
}
|
||
|
||
private function saveSupplierPayable(
|
||
string $key,
|
||
string $supplier,
|
||
string $supplierBalance,
|
||
string $month,
|
||
array $extra = [],
|
||
): void {
|
||
app(SupplierPayableOverrideStore::class)->save(
|
||
PayablesFinalActions::DATASET_PREFIX . 'supplier:supplier-payables-report',
|
||
$key,
|
||
$extra + [
|
||
'summary_month' => $month,
|
||
'supplier' => $supplier,
|
||
'supplier_balance' => $supplierBalance,
|
||
],
|
||
(int) $this->company->id,
|
||
(int) $this->user->id,
|
||
);
|
||
}
|
||
}
|