按供应商名称批量匹配供应商管理中的采购人员,并在负数原因后展示。 Co-authored-by: 苏木 <594326199@qq.com> Co-committed-by: 苏木 <594326199@qq.com>
203 lines
7.6 KiB
PHP
203 lines
7.6 KiB
PHP
<?php
|
||
|
||
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;
|
||
|
||
/**
|
||
* 供应商负数预警:直接读取「供应商应付(终版)→ 供应商应付表」,
|
||
* 仅保留「供应商余额 - 应付余额 + 预估金额 < 0」的记录。
|
||
*/
|
||
class SupplierNegativeAlertPageData
|
||
{
|
||
private const DATASET = 'supplier-payables-report';
|
||
|
||
private const PER_PAGE = 25;
|
||
|
||
public function __construct(
|
||
private readonly SupplierPayableOverrideStore $store,
|
||
private readonly PayablesFinalRegistry $registry,
|
||
private readonly SupplierManagementMirrorRepository $supplierManagement,
|
||
) {
|
||
}
|
||
|
||
/** @return array<string,mixed> */
|
||
public function page(): array
|
||
{
|
||
$rows = $this->store->listRows(
|
||
PayablesFinalActions::DATASET_PREFIX . 'supplier:' . self::DATASET,
|
||
(int) (company_id() ?: 1),
|
||
);
|
||
|
||
$negativeRows = [];
|
||
foreach ($rows as $row) {
|
||
$balance = $this->number($row['supplier_balance'] ?? null) ?? 0.0;
|
||
$shouldPay = $this->number($row['should_pay'] ?? null) ?? 0.0;
|
||
$estimatedPrice = $this->number($row['estimated_price'] ?? null) ?? 0.0;
|
||
$alertAmount = $balance - $shouldPay + $estimatedPrice;
|
||
if ($alertAmount >= 0) {
|
||
continue;
|
||
}
|
||
|
||
$row['supplier_balance'] = $balance;
|
||
$row['_negative_alert_amount'] = round($alertAmount, 2);
|
||
$negativeRows[] = $row;
|
||
}
|
||
|
||
usort($negativeRows, static function (array $left, array $right): int {
|
||
return strcmp(
|
||
(string) ($right['summary_month'] ?? ''),
|
||
(string) ($left['summary_month'] ?? ''),
|
||
) ?: strnatcasecmp(
|
||
(string) ($left['supplier'] ?? ''),
|
||
(string) ($right['supplier'] ?? ''),
|
||
);
|
||
});
|
||
|
||
$months = [];
|
||
foreach ($negativeRows as $row) {
|
||
$month = trim((string) ($row['summary_month'] ?? ''));
|
||
if (preg_match('/^\d{4}-\d{2}$/', $month) === 1) {
|
||
$months[$month] = true;
|
||
}
|
||
}
|
||
$months = array_keys($months);
|
||
rsort($months);
|
||
|
||
$requestedMonth = trim((string) request()->query('month', ''));
|
||
$month = in_array($requestedMonth, $months, true) ? $requestedMonth : '';
|
||
if ($month !== '') {
|
||
$negativeRows = array_values(array_filter(
|
||
$negativeRows,
|
||
static fn (array $row): bool => (string) ($row['summary_month'] ?? '') === $month,
|
||
));
|
||
}
|
||
|
||
$supplierNames = [];
|
||
foreach ($negativeRows as $row) {
|
||
$supplier = trim((string) ($row['supplier'] ?? ''));
|
||
if ($supplier !== '') {
|
||
$supplierNames[$supplier] = true;
|
||
}
|
||
}
|
||
$supplierNames = array_keys($supplierNames);
|
||
sort($supplierNames, SORT_NATURAL | SORT_FLAG_CASE);
|
||
|
||
$supplierQuery = mb_substr(trim((string) request()->query('supplier', '')), 0, 191);
|
||
if ($supplierQuery !== '') {
|
||
$needle = mb_strtolower($supplierQuery);
|
||
$negativeRows = array_values(array_filter(
|
||
$negativeRows,
|
||
static function (array $row) use ($needle): bool {
|
||
$haystack = mb_strtolower(implode("\n", [
|
||
(string) ($row['supplier'] ?? ''),
|
||
(string) ($row['company'] ?? ''),
|
||
]));
|
||
|
||
return str_contains($haystack, $needle);
|
||
},
|
||
));
|
||
}
|
||
|
||
$supplierCounts = [];
|
||
foreach ($negativeRows as $row) {
|
||
$key = trim((string) ($row['company'] ?? '')) . "\0" . trim((string) ($row['supplier'] ?? ''));
|
||
$supplierCounts[$key] = true;
|
||
}
|
||
|
||
$total = count($negativeRows);
|
||
$pages = max((int) ceil($total / self::PER_PAGE), 1);
|
||
$page = min(max((int) request()->query('page', 1), 1), $pages);
|
||
$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' => $negativeAlertFields,
|
||
'negativeAlertRows' => new LengthAwarePaginator(
|
||
$items,
|
||
$total,
|
||
self::PER_PAGE,
|
||
$page,
|
||
[
|
||
'path' => request()->url(),
|
||
'query' => request()->except('page'),
|
||
],
|
||
),
|
||
'negativeAlertMonths' => $months,
|
||
'negativeAlertMonth' => $month,
|
||
'negativeAlertSupplier' => $supplierQuery,
|
||
'negativeAlertSupplierNames' => $supplierNames,
|
||
'negativeAlertSupplierCount' => count($supplierCounts),
|
||
'negativeAlertTotal' => $total,
|
||
'negativeAlertSourceUrl' => route('business.pages.show', [
|
||
'section' => 'supplier-payables-final',
|
||
'page' => 'supplier-payables-report',
|
||
]),
|
||
];
|
||
}
|
||
|
||
private function number(mixed $value): ?float
|
||
{
|
||
if ($value === null || trim((string) $value) === '') {
|
||
return null;
|
||
}
|
||
|
||
$normalized = str_replace([',', ',', ' '], '', trim((string) $value));
|
||
|
||
return is_numeric($normalized) ? (float) $normalized : null;
|
||
}
|
||
}
|