feat: 供应商负数预警展示采购人员 (#734)

按供应商名称批量匹配供应商管理中的采购人员,并在负数原因后展示。
Co-authored-by: 苏木 <594326199@qq.com>
Co-committed-by: 苏木 <594326199@qq.com>
This commit is contained in:
苏木 2026-10-28 14:41:10 +08:00 committed by sumu
parent 780775da3d
commit 6eae3c6da5
4 changed files with 80 additions and 13 deletions

View File

@ -141,7 +141,7 @@ class SupplierManagementMirrorRepository
return $this->mirror->connection()->table($this->datasets->get(self::DATASET)->targetTable())
->whereIn('scmname', $names)
->orderBy('Id')
->get(['Id', 'scmcode', 'scmname'])
->get(['Id', 'scmcode', 'scmname', 'buyer'])
->all();
}

View File

@ -4,8 +4,10 @@ namespace Modules\EcommerceFinance\View;
use App\Http\Controllers\Business\PayablesFinalActions;
use Illuminate\Pagination\LengthAwarePaginator;
use Modules\EcommerceFinance\Repositories\SupplierManagementMirrorRepository;
use Modules\EcommerceFinance\Services\PayablesFinalRegistry;
use Modules\EcommerceFinance\Services\SupplierPayableOverrideStore;
use Throwable;
/**
* 供应商负数预警:直接读取「供应商应付(终版)→ 供应商应付表」,
@ -20,6 +22,7 @@ class SupplierNegativeAlertPageData
public function __construct(
private readonly SupplierPayableOverrideStore $store,
private readonly PayablesFinalRegistry $registry,
private readonly SupplierManagementMirrorRepository $supplierManagement,
) {
}
@ -110,19 +113,59 @@ class SupplierNegativeAlertPageData
$total = count($negativeRows);
$pages = max((int) ceil($total / self::PER_PAGE), 1);
$page = min(max((int) request()->query('page', 1), 1), $pages);
$items = array_map(
static fn (array $row): object => (object) $row,
array_slice($negativeRows, ($page - 1) * self::PER_PAGE, self::PER_PAGE),
$pageRows = array_slice($negativeRows, ($page - 1) * self::PER_PAGE, self::PER_PAGE);
$pageSupplierNames = array_values(array_unique(array_filter(
array_map(
static fn (array $row): string => trim((string) ($row['supplier'] ?? '')),
$pageRows,
),
static fn (string $name): bool => $name !== '',
)));
$purchasersBySupplier = [];
if ($pageSupplierNames !== []) {
try {
foreach ($this->supplierManagement->suppliersByNames($pageSupplierNames) as $supplier) {
$name = trim((string) ($supplier->scmname ?? ''));
$buyer = trim((string) ($supplier->buyer ?? ''));
if ($name !== '' && $buyer !== '') {
$purchasersBySupplier[$name][$buyer] = true;
}
}
} catch (Throwable $exception) {
report($exception);
}
}
$purchasersBySupplier = array_map(
static fn (array $buyers): string => implode('、', array_keys($buyers)),
$purchasersBySupplier,
);
$items = array_map(
static function (array $row) use ($purchasersBySupplier): object {
$supplier = trim((string) ($row['supplier'] ?? ''));
$row['purchaser'] = $purchasersBySupplier[$supplier] ?? '';
return (object) $row;
},
$pageRows,
);
$negativeAlertFields = [];
$fields = collect($this->registry->fields(
PayablesFinalRegistry::SECTION_SUPPLIER,
self::DATASET,
))->map(
static fn (array $definition, string $name): array => ['name' => $name] + $definition,
)->values()->all();
foreach ($fields as $field) {
$negativeAlertFields[] = $field;
if (($field['name'] ?? '') === 'negative_reason') {
$negativeAlertFields[] = ['name' => 'purchaser', 'label' => '采购', 'type' => 'text'];
}
}
return [
'view' => 'business.pages.negative-alerts.supplier-negative-alert',
'negativeAlertFields' => collect($this->registry->fields(
PayablesFinalRegistry::SECTION_SUPPLIER,
self::DATASET,
))->map(
static fn (array $definition, string $name): array => ['name' => $name] + $definition,
)->values()->all(),
'negativeAlertFields' => $negativeAlertFields,
'negativeAlertRows' => new LengthAwarePaginator(
$items,
$total,

View File

@ -6,6 +6,7 @@ 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\SupplierPayableOverrideStore;
use Tests\Feature\FeatureTestCase;
@ -57,6 +58,17 @@ class NegativeAlertPagesTest extends FeatureTestCase
'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', [
@ -72,6 +84,8 @@ class NegativeAlertPagesTest extends FeatureTestCase
->assertSeeText('公式命中供应商')
->assertSeeText('-128.50')
->assertSeeText('历史多付款')
->assertSeeText('采购甲')
->assertSeeText('采购乙')
->assertDontSeeText('正余额供应商')
->assertDontSeeText('负余额但公式为正供应商');
@ -81,7 +95,16 @@ class NegativeAlertPagesTest extends FeatureTestCase
);
$html = (string) $response->getContent();
preg_match_all('/<th(?:\s|>)/', $html, $headers);
$this->assertCount(count($fields), $headers[0]);
// 序号 + 隐藏主体名称后的原字段 + 动态匹配的采购。
$this->assertCount(count($fields) + 1, $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->assertStringNotContainsString('供应商公司主体名称', $html);
$this->assertStringNotContainsString('测试供应商有限公司', $html);

View File

@ -157,14 +157,15 @@ class SupplierManagementMirrorRepositoryTest extends TestCase
{
$this->ready(SupplierManagementMirrorRepository::DATASET);
$this->table('js_supplier_management')->insert([
['Id' => 9, 'scmcode' => 'N901', 'scmname' => '一泰'],
['Id' => 11, 'scmcode' => 'Y411', 'scmname' => '七彩'],
['Id' => 9, 'scmcode' => 'N901', 'scmname' => '一泰', 'buyer' => '采购甲'],
['Id' => 11, 'scmcode' => 'Y411', 'scmname' => '七彩', 'buyer' => '采购乙'],
]);
$rows = $this->repository->suppliersByNames(['一泰', '不存在']);
$this->assertCount(1, $rows);
$this->assertSame('N901', $rows[0]->scmcode);
$this->assertSame('采购甲', $rows[0]->buyer);
}
private function ready(string $key): void