'全部', Auditor::DECISION_PASS => '可报销', Auditor::DECISION_CANCEL => '取消工单', Auditor::DECISION_RECHECK => '待复查']; public const COLUMNS = [ 'decision_label' => '处理结果', 'issue_text' => '原因', 'work_order_no' => '工单编号', 'status' => '工单状态', 'paid_at' => '付款时间', 'initiator' => '发起人', 'shop_name' => '店铺', 'profit_center' => '所属利润中心', 'online_order_no' => '线上订单号', 'task_payment' => '任务付款', 'task_freight' => '快递费', 'task_commission' => '佣金', 'jingxi_price_diff' => '京喜差价', 'task_deduction' => '扣款', 'task_empty_package_fee' => '空包费', 'reimbursement_amount' => '最终报销金额', 'actual_paid_amount' => '买家实付', 'payee_name' => '收款人', 'payee_account' => '账号', 'payee_bank' => '开户行', 'erp_order_label' => 'ERP订单标签', 'task_remark' => '任务备注', ]; public const MONEY_COLUMNS = [ 'task_payment', 'task_freight', 'task_commission', 'jingxi_price_diff', 'task_deduction', 'task_empty_package_fee', 'reimbursement_amount', 'actual_paid_amount', ]; public function __construct( private readonly Workbench $workbench, private readonly Repository $repository, private readonly References $references, ) {} /** @return array{date:string,paid_from:string,scope:string,decision:string} */ public function filters(): array { $date = trim((string) request('date', '')) ?: Workbench::defaultDate(); try { $paidFrom = trim((string) request('paid_from', '')) ?: Workbench::defaultPaidFrom($date); Workbench::paidWindow($date, $paidFrom); } catch (InvalidArgumentException $exception) { abort(422, $exception->getMessage()); } $scope = (string) request('scope', '') ?: 'pending'; abort_unless(isset(Workbench::SCOPES[$scope]), 422, '取单范围无效。'); $decision = (string) request('decision', '') ?: 'all'; abort_unless(isset(self::DECISION_FILTERS[$decision]), 422, '处理结果筛选无效。'); return ['date' => $date, 'paid_from' => $paidFrom, 'scope' => $scope, 'decision' => $decision]; } /** @param array{date:string,paid_from:string,scope:string} $filters */ public function build(array $filters): array { return $this->workbench->build((int) company_id(), $filters['date'], $filters['paid_from'], Workbench::scopeStatuses($filters['scope'])); } public function page(): array { $filters = $this->filters(); $sync = $this->repository->syncStatus(); $result = null; $error = null; if (! in_array($sync['code'], ['ready', 'failed'], true)) { $error = '聚工单镜像未就绪,暂时无法生成报销批次。'; } else { try { $result = $this->build($filters); } catch (\Throwable $exception) { report($exception); $error = '种菜报销工作台数据暂时无法读取,请稍后重试。'; } } $rows = $result['rows'] ?? []; if ($filters['decision'] !== 'all') { $rows = array_values(array_filter($rows, static fn (array $row): bool => $row['decision'] === $filters['decision'])); } // 先看要处理的:取消、待复查在前。 $order = [Auditor::DECISION_CANCEL => 0, Auditor::DECISION_RECHECK => 1, Auditor::DECISION_PASS => 2]; usort($rows, static fn (array $a, array $b): int => [$order[$a['decision']], $a['initiator'], $a['paid_at']] <=> [$order[$b['decision']], $b['initiator'], $b['paid_at']]); // 镜像停更超过 20 分钟或最近一次同步失败:数据不是最新的,提醒先别报销。 $syncStale = $sync['code'] !== 'ready' || $sync['last_succeeded_at'] === null || now()->diffInMinutes(\Illuminate\Support\Carbon::parse($sync['last_succeeded_at']), true) > self::STALE_MINUTES; return [ 'view' => 'business.pages.billing-transactions.planting-reimbursement-workbench', 'workbenchSyncStale' => $syncStale, 'workbenchFilters' => $filters, 'workbenchWindow' => Workbench::paidWindow($filters['date'], $filters['paid_from']), 'workbenchSync' => $sync, 'workbenchError' => $error, 'workbenchSummary' => $result['summary'] ?? null, 'workbenchRows' => $rows, 'workbenchUploads' => $result['uploads'] ?? $this->safeUploads(), ]; } /** @return array */ private function safeUploads(): array { try { return $this->references->uploads((int) company_id()); } catch (\Throwable $exception) { report($exception); return array_fill_keys(array_keys(References::KINDS), null); } } }