ExportCommonCatalogService.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Services\Export;
  4. use App\Models\CommonCatalogItem;
  5. use App\Models\File;
  6. use Illuminate\Support\Facades\Storage;
  7. use PhpOffice\PhpSpreadsheet\Cell\DataType;
  8. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  9. use PhpOffice\PhpSpreadsheet\Style\Alignment;
  10. use PhpOffice\PhpSpreadsheet\Style\Border;
  11. use PhpOffice\PhpSpreadsheet\Style\Fill;
  12. use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
  13. use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
  14. use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
  15. use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
  16. class ExportCommonCatalogService
  17. {
  18. public const TECHNICAL_DESCRIPTIONS_SHEET = 'Техописания';
  19. public const TECHNICAL_DESCRIPTION_HEADERS = [
  20. 'Артикул',
  21. 'Наименование',
  22. 'Наименование для формы',
  23. 'Группа продукции',
  24. 'Характеристики',
  25. 'Техническое описание',
  26. 'Краткое техническое описание',
  27. ];
  28. public const HEADERS = [
  29. 'Артикул',
  30. 'Наименование',
  31. 'Вид',
  32. 'Габариты',
  33. null,
  34. null,
  35. 'Размер участка',
  36. null,
  37. 'Высота падения',
  38. 'Дополнительные сведения',
  39. "Единица измерения\nгабаритов",
  40. 'Вес, кг.',
  41. 'Объем, м3',
  42. 'Места',
  43. 'Состав',
  44. 'Возрастная группа',
  45. 'Макс.кол-во пользователей',
  46. 'Ед.',
  47. 'Внешний вид',
  48. 'Серия',
  49. 'ТМ',
  50. 'Калькулятор',
  51. 'строители',
  52. 'опт',
  53. 'рек',
  54. 'розница',
  55. 'проект',
  56. 'проект+м',
  57. 'пик',
  58. 'рек+10',
  59. ];
  60. public const SUBHEADERS = [
  61. null,
  62. null,
  63. null,
  64. 'Длина',
  65. 'Ширина',
  66. 'Высота',
  67. 'Длина',
  68. 'Ширина',
  69. null,
  70. null,
  71. null,
  72. null,
  73. null,
  74. null,
  75. null,
  76. null,
  77. null,
  78. null,
  79. null,
  80. null,
  81. null,
  82. null,
  83. null,
  84. 1.05,
  85. 1.1,
  86. 1.5,
  87. 2,
  88. 1.2,
  89. 'инд.',
  90. 1.21,
  91. ];
  92. public function handle(int $userId): string
  93. {
  94. $spreadsheet = new Spreadsheet;
  95. $sheet = $spreadsheet->getActiveSheet();
  96. $sheet->setTitle('Общие сведения');
  97. $sheet->fromArray(self::HEADERS, null, 'A1');
  98. $sheet->fromArray(self::SUBHEADERS, null, 'A2');
  99. $this->mergeHeaders($sheet);
  100. $this->styleHeaders($sheet);
  101. $technicalDescriptionsSheet = $spreadsheet->createSheet();
  102. $technicalDescriptionsSheet->setTitle(self::TECHNICAL_DESCRIPTIONS_SHEET);
  103. $technicalDescriptionsSheet->fromArray(self::TECHNICAL_DESCRIPTION_HEADERS, null, 'A1');
  104. $this->styleTechnicalDescriptionsSheet($technicalDescriptionsSheet);
  105. $row = 3;
  106. $technicalDescriptionRow = 2;
  107. CommonCatalogItem::query()
  108. ->with('imageFile')
  109. ->orderBy('article')
  110. ->orderBy('calculator_name')
  111. ->chunk(200, function ($items) use ($sheet, $technicalDescriptionsSheet, &$row, &$technicalDescriptionRow): void {
  112. foreach ($items as $item) {
  113. $sheet->setCellValueExplicit("A{$row}", $item->article, DataType::TYPE_STRING);
  114. $sheet->setCellValue("B{$row}", $item->calculator_name);
  115. $sheet->setCellValue("C{$row}", $item->kind);
  116. $sheet->setCellValue("D{$row}", $item->dimension_length);
  117. $sheet->setCellValue("E{$row}", $item->dimension_width);
  118. $sheet->setCellValue("F{$row}", $item->dimension_height);
  119. $sheet->setCellValue("G{$row}", $item->site_length);
  120. $sheet->setCellValue("H{$row}", $item->site_width);
  121. $sheet->setCellValue("I{$row}", $item->fall_height);
  122. $sheet->setCellValue("J{$row}", $item->additional_info);
  123. $sheet->setCellValue("K{$row}", $item->dimension_unit);
  124. $sheet->setCellValue("L{$row}", $item->weight);
  125. $sheet->setCellValue("M{$row}", $item->volume);
  126. $sheet->setCellValue("N{$row}", $item->places);
  127. $sheet->setCellValue("O{$row}", $item->composition);
  128. $sheet->setCellValue("P{$row}", $item->age_group);
  129. $sheet->setCellValue("Q{$row}", $item->max_users);
  130. $sheet->setCellValue("R{$row}", $item->unit);
  131. $sheet->setCellValue("T{$row}", $item->series);
  132. $sheet->setCellValue("U{$row}", $item->trademark);
  133. $sheet->setCellValue("V{$row}", match ($item->calculator_enabled) {
  134. true => 'да',
  135. false => 'нет',
  136. null => null,
  137. });
  138. $sheet->setCellValue("W{$row}", $item->builders_price);
  139. $sheet->setCellValue("X{$row}", $item->wholesale_price);
  140. $sheet->setCellValue("Y{$row}", $item->recommended_price);
  141. $sheet->setCellValue("Z{$row}", $item->retail_price);
  142. $sheet->setCellValue("AA{$row}", $item->project_price);
  143. $sheet->setCellValue("AB{$row}", $item->project_with_installation_price);
  144. $sheet->setCellValue("AC{$row}", $item->pik_price);
  145. $sheet->setCellValue("AD{$row}", $item->recommended_plus_10_price);
  146. $this->addImage($sheet, $item, $row);
  147. $this->addTechnicalDescription($technicalDescriptionsSheet, $item, $technicalDescriptionRow);
  148. $row++;
  149. $technicalDescriptionRow++;
  150. }
  151. });
  152. if ($row > 3) {
  153. $lastRow = $row - 1;
  154. $sheet->getStyle("A1:AD{$lastRow}")
  155. ->getBorders()
  156. ->getAllBorders()
  157. ->setBorderStyle(Border::BORDER_THIN);
  158. $sheet->getStyle("A3:AD{$lastRow}")->getAlignment()
  159. ->setVertical(Alignment::VERTICAL_TOP)
  160. ->setWrapText(true);
  161. $sheet->getStyle("W3:AD{$lastRow}")
  162. ->getNumberFormat()
  163. ->setFormatCode(NumberFormat::FORMAT_NUMBER_COMMA_SEPARATED1);
  164. }
  165. $this->setColumnWidths($sheet);
  166. if ($technicalDescriptionRow > 2) {
  167. $lastTechnicalDescriptionRow = $technicalDescriptionRow - 1;
  168. $technicalDescriptionsSheet->getStyle("A1:G{$lastTechnicalDescriptionRow}")
  169. ->getBorders()
  170. ->getAllBorders()
  171. ->setBorderStyle(Border::BORDER_THIN);
  172. $technicalDescriptionsSheet->getStyle("A2:G{$lastTechnicalDescriptionRow}")
  173. ->getAlignment()
  174. ->setVertical(Alignment::VERTICAL_TOP)
  175. ->setWrapText(true);
  176. }
  177. $spreadsheet->setActiveSheetIndex(0);
  178. $directory = 'export/common_catalog';
  179. $filename = 'common_catalog_'.now()->format('Y-m-d_H-i-s').'.xlsx';
  180. $relativePath = "{$directory}/{$filename}";
  181. Storage::disk('public')->makeDirectory($directory);
  182. (new Xlsx($spreadsheet))->save(Storage::disk('public')->path($relativePath));
  183. $spreadsheet->disconnectWorksheets();
  184. $link = url('/storage/'.$relativePath);
  185. File::query()->create([
  186. 'link' => $link,
  187. 'path' => $relativePath,
  188. 'user_id' => $userId,
  189. 'original_name' => $filename,
  190. 'mime_type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  191. 'is_generated' => true,
  192. ]);
  193. return $link;
  194. }
  195. private function mergeHeaders(Worksheet $sheet): void
  196. {
  197. foreach (['A', 'B', 'C', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W'] as $column) {
  198. $sheet->mergeCells("{$column}1:{$column}2");
  199. }
  200. $sheet->mergeCells('D1:F1');
  201. $sheet->mergeCells('G1:H1');
  202. }
  203. private function styleHeaders(Worksheet $sheet): void
  204. {
  205. $sheet->getStyle('A1:AD2')->applyFromArray([
  206. 'font' => ['bold' => true],
  207. 'fill' => [
  208. 'fillType' => Fill::FILL_SOLID,
  209. 'startColor' => ['rgb' => 'D9EAF7'],
  210. ],
  211. 'alignment' => [
  212. 'horizontal' => Alignment::HORIZONTAL_CENTER,
  213. 'vertical' => Alignment::VERTICAL_CENTER,
  214. 'wrapText' => true,
  215. ],
  216. ]);
  217. $sheet->getRowDimension(1)->setRowHeight(36);
  218. $sheet->getRowDimension(2)->setRowHeight(24);
  219. $sheet->freezePane('A3');
  220. }
  221. private function setColumnWidths(Worksheet $sheet): void
  222. {
  223. foreach ([
  224. 'A' => 14,
  225. 'B' => 38,
  226. 'C' => 24,
  227. 'D' => 13,
  228. 'E' => 13,
  229. 'F' => 13,
  230. 'G' => 13,
  231. 'H' => 13,
  232. 'I' => 15,
  233. 'J' => 28,
  234. 'K' => 18,
  235. 'L' => 14,
  236. 'M' => 14,
  237. 'N' => 11,
  238. 'O' => 28,
  239. 'P' => 18,
  240. 'Q' => 18,
  241. 'R' => 10,
  242. 'S' => 18,
  243. 'T' => 20,
  244. 'U' => 18,
  245. 'V' => 14,
  246. ] as $column => $width) {
  247. $sheet->getColumnDimension($column)->setWidth($width);
  248. }
  249. foreach (['W', 'X', 'Y', 'Z', 'AA', 'AB', 'AC', 'AD'] as $column) {
  250. $sheet->getColumnDimension($column)->setWidth(16);
  251. }
  252. }
  253. private function addImage(Worksheet $sheet, CommonCatalogItem $item, int $row): void
  254. {
  255. $path = $item->imageFile?->path;
  256. if (! $path || ! Storage::disk('public')->exists($path)) {
  257. return;
  258. }
  259. $drawing = new Drawing;
  260. $drawing->setName($item->article);
  261. $drawing->setPath(Storage::disk('public')->path($path));
  262. $drawing->setCoordinates("S{$row}");
  263. $drawing->setHeight(68);
  264. $drawing->setOffsetX(4);
  265. $drawing->setOffsetY(4);
  266. $drawing->setWorksheet($sheet);
  267. $sheet->getRowDimension($row)->setRowHeight(56);
  268. }
  269. private function addTechnicalDescription(Worksheet $sheet, CommonCatalogItem $item, int $row): void
  270. {
  271. $sheet->setCellValueExplicit("A{$row}", $item->article, DataType::TYPE_STRING);
  272. $sheet->setCellValue("B{$row}", $item->calculator_name);
  273. $sheet->setCellValue("C{$row}", $item->print_name);
  274. $sheet->setCellValue("D{$row}", $item->product_group);
  275. $sheet->setCellValue("E{$row}", $item->characteristics);
  276. $sheet->setCellValue("F{$row}", $item->technical_description);
  277. $sheet->setCellValue("G{$row}", $item->technical_description_short);
  278. }
  279. private function styleTechnicalDescriptionsSheet(Worksheet $sheet): void
  280. {
  281. $sheet->getStyle('A1:G1')->applyFromArray([
  282. 'font' => ['bold' => true],
  283. 'fill' => [
  284. 'fillType' => Fill::FILL_SOLID,
  285. 'startColor' => ['rgb' => 'D9EAF7'],
  286. ],
  287. 'alignment' => [
  288. 'horizontal' => Alignment::HORIZONTAL_CENTER,
  289. 'vertical' => Alignment::VERTICAL_CENTER,
  290. 'wrapText' => true,
  291. ],
  292. ]);
  293. $sheet->getRowDimension(1)->setRowHeight(36);
  294. $sheet->freezePane('A2');
  295. foreach ([
  296. 'A' => 14,
  297. 'B' => 38,
  298. 'C' => 38,
  299. 'D' => 28,
  300. 'E' => 55,
  301. 'F' => 70,
  302. 'G' => 55,
  303. ] as $column => $width) {
  304. $sheet->getColumnDimension($column)->setWidth($width);
  305. }
  306. }
  307. }