ContractorController.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Models\Contractor;
  4. use App\Models\Product;
  5. use App\Models\Scopes\YearScope;
  6. use App\Services\ContractorPriceService;
  7. use Illuminate\Http\RedirectResponse;
  8. use Illuminate\Http\Request;
  9. use Illuminate\View\View;
  10. use Symfony\Component\HttpFoundation\BinaryFileResponse;
  11. class ContractorController extends Controller
  12. {
  13. protected string $id = 'contractors';
  14. protected array $header = [
  15. 'id' => 'ID',
  16. 'name' => 'Наименование',
  17. 'legal_name' => 'Юридическое имя',
  18. 'contract_number' => '№ договора',
  19. 'contract_date' => 'Дата договора',
  20. 'organization_form_name' => 'Форма',
  21. 'tax_rate_name' => 'Налог',
  22. 'hidden_txt' => 'Скрыт',
  23. ];
  24. protected array $searchFields = ['name', 'legal_name', 'contract_number', 'director_name'];
  25. public function index(Request $request): View
  26. {
  27. session(['gp_contractors' => $request->query()]);
  28. $nav = $this->startNavigationContext($request);
  29. $allowedSortFields = ['id', 'name', 'legal_name', 'contract_number', 'contract_date', 'organization_form', 'tax_rate', 'hidden'];
  30. $sortBy = in_array($request->get('sortBy'), $allowedSortFields, true) ? $request->get('sortBy') : 'name';
  31. $orderBy = $request->get('order') === 'desc' ? 'desc' : 'asc';
  32. $contractors = Contractor::query();
  33. if ($request->filled('s')) {
  34. $search = $request->get('s');
  35. $contractors->where(function ($query) use ($search) {
  36. foreach ($this->searchFields as $field) {
  37. $query->orWhere($field, 'like', '%' . $search . '%');
  38. }
  39. });
  40. }
  41. $contractors = $contractors->orderBy($sortBy, $orderBy)->get();
  42. return view('contractors.index', [
  43. 'active' => 'contractors',
  44. 'id' => $this->id,
  45. 'header' => $this->header,
  46. 'sortBy' => $sortBy,
  47. 'orderBy' => $orderBy,
  48. 'searchFields' => $this->searchFields,
  49. 'contractors' => $contractors,
  50. 'organizationForms' => Contractor::ORGANIZATION_FORMS,
  51. 'taxRates' => Contractor::TAX_RATES,
  52. 'nav' => $nav,
  53. ]);
  54. }
  55. public function create(Request $request, ContractorPriceService $priceService): View
  56. {
  57. return $this->showForm($request, null, $priceService);
  58. }
  59. public function show(Request $request, Contractor $contractor, ContractorPriceService $priceService): View
  60. {
  61. return $this->showForm($request, $contractor, $priceService);
  62. }
  63. public function store(Request $request): RedirectResponse
  64. {
  65. $validated = $this->validatedContractor($request);
  66. $contractor = Contractor::query()->create($validated);
  67. return redirect()->route('contractors.show', $contractor)->with('success', 'Подрядчик создан');
  68. }
  69. public function update(Request $request, Contractor $contractor): RedirectResponse
  70. {
  71. $contractor->update($this->validatedContractor($request));
  72. return redirect()->route('contractors.show', $contractor)->with('success', 'Подрядчик сохранён');
  73. }
  74. public function updatePrice(Request $request, Contractor $contractor, ContractorPriceService $priceService): RedirectResponse
  75. {
  76. $validated = $request->validate([
  77. 'product_id' => ['required', 'integer', 'exists:products,id'],
  78. 'name_in_spec' => ['nullable', 'string', 'max:255'],
  79. 'price' => ['nullable', 'numeric', 'min:0'],
  80. ]);
  81. $product = Product::withoutGlobalScope(YearScope::class)->withTrashed()->findOrFail($validated['product_id']);
  82. $priceService->updatePrice(
  83. $contractor,
  84. $product,
  85. year(),
  86. $validated['name_in_spec'] ?? null,
  87. (float) ($validated['price'] ?? 0),
  88. );
  89. return redirect()->route('contractors.show', $contractor)->with('success', 'Цена монтажа сохранена');
  90. }
  91. public function importPrices(Request $request, Contractor $contractor, ContractorPriceService $priceService): RedirectResponse
  92. {
  93. $validated = $request->validate([
  94. 'import_file' => ['required', 'file', 'mimes:xlsx,xls'],
  95. ]);
  96. $result = $priceService->import($contractor, $validated['import_file'], year());
  97. $message = "Импорт завершён. Обновлено: {$result['updated']}. Без изменений: {$result['unchanged']}. Ошибок: " . count($result['errors']) . '.';
  98. return redirect()
  99. ->route('contractors.show', $contractor)
  100. ->with('success', $message)
  101. ->with('contractor_import_errors', $result['errors']);
  102. }
  103. public function exportPrices(Request $request, Contractor $contractor, ContractorPriceService $priceService): BinaryFileResponse
  104. {
  105. $catalogYear = year();
  106. $path = $priceService->export($contractor, $catalogYear);
  107. return response()
  108. ->download($path, 'Цены монтажа ' . $contractor->name . ' ' . $catalogYear . '.xlsx')
  109. ->deleteFileAfterSend();
  110. }
  111. private function showForm(Request $request, ?Contractor $contractor, ContractorPriceService $priceService): View
  112. {
  113. $nav = $this->resolveNavToken($request);
  114. $this->rememberNavigation($request, $nav);
  115. $year = year();
  116. return view('contractors.edit', [
  117. 'active' => 'contractors',
  118. 'contractor' => $contractor,
  119. 'organizationForms' => Contractor::ORGANIZATION_FORMS,
  120. 'taxRates' => Contractor::TAX_RATES,
  121. 'priceRows' => $contractor ? $priceService->rowsForContractor($contractor, $year) : collect(),
  122. 'catalogYear' => $year,
  123. 'nav' => $nav,
  124. 'back_url' => $this->navigationBackUrl($request, $nav, route('contractors.index', session('gp_contractors'))),
  125. ]);
  126. }
  127. private function validatedContractor(Request $request): array
  128. {
  129. return $request->validate([
  130. 'name' => ['required', 'string', 'max:255'],
  131. 'legal_name' => ['required', 'string', 'max:255'],
  132. 'contract_number' => ['required', 'string', 'max:255'],
  133. 'contract_date' => ['required', 'date'],
  134. 'director_name' => ['required', 'string', 'max:255'],
  135. 'organization_form' => ['required', 'string', 'in:' . implode(',', array_keys(Contractor::ORGANIZATION_FORMS))],
  136. 'tax_rate' => ['required', 'string', 'in:' . implode(',', array_keys(Contractor::TAX_RATES))],
  137. 'contract_header' => ['required', 'string'],
  138. 'hidden' => ['nullable', 'boolean'],
  139. ]) + ['hidden' => false];
  140. }
  141. }