155 lines
6.0 KiB
PHP
155 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Portal;
|
|
|
|
use App\Models\Common\Contact;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Routing\Controller;
|
|
use Illuminate\View\View;
|
|
use Modules\EcommerceFinance\Services\SupplierReturnCsvExporter;
|
|
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;
|
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
|
|
|
/**
|
|
* 厂家门户:账号绑定供应商后,仅可查看和操作自己的对账数据。
|
|
*/
|
|
class SupplierReconciliation extends Controller
|
|
{
|
|
public function __construct(
|
|
private readonly SupplierStatementTemplate $template,
|
|
private readonly SupplierStatementStore $statements,
|
|
private readonly SupplierReturnConfirmation $returnConfirmations,
|
|
private readonly SupplierReconciliationPageData $pageData,
|
|
private readonly SupplierReturnCsvExporter $returnCsvExporter,
|
|
) {
|
|
}
|
|
|
|
public function index(): View
|
|
{
|
|
[$contact, $supplier, $companyId] = $this->supplierContext();
|
|
$data = $this->pageData->portalData($supplier, $companyId);
|
|
|
|
return view('portal.supplier-reconciliation.index', array_merge($data, [
|
|
'contact' => $contact,
|
|
'supplier' => $supplier,
|
|
'statementTemplateUrl' => route('portal.supplier-reconciliation.template'),
|
|
'uploadUrl' => route('portal.supplier-reconciliation.statement.upload'),
|
|
'returnConfirmUrl' => route('portal.supplier-reconciliation.return.confirm'),
|
|
'returnDisputeUrl' => route('portal.supplier-reconciliation.return.dispute'),
|
|
'returnConfirmAllUrl' => route('portal.supplier-reconciliation.return.confirm-all'),
|
|
]));
|
|
}
|
|
|
|
public function template(): BinaryFileResponse
|
|
{
|
|
$this->supplierContext();
|
|
$path = $this->template->path();
|
|
abort_unless(is_file($path), 404, '对账单模板不存在。');
|
|
|
|
return response()->download($path, $this->template->filename());
|
|
}
|
|
|
|
public function upload(Request $request): JsonResponse|RedirectResponse
|
|
{
|
|
$request->validate([
|
|
'import' => ['required', 'file', 'mimes:xlsx,xls,csv', 'max:51200'],
|
|
]);
|
|
[, $supplier, $companyId] = $this->supplierContext();
|
|
|
|
$records = $this->template->parse($request->file('import'), $supplier);
|
|
$rows = $this->statements->save(
|
|
$supplier,
|
|
$records,
|
|
(string) $request->file('import')->getClientOriginalName(),
|
|
$companyId,
|
|
user_id() ? (int) user_id() : null,
|
|
);
|
|
$message = "对账单已保存:{$supplier} 共 {$rows} 行。";
|
|
|
|
return $request->expectsJson()
|
|
? response()->json(['message' => $message, 'rows' => $rows])
|
|
: redirect()
|
|
->route('portal.supplier-reconciliation.index', ['company_id' => $companyId])
|
|
->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->supplierContext();
|
|
$keys = array_values(array_map(
|
|
static fn (array $row): string => (string) ($row['_action_key'] ?? ''),
|
|
$this->pageData->returnRowsForFilters($supplier, $companyId, $request->all()),
|
|
));
|
|
$confirmed = $this->returnConfirmations->confirmAll(
|
|
$supplier,
|
|
$keys,
|
|
$companyId,
|
|
user_id() ? (int) user_id() : null,
|
|
);
|
|
|
|
return back()->with('success', "已全部确认:{$confirmed} 行退货扣减生效(异议行保持暂不扣减)。");
|
|
}
|
|
|
|
public function exportReturns(Request $request): StreamedResponse
|
|
{
|
|
[, $supplier, $companyId] = $this->supplierContext();
|
|
$rows = $this->pageData->returnRowsForFilters($supplier, $companyId, $request->query());
|
|
|
|
return $this->returnCsvExporter->download($rows, $supplier);
|
|
}
|
|
|
|
private function markReturn(Request $request, string $status, string $message): RedirectResponse
|
|
{
|
|
$request->validate(['key' => ['required', 'string', 'max:191']]);
|
|
[, $supplier, $companyId] = $this->supplierContext();
|
|
$key = (string) $request->input('key');
|
|
$owned = collect($this->pageData->returnRows($supplier, $companyId))
|
|
->contains(static fn (array $row): bool => (string) ($row['_action_key'] ?? '') === $key);
|
|
abort_unless($owned, 403, '该退货明细不属于当前厂家账号。');
|
|
|
|
$this->returnConfirmations->mark(
|
|
$supplier,
|
|
$key,
|
|
$status,
|
|
$companyId,
|
|
user_id() ? (int) user_id() : null,
|
|
);
|
|
|
|
return back()->with('success', $message);
|
|
}
|
|
|
|
/** @return array{0:Contact,1:string,2:int} */
|
|
private function supplierContext(): array
|
|
{
|
|
$contact = user()->contact;
|
|
abort_unless(
|
|
$contact instanceof Contact
|
|
&& $contact->type === Contact::VENDOR_TYPE
|
|
&& $contact->enabled,
|
|
403,
|
|
'当前账号未绑定有效的厂家资料,请联系财务人员。',
|
|
);
|
|
|
|
$supplier = trim((string) $contact->name);
|
|
abort_if($supplier === '', 403, '当前厂家账号未设置供应商名称,请联系财务人员。');
|
|
|
|
return [$contact, $supplier, (int) (company_id() ?: 1)];
|
|
}
|
|
}
|