ExportMafRegistryService.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. <?php
  2. namespace App\Services;
  3. use App\Helpers\DateHelper;
  4. use App\Models\File;
  5. use App\Models\ProductSKU;
  6. use App\Models\Scopes\YearScope;
  7. use Exception;
  8. use Illuminate\Support\Collection;
  9. use Illuminate\Support\Facades\DB;
  10. use Illuminate\Support\Facades\Storage;
  11. use PhpOffice\PhpSpreadsheet\IOFactory;
  12. use PhpOffice\PhpSpreadsheet\Style\Border;
  13. use PhpOffice\PhpSpreadsheet\Style\Color;
  14. use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
  15. use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
  16. use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
  17. class ExportMafRegistryService
  18. {
  19. private const TEMPLATE = './templates/MafRegistry.xlsx';
  20. private const SHEET_TITLE = 'НАШ ДВОР';
  21. private const FIRST_DATA_ROW = 4;
  22. /**
  23. * @throws Exception
  24. */
  25. public function handle(int $userId, string $updNumber, int $year): string
  26. {
  27. $updNumber = trim($updNumber);
  28. if ($updNumber === '') {
  29. throw new Exception('Укажите номер УПД.');
  30. }
  31. return DB::transaction(function () use ($userId, $updNumber, $year): string {
  32. $mafs = $this->getMafsForRegistry($year);
  33. if ($mafs->isEmpty()) {
  34. throw new Exception('Нет МАФ с заполненным № ведомости и пустым № УПД.');
  35. }
  36. $reader = IOFactory::createReader('Xlsx');
  37. $spreadsheet = $reader->load(self::TEMPLATE);
  38. $sheet = $spreadsheet->getActiveSheet();
  39. $sheet->setTitle(self::SHEET_TITLE);
  40. $this->prepareTemplateRows($sheet, $mafs->count());
  41. $this->fillHeader($sheet, $updNumber, $mafs->count());
  42. $this->fillRows($sheet, $mafs, $updNumber);
  43. $lastRow = self::FIRST_DATA_ROW + $mafs->count() - 1;
  44. $sheet->getStyle('A1:P' . $lastRow)
  45. ->getBorders()
  46. ->getAllBorders()
  47. ->setBorderStyle(Border::BORDER_THIN)
  48. ->setColor(new Color('777777'));
  49. $sheet->getStyle('H' . self::FIRST_DATA_ROW . ':H' . $lastRow)
  50. ->getNumberFormat()
  51. ->setFormatCode(NumberFormat::FORMAT_DATE_DDMMYYYY);
  52. $sheet->getStyle('O' . self::FIRST_DATA_ROW . ':O' . $lastRow)
  53. ->getNumberFormat()
  54. ->setFormatCode(NumberFormat::FORMAT_NUMBER_00);
  55. $fileName = fileName('Реестр на оплату УПД ' . $updNumber . ' ' . date('Y-m-d H-i-s') . '.xlsx');
  56. $directory = 'export/maf-registry';
  57. $path = $directory . '/' . $fileName;
  58. Storage::disk('public')->makeDirectory($directory);
  59. Storage::disk('public')->delete($path);
  60. (new Xlsx($spreadsheet))->save(Storage::disk('public')->path($path));
  61. ProductSKU::query()
  62. ->withoutGlobalScope(YearScope::class)
  63. ->whereIn('id', $mafs->pluck('id'))
  64. ->update(['upd_number' => $updNumber]);
  65. File::query()->create([
  66. 'link' => url('/storage/' . $path),
  67. 'path' => $path,
  68. 'user_id' => $userId,
  69. 'original_name' => $fileName,
  70. 'mime_type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  71. 'is_generated' => true,
  72. ]);
  73. return url('/storage/' . $path);
  74. });
  75. }
  76. private function getMafsForRegistry(int $year): Collection
  77. {
  78. return ProductSKU::query()
  79. ->withoutGlobalScope(YearScope::class)
  80. ->where('year', $year)
  81. ->whereNotNull('statement_number')
  82. ->whereRaw("TRIM(statement_number) <> ''")
  83. ->where(function ($query): void {
  84. $query->whereNull('upd_number')
  85. ->orWhereRaw("TRIM(upd_number) = ''");
  86. })
  87. ->with([
  88. 'product' => fn ($query) => $query->withoutGlobalScope(YearScope::class),
  89. 'order' => fn ($query) => $query->withoutGlobalScope(YearScope::class)->with(['district', 'area', 'objectType']),
  90. ])
  91. ->orderBy('statement_number')
  92. ->orderBy('id')
  93. ->lockForUpdate()
  94. ->get();
  95. }
  96. private function prepareTemplateRows(Worksheet $sheet, int $rowsCount): void
  97. {
  98. $highestRow = $sheet->getHighestRow();
  99. if ($highestRow > self::FIRST_DATA_ROW) {
  100. $sheet->removeRow(self::FIRST_DATA_ROW + 1, $highestRow - self::FIRST_DATA_ROW);
  101. }
  102. if ($rowsCount > 1) {
  103. $sheet->insertNewRowBefore(self::FIRST_DATA_ROW + 1, $rowsCount - 1);
  104. }
  105. $templateStyle = $sheet->getStyle('A' . self::FIRST_DATA_ROW . ':P' . self::FIRST_DATA_ROW);
  106. for ($row = self::FIRST_DATA_ROW; $row < self::FIRST_DATA_ROW + $rowsCount; $row++) {
  107. $sheet->duplicateStyle($templateStyle, 'A' . $row . ':P' . $row);
  108. }
  109. }
  110. private function fillHeader(Worksheet $sheet, string $updNumber, int $rowsCount): void
  111. {
  112. $lastRow = self::FIRST_DATA_ROW + $rowsCount - 1;
  113. $sheet->setCellValue('M1', '=SUM(M' . self::FIRST_DATA_ROW . ':M' . $lastRow . ')');
  114. $sheet->setCellValue('N1', 0);
  115. $sheet->setCellValue('O1', '=SUM(O' . self::FIRST_DATA_ROW . ':O' . $lastRow . ')');
  116. $sheet->setCellValue('C2', 'Реестр оборудования и ведомостей "технической приемки" к УПД ' . $updNumber);
  117. }
  118. private function fillRows(Worksheet $sheet, Collection $mafs, string $updNumber): void
  119. {
  120. $row = self::FIRST_DATA_ROW;
  121. foreach ($mafs as $maf) {
  122. $product = $maf->product;
  123. $order = $maf->order;
  124. $sheet->setCellValue('A' . $row, self::SHEET_TITLE);
  125. $sheet->setCellValue('B' . $row, '4');
  126. $sheet->setCellValue('C' . $row, $updNumber);
  127. $sheet->setCellValue('D' . $row, $product?->nomenclature_number);
  128. $sheet->setCellValue('E' . $row, $product?->statement_name);
  129. $sheet->setCellValue('F' . $row, $product?->article);
  130. $sheet->setCellValue('G' . $row, $maf->statement_number);
  131. $sheet->setCellValue('H' . $row, $maf->statement_date ? DateHelper::ISODateToExcelDate((string) $maf->statement_date) : '');
  132. $sheet->setCellValue('I' . $row, $order?->name);
  133. $sheet->setCellValue('J' . $row, $order?->objectType?->name);
  134. $sheet->setCellValue('K' . $row, $order?->area?->name);
  135. $sheet->setCellValue('L' . $row, $order?->district?->shortname);
  136. $sheet->setCellValue('M' . $row, 1);
  137. $sheet->setCellValue('N' . $row, 0);
  138. $sheet->setCellValue('O' . $row, $product?->total_price ?? 0);
  139. $sheet->setCellValue('P' . $row, 'С бетоном');
  140. $row++;
  141. }
  142. }
  143. }