Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
110 / 110
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
TemplateMediaBlockCompiler
100.00% covered (success)
100.00%
109 / 109
100.00% covered (success)
100.00%
8 / 8
14
100.00% covered (success)
100.00%
1 / 1
 compileBrandHeader
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 compileImageBox
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
4
 compileBarcodeQr
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
4
 compileBankTransferQrBox
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 compileChartSparklineBlock
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 compileVerificationBadgeBlock
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 compileSignatureBlock
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
1
 compileSignaturesBox
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Core\Template\Application\Service\BlockCompiler;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Core\Template\Domain\Model\TemplateBlock;
12
13/**
14 * Media, brand header, QR/barcode, and signature block compiler.
15 *
16 * Compiles logos, headers, barcodes, QR codes, verification badges,
17 * and signature boxes into HTML/Twig markup.
18 *
19 * @package App\Core\Template\Application\Service\BlockCompiler
20 */
21final class TemplateMediaBlockCompiler
22{
23    private const DEFAULT_RECORD_ID_OR_PREFIX = '{{ record.prefix_number|default(record.id) }}';
24
25    /**
26     * Compiles Brand Header block.
27     *
28     * @param TemplateBlock $block Block instance.
29     * @return string Compiled HTML.
30     */
31    public function compileBrandHeader(TemplateBlock $block): string
32    {
33        $title = (string)($block->props['title'] ?? 'DOKUMENT HANDLOWY');
34        $subtitle = (string)($block->props['subtitle'] ?? self::DEFAULT_RECORD_ID_OR_PREFIX);
35
36        return '<table style="width:100%;border-bottom:2px solid #0f172a;padding-bottom:12px;margin-bottom:16px;">'
37            . '<tr><td style="vertical-align:middle;">{{ company_logo(160, 50) }}</td>'
38            . '<td style="text-align:right;vertical-align:middle;">'
39            . '<h2 style="margin:0;font-size:16pt;color:#0f172a;">' . htmlspecialchars($title, ENT_QUOTES, 'UTF-8')
40            . '</h2>'
41            . '<p style="margin:4px 0 0 0;font-size:10pt;color:#64748b;">' . $subtitle . '</p>'
42            . '</td></tr></table>';
43    }
44
45    /**
46     * Compiles image or logo block primitive.
47     *
48     * @param TemplateBlock $block Block instance.
49     * @return string Compiled HTML.
50     */
51    public function compileImageBox(TemplateBlock $block): string
52    {
53        $props = $block->props;
54        $source = (string)($props['source'] ?? 'logo');
55        $width = htmlspecialchars((string)($props['width'] ?? '180px'), ENT_QUOTES, 'UTF-8');
56        $align = htmlspecialchars((string)($props['align'] ?? 'left'), ENT_QUOTES, 'UTF-8');
57
58        if ($source === 'logo') {
59            return sprintf(
60                '<div style="text-align:%s;margin:8px 0;">{{ company_logo(%d, 60) }}</div>',
61                $align,
62                (int)$width > 0 ? (int)$width : 180
63            );
64        }
65
66        $url = htmlspecialchars((string)($props['url'] ?? ''), ENT_QUOTES, 'UTF-8');
67
68        return sprintf(
69            '<div class="template-image-box" style="text-align:%s;margin:8px 0;">'
70            . '<img src="%s" style="max-width:%s;height:auto;" alt="Document Image" /></div>',
71            $align,
72            $url !== '' ? $url : '{{ record.image_url|default(\'\') }}',
73            $width
74        );
75    }
76
77    /**
78     * Compiles QR code or barcode primitive.
79     *
80     * @param TemplateBlock $block Block instance.
81     * @return string Compiled HTML.
82     */
83    public function compileBarcodeQr(TemplateBlock $block): string
84    {
85        $props = $block->props;
86        $codeType = (string)($props['code_type'] ?? 'qr');
87        $fieldKey = htmlspecialchars((string)($props['field_key'] ?? 'prefix_number'), ENT_QUOTES, 'UTF-8');
88        $caption = htmlspecialchars((string)($props['caption'] ?? ''), ENT_QUOTES, 'UTF-8');
89
90        if ($codeType === 'barcode') {
91            return sprintf(
92                '<div class="template-code-box" style="text-align:center;margin:8px 0;">'
93                . '{{ barcode(record.%s|default(\'12345678\')) }}'
94                . ($caption !== '' ? sprintf('<div style="font-size:7.5pt;color:#64748b;">%s</div>', $caption) : '')
95                . '</div>',
96                $fieldKey
97            );
98        }
99
100        return sprintf(
101            '<div class="template-code-box" style="text-align:center;margin:8px 0;">'
102            . '{{ qr_code(record.%s|default(\'https://ammonly.com\'), 110) }}'
103            . ($caption !== '' ? sprintf('<div style="font-size:7.5pt;color:#64748b;">%s</div>', $caption) : '')
104            . '</div>',
105            $fieldKey
106        );
107    }
108
109    /**
110     * Compiles Bank Transfer QR Code block.
111     *
112     * @param TemplateBlock $block Block instance.
113     * @return string Compiled HTML.
114     */
115    public function compileBankTransferQrBox(TemplateBlock $block): string
116    {
117        $account = (string)($block->props['account'] ?? '{{ record.bank_account|default("PL00000000000000000000") }}');
118        $recipient = (string)($block->props['recipient'] ?? '{{ system.company_name }}');
119        $amount = (string)($block->props['amount'] ?? '{{ record.amount_gross|default(0) }}');
120        $title = (string)($block->props['title'] ?? self::DEFAULT_RECORD_ID_OR_PREFIX);
121
122        return sprintf(
123            '{{ bank_transfer_qr(%s, %s, %s, %s) }}',
124            var_export($account, true),
125            var_export($recipient, true),
126            var_export($amount, true),
127            var_export($title, true)
128        );
129    }
130
131    /**
132     * Compiles Vector Sparkline Chart block.
133     *
134     * @param TemplateBlock $block Block instance.
135     * @return string Compiled HTML.
136     */
137    public function compileChartSparklineBlock(TemplateBlock $block): string
138    {
139        $type = (string)($block->props['type'] ?? 'bar');
140        $width = (int)($block->props['width'] ?? 140);
141        $height = (int)($block->props['height'] ?? 32);
142        $color = (string)($block->props['color'] ?? '#2563eb');
143
144        return sprintf(
145            '<div style="margin:12px 0;">{{ chart_sparkline([15, 25, 40, 32, 55, 68, 85], %s, %d, %d, %s) }}</div>',
146            var_export($type, true),
147            $width,
148            $height,
149            var_export($color, true)
150        );
151    }
152
153    /**
154     * Compiles Digital Document Verification Badge block.
155     *
156     * @param TemplateBlock $block Block instance.
157     * @return string Compiled HTML.
158     */
159    public function compileVerificationBadgeBlock(TemplateBlock $block): string
160    {
161        $defaultHash = (string)($block->props['default_hash'] ?? '00000000000000000000000000000000');
162
163        return sprintf(
164            '{{ verification_badge(record.verification_hash|default(%s)) }}',
165            var_export($defaultHash, true)
166        );
167    }
168
169    /**
170     * Compiles Signature block.
171     *
172     * @param TemplateBlock $block Block instance.
173     * @return string Compiled HTML.
174     */
175    public function compileSignatureBlock(TemplateBlock $block): string
176    {
177        $issuer = htmlspecialchars(
178            (string)($block->props['issuer_label'] ?? 'Issuer signature'),
179            ENT_QUOTES,
180            'UTF-8'
181        );
182        $client = htmlspecialchars(
183            (string)($block->props['client_label'] ?? 'Client / Recipient signature'),
184            ENT_QUOTES,
185            'UTF-8'
186        );
187
188        return '<table style="width:100%;margin-top:40px;border-collapse:collapse;">'
189            . '<tr>'
190            . '<td style="width:45%;text-align:center;vertical-align:bottom;">'
191            . '<div style="border-bottom:1px solid #94a3b8;padding-bottom:60px;"></div>'
192            . '<span style="font-size:9pt;color:#64748b;">' . $issuer . '</span>'
193            . '</td>'
194            . '<td style="width:10%;"></td>'
195            . '<td style="width:45%;text-align:center;vertical-align:bottom;">'
196            . '<div style="border-bottom:1px solid #94a3b8;padding-bottom:60px;"></div>'
197            . '<span style="font-size:9pt;color:#64748b;">' . $client . '</span>'
198            . '</td>'
199            . '</tr></table>';
200    }
201
202    /**
203     * Compiles formal signature boxes primitive.
204     *
205     * @param TemplateBlock $block Block instance.
206     * @return string Compiled HTML.
207     */
208    public function compileSignaturesBox(TemplateBlock $block): string
209    {
210        $props = $block->props;
211        $left = htmlspecialchars((string)($props['left_party'] ?? 'Wystawca dokumentu'), ENT_QUOTES, 'UTF-8');
212        $right = htmlspecialchars((string)($props['right_party'] ?? 'Odbiorca dokumentu'), ENT_QUOTES, 'UTF-8');
213
214        return sprintf(
215            '<div class="template-signatures-box" style="display:flex;justify-content:space-between;margin:24px 0;'
216            . 'padding-top:16px;width:100%%;">'
217            . '<div style="width:45%%;text-align:center;">'
218            . '<div style="border-top:1px dashed #94a3b8;padding-top:6px;font-size:8pt;color:#64748b;">%s</div></div>'
219            . '<div style="width:45%%;text-align:center;">'
220            . '<div style="border-top:1px dashed #94a3b8;padding-top:6px;font-size:8pt;color:#64748b;">%s</div></div>'
221            . '</div>',
222            $left,
223            $right
224        );
225    }
226}