null, 'couriers' => $this->service->courierOptions()]); } public function store(Request $request): RedirectResponse { $data = $request->validate([ ...$this->metadataRules(), 'courier_expcode' => ['required', 'string', 'max:64'], 'quote_type' => ['required', 'string', 'max:64'], 'sheet_name' => ['nullable', 'string', 'max:255'], ...$this->fileRules(true), ]); $entry = $this->service->create($data, (int) user_id()); return redirect()->route('business.courier-quote-changes.show', $entry->change) ->with('success', '存量报价草稿已保存。请核对原报价、计费规则和实际执行日期后提交审核。'); } public function edit(CourierStockQuoteEntry $stockQuoteEntry): View { $this->ensureEditable($stockQuoteEntry); return view('business.pages.courier-payables.stock-quote-entry', ['entry' => $stockQuoteEntry, 'couriers' => []]); } public function update(Request $request, CourierStockQuoteEntry $stockQuoteEntry): RedirectResponse { $this->ensureEditable($stockQuoteEntry); $data = $request->validate([...$this->metadataRules(), ...$this->fileRules(false), 'sheet_name' => ['nullable', 'string', 'max:255']]); $this->service->update($stockQuoteEntry, $data, (int) user_id()); return redirect()->route('business.courier-quote-changes.show', $stockQuoteEntry->change)->with('success', '草稿已更新,历次原报价及附件已保留。请重新核对后提交审核。'); } public function download(CourierStockQuoteEntry $stockQuoteEntry, int $file): StreamedResponse { abort_unless($stockQuoteEntry->company_id === (int) company_id(), 404); $attachment = $stockQuoteEntry->files[$file] ?? null; abort_if($attachment === null || ! Storage::disk('uploads')->exists($attachment['path']), 404, '附件不存在。'); return Storage::disk('uploads')->download($attachment['path'], $attachment['name']); } private function metadataRules(): array { return [ 'effective_date' => ['required', 'date_format:Y-m-d'], 'billing_rules' => ['required', 'string', 'min:2', 'max:4000'], 'summary' => ['required', 'string', 'min:2', 'max:2000'], ]; } private function fileRules(bool $requiresSource): array { return [ 'source_quote' => [$requiresSource ? 'required' : 'nullable', 'file', 'mimes:xlsx,xls', 'max:20480'], 'confirmation_files' => ['nullable', 'array', 'max:10'], 'confirmation_files.*' => ['file', 'mimes:pdf,doc,docx,jpg,jpeg,png,xlsx,xls,eml,msg', 'max:20480'], 'signed_files' => ['nullable', 'array', 'max:10'], 'signed_files.*' => ['file', 'mimes:pdf,doc,docx,jpg,jpeg,png', 'max:20480'], ]; } private function ensureEditable(CourierStockQuoteEntry $entry): void { abort_unless($entry->company_id === (int) company_id() && $entry->change?->isEditable() && $entry->change->created_by === user_id(), 403); } }