ExportMafRegistryService.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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. $paymentStatusService = app(OrderPaymentStatusService::class);
  66. foreach ($mafs->pluck('order_id')->filter()->unique() as $orderId) {
  67. $paymentStatusService->markPaidIfAllMafsHavePaymentData((int) $orderId);
  68. }
  69. File::query()->create([
  70. 'link' => url('/storage/' . $path),
  71. 'path' => $path,
  72. 'user_id' => $userId,
  73. 'original_name' => $fileName,
  74. 'mime_type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  75. 'is_generated' => true,
  76. ]);
  77. return url('/storage/' . $path);
  78. });
  79. }
  80. private function getMafsForRegistry(int $year): Collection
  81. {
  82. return ProductSKU::query()
  83. ->withoutGlobalScope(YearScope::class)
  84. ->where('year', $year)
  85. ->whereNotNull('statement_number')
  86. ->whereRaw("TRIM(statement_number) <> ''")
  87. ->where(function ($query): void {
  88. $query->whereNull('upd_number')
  89. ->orWhereRaw("TRIM(upd_number) = ''");
  90. })
  91. ->with([
  92. 'product' => fn ($query) => $query->withoutGlobalScope(YearScope::class),
  93. 'order' => fn ($query) => $query->withoutGlobalScope(YearScope::class)->with(['district', 'area', 'objectType']),
  94. ])
  95. ->orderBy('statement_number')
  96. ->orderBy('id')
  97. ->lockForUpdate()
  98. ->get();
  99. }
  100. private function prepareTemplateRows(Worksheet $sheet, int $rowsCount): void
  101. {
  102. $highestRow = $sheet->getHighestRow();
  103. if ($highestRow > self::FIRST_DATA_ROW) {
  104. $sheet->removeRow(self::FIRST_DATA_ROW + 1, $highestRow - self::FIRST_DATA_ROW);
  105. }
  106. if ($rowsCount > 1) {
  107. $sheet->insertNewRowBefore(self::FIRST_DATA_ROW + 1, $rowsCount - 1);
  108. }
  109. $templateStyle = $sheet->getStyle('A' . self::FIRST_DATA_ROW . ':P' . self::FIRST_DATA_ROW);
  110. for ($row = self::FIRST_DATA_ROW; $row < self::FIRST_DATA_ROW + $rowsCount; $row++) {
  111. $sheet->duplicateStyle($templateStyle, 'A' . $row . ':P' . $row);
  112. }
  113. }
  114. private function fillHeader(Worksheet $sheet, string $updNumber, int $rowsCount): void
  115. {
  116. $lastRow = self::FIRST_DATA_ROW + $rowsCount - 1;
  117. $sheet->setCellValue('M1', '=SUM(M' . self::FIRST_DATA_ROW . ':M' . $lastRow . ')');
  118. $sheet->setCellValue('N1', 0);
  119. $sheet->setCellValue('O1', '=SUM(O' . self::FIRST_DATA_ROW . ':O' . $lastRow . ')');
  120. $sheet->setCellValue('C2', 'Реестр оборудования и ведомостей "технической приемки" к УПД ' . $updNumber);
  121. }
  122. private function fillRows(Worksheet $sheet, Collection $mafs, string $updNumber): void
  123. {
  124. $row = self::FIRST_DATA_ROW;
  125. foreach ($mafs as $maf) {
  126. $product = $maf->product;
  127. $order = $maf->order;
  128. $sheet->setCellValue('A' . $row, self::SHEET_TITLE);
  129. $sheet->setCellValue('B' . $row, '4');
  130. $sheet->setCellValue('C' . $row, $updNumber);
  131. $sheet->setCellValue('D' . $row, $product?->nomenclature_number);
  132. $sheet->setCellValue('E' . $row, $product?->statement_name);
  133. $sheet->setCellValue('F' . $row, $product?->article);
  134. $sheet->setCellValue('G' . $row, $maf->statement_number);
  135. $sheet->setCellValue('H' . $row, $maf->statement_date ? DateHelper::ISODateToExcelDate((string) $maf->statement_date) : '');
  136. $sheet->setCellValue('I' . $row, $order?->name);
  137. $sheet->setCellValue('J' . $row, $order?->objectType?->name);
  138. $sheet->setCellValue('K' . $row, $order?->area?->name);
  139. $sheet->setCellValue('L' . $row, $order?->district?->shortname);
  140. $sheet->setCellValue('M' . $row, 1);
  141. $sheet->setCellValue('N' . $row, 0);
  142. $sheet->setCellValue('O' . $row, $product?->total_price ?? 0);
  143. $sheet->setCellValue('P' . $row, 'С бетоном');
  144. $row++;
  145. }
  146. }
  147. }