Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
94 / 94
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
SqlTaxRateRepository
100.00% covered (success)
100.00%
93 / 93
100.00% covered (success)
100.00%
8 / 8
27
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 findById
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 findByCode
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 findDefault
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 findAll
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
5
 save
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
8
 delete
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 mapRowToEntity
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
5
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\Modules\Tax\Infrastructure\Repository;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Core\Instance\Application\Service\InstanceContextManagerInterface;
12use App\Modules\Tax\Domain\Model\TaxRate;
13use App\Modules\Tax\Domain\Repository\TaxRateRepositoryInterface;
14use App\Modules\Tax\Domain\ValueObject\TaxCalculationMode;
15use App\Core\Database\Repository\TenantAwareRepositoryTrait;
16use App\Modules\Tax\Domain\ValueObject\TaxStatus;
17use PDO;
18
19/**
20 * Concrete SQL/PDO repository for managing TaxRate entities.
21 */
22final readonly class SqlTaxRateRepository implements TaxRateRepositoryInterface
23{
24    use TenantAwareRepositoryTrait;
25
26    /**
27     * SqlTaxRateRepository constructor.
28     */
29    public function __construct(
30        protected PDO $pdo,
31        private string $tablePrefix = 'a_',
32        protected ?InstanceContextManagerInterface $instanceManager = null,
33        protected ?PDO $clientPdo = null
34    ) {
35    }
36
37    /**
38     * @inheritDoc
39     */
40    public function findById(int $id): ?TaxRate
41    {
42        $table = $this->tablePrefix . 'mod_tax_rates_records';
43        $sql = "SELECT `id`, `tax_code`, `tax_name`, `rate_percent`, `calculation_mode`, `tax_status`,
44                       `legal_exemption_note`, `fiscal_code`, `is_default`, `is_active`, `owner`,
45                       `created_at`, `updated_at`
46                FROM `{$table}`
47                WHERE `id` = :id AND `special_access` != 0
48                LIMIT 1";
49
50        $stmt = $this->getPdo()->prepare($sql);
51        $stmt->bindValue(':id', $id, PDO::PARAM_INT);
52        $stmt->execute();
53        $row = $stmt->fetch(PDO::FETCH_ASSOC);
54
55        return $row ? $this->mapRowToEntity($row) : null;
56    }
57
58    /**
59     * @inheritDoc
60     */
61    public function findByCode(string $code): ?TaxRate
62    {
63        $table = $this->tablePrefix . 'mod_tax_rates_records';
64        $sql = "SELECT `id`, `tax_code`, `tax_name`, `rate_percent`, `calculation_mode`, `tax_status`,
65                       `legal_exemption_note`, `fiscal_code`, `is_default`, `is_active`, `owner`,
66                       `created_at`, `updated_at`
67                FROM `{$table}`
68                WHERE `tax_code` = :code AND `special_access` != 0
69                LIMIT 1";
70
71        $stmt = $this->getPdo()->prepare($sql);
72        $stmt->bindValue(':code', $code, PDO::PARAM_STR);
73        $stmt->execute();
74        $row = $stmt->fetch(PDO::FETCH_ASSOC);
75
76        return $row ? $this->mapRowToEntity($row) : null;
77    }
78
79    /**
80     * @inheritDoc
81     */
82    public function findDefault(): ?TaxRate
83    {
84        $table = $this->tablePrefix . 'mod_tax_rates_records';
85        $sql = "SELECT `id`, `tax_code`, `tax_name`, `rate_percent`, `calculation_mode`, `tax_status`,
86                       `legal_exemption_note`, `fiscal_code`, `is_default`, `is_active`, `owner`,
87                       `created_at`, `updated_at`
88                FROM `{$table}`
89                WHERE `is_default` = 1 AND `is_active` = 1 AND `special_access` != 0
90                LIMIT 1";
91
92        $stmt = $this->getPdo()->query($sql);
93        $row = $stmt ? $stmt->fetch(PDO::FETCH_ASSOC) : false;
94
95        return $row ? $this->mapRowToEntity($row) : null;
96    }
97
98    /**
99     * @inheritDoc
100     */
101    public function findAll(array $filters = []): array
102    {
103        $table = $this->tablePrefix . 'mod_tax_rates_records';
104        $where = ['`special_access` != 0'];
105        $params = [];
106
107        if (isset($filters['is_active'])) {
108            $where[] = '`is_active` = :is_active';
109            $params[':is_active'] = (int) $filters['is_active'];
110        }
111
112        if (!empty($filters['tax_status'])) {
113            $where[] = '`tax_status` = :tax_status';
114            $params[':tax_status'] = (string) $filters['tax_status'];
115        }
116
117        $whereSql = implode(' AND ', $where);
118        $sql = "SELECT `id`, `tax_code`, `tax_name`, `rate_percent`, `calculation_mode`, `tax_status`,
119                       `legal_exemption_note`, `fiscal_code`, `is_default`, `is_active`, `owner`,
120                       `created_at`, `updated_at`
121                FROM `{$table}`
122                WHERE {$whereSql}
123                ORDER BY `is_default` DESC, `rate_percent` DESC, `id` ASC";
124
125        $stmt = $this->getPdo()->prepare($sql);
126        foreach ($params as $key => $val) {
127            $stmt->bindValue($key, $val);
128        }
129        $stmt->execute();
130
131        $rates = [];
132        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
133            $rates[] = $this->mapRowToEntity($row);
134        }
135
136        return $rates;
137    }
138
139    /**
140     * @inheritDoc
141     */
142    public function save(TaxRate $taxRate): TaxRate
143    {
144        $table = $this->tablePrefix . 'mod_tax_rates_records';
145
146        if ($taxRate->isDefault) {
147            $this->getPdo()->exec("UPDATE `{$table}` SET `is_default` = 0 WHERE 1=1");
148        }
149
150        if ($taxRate->id !== null && $taxRate->id > 0) {
151            $sql = "UPDATE `{$table}` SET
152                        `tax_code` = :tax_code,
153                        `tax_name` = :tax_name,
154                        `rate_percent` = :rate_percent,
155                        `calculation_mode` = :calculation_mode,
156                        `tax_status` = :tax_status,
157                        `legal_exemption_note` = :legal_exemption_note,
158                        `fiscal_code` = :fiscal_code,
159                        `is_default` = :is_default,
160                        `is_active` = :is_active,
161                        `owner` = :owner
162                    WHERE `id` = :id";
163            $stmt = $this->getPdo()->prepare($sql);
164            $stmt->bindValue(':id', $taxRate->id, PDO::PARAM_INT);
165        } else {
166            $sql = "INSERT INTO `{$table}` (
167                        `tax_code`, `tax_name`, `rate_percent`, `calculation_mode`, `tax_status`,
168                        `legal_exemption_note`, `fiscal_code`, `is_default`, `is_active`, `owner`
169                    ) VALUES (
170                        :tax_code, :tax_name, :rate_percent, :calculation_mode, :tax_status,
171                        :legal_exemption_note, :fiscal_code, :is_default, :is_active, :owner
172                    )";
173            $stmt = $this->getPdo()->prepare($sql);
174        }
175
176        $stmt->bindValue(':tax_code', $taxRate->taxCode, PDO::PARAM_STR);
177        $stmt->bindValue(':tax_name', $taxRate->taxName, PDO::PARAM_STR);
178        $stmt->bindValue(':rate_percent', $taxRate->ratePercent);
179        $stmt->bindValue(':calculation_mode', $taxRate->calculationMode->value, PDO::PARAM_STR);
180        $stmt->bindValue(':tax_status', $taxRate->taxStatus->value, PDO::PARAM_STR);
181        $stmt->bindValue(':legal_exemption_note', $taxRate->legalExemptionNote, PDO::PARAM_STR);
182        $stmt->bindValue(':fiscal_code', $taxRate->fiscalCode, PDO::PARAM_STR);
183        $stmt->bindValue(':is_default', $taxRate->isDefault ? 1 : 0, PDO::PARAM_INT);
184        $stmt->bindValue(':is_active', $taxRate->isActive ? 1 : 0, PDO::PARAM_INT);
185        $stmt->bindValue(':owner', $taxRate->owner, PDO::PARAM_INT);
186        $stmt->execute();
187
188        if ($taxRate->id === null || $taxRate->id <= 0) {
189            $taxRate->id = (int) $this->getPdo()->lastInsertId();
190        }
191
192        return $taxRate;
193    }
194
195    /**
196     * @inheritDoc
197     */
198    public function delete(int $id): bool
199    {
200        $table = $this->tablePrefix . 'mod_tax_rates_records';
201        $sql = "UPDATE `{$table}` SET `special_access` = 0 WHERE `id` = :id";
202        $stmt = $this->getPdo()->prepare($sql);
203        $stmt->bindValue(':id', $id, PDO::PARAM_INT);
204
205        return $stmt->execute();
206    }
207
208    /**
209     * Map database row array to TaxRate domain model.
210     *
211     * @param array<string, mixed> $row
212     */
213    private function mapRowToEntity(array $row): TaxRate
214    {
215        return new TaxRate(
216            id: (int) $row['id'],
217            taxCode: (string) $row['tax_code'],
218            taxName: (string) $row['tax_name'],
219            ratePercent: (float) $row['rate_percent'],
220            calculationMode: TaxCalculationMode::from((string) $row['calculation_mode']),
221            taxStatus: TaxStatus::from((string) $row['tax_status']),
222            legalExemptionNote: $row['legal_exemption_note'] !== null ? (string) $row['legal_exemption_note'] : null,
223            fiscalCode: $row['fiscal_code'] !== null ? (string) $row['fiscal_code'] : null,
224            isDefault: (bool) $row['is_default'],
225            isActive: (bool) $row['is_active'],
226            owner: (int) ($row['owner'] ?? 1),
227            createdAt: isset($row['created_at']) ? (string) $row['created_at'] : null,
228            updatedAt: isset($row['updated_at']) ? (string) $row['updated_at'] : null,
229        );
230    }
231}