Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
63.79% covered (warning)
63.79%
192 / 301
33.33% covered (danger)
33.33%
7 / 21
CRAP
0.00% covered (danger)
0.00%
0 / 1
PrintQrcodeItemService
63.79% covered (warning)
63.79%
192 / 301
33.33% covered (danger)
33.33%
7 / 21
211.29
0.00% covered (danger)
0.00%
0 / 1
 mountItemQrCdode
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
1
 mountProductQrCdode
95.65% covered (success)
95.65%
22 / 23
0.00% covered (danger)
0.00%
0 / 1
3
 buildItemPreviewLabel
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 buildProductPreviewLabel
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 mountZplBlockSmall
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
2
 mountZplBlockMedium
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
2
 mountZplBlockMediumLarge
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
2
 mountZplBlockLarge
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
2
 printQrcodeSmallTag
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
4.01
 printQrcodeMediumTag
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
4.01
 printQrcodeMediumLargeTag
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 printQrcodeLargeTag
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 printProductQrcodeSmallTag
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 printProductQrcodeMediumTag
95.65% covered (success)
95.65%
22 / 23
0.00% covered (danger)
0.00%
0 / 1
4
 printProductQrcodeMediumLargeTag
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
12
 printProductQrcodeLargeTag
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
12
 buildPriorityText
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
4.03
 repeatItemsForCopies
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 mountProductMediumZplBlock
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 truncateLabelValue
40.00% covered (danger)
40.00%
2 / 5
0.00% covered (danger)
0.00%
0 / 1
4.94
 formatLabelDescription
