| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- <?php
- namespace App\Services;
- use App\Models\File;
- use App\Models\PricingCode;
- use App\Models\Product;
- use App\Models\Reclamation;
- use App\Models\SparePart;
- use App\Helpers\DateHelper;
- use Illuminate\Support\Facades\Storage;
- use PhpOffice\PhpSpreadsheet\Cell\DataType;
- use PhpOffice\PhpSpreadsheet\IOFactory;
- use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
- use PhpOffice\PhpSpreadsheet\Style\Alignment;
- use PhpOffice\PhpSpreadsheet\Style\Border;
- use PhpOffice\PhpSpreadsheet\Style\Color;
- use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
- use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
- class ExportReclamationsService
- {
- private const TEMPLATE = './templates/ReclamationsExport.xlsx';
- private const BASE_PRICING_CODE = '14.20-38-1';
- public function handle(array $reclamationIds, int $userId): string
- {
- $reader = IOFactory::createReader('Xlsx');
- $spreadsheet = $reader->load(self::TEMPLATE);
- $sheet = $spreadsheet->getActiveSheet();
- $templateLastRow = $sheet->getHighestRow();
- $templateStyle = $sheet->getStyle('A2:X2');
- $reclamations = Reclamation::query()
- ->whereIn('id', $reclamationIds)
- ->with([
- 'order.district',
- 'order.area',
- 'skus.product' => fn($q) => $q->withoutGlobalScopes(),
- 'spareParts.pricingCodes',
- ])
- ->get()
- ->keyBy('id');
- $row = 2;
- foreach ($reclamationIds as $reclamationId) {
- $reclamation = $reclamations->get($reclamationId);
- if (!$reclamation) {
- continue;
- }
- $base = $this->buildBaseRow($reclamation);
- $groupRows = $this->buildGroupRows($reclamation, $base);
- foreach ($groupRows as $group) {
- $count = max(count($group), 1);
- for ($i = 0; $i < $count; $i++) {
- $data = $group[$i] ?? $base;
- $sheet->duplicateStyle($templateStyle, 'A' . $row . ':X' . $row);
- $this->writeRow($sheet, $row, $data);
- $row++;
- }
- }
- }
- if ($row <= $templateLastRow) {
- $sheet->removeRow($row, $templateLastRow - $row + 1);
- }
- $this->applyBorders($sheet, 1, $row - 1);
- $this->applyDefaultStyles($sheet, 2, $row - 1);
- $fileName = fileName('Рекламации ' . date('Y-m-d H-i-s') . '.xlsx');
- $fd = 'export/reclamations';
- Storage::disk('public')->makeDirectory($fd);
- $fp = storage_path('app/public/' . $fd . '/') . $fileName;
- Storage::disk('public')->delete($fd . '/' . $fileName);
- (new Xlsx($spreadsheet))->save($fp);
- $link = url('/storage/' . $fd . '/' . $fileName);
- File::query()->create([
- 'link' => $link,
- 'path' => $fp,
- 'user_id' => $userId,
- 'original_name' => $fileName,
- 'mime_type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
- ]);
- return $link;
- }
- private function buildBaseRow(Reclamation $reclamation): array
- {
- $order = $reclamation->order;
- return [
- 'A' => null, // № папки
- 'B' => $order?->district?->shortname ?? $order?->district_name ?? null, // Округ
- 'C' => $order?->area?->name ?? $order?->area_name ?? null, // Район
- 'D' => $order?->object_address ?? null, // Адрес
- 'E' => null, // Артикул
- 'F' => null, // Тип
- 'G' => null, // Наименование МАФ по паспорту
- 'H' => $reclamation->whats_done ?? null, // Что сделали
- 'I' => $reclamation->create_date ?? null, // Дата заявки
- 'J' => $order?->year ?? null, // Год поставки МАФ
- 'K' => $reclamation->reason ?? null, // Тип обращения
- 'L' => null, // Применяемая деталь
- 'M' => null, // Кол-во
- 'N' => null, // Цена по КС
- 'O' => null, // Лимит по МАФ
- 'P' => null, // Цена детали
- 'Q' => null, // Подтверждение цены
- 'R' => null, // Шифр 1
- 'S' => null, // Наименование 1
- 'T' => null, // Шифр 2
- 'U' => null, // Наименование 2
- 'V' => null, // Шифр 3
- 'W' => null, // Наименование 3
- 'X' => $reclamation->comment ?? null, // Комментарий
- ];
- }
- private function buildGroupRows(Reclamation $reclamation, array $base): array
- {
- $skus = $reclamation->skus;
- $spareParts = $reclamation->spareParts;
- $details = $reclamation->details;
- $groups = [];
- if ($skus->isEmpty()) {
- $baseDescription = PricingCode::getPricingCodeDescription(self::BASE_PRICING_CODE);
- $codes = [
- [self::BASE_PRICING_CODE, $baseDescription],
- ];
- $groups[] = [$this->applyPricingCodes($this->buildRowForMaf($base, null), $codes)];
- return $groups;
- }
- foreach ($skus as $idx => $sku) {
- $mafRow = $this->buildRowForMaf($base, $sku);
- if ($idx > 0) {
- $this->blankColumns($mafRow, ['A', 'B', 'C', 'D']);
- }
- $detailRows = $this->buildDetailRows($mafRow, $spareParts, $details);
- $groups[] = $detailRows;
- }
- return $groups;
- }
- private function buildRowForMaf(array $base, $sku): array
- {
- if (!$sku || !$sku->product) {
- return $base;
- }
- $product = $sku->product;
- $year = $sku->year ?? $product->year;
- $passportName = null;
- if ($product->article && $year) {
- $productForYear = Product::withoutGlobalScopes()
- ->where('year', $year)
- ->where('article', $product->article)
- ->first();
- $passportName = $productForYear?->passport_name;
- }
- $row = $base;
- $row['E'] = $product->article;
- $row['F'] = $product->article;
- $row['G'] = $passportName;
- return $row;
- }
- private function buildDetailRows(array $mafRow, $spareParts, $details): array
- {
- $rows = [];
- $items = [];
- foreach ($spareParts as $sp) {
- $items[] = ['type' => 'spare', 'data' => $sp];
- }
- foreach ($details as $detail) {
- $items[] = ['type' => 'detail', 'data' => $detail];
- }
- if (empty($items)) {
- $baseDescription = PricingCode::getPricingCodeDescription(self::BASE_PRICING_CODE);
- $codes = [
- [self::BASE_PRICING_CODE, $baseDescription],
- ];
- $rows[] = $this->applyPricingCodes($mafRow, $codes);
- return $rows;
- }
- foreach ($items as $index => $item) {
- $row = $mafRow;
- if ($index > 0) {
- $this->blankColumns($row, ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'X']);
- }
- if ($item['type'] === 'spare') {
- $rows[] = $this->buildRowsForSparePart($row, $item['data']);
- } else {
- $rows[] = $this->buildRowForDetail($row, $item['data']);
- }
- }
- return $rows;
- }
- private function buildRowsForSparePart(array $mafRow, SparePart $sp): array
- {
- $row = $mafRow;
- $row['L'] = $sp->article;
- $row['M'] = $sp->pivot?->quantity ?? null;
- $row['P'] = $sp->expertise_price;
- $row['Q'] = $sp->tsn_number;
- $codes = [];
- $baseDescription = PricingCode::getPricingCodeDescription(self::BASE_PRICING_CODE);
- $codes[] = [self::BASE_PRICING_CODE, $baseDescription];
- foreach ($sp->pricingCodes as $pc) {
- if ($pc->type !== PricingCode::TYPE_PRICING_CODE) {
- continue;
- }
- if ($pc->code === self::BASE_PRICING_CODE) {
- continue;
- }
- $codes[] = [$pc->code, $pc->description];
- }
- return $this->applyPricingCodes($row, $codes);
- }
- private function buildRowForDetail(array $mafRow, $detail): array
- {
- $row = $mafRow;
- $row['L'] = $detail->name;
- $row['M'] = $detail->quantity;
- $row['P'] = null;
- $row['Q'] = null;
- $baseDescription = PricingCode::getPricingCodeDescription(self::BASE_PRICING_CODE);
- $codes = [
- [self::BASE_PRICING_CODE, $baseDescription],
- ];
- return $this->applyPricingCodes($row, $codes);
- }
- private function applyPricingCodes(array $row, array $codes): array
- {
- $slots = [
- ['R', 'S'],
- ['T', 'U'],
- ['V', 'W'],
- ];
- for ($i = 0; $i < count($slots); $i++) {
- $row[$slots[$i][0]] = null;
- $row[$slots[$i][1]] = null;
- }
- $idx = 0;
- foreach ($codes as $code) {
- if ($idx >= count($slots)) {
- break;
- }
- $row[$slots[$idx][0]] = $code[0] ?? null;
- $row[$slots[$idx][1]] = $code[1] ?? null;
- $idx++;
- }
- return $row;
- }
- private function blankColumns(array &$row, array $cols): void
- {
- foreach ($cols as $col) {
- $row[$col] = null;
- }
- }
- private function writeRow(Worksheet $sheet, int $row, array $data): void
- {
- $rowRange = 'A' . $row . ':X' . $row;
- $sheet->getStyle($rowRange)->getAlignment()->setWrapText(true);
- $sheet->getStyle($rowRange)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
- $sheet->getStyle($rowRange)->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
- foreach ($data as $col => $value) {
- if ($col === 'I') {
- if (!empty($value) && DateHelper::isDate($value)) {
- $excelDate = DateHelper::ISODateToExcelDate($value);
- $sheet->setCellValue('I' . $row, $excelDate);
- $sheet->getStyle('I' . $row)->getNumberFormat()->setFormatCode('m/d/yyyy');
- } else {
- $sheet->setCellValueExplicit('I' . $row, (string)$value, DataType::TYPE_STRING);
- $sheet->getStyle('I' . $row)->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_TEXT);
- }
- continue;
- }
- if ($col === 'P') {
- if(isset($value) && is_numeric($value)) {
- $sheet->setCellValue('P' . $row, (float)$value);
- }
- $sheet->getStyle('P' . $row)->getNumberFormat()->setFormatCode('#,##0.00\\₽');
- continue;
- }
- if ($value === null || $value === '') {
- $sheet->setCellValue($col . $row, null);
- continue;
- }
- $sheet->setCellValueExplicit($col . $row, (string)$value, DataType::TYPE_STRING);
- $sheet->getStyle($col . $row)->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_TEXT);
- }
- }
- private function applyBorders(Worksheet $sheet, int $fromRow, int $toRow): void
- {
- $highestCol = $sheet->getHighestColumn();
- $range = 'A' . $fromRow . ':' . $highestCol . $toRow;
- $sheet->getStyle($range)
- ->getBorders()
- ->getAllBorders()
- ->setBorderStyle(Border::BORDER_THIN)
- ->setColor(new Color('777777'));
- }
- private function applyDefaultStyles(Worksheet $sheet, int $fromRow, int $toRow): void
- {
- if ($toRow < $fromRow) {
- return;
- }
- $highestCol = $sheet->getHighestColumn();
- $range = 'A' . $fromRow . ':' . $highestCol . $toRow;
- $sheet->getStyle($range)->getAlignment()->setWrapText(true);
- $sheet->getStyle($range)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
- $sheet->getStyle($range)->getAlignment()->setVertical(Alignment::VERTICAL_CENTER);
- }
- }
|