供应商对账:退货明细改为「退货扣减确认」面板 (#369)
退货扣减确认:顶部汇总、逐行确认/异议、全部确认(不覆盖异议)、金额空按预估金额扣减,展示金额最高 15 行。
This commit is contained in:
parent
8aa7676853
commit
2848c81a5e
@ -6,8 +6,10 @@ use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Modules\EcommerceFinance\Services\SupplierReturnConfirmation;
|
||||
use Modules\EcommerceFinance\Services\SupplierStatementStore;
|
||||
use Modules\EcommerceFinance\Services\SupplierStatementTemplate;
|
||||
use Modules\EcommerceFinance\View\SupplierReconciliationPageData;
|
||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||
|
||||
/**
|
||||
@ -18,6 +20,8 @@ class SupplierReconciliation extends Controller
|
||||
public function __construct(
|
||||
private readonly SupplierStatementTemplate $template,
|
||||
private readonly SupplierStatementStore $statements,
|
||||
private readonly SupplierReturnConfirmation $returnConfirmations,
|
||||
private readonly SupplierReconciliationPageData $pageData,
|
||||
) {
|
||||
$this->middleware('auth');
|
||||
$this->middleware('company.identify');
|
||||
@ -58,4 +62,54 @@ class SupplierReconciliation extends Controller
|
||||
? response()->json(['message' => $message, 'rows' => $rows])
|
||||
: back()->with('success', $message);
|
||||
}
|
||||
|
||||
public function confirmReturn(Request $request): RedirectResponse
|
||||
{
|
||||
return $this->markReturn($request, SupplierReturnConfirmation::STATUS_CONFIRMED, '已确认该行退货扣减。');
|
||||
}
|
||||
|
||||
public function disputeReturn(Request $request): RedirectResponse
|
||||
{
|
||||
return $this->markReturn($request, SupplierReturnConfirmation::STATUS_DISPUTED, '已提交异议,复核通过前该行暂不扣减。');
|
||||
}
|
||||
|
||||
public function confirmAllReturns(Request $request): RedirectResponse
|
||||
{
|
||||
[$supplier, $companyId] = $this->returnTarget($request);
|
||||
$keys = array_values(array_map(
|
||||
static fn (array $row): string => (string) ($row['_action_key'] ?? ''),
|
||||
$this->pageData->returnRows($supplier, $companyId),
|
||||
));
|
||||
$confirmed = $this->returnConfirmations->confirmAll(
|
||||
$supplier,
|
||||
$keys,
|
||||
$companyId,
|
||||
user_id() ? (int) user_id() : null,
|
||||
);
|
||||
|
||||
return back()->with('success', "已全部确认:{$confirmed} 行退货扣减生效(异议行保持暂不扣减)。");
|
||||
}
|
||||
|
||||
private function markReturn(Request $request, string $status, string $message): RedirectResponse
|
||||
{
|
||||
$request->validate(['key' => ['required', 'string', 'max:191']]);
|
||||
[$supplier, $companyId] = $this->returnTarget($request);
|
||||
$this->returnConfirmations->mark(
|
||||
$supplier,
|
||||
(string) $request->input('key'),
|
||||
$status,
|
||||
$companyId,
|
||||
user_id() ? (int) user_id() : null,
|
||||
);
|
||||
|
||||
return back()->with('success', $message);
|
||||
}
|
||||
|
||||
/** @return array{0:string,1:int} */
|
||||
private function returnTarget(Request $request): array
|
||||
{
|
||||
$request->validate(['supplier' => ['required', 'string', 'max:191']]);
|
||||
|
||||
return [trim((string) $request->input('supplier')), (int) (company_id() ?: 1)];
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\EcommerceFinance\Services;
|
||||
|
||||
/**
|
||||
* 退货扣减确认:厂家逐行「确认 / 异议」,或一键全部确认。
|
||||
* 状态存在按表数据表里(数据集 return-confirmation:<厂家>,记录键沿用退货行的 _action_key)。
|
||||
*/
|
||||
class SupplierReturnConfirmation
|
||||
{
|
||||
public const PREFIX = 'return-confirmation:';
|
||||
|
||||
public const STATUS_CONFIRMED = 'confirmed';
|
||||
|
||||
public const STATUS_DISPUTED = 'disputed';
|
||||
|
||||
public function __construct(
|
||||
private readonly SupplierPayableOverrideStore $store,
|
||||
) {
|
||||
}
|
||||
|
||||
/** @return array<string,string> 记录键 => 状态 */
|
||||
public function statuses(string $supplier, int $companyId): array
|
||||
{
|
||||
$statuses = [];
|
||||
foreach ($this->store->listRows(self::PREFIX . $supplier, $companyId) as $row) {
|
||||
$key = (string) ($row['_action_key'] ?? '');
|
||||
$status = (string) ($row['status'] ?? '');
|
||||
if ($key !== '' && in_array($status, [self::STATUS_CONFIRMED, self::STATUS_DISPUTED], true)) {
|
||||
$statuses[$key] = $status;
|
||||
}
|
||||
}
|
||||
|
||||
return $statuses;
|
||||
}
|
||||
|
||||
public function mark(string $supplier, string $recordKey, string $status, int $companyId, ?int $userId): void
|
||||
{
|
||||
if (! in_array($status, [self::STATUS_CONFIRMED, self::STATUS_DISPUTED], true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->store->save(self::PREFIX . $supplier, $recordKey, [
|
||||
'status' => $status,
|
||||
'marked_at' => now()->toDateTimeString(),
|
||||
], $companyId, $userId, 'manual');
|
||||
}
|
||||
|
||||
/** @param list<string> $recordKeys 一键确认全部(已异议的行保持异议,不覆盖) */
|
||||
public function confirmAll(string $supplier, array $recordKeys, int $companyId, ?int $userId): int
|
||||
{
|
||||
$statuses = $this->statuses($supplier, $companyId);
|
||||
|
||||
$confirmed = 0;
|
||||
$this->store->transaction(function () use ($supplier, $recordKeys, $statuses, $companyId, $userId, &$confirmed): void {
|
||||
foreach ($recordKeys as $key) {
|
||||
if ($key === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$status = $statuses[$key] ?? '';
|
||||
if ($status === self::STATUS_DISPUTED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($status !== self::STATUS_CONFIRMED) {
|
||||
$this->mark($supplier, $key, self::STATUS_CONFIRMED, $companyId, $userId);
|
||||
}
|
||||
$confirmed++;
|
||||
}
|
||||
});
|
||||
|
||||
return $confirmed;
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ namespace Modules\EcommerceFinance\View;
|
||||
|
||||
use App\Http\Controllers\Business\PayablesFinalActions;
|
||||
use Modules\EcommerceFinance\Services\SupplierPayableOverrideStore;
|
||||
use Modules\EcommerceFinance\Services\SupplierReturnConfirmation;
|
||||
use Modules\EcommerceFinance\Services\SupplierStatementMatcher;
|
||||
use Modules\EcommerceFinance\Services\SupplierStatementStore;
|
||||
|
||||
@ -20,6 +21,7 @@ class SupplierReconciliationPageData
|
||||
private readonly SupplierPayableOverrideStore $store,
|
||||
private readonly SupplierStatementStore $statements,
|
||||
private readonly SupplierStatementMatcher $matcher,
|
||||
private readonly SupplierReturnConfirmation $returnConfirmations,
|
||||
) {
|
||||
}
|
||||
|
||||
@ -149,6 +151,7 @@ class SupplierReconciliationPageData
|
||||
$page = max((int) request()->query('rows_page', 1), 1);
|
||||
$statementRows = $selected !== '' ? $this->statements->rows($selected, $companyId) : [];
|
||||
$returnRows = $selected !== '' ? $this->returnRows($selected, $companyId) : [];
|
||||
$returnPanel = $selected !== '' ? $this->returnConfirmationPanel($selected, $returnRows, $companyId) : null;
|
||||
|
||||
return [
|
||||
'view' => 'business.pages.supplier-reconciliation.supplier-data',
|
||||
@ -157,15 +160,77 @@ class SupplierReconciliationPageData
|
||||
'uploads' => $selected !== '' ? $this->statements->uploads($selected, $companyId) : [],
|
||||
'statementRows' => array_slice($statementRows, ($page - 1) * self::PER_PAGE, self::PER_PAGE),
|
||||
'statementTotal' => count($statementRows),
|
||||
'returnRows' => array_slice($returnRows, ($page - 1) * self::PER_PAGE, self::PER_PAGE),
|
||||
'returnTotal' => count($returnRows),
|
||||
'returnPanel' => $returnPanel,
|
||||
'rowsPage' => $page,
|
||||
'perPage' => self::PER_PAGE,
|
||||
'statementTemplateUrl' => route('business.supplier-reconciliation.template'),
|
||||
'uploadUrl' => route('business.supplier-reconciliation.statement.upload'),
|
||||
'returnConfirmUrl' => route('business.supplier-reconciliation.return.confirm'),
|
||||
'returnDisputeUrl' => route('business.supplier-reconciliation.return.dispute'),
|
||||
'returnConfirmAllUrl' => route('business.supplier-reconciliation.return.confirm-all'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 退货扣减确认面板:共 N 行、合计扣减、异议暂不扣减;表格展示金额最高的 15 行。
|
||||
*
|
||||
* @param list<array<string,mixed>> $returnRows
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function returnConfirmationPanel(string $supplier, array $returnRows, int $companyId): array
|
||||
{
|
||||
$statuses = $this->returnConfirmations->statuses($supplier, $companyId);
|
||||
$total = 0.0;
|
||||
$confirmedAmount = 0.0;
|
||||
$disputedAmount = 0.0;
|
||||
$disputedCount = 0;
|
||||
$confirmedCount = 0;
|
||||
|
||||
$rows = [];
|
||||
foreach ($returnRows as $row) {
|
||||
$key = (string) ($row['_action_key'] ?? '');
|
||||
$deduction = $this->returnDeduction($row);
|
||||
$status = $statuses[$key] ?? '';
|
||||
|
||||
$total += $deduction;
|
||||
if ($status === SupplierReturnConfirmation::STATUS_CONFIRMED) {
|
||||
$confirmedAmount += $deduction;
|
||||
$confirmedCount++;
|
||||
} elseif ($status === SupplierReturnConfirmation::STATUS_DISPUTED) {
|
||||
$disputedAmount += $deduction;
|
||||
$disputedCount++;
|
||||
}
|
||||
|
||||
$rows[] = ['_key' => $key, 'status' => $status, 'deduction' => $deduction] + $row;
|
||||
}
|
||||
|
||||
usort($rows, static fn (array $a, array $b): int => $b['deduction'] <=> $a['deduction']);
|
||||
|
||||
return [
|
||||
'supplier' => $supplier,
|
||||
'rows' => array_slice($rows, 0, 15),
|
||||
'total_rows' => count($returnRows),
|
||||
'total_amount' => round($total, 2),
|
||||
'confirmed_count' => $confirmedCount,
|
||||
'confirmed_amount' => round($confirmedAmount, 2),
|
||||
'disputed_count' => $disputedCount,
|
||||
'disputed_amount' => round($disputedAmount, 2),
|
||||
];
|
||||
}
|
||||
|
||||
/** 扣减金额:金额为空时用预估金额(与采购方记账口径一致)。 */
|
||||
private function returnDeduction(array $row): float
|
||||
{
|
||||
$amount = trim((string) ($row['amount'] ?? ''));
|
||||
if ($amount !== '' && is_numeric(str_replace(',', '', $amount))) {
|
||||
return (float) str_replace(',', '', $amount);
|
||||
}
|
||||
|
||||
$guess = trim((string) ($row['guess_amount'] ?? ''));
|
||||
|
||||
return is_numeric(str_replace(',', '', $guess)) ? (float) str_replace(',', '', $guess) : 0.0;
|
||||
}
|
||||
|
||||
/** @return array<string,mixed> */
|
||||
private function detail(array $mine, string $supplier, int $companyId): array
|
||||
{
|
||||
@ -182,8 +247,8 @@ class SupplierReconciliationPageData
|
||||
return $compared;
|
||||
}
|
||||
|
||||
/** @return list<array<string,mixed>> 该厂家的退货明细(我方数据,只读展示给厂家) */
|
||||
private function returnRows(string $supplier, int $companyId): array
|
||||
/** @return list<array<string,mixed>> 该厂家的退货明细(我方数据,展示给厂家确认扣减) */
|
||||
public function returnRows(string $supplier, int $companyId): array
|
||||
{
|
||||
$rows = $this->store->listRows(
|
||||
PayablesFinalActions::DATASET_PREFIX . 'supplier:return-reconciliation',
|
||||
|
||||
@ -19,6 +19,26 @@
|
||||
.supplier-reconciliation-page .sr-empty { padding: 28px; border: 1px dashed var(--vip-border, #D8D2C7); border-radius: 8px; color: var(--vip-content-muted, #595959); font-size: .8125rem; text-align: center; }
|
||||
.supplier-reconciliation-page .sr-upload { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
|
||||
.supplier-reconciliation-page .sr-pagination { display: flex; align-items: center; gap: 12px; color: var(--vip-content-muted, #595959); font-size: .8125rem; }
|
||||
.supplier-reconciliation-page .sr-button.is-small { height: 24px; padding: 0 10px; font-size: .75rem; }
|
||||
.supplier-reconciliation-page .sr-return-banner {
|
||||
display: flex; align-items: flex-start; gap: 8px;
|
||||
padding: 10px 12px; border: 1px solid #E4DCCB; border-radius: 8px;
|
||||
background: var(--vip-content-accent-soft, #F7F0DE);
|
||||
color: #5B5343; font-size: .8125rem; line-height: 1.7;
|
||||
}
|
||||
.supplier-reconciliation-page .sr-return-banner .material-icons-outlined { color: var(--vip-content-accent-dark, #856632); font-size: 18px; }
|
||||
.supplier-reconciliation-page .sr-return-status { display: flex; flex-wrap: wrap; gap: 4px 10px; color: var(--vip-content-muted, #595959); font-size: .75rem; }
|
||||
.supplier-reconciliation-page .sr-return-table { min-width: 1400px; }
|
||||
.supplier-reconciliation-page .sr-type-tag {
|
||||
display: inline-flex; align-items: center; height: 22px; padding: 0 8px;
|
||||
border-radius: 4px; background: #EFEAE0; color: #595959; font-size: .75rem; white-space: nowrap;
|
||||
}
|
||||
.supplier-reconciliation-page .sr-tag { display: inline-flex; align-items: center; height: 22px; padding: 0 8px; border-radius: 4px; font-size: .75rem; white-space: nowrap; }
|
||||
.supplier-reconciliation-page .sr-tag.is-match { background: #ECF4EC; color: #3F7A46; }
|
||||
.supplier-reconciliation-page .sr-tag.is-mismatch { background: #FBEBE9; color: #A6392F; }
|
||||
.supplier-reconciliation-page .sr-inline-form { display: inline-flex; }
|
||||
.supplier-reconciliation-page .sr-return-actions { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
|
||||
.supplier-reconciliation-page .sr-return-panel > .sr-return-actions { gap: 12px; }
|
||||
</style>
|
||||
|
||||
@if (is_string(session('success')))
|
||||
@ -124,37 +144,89 @@
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="sr-card">
|
||||
<h2>{{ $selectedSupplier }} · 退货明细(我方数据,只读,共 {{ number_format($returnTotal) }} 行)</h2>
|
||||
<div class="sr-card sr-return-panel">
|
||||
<div class="sr-return-banner" role="note">
|
||||
<span class="material-icons-outlined" aria-hidden="true">info</span>
|
||||
<span>
|
||||
<strong>① 退货扣减确认</strong>(共 {{ number_format($returnPanel['total_rows']) }} 行,合计扣减 ¥{{ number_format($returnPanel['total_amount'], 2) }})。
|
||||
核对无误请点「全部确认无异议」;个别有问题的行,在该行单独点「异议」,复核通过前该行暂不扣减。
|
||||
</span>
|
||||
</div>
|
||||
<div class="sr-return-status">
|
||||
<span>已确认 {{ $returnPanel['confirmed_count'] }} 行(¥{{ number_format($returnPanel['confirmed_amount'], 2) }})</span>
|
||||
<span>· 异议 {{ $returnPanel['disputed_count'] }} 行(暂不扣减 ¥{{ number_format($returnPanel['disputed_amount'], 2) }})</span>
|
||||
<span>· 待确认 {{ max($returnPanel['total_rows'] - $returnPanel['confirmed_count'] - $returnPanel['disputed_count'], 0) }} 行</span>
|
||||
</div>
|
||||
<div class="sr-scroll">
|
||||
<table>
|
||||
<table class="sr-return-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">月份</th><th scope="col">发货时间</th><th scope="col">明细供应商</th><th scope="col">条形码</th>
|
||||
<th scope="col">商品名称</th><th scope="col">发货量</th><th scope="col">单价</th><th scope="col">金额</th>
|
||||
<th scope="col">仓库名称</th><th scope="col">快递单号</th>
|
||||
<th scope="col">发货量</th><th scope="col">单价</th><th scope="col">金额</th>
|
||||
<th scope="col">预估单价</th><th scope="col">预估金额</th><th scope="col">供应商条码</th>
|
||||
<th scope="col">备注</th><th scope="col">标题</th><th scope="col">wms配货单号</th>
|
||||
<th scope="col">仓库名称</th><th scope="col">快递单号</th><th scope="col">发货时间</th>
|
||||
<th scope="col">原条码</th><th scope="col">类型</th><th scope="col">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse ($returnRows as $row)
|
||||
@forelse ($returnPanel['rows'] as $row)
|
||||
<tr>
|
||||
<td>{{ $row['summary_month'] ?? '—' }}</td>
|
||||
<td>{{ $row['send_time'] ?? '—' }}</td>
|
||||
<td>{{ $row['supplier'] ?? '—' }}</td>
|
||||
<td>{{ $row['barcode'] ?? '—' }}</td>
|
||||
<td>{{ $row['sku_name'] ?? '—' }}</td>
|
||||
<td class="is-num">{{ $row['qty'] ?? '—' }}</td>
|
||||
<td class="is-num">{{ $row['unit_price'] ?? '—' }}</td>
|
||||
<td class="is-num">{{ $row['amount'] ?? '—' }}</td>
|
||||
<td class="is-num">{{ $row['guess_unit_price'] ?? '—' }}</td>
|
||||
<td class="is-num">{{ $row['guess_amount'] ?? '—' }}</td>
|
||||
<td>{{ $row['supplier_barcode'] ?? '—' }}</td>
|
||||
<td>{{ $row['remark'] ?? '—' }}</td>
|
||||
<td>{{ $row['title'] ?? '—' }}</td>
|
||||
<td>{{ $row['wms_pick_no'] ?? '—' }}</td>
|
||||
<td>{{ $row['warehouse'] ?? '—' }}</td>
|
||||
<td>{{ $row['express_no'] ?? '—' }}</td>
|
||||
<td>{{ $row['send_time'] ?? '—' }}</td>
|
||||
<td>{{ $row['barcode'] ?? '—' }}</td>
|
||||
<td><span class="sr-type-tag">退货</span></td>
|
||||
<td>
|
||||
<span class="sr-return-actions">
|
||||
@if ($row['status'] === 'confirmed')
|
||||
<span class="sr-tag is-match">已确认</span>
|
||||
@elseif ($row['status'] === 'disputed')
|
||||
<span class="sr-tag is-mismatch">异议中</span>
|
||||
@else
|
||||
<form method="POST" action="{{ $returnConfirmUrl }}" class="sr-inline-form">
|
||||
@csrf
|
||||
<input type="hidden" name="supplier" value="{{ $selectedSupplier }}">
|
||||
<input type="hidden" name="key" value="{{ $row['_key'] }}">
|
||||
<button type="submit" class="sr-button is-small is-primary">确认</button>
|
||||
</form>
|
||||
<form method="POST" action="{{ $returnDisputeUrl }}" class="sr-inline-form">
|
||||
@csrf
|
||||
<input type="hidden" name="supplier" value="{{ $selectedSupplier }}">
|
||||
<input type="hidden" name="key" value="{{ $row['_key'] }}">
|
||||
<button type="submit" class="sr-button is-small">异议</button>
|
||||
</form>
|
||||
@endif
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="10"><div class="sr-empty">该厂家暂无退货数据。</div></td></tr>
|
||||
<tr><td colspan="15"><div class="sr-empty">该厂家暂无退货数据。</div></td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p class="sr-hint">
|
||||
列名取自《退货明细》原始表头(标记多标签、创建人、对账人、退货单备注等内部字段不展示),左右滑动查看全部。
|
||||
退货共 {{ number_format($returnPanel['total_rows']) }} 行,上表展示金额最高的 {{ count($returnPanel['rows']) }} 行。
|
||||
金额列为空的行以预估金额参与扣减,与采购方记账口径一致。
|
||||
</p>
|
||||
<div class="sr-return-actions">
|
||||
<form method="POST" action="{{ $returnConfirmAllUrl }}" class="sr-inline-form">
|
||||
@csrf
|
||||
<input type="hidden" name="supplier" value="{{ $selectedSupplier }}">
|
||||
<button type="submit" class="sr-button is-primary">全部确认无异议</button>
|
||||
</form>
|
||||
<span class="sr-hint">确认后退货扣减生效,明细列入结算确认函。</span>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@ -395,6 +395,19 @@ Route::post('workspace/supplier-reconciliation/statement', [SupplierReconciliati
|
||||
->middleware(['business.page:supplier-reconciliation,supplier-data', 'import'])
|
||||
->name('business.supplier-reconciliation.statement.upload');
|
||||
|
||||
// 退货扣减确认:逐行确认/异议 + 一键全部确认
|
||||
Route::post('workspace/supplier-reconciliation/returns/confirm', [SupplierReconciliationActions::class, 'confirmReturn'])
|
||||
->middleware('business.page:supplier-reconciliation,supplier-data')
|
||||
->name('business.supplier-reconciliation.return.confirm');
|
||||
|
||||
Route::post('workspace/supplier-reconciliation/returns/dispute', [SupplierReconciliationActions::class, 'disputeReturn'])
|
||||
->middleware('business.page:supplier-reconciliation,supplier-data')
|
||||
->name('business.supplier-reconciliation.return.dispute');
|
||||
|
||||
Route::post('workspace/supplier-reconciliation/returns/confirm-all', [SupplierReconciliationActions::class, 'confirmAllReturns'])
|
||||
->middleware('business.page:supplier-reconciliation,supplier-data')
|
||||
->name('business.supplier-reconciliation.return.confirm-all');
|
||||
|
||||
Route::get('workspace/refunds/non-jingxi-refund-data/export', 'Business\Pages@exportNonJingxiRefunds')
|
||||
->middleware('business.page:refunds,non-jingxi-refund-data')
|
||||
->name('business.refunds.non-jingxi-refund-data.export');
|
||||
|
||||
@ -127,8 +127,57 @@ class SupplierReconciliationTest extends FeatureTestCase
|
||||
->assertOk()
|
||||
->assertSeeText('厂家对账单 · 上传与实时数据')
|
||||
->assertSeeText('上传记录')
|
||||
->assertSeeText('退货明细')
|
||||
->assertSeeText('款式十一');
|
||||
->assertSeeText('退货扣减确认')
|
||||
->assertSeeText('合计扣减')
|
||||
->assertSeeText('全部确认无异议')
|
||||
->assertSeeText('待确认')
|
||||
->assertSeeText('ZSJ-1');
|
||||
}
|
||||
|
||||
public function test_return_rows_can_be_confirmed_disputed_and_confirmed_all(): void
|
||||
{
|
||||
$store = app(SupplierPayableOverrideStore::class);
|
||||
$companyId = (int) $this->company->id;
|
||||
foreach ([
|
||||
'return:1' => ['barcode' => 'CJ019316N1097V01', 'qty' => '7', 'unit_price' => '196.50', 'amount' => '1375.50'],
|
||||
'return:2' => ['barcode' => 'DNZ016720N1097V01', 'qty' => '7', 'unit_price' => '182.50', 'amount' => '1277.50'],
|
||||
] as $key => $row) {
|
||||
$store->save('final:supplier:return-reconciliation', $key, $row + [
|
||||
'summary_month' => '2026-09', 'send_time' => '2026-09-13 09:10:24', 'supplier' => '晨丰',
|
||||
'warehouse' => '客退临时上架仓', 'title' => '', 'remark' => '',
|
||||
], $companyId, null);
|
||||
}
|
||||
$this->seedStatement('晨丰', [$this->statementRow('ZSJ-1', '128.00', '2', '256.00')]);
|
||||
|
||||
$page = fn () => $this->loginAs()->get(route('business.pages.show', [
|
||||
'section' => 'supplier-reconciliation',
|
||||
'page' => 'supplier-data',
|
||||
'supplier' => '晨丰',
|
||||
]))->assertOk();
|
||||
|
||||
$page()->assertSeeText('合计扣减 ¥2,653.00');
|
||||
|
||||
$this->loginAs()
|
||||
->post(route('business.supplier-reconciliation.return.confirm'), ['supplier' => '晨丰', 'key' => 'return:1'])
|
||||
->assertStatus(302)
|
||||
->assertSessionHas('success', '已确认该行退货扣减。');
|
||||
|
||||
$page()->assertSeeText('已确认')->assertSeeText('¥1,375.50');
|
||||
|
||||
$this->loginAs()
|
||||
->post(route('business.supplier-reconciliation.return.dispute'), ['supplier' => '晨丰', 'key' => 'return:2'])
|
||||
->assertStatus(302)
|
||||
->assertSessionHas('success', '已提交异议,复核通过前该行暂不扣减。');
|
||||
|
||||
$this->loginAs()
|
||||
->post(route('business.supplier-reconciliation.return.confirm-all'), ['supplier' => '晨丰'])
|
||||
->assertStatus(302)
|
||||
->assertSessionHas('success', '已全部确认:1 行退货扣减生效(异议行保持暂不扣减)。');
|
||||
|
||||
$statuses = app(\Modules\EcommerceFinance\Services\SupplierReturnConfirmation::class)
|
||||
->statuses('晨丰', $companyId);
|
||||
$this->assertSame('confirmed', $statuses['return:1']);
|
||||
$this->assertSame('disputed', $statuses['return:2'], '一键确认不应覆盖已提交的异议');
|
||||
}
|
||||
|
||||
public function test_statement_template_downloads_the_bundled_file(): void
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user