64.29% covered (warning)
64.29%
9 / 14
0.00% covered (danger)
0.00%
0 / 1
3.41
1<?php
2
3namespace Domain\Item\Services;
4
5use App\Models\Item\Item;
6use App\Models\Product\Product;
7use Exception;
8
9class PrintQrcodeItemService
10{
11    private const TWO_COLUMN_PRINT_WIDTH = 812;
12
13    private const SECOND_COLUMN_OFFSET = 400;
14
15    public function mountItemQrCdode($item): array
16    {
17        $item = Item::find($item);
18
19        $item->load([
20            'product',
21            'integration',
22        ]);
23
24        $qrcode = [
25            'type' => 'product',
26            'id' => $item->product_id,
27            'pf' => $item->integration->parameters['priority_field'],
28            'pv' => $item->integration->parameters['priority_value'],
29            'q' => 0,
30        ];
31        $product = [
32            'description' => $item->product->description,
33            'product_code' => $item->product->integration->parameters['integration_product'],
34            'product_derivation' => $item->product->integration->parameters['integration_derivation'],
35        ];
36
37        return [
38            'qrcode' => $qrcode,
39            'product' => $product,
40        ];
41    }
42
43    public function mountProductQrCdode($product): array
44    {
45        $product = Product::find($product);
46
47        $product->load([
48            'integration',
49        ]);
50
51        $priorityField = $product->priority_field;
52        if (is_object($priorityField) && property_exists($priorityField, 'value')) {
53            $priorityField = $priorityField->value;
54        }
55
56        $qrcode = [
57            'type' => 'product',
58            'id' => $product->id,
59            'pf' => $priorityField,
60            'pv' => null,
61            'q' => 0,
62        ];
63        $productData = [
64            'description' => $product->description,
65            'product_code' => $product->integration?->parameters['integration_product'] ?? '',
66            'product_derivation' => $product->integration?->parameters['integration_derivation'] ?? '',
67        ];
68
69        return [
70            'qrcode' => $qrcode,
71            'product' => $productData,
72        ];
73    }
74
75    public function buildItemPreviewLabel(int $itemId): array
76    {
77        $data = $this->mountItemQrCdode($itemId);
78
79        return [
80            'title' => 'Etiqueta de Item',
81            'qrcode' => $data['qrcode'],
82            'lines' => [
83                $data['product']['description'],
84                'Cod. Pro.: '.$data['product']['product_code'],
85                'Cod. Der.: '.$data['product']['product_derivation'],
86            ],
87        ];
88    }
89
90    public function buildProductPreviewLabel(int $productId): array
91    {
92        $data = $this->mountProductQrCdode($productId);
93
94        return [
95            'title' => 'Etiqueta de Produto',
96            'qrcode' => $data['qrcode'],
97            'lines' => [
98                $data['product']['description'],
99                'Cod. Pro.: '.$data['product']['product_code'],
100                'Cod. Der.: '.$data['product']['product_derivation'],
101            ],
102        ];
103    }
104
105    public function mountZplBlockSmall($item, $offset): string
106    {
107        $data = $this->mountItemQrCdode($item);
108        $jsonContent = json_encode($data['qrcode']);
109        $description = $this->formatLabelDescription($data['product']['description'], 240, 20, 2);
110        $productCode = $data['product']['product_code'];
111        $derivation = $data['product']['product_derivation'];
112        $priority = $this->buildPriorityText($data['qrcode'], 14);
113
114        $codeAndDer = $productCode.' - '.$derivation;
115        $priorityBlock = $priority
116            ? "\n            ^FO".(155 + $offset).",135^A0N,17,17^FB240,1,,L^FD$priority^FS"
117            : '';
118
119        return '
120            ^FO'.(25 + $offset).",10^BQN,2,3
121            ^FDQA,$jsonContent^FS
122
123            ^FO".(155 + $offset).",15^A0N,20,20^FB240,4,5,L^FD$description^FS
124
125            ^FO".(155 + $offset).",110^A0N,25,25^FB240,1,,L^FD$codeAndDer^FS
126            $priorityBlock
127        ";
128    }
129
130    public function mountZplBlockMedium($item, $offset): string
131    {
132        $data = $this->mountItemQrCdode($item);
133        $jsonContent = json_encode($data['qrcode']);
134        $description = $this->formatLabelDescription($data['product']['description'], 600, 30, 3);
135        $productCode = $data['product']['product_code'];
136        $derivation = $data['product']['product_derivation'];
137        $priority = $this->buildPriorityText($data['qrcode'], 22);
138        $priorityBlock = $priority
139            ? "\n            ^FO".(90 + $offset).",340^A0R,20,20^FD$priority^FS"
140            : '';
141
142        return '
143            ^FO'.(60 + $offset).",50^BQN,2,6
144            ^FDQA,$jsonContent^FS
145
146            ^FO".(285 + $offset).",15^A0R,30,30^FB600,3,,L^FD$description^FS
147
148            ^FO".(260 + $offset).',340^A0R,25,25^FDCOD. PRO.^FS
149            ^FO'.(220 + $offset).",340^A0R,35,35^FD$productCode^FS
150
151            ^FO".(160 + $offset).',340^A0R,20,20^FDCOD. DER.^FS
152            ^FO'.(120 + $offset).",340^A0R,25,25^FD$derivation^FS
153            $priorityBlock
154        ";
155    }
156
157    public function mountZplBlockMediumLarge($item): string
158    {
159        $data = $this->mountItemQrCdode($item);
160        $jsonContent = json_encode($data['qrcode']);
161        $description = $data['product']['description'];
162        $productCode = $data['product']['product_code'];
163        $derivation = $data['product']['product_derivation'];
164        $priority = $this->buildPriorityText($data['qrcode'], 26);
165        $priorityBlock = $priority
166            ? "\n            ^FO350,480^A0,35,35^FD$priority^FS"
167            : '';
168
169        $jsonHex = str_replace('"', '_22', $jsonContent);
170        // O QB exige a contagem de caracteres original (antes da troca para hex)
171        // Formatado com 4 dígitos (ex: 0085)
172        $magnificacao = '9'; // 57 * 9 = 513 pontos (~6.4cm)
173
174        // Cada etiqueta Ã© um bloco isolado de ^XA até ^XZ
175        return "
176            ^XA
177            ^CI28
178            ^PW799
179            ^LL560
180
181            ^FO40,210^BQN,2,7
182            ^FH^FDQA,$jsonHex^FS
183
184            ^FO40,40^A0,50,50^FB750,3,,L^FD$description^FS
185
186            ^FO350,210^A0,40,40^FDCOD. PRO.:^FS
187            ^FO350,260^A0,70,70^FD$productCode^FS
188
189            ^FO350,390^A0,40,40^FDCOD. DER.:^FS
190            ^FO350,430^A0,50,50^FD$derivation^FS
191            $priorityBlock
192
193            ^XZ";
194    }
195
196    public function mountZplBlockLarge($item): string
197    {
198        $data = $this->mountItemQrCdode($item);
199        $jsonContent = json_encode($data['qrcode']);
200        $description = $data['product']['description'];
201        $productCode = $data['product']['product_code'];
202        $derivation = $data['product']['product_derivation'];
203        $priority = $this->buildPriorityText($data['qrcode'], 30);
204        $priorityBlock = $priority
205            ? "\n            ^FO70,600^A0R,35,35^FD$priority^FS"
206            : '';
207
208        $jsonHex = str_replace('"', '_22', $jsonContent);
209        // O QB exige a contagem de caracteres original (antes da troca para hex)
210        // Formatado com 4 dígitos (ex: 0085)
211        $magnificacao = '9'; // 57 * 9 = 513 pontos (~6.4cm)
212
213        // Cada etiqueta Ã© um bloco isolado de ^XA até ^XZ
214        return "
215            ^XA
216            ^CI28
217            ^PW840
218            ^LL1184
219
220            ^FO200,50^BQN,2,$magnificacao
221            ^FH^FDQB,$jsonHex^FS
222
223            ^FO680,50^A0R,60,60^FB1000,2,,L^FD$description^FS
224
225            ^FO480,600^A0R,50,50^FDCOD. PRO.:^FS
226            ^FO380,600^A0R,100,100^FD$productCode^FS
227
228            ^FO250,600^A0R,40,40^FDCOD. DER.:^FS
229            ^FO180,600^A0R,60,60^FD$derivation^FS
230            $priorityBlock
231
232            ^XZ";
233    }
234
235    /**
236     * @throws Exception
237     */
238    public function printQrcodeSmallTag($item, int $quantity = 1): void
239    {
240        $zpl = '';
241        foreach (array_chunk($this->repeatItemsForCopies($item, $quantity), 2) as $item) {
242
243            $zpl .= '^XA^CI28^PW'.self::TWO_COLUMN_PRINT_WIDTH;
244            $zpl .= $this->mountZplBlockSmall($item[0], 0);
245
246            if (isset($item[1])) {
247                $zpl .= $this->mountZplBlockSmall($item[1], self::SECOND_COLUMN_OFFSET);
248            }
249
250            $zpl .= '^XZ';
251        }
252
253        $fp = fsockopen(env('WAREHOUSE_PRINTER_IP'), env('WAREHOUSE_PRINTER_PORT'), $errno, $errstr, 5);
254
255        if (! $fp) {
256            throw new Exception("Erro: $errstr ($errno)");
257        }
258
259        fwrite($fp, $zpl);
260        fclose($fp);
261    }
262
263    /**
264     * @throws Exception
265     */
266    public function printQrcodeMediumTag($items, int $quantity = 1): void
267    {
268        $zpl = '';
269        foreach (array_chunk($this->repeatItemsForCopies($items, $quantity), 2) as $item) {
270            $zpl .= '^XA^CI28^PW'.self::TWO_COLUMN_PRINT_WIDTH;
271            $zpl .= $this->mountZplBlockMedium($item[0], 0);
272
273            if (isset($item[1])) {
274                $zpl .= $this->mountZplBlockMedium($item[1], self::SECOND_COLUMN_OFFSET);
275            }
276
277            $zpl .= '^XZ';
278        }
279
280        $fp = fsockopen(env('WAREHOUSE_PRINTER_IP'), env('WAREHOUSE_PRINTER_PORT'), $errno, $errstr, 5);
281
282        if (! $fp) {
283            throw new Exception("Erro: $errstr ($errno)");
284        }
285
286        fwrite($fp, $zpl);
287        fclose($fp);
288    }
289
290    /**
291     * @throws Exception
292     */
293    public function printQrcodeMediumLargeTag($items, int $quantity = 1): void
294    {
295        $zpl = '';
296        foreach ($items as $item) {
297            for ($i = 0; $i < $quantity; $i++) {
298                $zpl .= $this->mountZplBlockMediumLarge($item);
299            }
300        }
301
302        $fp = fsockopen(env('WAREHOUSE_PRINTER_IP'), env('WAREHOUSE_PRINTER_PORT'), $errno, $errstr, 5);
303
304        if (! $fp) {
305            throw new Exception("Erro: $errstr ($errno)");
306        }
307
308        fwrite($fp, $zpl);
309        fclose($fp);
310    }
311
312    /**
313     * @throws Exception
314     */
315    public function printQrcodeLargeTag($items, int $quantity = 1): void
316    {
317        $zpl = '';
318        foreach ($items as $item) {
319            for ($i = 0; $i < $quantity; $i++) {
320                $zpl .= $this->mountZplBlockLarge($item);
321            }
322        }
323
324        $fp = fsockopen(env('WAREHOUSE_PRINTER_IP'), env('WAREHOUSE_PRINTER_PORT'), $errno, $errstr, 5);
325
326        if (! $fp) {
327            throw new Exception("Erro: $errstr ($errno)");
328        }
329
330        fwrite($fp, $zpl);
331        fclose($fp);
332
333    }
334
335    /**
336     * @throws Exception
337     */
338    public function printProductQrcodeSmallTag(int $productId, int $quantity = 1): void
339    {
340        $data = $this->mountProductQrCdode($productId);
341        $jsonContent = json_encode($data['qrcode']);
342        $description = $this->formatLabelDescription($data['product']['description'], 240, 20, 2);
343        $codeAndDer = $data['product']['product_code'].' - '.$data['product']['product_derivation'];
344
345        $zpl = '';
346        for ($i = 0; $i < $quantity; $i++) {
347            $zpl .= '^XA^CI28^PW824';
348            $zpl .= "
349                ^FO25,10^BQN,2,3
350                ^FDQA,$jsonContent^FS
351
352                ^FO155,15^A0N,20,20^FB240,4,5,L^FD$description^FS
353
354                ^FO155,120^A0N,25,25^FB240,1,,L^FD$codeAndDer^FS
355            ";
356            $zpl .= '^XZ';
357        }
358
359        $fp = fsockopen(env('WAREHOUSE_PRINTER_IP'), env('WAREHOUSE_PRINTER_PORT'), $errno, $errstr, 5);
360        if (! $fp) {
361            throw new Exception("Erro: $errstr ($errno)");
362        }
363
364        fwrite($fp, $zpl);
365        fclose($fp);
366    }
367
368    /**
369     * @throws Exception
370     */
371    public function printProductQrcodeMediumTag(int $productId, int $quantity = 1): void
372    {
373        $data = $this->mountProductQrCdode($productId);
374        $jsonContent = json_encode($data['qrcode']);
375        $description = $this->formatLabelDescription($data['product']['description'], 600, 30, 3);
376        $productCode = $data['product']['product_code'];
377        $derivation = $data['product']['product_derivation'];
378
379        $zpl = '';
380        foreach (array_chunk(array_fill(0, $quantity, true), 2) as $copies) {
381            $zpl .= '^XA^CI28^PW824';
382            $zpl .= $this->mountProductMediumZplBlock($jsonContent, $description, $productCode, $derivation, 0);
383
384            if (isset($copies[1])) {
385                $zpl .= $this->mountProductMediumZplBlock(
386                    $jsonContent,
387                    $description,
388                    $productCode,
389                    $derivation,
390                    self::SECOND_COLUMN_OFFSET
391                );
392            }
393
394            $zpl .= '^XZ';
395        }
396
397        $fp = fsockopen(env('WAREHOUSE_PRINTER_IP'), env('WAREHOUSE_PRINTER_PORT'), $errno, $errstr, 5);
398        if (! $fp) {
399            throw new Exception("Erro: $errstr ($errno)");
400        }
401
402        fwrite($fp, $zpl);
403        fclose($fp);
404    }
405
406    /**
407     * @throws Exception
408     */
409    public function printProductQrcodeMediumLargeTag(int $productId, int $quantity = 1): void
410    {
411        $data = $this->mountProductQrCdode($productId);
412        $jsonContent = json_encode($data['qrcode']);
413        $description = $data['product']['description'];
414        $productCode = $data['product']['product_code'];
415        $derivation = $data['product']['product_derivation'];
416        $jsonHex = str_replace('"', '_22', $jsonContent);
417
418        $zpl = '';
419        for ($i = 0; $i < $quantity; $i++) {
420            $zpl .= "
421                ^XA
422                ^CI28
423                ^PW799
424                ^LL560
425
426                ^FO40,240^BQN,2,7
427                ^FH^FDQA,$jsonHex^FS
428
429                ^FO40,90^A0,50,50^FB750,3,,L^FD$description^FS
430
431                ^FO350,250^A0,40,40^FDCOD. PRO.:^FS
432                ^FO350,300^A0,70,70^FD$productCode^FS
433
434                ^FO350,430^A0,40,40^FDCOD. DER.:^FS
435                ^FO350,470^A0,50,50^FD$derivation^FS
436
437                ^XZ";
438        }
439
440        $fp = fsockopen(env('WAREHOUSE_PRINTER_IP'), env('WAREHOUSE_PRINTER_PORT'), $errno, $errstr, 5);
441        if (! $fp) {
442            throw new Exception("Erro: $errstr ($errno)");
443        }
444
445        fwrite($fp, $zpl);
446        fclose($fp);
447    }
448
449    /**
450     * @throws Exception
451     */
452    public function printProductQrcodeLargeTag(int $productId, int $quantity = 1): void
453    {
454        $data = $this->mountProductQrCdode($productId);
455        $jsonContent = json_encode($data['qrcode']);
456        $description = $data['product']['description'];
457        $productCode = $data['product']['product_code'];
458        $derivation = $data['product']['product_derivation'];
459        $jsonHex = str_replace('"', '_22', $jsonContent);
460        $magnificacao = '9';
461
462        $zpl = '';
463        for ($i = 0; $i < $quantity; $i++) {
464            $zpl .= "
465                ^XA
466                ^CI28
467                ^PW840
468                ^LL1184
469
470                ^FO200,50^BQN,2,$magnificacao
471                ^FH^FDQB,$jsonHex^FS
472
473                ^FO680,50^A0R,60,60^FB1000,2,,L^FD$description^FS
474
475                ^FO480,600^A0R,50,50^FDCOD. PRO.:^FS
476                ^FO380,600^A0R,100,100^FD$productCode^FS
477
478                ^FO250,600^A0R,40,40^FDCOD. DER.:^FS
479                ^FO180,600^A0R,60,60^FD$derivation^FS
480
481                ^XZ";
482        }
483
484        $fp = fsockopen(env('WAREHOUSE_PRINTER_IP'), env('WAREHOUSE_PRINTER_PORT'), $errno, $errstr, 5);
485        if (! $fp) {
486            throw new Exception("Erro: $errstr ($errno)");
487        }
488
489        fwrite($fp, $zpl);
490        fclose($fp);
491    }
492
493    private function buildPriorityText(array $qrcode, int $maxValueLength): ?string
494    {
495        $priorityField = $qrcode['pf'] ?? null;
496        $priorityValue = $qrcode['pv'] ?? null;
497
498        if (is_null($priorityField) || is_null($priorityValue)) {
499            return null;
500        }
501
502        $priorityValue = trim((string) $priorityValue);
503        if ($priorityValue === '') {
504            return null;
505        }
506
507        return 'VLR. PRIO.: '.$this->truncateLabelValue($priorityValue, $maxValueLength);
508    }
509
510    private function repeatItemsForCopies(array $items, int $quantity): array
511    {
512        $copies = [];
513
514        foreach ($items as $item) {
515            for ($i = 0; $i < $quantity; $i++) {
516                $copies[] = $item;
517            }
518        }
519
520        return $copies;
521    }
522
523    private function mountProductMediumZplBlock(
524        string $jsonContent,
525        string $description,
526        string $productCode,
527        string $derivation,
528        int $offset
529    ): string {
530        return '
531                ^FO'.(60 + $offset).",50^BQN,2,6
532                ^FDQA,$jsonContent^FS
533
534                ^FO".(285 + $offset).",15^A0R,30,30^FB600,3,,L^FD$description^FS
535
536                ^FO".(260 + $offset).',340^A0R,25,25^FDCOD. PRO.^FS
537                ^FO'.(220 + $offset).",340^A0R,35,35^FD$productCode^FS
538
539                ^FO".(160 + $offset).',340^A0R,20,20^FDCOD. DER.^FS
540                ^FO'.(120 + $offset).",340^A0R,25,25^FD$derivation^FS
541            ";
542    }
543
544    private function truncateLabelValue(string $value, int $maxLength): string
545    {
546        if (mb_strlen($value) <= $maxLength) {
547            return $value;
548        }
549
550        if ($maxLength <= 3) {
551            return mb_substr($value, 0, $maxLength);
552        }
553
554        return mb_substr($value, 0, $maxLength - 3).'...';
555    }
556
557    public function formatLabelDescription(
558        string $text,
559        int $boxWidth,
560        int $fontWidth,
561        int $maxLines
562    ): string {
563        $avgCharWidth = $fontWidth * 0.55;
564        $charsPerLine = (int) floor($boxWidth / $avgCharWidth);
565
566        // quebra o texto
567        $wrappedLines = wordwrap($text, $charsPerLine, "\n", true);
568        $lines = explode("\n", $wrappedLines);
569
570        // limita quantidade de linhas
571        if (count($lines) > $maxLines) {
572            $lines = array_slice($lines, 0, $maxLines);
573
574            $lastLineIndex = $maxLines - 1;
575            $lastLine = $lines[$lastLineIndex];
576
577            if (mb_strlen($lastLine) > 3) {
578                $lines[$lastLineIndex] = mb_substr($lastLine, 0, -3).'...';
579            }
580        }
581
582        // ðŸ‘‡ garante espaço no final de cada linha
583        $lines = array_map(function ($line) {
584            return rtrim($line).' ';
585        }, $lines);
586
587        return implode("\n", $lines);
588    }
589}