Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
72 / 72
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
SqlTaxRuleRepository
100.00% covered (success)
100.00%
71 / 71
100.00% covered (success)
100.00%
7 / 7
18
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
 findAllActive
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 findAll
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
4
 save
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
6
 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
3
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\TaxRule;
13use App\Modules\Tax\Domain\Repository\TaxRateRepositoryInterface;
14use App\Modules\Tax\Domain\Repository\TaxRuleRepositoryInterface;
15use App\Core\Database\Repository\TenantAwareRepositoryTrait;
16use PDO;
17
18/**
19 * SQL/PDO repository for managing TaxRule entities.
20 */
21final readonly class SqlTaxRuleRepository implements TaxRuleRepositoryInterface
22{
23    use TenantAwareRepositoryTrait;
24
25    /**
26     * SqlTaxRuleRepository constructor.
27     */
28    public function __construct(
29        protected PDO $pdo,
30        private TaxRateRepositoryInterface $taxRateRepo,
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): ?TaxRule
41    {
42        $table = $this->tablePrefix . 'mod_tax_rules_records';
43        $sql = "SELECT `id`, `rule_name`, `priority`, `customer_type`, `geo_zone`, `item_type`,
44                       `target_tax_rate_id`, `is_active`, `owner`, `created_at`, `updated_at`
45                FROM `{$table}`
46                WHERE `id` = :id AND `special_access` != 0
47                LIMIT 1";
48
49        $stmt = $this->getPdo()->prepare($sql);
50        $stmt->bindValue(':id', $id, PDO::PARAM_INT);
51        $stmt->execute();
52        $row = $stmt->fetch(PDO::FETCH_ASSOC);
53
54        return $row ? $this->mapRowToEntity($row) : null;
55    }
56
57    /**
58     * @inheritDoc
59     */
60    public function findAllActive(): array
61    {
62        return $this->findAll(['is_active' => 1]);
63    }
64
65    /**
66     * @inheritDoc
67     */
68    public function findAll(array $filters = []): array
69    {
70        $table = $this->tablePrefix . 'mod_tax_rules_records';
71        $where = ['`special_access` != 0'];
72        $params = [];
73
74        if (isset($filters['is_active'])) {
75            $where[] = '`is_active` = :is_active';
76            $params[':is_active'] = (int) $filters['is_active'];
77        }
78
79        $whereSql = implode(' AND ', $where);
80        $sql = "SELECT `id`, `rule_name`, `priority`, `customer_type`, `geo_zone`, `item_type`,
81                       `target_tax_rate_id`, `is_active`, `owner`, `created_at`, `updated_at`
82                FROM `{$table}`
83                WHERE {$whereSql}
84                ORDER BY `priority` ASC, `id` ASC";
85
86        $stmt = $this->getPdo()->prepare($sql);
87        foreach ($params as $key => $val) {
88            $stmt->bindValue($key, $val);
89        }
90        $stmt->execute();
91
92        $rules = [];
93        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
94            $rules[] = $this->mapRowToEntity($row);
95        }
96
97        return $rules;
98    }
99
100    /**
101     * @inheritDoc
102     */
103    public function save(TaxRule $taxRule): TaxRule
104    {
105        $table = $this->tablePrefix . 'mod_tax_rules_records';
106
107        if ($taxRule->id !== null && $taxRule->id > 0) {
108            $sql = "UPDATE `{$table}` SET
109                        `rule_name` = :rule_name,
110                        `priority` = :priority,
111                        `customer_type` = :customer_type,
112                        `geo_zone` = :geo_zone,
113                        `item_type` = :item_type,
114                        `target_tax_rate_id` = :target_tax_rate_id,
115                        `is_active` = :is_active,
116                        `owner` = :owner
117                    WHERE `id` = :id";
118            $stmt = $this->getPdo()->prepare($sql);
119            $stmt->bindValue(':id', $taxRule->id, PDO::PARAM_INT);
120        } else {
121            $sql = "INSERT INTO `{$table}` (
122                        `rule_name`, `priority`, `customer_type`, `geo_zone`, `item_type`,
123                        `target_tax_rate_id`, `is_active`, `owner`
124                    ) VALUES (
125                        :rule_name, :priority, :customer_type, :geo_zone, :item_type,
126                        :target_tax_rate_id, :is_active, :owner
127                    )";
128            $stmt = $this->getPdo()->prepare($sql);
129        }
130
131        $stmt->bindValue(':rule_name', $taxRule->ruleName, PDO::PARAM_STR);
132        $stmt->bindValue(':priority', $taxRule->priority, PDO::PARAM_INT);
133        $stmt->bindValue(':customer_type', $taxRule->customerType, PDO::PARAM_STR);
134        $stmt->bindValue(':geo_zone', $taxRule->geoZone, PDO::PARAM_STR);
135        $stmt->bindValue(':item_type', $taxRule->itemType, PDO::PARAM_STR);
136        $stmt->bindValue(':target_tax_rate_id', $taxRule->targetTaxRateId, PDO::PARAM_INT);
137        $stmt->bindValue(':is_active', $taxRule->isActive ? 1 : 0, PDO::PARAM_INT);
138        $stmt->bindValue(':owner', $taxRule->owner, PDO::PARAM_INT);
139        $stmt->execute();
140
141        if ($taxRule->id === null || $taxRule->id <= 0) {
142            $taxRule->id = (int) $this->getPdo()->lastInsertId();
143        }
144
145        return $taxRule;
146    }
147
148    /**
149     * @inheritDoc
150     */
151    public function delete(int $id): bool
152    {
153        $table = $this->tablePrefix . 'mod_tax_rules_records';
154        $sql = "UPDATE `{$table}` SET `special_access` = 0 WHERE `id` = :id";
155        $stmt = $this->getPdo()->prepare($sql);
156        $stmt->bindValue(':id', $id, PDO::PARAM_INT);
157
158        return $stmt->execute();
159    }
160
161    /**
162     * Map database row to TaxRule domain model.
163     *
164     * @param array<string, mixed> $row
165     */
166    private function mapRowToEntity(array $row): TaxRule
167    {
168        $rate = $this->taxRateRepo->findById((int) $row['target_tax_rate_id']);
169
170        return new TaxRule(
171            id: (int) $row['id'],
172            ruleName: (string) $row['rule_name'],
173            priority: (int) $row['priority'],
174            customerType: (string) $row['customer_type'],
175            geoZone: (string) $row['geo_zone'],
176            itemType: (string) $row['item_type'],
177            targetTaxRateId: (int) $row['target_tax_rate_id'],
178            isActive: (bool) $row['is_active'],
179            targetTaxRate: $rate,
180            owner: (int) ($row['owner'] ?? 1),
181            createdAt: isset($row['created_at']) ? (string) $row['created_at'] : null,
182            updatedAt: isset($row['updated_at']) ? (string) $row['updated_at'] : null,
183        );
184    }
185}