PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.4
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.4
1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk 1.2.0 All 47 releases
fluent-cart / app / Services / Filter / TaxFilter.php

TaxFilter.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.4, at app/Services/Filter/TaxFilter.php

266 lines 8.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Services\Filter;
4
5 use FluentCart\App\App;
6 use FluentCart\Framework\Support\Str;
7 use FluentCart\App\Models\OrderTaxRate;
8 use FluentCart\App\Services\Localization\LocalizationManager;
9
10 class TaxFilter extends BaseFilter
11 {
12 public function applySimpleFilter(?string $search = null): void
13 {
14 $isApplied = $this->applySimpleOperatorFilter($search);
15
16 if ($isApplied) {
17 return;
18 }
19
20 $this->query->when($search ?? $this->search, function ($query, $search) {
21 return $query
22 ->where(function ($query) use ($search) {
23 $search = trim($search);
24 $search = Str::of($search)->remove('#')->toString();
25
26 $query->when(
27 is_numeric($search),
28 fn ($query) => $query->where('id', $search)->orWhere('order_id', $search)
29 )->orWhereHas(
30 'tax_rate',
31 fn ($query) => $query->where('country', $search)
32 ->orWhere('state', $search)
33 ->orWhere('postcode', $search)
34 ->orWhere('name', 'LIKE', "%{$search}%")
35 );
36 });
37 });
38 }
39
40 public function tabsMap(): array
41 {
42 return [
43 'filed' => 'filed_at',
44 'not_filed' => 'filed_at',
45 ];
46 }
47
48 public function getModel(): string
49 {
50 return OrderTaxRate::class;
51 }
52
53 public static function getFilterName(): string
54 {
55 return 'taxes';
56 }
57
58 /**
59 * @return array<string, array<string, mixed>>
60 */
61 protected static function sortableColumns(): array
62 {
63 return [
64 'id' => ['label' => __('ID', 'fluent-cart'), 'column' => 'id'],
65 ];
66 }
67
68 /**
69 * `GET /taxes` mounts under `AdminPolicy` with no per-route permission, so
70 * a caller here is already a super admin and no entry on this map needs a
71 * further bar of its own.
72 *
73 * SCREEN key — `admin_tax_report` loads exactly what the report prints, and
74 * that is why it keeps the narrow `order` select: the report shows a
75 * currency, and a whole order row per line is both wasteful and more than
76 * the screen asked for.
77 *
78 * PUBLIC keys — `tax_rate` and `order` are the two relation names a
79 * consumer can reasonably ask a tax-report endpoint for. They give the
80 * plain, unnarrowed relation; the screen key's select is a property of that
81 * screen, not of the endpoint's published shape.
82 *
83 * @return array<string, callable>
84 */
85 protected function allowedWiths(): array
86 {
87 return [
88 'admin_tax_report' => [$this, 'adminTaxReport'],
89
90 'tax_rate' => [$this, 'publicTaxRate'],
91 'order' => [$this, 'publicOrder'],
92 ];
93 }
94
95 /**
96 * `GET /taxes`, sent by TaxesTable.js — the rate the row was charged at and
97 * the order's currency.
98 *
99 * No select on `tax_rate`: the report prints the rate's country, state,
100 * postcode and name, and the advanced filter matches on the same row. The
101 * `order` select lives INSIDE the relation closure — applied to `$query` it
102 * would narrow the tax-rate query itself, which is a different table.
103 *
104 * @param \FluentCart\Framework\Database\Orm\Builder $query
105 * @return \FluentCart\Framework\Database\Orm\Builder
106 */
107 protected function adminTaxReport($query)
108 {
109 return $query->with([
110 'tax_rate',
111 'order' => function ($orderQuery) {
112 $orderQuery->select(['id', 'currency']);
113 },
114 ]);
115 }
116
117 /**
118 * `with[]=tax_rate` — a public entry point. Covered by the route's own
119 * AdminPolicy, which admits nobody below a super admin.
120 *
121 * @param \FluentCart\Framework\Database\Orm\Builder $query
122 * @return \FluentCart\Framework\Database\Orm\Builder
123 */
124 protected function publicTaxRate($query)
125 {
126 return $query->with(['tax_rate']);
127 }
128
129 /**
130 * `with[]=order` — a public entry point, covered by the same AdminPolicy.
131 *
132 * @param \FluentCart\Framework\Database\Orm\Builder $query
133 * @return \FluentCart\Framework\Database\Orm\Builder
134 */
135 protected function publicOrder($query)
136 {
137 return $query->with(['order']);
138 }
139
140 /**
141 * Sent by TaxesTable.js so the tax report only counts rows whose order was
142 * actually paid.
143 *
144 * @return array<string, callable>
145 */
146 protected function allowedScopes(): array
147 {
148 return [
149 'validOrder' => [$this, 'validOrderScope'],
150 ];
151 }
152
153 /**
154 * Declared `($query)` on purpose: the array form of a `scopes` entry would
155 * otherwise hand this callback raw client arguments, and OrderTaxRate's
156 * `scopeValidOrder()` takes none.
157 *
158 * @param \FluentCart\Framework\Database\Orm\Builder $query
159 * @return \FluentCart\Framework\Database\Orm\Builder
160 */
161 protected function validOrderScope($query)
162 {
163 return $query->scopes(['validOrder']);
164 }
165
166 public function applyActiveViewFilter(?string $activeView = null): void
167 {
168 $activeView = $activeView ?? $this->activeView;
169 if (!$activeView) {
170 return;
171 }
172
173 $whereMethod = $activeView === 'filed' ? 'whereNotNull' : 'whereNull';
174
175 $this->query->{$whereMethod}('filed_at');
176 }
177
178 public static function advanceFilterOptions(): array
179 {
180 return [
181 'tax_rates' => [
182 'label' => __('Tax Property', 'fluent-cart'),
183 'value' => 'tax_rates',
184 'children' => [
185 [
186 'label' => __('Country', 'fluent-cart'),
187 'value' => 'country',
188 'column' => 'country',
189 'filter_type' => 'relation',
190 'relation' => 'tax_rate',
191 'remote_data_key' => 'countries',
192 'type' => 'selections',
193 'is_multiple' => true,
194 'options' => TaxFilter::getCountriesOptions(),
195 ],
196 [
197 'label' => __('Region', 'fluent-cart'),
198 'value' => 'region',
199 'column' => 'state',
200 'filter_type' => 'relation',
201 'relation' => 'tax_rate',
202 'remote_data_key' => 'tax_rate_states',
203 'type' => 'selections',
204 'options' => static::getStatesOptions(),
205 'is_multiple' => true,
206 'limit' => 10,
207 ],
208 [
209 'label' => __('Tax Name', 'fluent-cart'),
210 'value' => 'name',
211 'column' => 'name',
212 'filter_type' => 'relation',
213 'relation' => 'tax_rate',
214 'type' => 'text',
215 ],
216 [
217 'label' => __('Filed', 'fluent-cart'),
218 'value' => 'filed',
219 'column' => 'filed_at',
220 'filter_type' => 'custom',
221 'type' => 'selections',
222 'options' => [
223 'filed' => __('Filed', 'fluent-cart'),
224 'not_filed' => __('Not Filed', 'fluent-cart'),
225 ],
226 'callback' => function ($query, $data) {
227 if ($data === 'filed') {
228 $query->whereNotNull('filed_at');
229 } else {
230 $query->whereNull('filed_at');
231 }
232 },
233 ],
234 ],
235 ],
236 ];
237 }
238
239 public static function getCountriesOptions()
240 {
241 return LocalizationManager::getInstance()->countryIsoList();
242 }
243
244 public static function getStatesOptions()
245 {
246 $statesFromDB = App::db()->table('fct_tax_rates')
247 ->select(['country', 'state'])
248 ->whereNotNull('state')
249 ->where('state', '!=', '')
250 ->groupBy(['country', 'state'])
251 ->get();
252
253 $states = LocalizationManager::getInstance()->states();
254
255 $statesList = [];
256
257 foreach ($statesFromDB as $state) {
258 if (isset($states[$state->country]) && isset($states[$state->country][$state->state])) {
259 $statesList[$state->state] = $states[$state->country][$state->state];
260 }
261 }
262
263 return $statesList;
264 }
265 }
266