'ID', 'name' => 'Наименование', 'legal_name' => 'Юридическое имя', 'contract_number' => '№ договора', 'contract_date' => 'Дата договора', 'organization_form_name' => 'Форма', 'tax_rate_name' => 'Налог', 'hidden_txt' => 'Скрыт', ]; protected array $searchFields = ['name', 'legal_name', 'contract_number', 'director_name']; public function index(Request $request): View { session(['gp_contractors' => $request->query()]); $nav = $this->startNavigationContext($request); $allowedSortFields = ['id', 'name', 'legal_name', 'contract_number', 'contract_date', 'organization_form', 'tax_rate', 'hidden']; $sortBy = in_array($request->get('sortBy'), $allowedSortFields, true) ? $request->get('sortBy') : 'name'; $orderBy = $request->get('order') === 'desc' ? 'desc' : 'asc'; $contractors = Contractor::query(); if ($request->filled('s')) { $search = $request->get('s'); $contractors->where(function ($query) use ($search) { foreach ($this->searchFields as $field) { $query->orWhere($field, 'like', '%' . $search . '%'); } }); } $contractors = $contractors->orderBy($sortBy, $orderBy)->get(); return view('contractors.index', [ 'active' => 'contractors', 'id' => $this->id, 'header' => $this->header, 'sortBy' => $sortBy, 'orderBy' => $orderBy, 'searchFields' => $this->searchFields, 'contractors' => $contractors, 'organizationForms' => Contractor::ORGANIZATION_FORMS, 'taxRates' => Contractor::TAX_RATES, 'nav' => $nav, ]); } public function create(Request $request, ContractorPriceService $priceService): View { return $this->showForm($request, null, $priceService); } public function show(Request $request, Contractor $contractor, ContractorPriceService $priceService): View { return $this->showForm($request, $contractor, $priceService); } public function store(Request $request): RedirectResponse { $validated = $this->validatedContractor($request); $contractor = Contractor::query()->create($validated); return redirect()->route('contractors.show', $contractor)->with('success', 'Подрядчик создан'); } public function update(Request $request, Contractor $contractor): RedirectResponse { $contractor->update($this->validatedContractor($request)); return redirect()->route('contractors.show', $contractor)->with('success', 'Подрядчик сохранён'); } public function updatePrice(Request $request, Contractor $contractor, ContractorPriceService $priceService): RedirectResponse { $validated = $request->validate([ 'product_id' => ['required', 'integer', 'exists:products,id'], 'name_in_spec' => ['nullable', 'string', 'max:255'], 'price' => ['nullable', 'numeric', 'min:0'], ]); $product = Product::withoutGlobalScope(YearScope::class)->withTrashed()->findOrFail($validated['product_id']); $priceService->updatePrice( $contractor, $product, year(), $validated['name_in_spec'] ?? null, (float) ($validated['price'] ?? 0), ); return redirect()->route('contractors.show', $contractor)->with('success', 'Цена монтажа сохранена'); } public function importPrices(Request $request, Contractor $contractor, ContractorPriceService $priceService): RedirectResponse { $validated = $request->validate([ 'import_file' => ['required', 'file', 'mimes:xlsx,xls'], ]); $result = $priceService->import($contractor, $validated['import_file'], year()); $message = "Импорт завершён. Обновлено: {$result['updated']}. Без изменений: {$result['unchanged']}. Ошибок: " . count($result['errors']) . '.'; return redirect() ->route('contractors.show', $contractor) ->with('success', $message) ->with('contractor_import_errors', $result['errors']); } public function exportPrices(Request $request, Contractor $contractor, ContractorPriceService $priceService): BinaryFileResponse { $catalogYear = year(); $path = $priceService->export($contractor, $catalogYear); return response() ->download($path, 'Цены монтажа ' . $contractor->name . ' ' . $catalogYear . '.xlsx') ->deleteFileAfterSend(); } private function showForm(Request $request, ?Contractor $contractor, ContractorPriceService $priceService): View { $nav = $this->resolveNavToken($request); $this->rememberNavigation($request, $nav); $year = year(); return view('contractors.edit', [ 'active' => 'contractors', 'contractor' => $contractor, 'organizationForms' => Contractor::ORGANIZATION_FORMS, 'taxRates' => Contractor::TAX_RATES, 'priceRows' => $contractor ? $priceService->rowsForContractor($contractor, $year) : collect(), 'catalogYear' => $year, 'nav' => $nav, 'back_url' => $this->navigationBackUrl($request, $nav, route('contractors.index', session('gp_contractors'))), ]); } private function validatedContractor(Request $request): array { return $request->validate([ 'name' => ['required', 'string', 'max:255'], 'legal_name' => ['required', 'string', 'max:255'], 'contract_number' => ['required', 'string', 'max:255'], 'contract_date' => ['required', 'date'], 'director_name' => ['required', 'string', 'max:255'], 'organization_form' => ['required', 'string', 'in:' . implode(',', array_keys(Contractor::ORGANIZATION_FORMS))], 'tax_rate' => ['required', 'string', 'in:' . implode(',', array_keys(Contractor::TAX_RATES))], 'contract_header' => ['required', 'string'], 'hidden' => ['nullable', 'boolean'], ]) + ['hidden' => false]; } }