fix: calculate withdrawal compliance from dated amounts
This commit is contained in:
parent
e224cbebc3
commit
efeb7ecccb
@ -33,7 +33,8 @@ class WithdrawalManagementReport
|
||||
* filters:array{platform:string,withdrawalPath:string,storeEntity:string,keyword:string,balanceStatus:string},
|
||||
* platforms:list<string>,paths:list<string>,storeEntities:list<string>,rows:Collection<int,StoreReceivingAccount>,
|
||||
* balances:array<int,array{status:string,balance:?float,source_store_name:?string,source_platform:?string}>,
|
||||
* reserveStandards:array<int,int|null>,summary:array{source:int,matched:int,ambiguous:int,unmatched:int,not_ready:int},
|
||||
* reserveStandards:array<int,int|null>,withdrawalAmounts:array<int,float|null>,compliance:array<int,string|null>,
|
||||
* summary:array{source:int,matched:int,ambiguous:int,unmatched:int,not_ready:int},
|
||||
* balanceColumnLabel:string,balanceDate:string,sort:array{field:string,direction:string}
|
||||
* }
|
||||
*/
|
||||
@ -81,9 +82,23 @@ class WithdrawalManagementReport
|
||||
),
|
||||
],
|
||||
)->all();
|
||||
$withdrawalAmounts = $rows->mapWithKeys(
|
||||
fn (StoreReceivingAccount $row): array => [
|
||||
(int) $row->id => $this->withdrawalAmountFor($row, $balanceDate),
|
||||
],
|
||||
)->all();
|
||||
$compliance = $rows->mapWithKeys(
|
||||
fn (StoreReceivingAccount $row): array => [
|
||||
(int) $row->id => $this->complianceFor(
|
||||
$balances[(int) $row->id] ?? ['status' => 'not_ready', 'balance' => null],
|
||||
$reserveStandards[(int) $row->id] ?? null,
|
||||
$withdrawalAmounts[(int) $row->id] ?? null,
|
||||
),
|
||||
],
|
||||
)->all();
|
||||
$rows = $sort['field'] === ''
|
||||
? $this->sortByBalanceStatus($rows, $balances)
|
||||
: $this->sortRows($rows, $sort, $balances, $reserveStandards);
|
||||
: $this->sortRows($rows, $sort, $balances, $reserveStandards, $withdrawalAmounts, $compliance);
|
||||
|
||||
return [
|
||||
'filters' => $filters,
|
||||
@ -93,6 +108,8 @@ class WithdrawalManagementReport
|
||||
'rows' => $rows,
|
||||
'balances' => $balances,
|
||||
'reserveStandards' => $reserveStandards,
|
||||
'withdrawalAmounts' => $withdrawalAmounts,
|
||||
'compliance' => $compliance,
|
||||
'summary' => [
|
||||
'source' => $this->shopBalances->countForDate($balanceDate),
|
||||
'matched' => (int) $balanceCounts->get('matched', 0),
|
||||
@ -178,13 +195,15 @@ class WithdrawalManagementReport
|
||||
* @param array{field:string,direction:string} $sort
|
||||
* @param array<int,array{status:string,balance:?float,source_store_name:?string,source_platform:?string}> $balances
|
||||
* @param array<int,int|null> $reserveStandards
|
||||
* @param array<int,float|null> $withdrawalAmounts
|
||||
* @param array<int,string|null> $compliance
|
||||
* @return Collection<int,StoreReceivingAccount>
|
||||
*/
|
||||
private function sortRows(Collection $rows, array $sort, array $balances, array $reserveStandards): Collection
|
||||
private function sortRows(Collection $rows, array $sort, array $balances, array $reserveStandards, array $withdrawalAmounts, array $compliance): Collection
|
||||
{
|
||||
return $rows->sort(function (StoreReceivingAccount $left, StoreReceivingAccount $right) use ($sort, $balances, $reserveStandards): int {
|
||||
$leftValue = $this->sortValue($left, $sort['field'], $balances, $reserveStandards);
|
||||
$rightValue = $this->sortValue($right, $sort['field'], $balances, $reserveStandards);
|
||||
return $rows->sort(function (StoreReceivingAccount $left, StoreReceivingAccount $right) use ($sort, $balances, $reserveStandards, $withdrawalAmounts, $compliance): int {
|
||||
$leftValue = $this->sortValue($left, $sort['field'], $balances, $reserveStandards, $withdrawalAmounts, $compliance);
|
||||
$rightValue = $this->sortValue($right, $sort['field'], $balances, $reserveStandards, $withdrawalAmounts, $compliance);
|
||||
$comparison = $this->compareSortValues($leftValue, $rightValue, $sort['direction']);
|
||||
|
||||
return $comparison !== 0
|
||||
@ -196,8 +215,10 @@ class WithdrawalManagementReport
|
||||
/**
|
||||
* @param array<int,array{status:string,balance:?float,source_store_name:?string,source_platform:?string}> $balances
|
||||
* @param array<int,int|null> $reserveStandards
|
||||
* @param array<int,float|null> $withdrawalAmounts
|
||||
* @param array<int,string|null> $compliance
|
||||
*/
|
||||
private function sortValue(StoreReceivingAccount $row, string $field, array $balances, array $reserveStandards): mixed
|
||||
private function sortValue(StoreReceivingAccount $row, string $field, array $balances, array $reserveStandards, array $withdrawalAmounts, array $compliance): mixed
|
||||
{
|
||||
return match ($field) {
|
||||
'platform' => $row->store_platform,
|
||||
@ -211,10 +232,34 @@ class WithdrawalManagementReport
|
||||
? $balances[(int) $row->id]['balance']
|
||||
: null,
|
||||
'reserve_standard' => $reserveStandards[(int) $row->id] ?? null,
|
||||
'withdrawal_amount' => $withdrawalAmounts[(int) $row->id] ?? null,
|
||||
'compliance' => $compliance[(int) $row->id] ?? null,
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
private function withdrawalAmountFor(StoreReceivingAccount $row, string $balanceDate): ?float
|
||||
{
|
||||
if ($row->withdrawal_balance_date !== $balanceDate || $row->withdrawal_amount === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return is_numeric($row->withdrawal_amount) ? (float) $row->withdrawal_amount : null;
|
||||
}
|
||||
|
||||
/** @param array{status:string,balance:?float} $balance */
|
||||
private function complianceFor(array $balance, ?int $reserve, ?float $withdrawalAmount): ?string
|
||||
{
|
||||
if (($balance['status'] ?? '') !== 'matched' || $balance['balance'] === null || $reserve === null || $withdrawalAmount === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$remainingCents = (int) round((float) $balance['balance'] * 100)
|
||||
- (int) round($withdrawalAmount * 100);
|
||||
|
||||
return $remainingCents <= $reserve * 100 ? 'yes' : 'no';
|
||||
}
|
||||
|
||||
private function compareSortValues(mixed $left, mixed $right, string $direction): int
|
||||
{
|
||||
$leftEmpty = $left === null || $left === '';
|
||||
|
||||
@ -34,7 +34,8 @@ class WithdrawalManagementSpreadsheet
|
||||
* @param array{
|
||||
* rows:\Illuminate\Support\Collection<int,StoreReceivingAccount>,
|
||||
* balances:array<int,array{status:string,balance:?float,source_store_name:?string,source_platform:?string}>,
|
||||
* reserveStandards:array<int,int|null>,balanceColumnLabel:string,balanceDate:string
|
||||
* reserveStandards:array<int,int|null>,withdrawalAmounts:array<int,float|null>,compliance:array<int,string|null>,
|
||||
* balanceColumnLabel:string,balanceDate:string
|
||||
* } $report
|
||||
*/
|
||||
public function export(array $report): StreamedResponse
|
||||
@ -52,17 +53,17 @@ class WithdrawalManagementSpreadsheet
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->setTitle('提现管理');
|
||||
$this->writeHeader($sheet, self::CONFIG_HEADERS);
|
||||
$this->sizeColumns($sheet, [14, 30, 20, 32, 16, 24, 22]);
|
||||
$this->writeHeader($sheet, [...self::CONFIG_HEADERS, '提现金额']);
|
||||
$this->sizeColumns($sheet, [14, 30, 20, 32, 16, 24, 22, 18]);
|
||||
|
||||
$instructions = $spreadsheet->createSheet();
|
||||
$instructions->setTitle('填写说明');
|
||||
$instructions->fromArray([
|
||||
['提现管理导入说明'],
|
||||
['1. 店铺平台、ERP店铺名称用于定位店铺;有平台店铺ID时优先按平台和ID匹配。'],
|
||||
['2. 店铺主体、提现路径、提现账户名称、银行卡号后四位为可更新字段。'],
|
||||
['2. 店铺主体、提现路径、提现账户名称、银行卡号后四位和提现金额为可更新字段。'],
|
||||
['3. 可更新字段留空表示保持原值,填写“' . self::CLEAR_VALUE . '”表示清空原值。'],
|
||||
['4. 银行卡号只填写后四位数字。上传后会先展示预览,确认后才写入。'],
|
||||
['4. 提现金额须为非负数,最多两位小数,只用于导入当天对应的余额数据日期;上传后先预览再确认。'],
|
||||
], null, 'A1');
|
||||
$instructions->getStyle('A1')->getFont()->setBold(true)->setSize(14);
|
||||
$instructions->getColumnDimension('A')->setWidth(88);
|
||||
@ -96,6 +97,7 @@ class WithdrawalManagementSpreadsheet
|
||||
}
|
||||
[$headerIndex, $headers] = $this->headers($matrix);
|
||||
$indexes = array_flip($headers);
|
||||
$balanceDate = now()->subDay()->toDateString();
|
||||
$accounts = StoreReceivingAccount::withoutGlobalScopes()
|
||||
->where('company_id', $companyId)
|
||||
->whereNull('deleted_at')
|
||||
@ -113,6 +115,9 @@ class WithdrawalManagementSpreadsheet
|
||||
foreach (self::CONFIG_HEADERS as $header) {
|
||||
$values[$header] = trim((string) ($row[$indexes[$header]] ?? ''));
|
||||
}
|
||||
$values['提现金额'] = isset($indexes['提现金额'])
|
||||
? trim((string) ($row[$indexes['提现金额']] ?? ''))
|
||||
: '';
|
||||
if (count(array_filter($values, static fn (string $value): bool => $value !== '')) === 0) {
|
||||
continue;
|
||||
}
|
||||
@ -153,7 +158,7 @@ class WithdrawalManagementSpreadsheet
|
||||
$seen[(int) $account->id] = $line;
|
||||
|
||||
try {
|
||||
$changes = $this->changes($account, $values);
|
||||
$changes = $this->changes($account, $values, $balanceDate);
|
||||
} catch (\InvalidArgumentException $exception) {
|
||||
$errors[] = "第 {$line} 行" . $exception->getMessage();
|
||||
continue;
|
||||
@ -169,11 +174,13 @@ class WithdrawalManagementSpreadsheet
|
||||
'receiving_method' => '提现路径',
|
||||
'receiving_account_name' => '提现账户名称',
|
||||
'bank_card_last_four' => '银行卡号后四位',
|
||||
'withdrawal_amount' => '提现金额',
|
||||
'withdrawal_balance_date' => '提现金额',
|
||||
];
|
||||
$changedLabels = array_map(
|
||||
$changedLabels = array_unique(array_map(
|
||||
static fn (string $field): string => $labels[$field],
|
||||
array_keys($changes),
|
||||
);
|
||||
));
|
||||
$plan[] = [
|
||||
'id' => (int) $account->id,
|
||||
'updated_at' => (string) $account->getRawOriginal('updated_at'),
|
||||
@ -230,6 +237,14 @@ class WithdrawalManagementSpreadsheet
|
||||
]);
|
||||
}
|
||||
|
||||
if (isset($item['changes']['withdrawal_balance_date'])
|
||||
&& $item['changes']['withdrawal_balance_date'] !== null
|
||||
&& $item['changes']['withdrawal_balance_date'] !== now()->subDay()->toDateString()) {
|
||||
throw ValidationException::withMessages([
|
||||
'import' => '余额日期已滚动,请重新上传并预览提现金额。',
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($item['changes'] as $field => $value) {
|
||||
if ($field === 'bank_card_last_four') {
|
||||
$account->bank_card_last_four = $value;
|
||||
@ -258,6 +273,8 @@ class WithdrawalManagementSpreadsheet
|
||||
foreach ($report['rows'] as $row) {
|
||||
$match = $report['balances'][(int) $row->id] ?? ['status' => 'not_ready', 'balance' => null];
|
||||
$reserve = $report['reserveStandards'][(int) $row->id] ?? null;
|
||||
$withdrawalAmount = $report['withdrawalAmounts'][(int) $row->id] ?? null;
|
||||
$compliance = $report['compliance'][(int) $row->id] ?? null;
|
||||
$textValues = [
|
||||
1 => (string) ($row->store_platform ?? ''),
|
||||
2 => (string) ($row->erp_store_name ?? ''),
|
||||
@ -281,14 +298,22 @@ class WithdrawalManagementSpreadsheet
|
||||
} else {
|
||||
$sheet->setCellValue([9, $rowNumber], $reserve);
|
||||
}
|
||||
$sheet->setCellValueExplicit([10, $rowNumber], '待接入', DataType::TYPE_STRING);
|
||||
$sheet->setCellValueExplicit([11, $rowNumber], '待核验', DataType::TYPE_STRING);
|
||||
if ($withdrawalAmount === null) {
|
||||
$sheet->setCellValueExplicit([10, $rowNumber], '待接入', DataType::TYPE_STRING);
|
||||
} else {
|
||||
$sheet->setCellValue([10, $rowNumber], $withdrawalAmount);
|
||||
}
|
||||
$sheet->setCellValueExplicit([11, $rowNumber], match ($compliance) {
|
||||
'yes' => '是',
|
||||
'no' => '否',
|
||||
default => '待核验',
|
||||
}, DataType::TYPE_STRING);
|
||||
$rowNumber++;
|
||||
}
|
||||
|
||||
if ($rowNumber > 2) {
|
||||
$sheet->getStyle('H2:I' . ($rowNumber - 1))->getNumberFormat()->setFormatCode('#,##0.00');
|
||||
$sheet->getStyle('H2:I' . ($rowNumber - 1))->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
|
||||
$sheet->getStyle('H2:J' . ($rowNumber - 1))->getNumberFormat()->setFormatCode('#,##0.00');
|
||||
$sheet->getStyle('H2:J' . ($rowNumber - 1))->getAlignment()->setHorizontal(Alignment::HORIZONTAL_RIGHT);
|
||||
}
|
||||
$sheet->getStyle('C2:C' . max(2, $rowNumber - 1))->getNumberFormat()->setFormatCode('@');
|
||||
$sheet->getStyle('G2:G' . max(2, $rowNumber - 1))->getNumberFormat()->setFormatCode('@');
|
||||
@ -310,6 +335,9 @@ class WithdrawalManagementSpreadsheet
|
||||
]);
|
||||
}
|
||||
}
|
||||
if (count(array_keys($headers, '提现金额', true)) > 1) {
|
||||
throw ValidationException::withMessages(['import' => '导入表头只能包含一个“提现金额”列。']);
|
||||
}
|
||||
|
||||
return [(int) $index, $headers];
|
||||
}
|
||||
@ -342,7 +370,7 @@ class WithdrawalManagementSpreadsheet
|
||||
}
|
||||
|
||||
/** @param array<string,string> $values @return array<string,string|null> */
|
||||
private function changes(StoreReceivingAccount $account, array $values): array
|
||||
private function changes(StoreReceivingAccount $account, array $values, string $balanceDate): array
|
||||
{
|
||||
$fields = [
|
||||
'店铺主体' => ['field' => 'store_entity_name', 'current' => $account->store_entity_name, 'max' => 191, 'clear' => ''],
|
||||
@ -373,6 +401,25 @@ class WithdrawalManagementSpreadsheet
|
||||
}
|
||||
}
|
||||
|
||||
$amount = $values['提现金额'] ?? '';
|
||||
if ($amount !== '' && $amount !== '待接入') {
|
||||
if ($amount === self::CLEAR_VALUE) {
|
||||
if ($account->withdrawal_amount !== null || $account->withdrawal_balance_date !== null) {
|
||||
$changes['withdrawal_amount'] = null;
|
||||
$changes['withdrawal_balance_date'] = null;
|
||||
}
|
||||
} else {
|
||||
if (preg_match('/^\d{1,10}(?:\.\d{1,2})?$/', $amount) !== 1) {
|
||||
throw new \InvalidArgumentException('的提现金额须为非负数,最多两位小数');
|
||||
}
|
||||
$normalized = number_format((float) $amount, 2, '.', '');
|
||||
if ($account->withdrawal_amount !== $normalized || $account->withdrawal_balance_date !== $balanceDate) {
|
||||
$changes['withdrawal_amount'] = $normalized;
|
||||
$changes['withdrawal_balance_date'] = $balanceDate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $changes;
|
||||
}
|
||||
|
||||
|
||||
@ -891,6 +891,8 @@ class Pages extends Controller
|
||||
'withdrawalStoreEntities' => $report['storeEntities'],
|
||||
'withdrawalBalances' => $report['balances'],
|
||||
'withdrawalReserveStandards' => $report['reserveStandards'],
|
||||
'withdrawalAmounts' => $report['withdrawalAmounts'],
|
||||
'withdrawalCompliance' => $report['compliance'],
|
||||
'withdrawalBalanceSummary' => $report['summary'],
|
||||
'withdrawalBalanceColumnLabel' => $report['balanceColumnLabel'],
|
||||
'withdrawalBalanceDate' => $report['balanceDate'],
|
||||
|
||||
@ -23,6 +23,8 @@ class StoreReceivingAccount extends Model
|
||||
'bank_card_last_four' => 'b',
|
||||
'store_id' => 'i',
|
||||
'source_platform' => 'r',
|
||||
'withdrawal_amount' => 'm',
|
||||
'withdrawal_balance_date' => 'd',
|
||||
];
|
||||
|
||||
protected $table = 'business_store_receiving_accounts';
|
||||
@ -35,6 +37,7 @@ class StoreReceivingAccount extends Model
|
||||
'payment_password', 'payment_verification_method', 'financial_account', 'financial_password',
|
||||
'payment_verification_method_secondary', 'withdrawal_path', 'withdrawal_account_name',
|
||||
'bank_card_last_four', 'store_id', 'source_platform',
|
||||
'withdrawal_amount', 'withdrawal_balance_date',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
@ -213,6 +216,26 @@ class StoreReceivingAccount extends Model
|
||||
$this->setMeta('source_platform', $this->clean($value));
|
||||
}
|
||||
|
||||
public function getWithdrawalAmountAttribute(): ?string
|
||||
{
|
||||
return $this->meta('withdrawal_amount');
|
||||
}
|
||||
|
||||
public function setWithdrawalAmountAttribute($value): void
|
||||
{
|
||||
$this->setMeta('withdrawal_amount', $this->clean($value));
|
||||
}
|
||||
|
||||
public function getWithdrawalBalanceDateAttribute(): ?string
|
||||
{
|
||||
return $this->meta('withdrawal_balance_date');
|
||||
}
|
||||
|
||||
public function setWithdrawalBalanceDateAttribute($value): void
|
||||
{
|
||||
$this->setMeta('withdrawal_balance_date', $this->clean($value));
|
||||
}
|
||||
|
||||
private function clean($value): ?string
|
||||
{
|
||||
$value = trim((string) $value);
|
||||
|
||||
@ -168,6 +168,8 @@
|
||||
? '聚水潭店铺:' . $balanceMatch['source_store_name'] . ';余额日期:' . $withdrawalBalanceDate
|
||||
: '余额日期:' . $withdrawalBalanceDate;
|
||||
$reserveStandard = $withdrawalReserveStandards[(int) $row->id] ?? null;
|
||||
$withdrawalAmount = $withdrawalAmounts[(int) $row->id] ?? null;
|
||||
$compliance = $withdrawalCompliance[(int) $row->id] ?? null;
|
||||
@endphp
|
||||
<tr>
|
||||
<td data-withdrawal-column="platform">{{ $row->store_platform ?: '未配置' }}</td>
|
||||
@ -179,8 +181,16 @@
|
||||
<td data-withdrawal-column="bank_card">{{ $bankCard }}</td>
|
||||
<td class="num" data-withdrawal-column="balance" title="{{ $balanceTitle }}">{{ $balanceText }}</td>
|
||||
<td class="num" data-withdrawal-column="reserve_standard">{{ $reserveStandard === null ? '待配置' : number_format($reserveStandard, 2) }}</td>
|
||||
<td class="num" data-withdrawal-column="withdrawal_amount">待接入</td>
|
||||
<td data-withdrawal-column="compliance"><span class="biz-badge biz-badge-no">待核验</span></td>
|
||||
<td class="num" data-withdrawal-column="withdrawal_amount">{{ $withdrawalAmount === null ? '待接入' : number_format($withdrawalAmount, 2) }}</td>
|
||||
<td data-withdrawal-column="compliance">
|
||||
@if ($compliance === 'yes')
|
||||
<span class="biz-badge biz-badge-yes">是</span>
|
||||
@elseif ($compliance === 'no')
|
||||
<span class="biz-badge biz-badge-no">否</span>
|
||||
@else
|
||||
<span class="biz-badge biz-badge-no">待核验</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
|
||||
@ -4,9 +4,13 @@ namespace Tests\Feature\Business;
|
||||
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\Business\StoreReceivingAccount;
|
||||
use App\Business\WithdrawalManagementReport;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Mockery;
|
||||
use Modules\EcommerceFinance\Repositories\JushuitanShopBalanceRepository;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
@ -25,6 +29,8 @@ class WithdrawalManagementImportExportTest extends FeatureTestCase
|
||||
'银行卡号(后四位)',
|
||||
];
|
||||
|
||||
private const IMPORT_HEADERS = [...self::HEADERS, '提现金额'];
|
||||
|
||||
/** @var list<string> */
|
||||
private array $temporaryFiles = [];
|
||||
|
||||
@ -163,7 +169,7 @@ class WithdrawalManagementImportExportTest extends FeatureTestCase
|
||||
|
||||
$spreadsheet = IOFactory::load($path);
|
||||
try {
|
||||
$this->assertSame(self::HEADERS, $spreadsheet->getSheet(0)->rangeToArray('A1:G1')[0]);
|
||||
$this->assertSame(self::IMPORT_HEADERS, $spreadsheet->getSheet(0)->rangeToArray('A1:H1')[0]);
|
||||
$this->assertSame('填写说明', $spreadsheet->getSheet(1)->getTitle());
|
||||
$this->assertStringContainsString('[清空]', (string) $spreadsheet->getSheet(1)->getCell('A4')->getValue());
|
||||
} finally {
|
||||
@ -319,6 +325,132 @@ class WithdrawalManagementImportExportTest extends FeatureTestCase
|
||||
$this->assertSame('他人已修改主体', $store->fresh()->store_entity_name);
|
||||
}
|
||||
|
||||
public function test_compliance_uses_balance_minus_current_date_withdrawal_and_matches_export(): void
|
||||
{
|
||||
Carbon::setTestNow('2026-09-24 09:00:00');
|
||||
|
||||
try {
|
||||
$stores = [];
|
||||
foreach ([
|
||||
['达标测试店', '5000.00', '2026-09-23'],
|
||||
['未达标测试店', '4999.99', '2026-09-23'],
|
||||
['过期金额测试店', '5000.00', '2026-09-22'],
|
||||
['缺少金额测试店', null, null],
|
||||
['缺少余额测试店', '5000.00', '2026-09-23'],
|
||||
] as [$name, $amount, $date]) {
|
||||
$stores[$name] = StoreReceivingAccount::query()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'store_platform' => '京东',
|
||||
'erp_store_name' => $name,
|
||||
'store_entity_name' => '测试主体',
|
||||
'withdrawal_amount' => $amount,
|
||||
'withdrawal_balance_date' => $date,
|
||||
]);
|
||||
}
|
||||
$stores['缺少标准测试店'] = StoreReceivingAccount::query()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'store_platform' => '唯品会',
|
||||
'erp_store_name' => '缺少标准测试店',
|
||||
'store_entity_name' => '测试主体',
|
||||
'withdrawal_amount' => '5000.00',
|
||||
'withdrawal_balance_date' => '2026-09-23',
|
||||
]);
|
||||
|
||||
$balances = [];
|
||||
foreach ($stores as $store) {
|
||||
$balances[(int) $store->id] = [
|
||||
'status' => $store->erp_store_name === '缺少余额测试店' ? 'unmatched' : 'matched',
|
||||
'balance' => $store->erp_store_name === '缺少余额测试店' ? null : 10000.00,
|
||||
'source_store_name' => $store->erp_store_name,
|
||||
'source_platform' => '京东',
|
||||
];
|
||||
}
|
||||
$mirror = Mockery::mock(JushuitanShopBalanceRepository::class);
|
||||
$mirror->shouldReceive('matchesForStores')->andReturn($balances);
|
||||
$mirror->shouldReceive('countForDate')->andReturn(6);
|
||||
$this->app->instance(JushuitanShopBalanceRepository::class, $mirror);
|
||||
|
||||
$report = $this->app->make(WithdrawalManagementReport::class)->results(request());
|
||||
$this->assertSame('yes', $report['compliance'][(int) $stores['达标测试店']->id]);
|
||||
$this->assertSame('no', $report['compliance'][(int) $stores['未达标测试店']->id]);
|
||||
$this->assertNull($report['compliance'][(int) $stores['过期金额测试店']->id]);
|
||||
$this->assertNull($report['compliance'][(int) $stores['缺少金额测试店']->id]);
|
||||
$this->assertNull($report['compliance'][(int) $stores['缺少余额测试店']->id]);
|
||||
$this->assertNull($report['compliance'][(int) $stores['缺少标准测试店']->id]);
|
||||
|
||||
$page = $this->loginAs()->get(route('business.pages.show', [
|
||||
'section' => 'fund-management',
|
||||
'page' => 'withdrawal-management',
|
||||
'platform' => '京东',
|
||||
]));
|
||||
$page->assertOk()
|
||||
->assertSeeText('待核验');
|
||||
$this->assertMatchesRegularExpression('/data-withdrawal-column="compliance">\s*<span class="biz-badge biz-badge-yes">是<\/span>/', $page->getContent());
|
||||
$this->assertMatchesRegularExpression('/data-withdrawal-column="compliance">\s*<span class="biz-badge biz-badge-no">否<\/span>/', $page->getContent());
|
||||
|
||||
$response = $this->get(route('business.withdrawal-management.export', ['platform' => '京东']));
|
||||
$path = $this->temporaryPath('withdrawal-compliance-export-');
|
||||
file_put_contents($path, $response->streamedContent());
|
||||
$spreadsheet = IOFactory::load($path);
|
||||
try {
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$values = [];
|
||||
for ($row = 2; $row <= $sheet->getHighestDataRow(); $row++) {
|
||||
$values[(string) $sheet->getCell("B{$row}")->getValue()] = [
|
||||
$sheet->getCell("J{$row}")->getValue(),
|
||||
$sheet->getCell("K{$row}")->getValue(),
|
||||
];
|
||||
}
|
||||
$this->assertSame([5000.0, '是'], $values['达标测试店']);
|
||||
$this->assertSame([4999.99, '否'], $values['未达标测试店']);
|
||||
$this->assertSame(['待接入', '待核验'], $values['过期金额测试店']);
|
||||
$this->assertSame(['待接入', '待核验'], $values['缺少金额测试店']);
|
||||
$this->assertSame([5000.0, '待核验'], $values['缺少余额测试店']);
|
||||
} finally {
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
}
|
||||
} finally {
|
||||
Carbon::setTestNow();
|
||||
}
|
||||
}
|
||||
|
||||
public function test_withdrawal_amount_import_is_validated_and_bound_to_current_balance_date(): void
|
||||
{
|
||||
Carbon::setTestNow('2026-09-24 09:00:00');
|
||||
|
||||
try {
|
||||
$store = StoreReceivingAccount::query()->create([
|
||||
'company_id' => $this->company->id,
|
||||
'store_platform' => '京东',
|
||||
'erp_store_name' => '金额导入测试店',
|
||||
'store_entity_name' => '测试主体',
|
||||
]);
|
||||
$this->loginAs();
|
||||
$this->withExceptionHandling()
|
||||
->post(route('business.withdrawal-management.import.preview'), [
|
||||
'import' => $this->workbook([['京东', '金额导入测试店', '', '', '', '', '', '-1']]),
|
||||
])
|
||||
->assertSessionHasErrors('import');
|
||||
$this->assertNull($store->fresh()->withdrawal_amount);
|
||||
|
||||
$previewResponse = $this->post(route('business.withdrawal-management.import.preview'), [
|
||||
'import' => $this->workbook([['京东', '金额导入测试店', '', '', '', '', '', '5000.00']]),
|
||||
]);
|
||||
$previewResponse->assertSessionHas('withdrawalImportPreview', function (array $preview): bool {
|
||||
return $preview['preview'][0]['changes'] === '提现金额';
|
||||
});
|
||||
$preview = $previewResponse->getSession()->get('withdrawalImportPreview');
|
||||
$this->post(route('business.withdrawal-management.import.commit'), [
|
||||
'preview_token' => $preview['token'],
|
||||
])->assertSessionHasNoErrors();
|
||||
|
||||
$this->assertSame('5000.00', $store->fresh()->withdrawal_amount);
|
||||
$this->assertSame('2026-09-23', $store->fresh()->withdrawal_balance_date);
|
||||
} finally {
|
||||
Carbon::setTestNow();
|
||||
}
|
||||
}
|
||||
|
||||
/** @param list<list<mixed>> $rows */
|
||||
private function workbook(array $rows): UploadedFile
|
||||
{
|
||||
@ -326,7 +458,7 @@ class WithdrawalManagementImportExportTest extends FeatureTestCase
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->setTitle('提现管理');
|
||||
$sheet->fromArray(self::HEADERS, null, 'A1');
|
||||
$sheet->fromArray(self::IMPORT_HEADERS, null, 'A1');
|
||||
if ($rows !== []) {
|
||||
$sheet->fromArray($rows, null, 'A2');
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user