|
1
|
|
|
<?php |
|
2
|
|
|
class Intraface_modules_debtor_DebtorGateway |
|
3
|
|
|
{ |
|
4
|
|
|
protected $kernel; |
|
5
|
|
|
protected $dbquery; |
|
6
|
|
|
public $error; |
|
7
|
|
|
protected $type; |
|
8
|
|
|
|
|
9
|
|
|
function __construct($kernel) |
|
10
|
|
|
{ |
|
11
|
|
|
$this->kernel = $kernel; |
|
12
|
|
|
$this->error = new Intraface_Error; |
|
13
|
|
|
} |
|
14
|
|
|
|
|
15
|
|
|
function getType() |
|
16
|
|
|
{ |
|
17
|
|
|
return $this->type; |
|
18
|
|
|
} |
|
19
|
|
|
|
|
20
|
|
|
/** |
|
21
|
|
|
* returns the possible debtor types! |
|
22
|
|
|
* |
|
23
|
|
|
* @return array types |
|
24
|
|
|
*/ |
|
25
|
|
|
static function getDebtorTypes() |
|
26
|
|
|
{ |
|
27
|
|
|
return array( |
|
28
|
|
|
1 => 'quotation', |
|
29
|
|
|
2 => 'order', |
|
30
|
|
|
3 => 'invoice', |
|
31
|
|
|
4 => 'credit_note'); |
|
32
|
|
|
} |
|
33
|
|
|
|
|
34
|
|
|
function setType($type) |
|
35
|
|
|
{ |
|
36
|
|
|
$this->type = $type; |
|
37
|
|
|
} |
|
38
|
|
|
|
|
39
|
|
|
function getTypeKey() |
|
40
|
|
|
{ |
|
41
|
|
|
return array_search($this->type, $this->getDebtorTypes()); |
|
42
|
|
|
} |
|
43
|
|
|
|
|
44
|
|
View Code Duplication |
function getDBQuery() |
|
|
|
|
|
|
45
|
|
|
{ |
|
46
|
|
|
if ($this->dbquery) { |
|
47
|
|
|
return $this->dbquery; |
|
48
|
|
|
} |
|
49
|
|
|
|
|
50
|
|
|
$this->dbquery = new Intraface_DBQuery($this->kernel, "debtor", "debtor.active = 1 AND debtor.intranet_id = ".$this->kernel->intranet->get("id")); |
|
51
|
|
|
$this->dbquery->setJoin("LEFT", "contact", "debtor.contact_id = contact.id AND contact.intranet_id = ".$this->kernel->intranet->get("id"), ''); |
|
52
|
|
|
$this->dbquery->setJoin("LEFT", "address", "address.belong_to_id = contact.id AND address.active = 1 AND address.type = 3", ''); |
|
53
|
|
|
$this->dbquery->setJoin("LEFT", "debtor_item", "debtor_item.debtor_id = debtor.id AND debtor_item.active = 1 AND debtor_item.intranet_id = ".$this->kernel->intranet->get("id"), ''); |
|
54
|
|
|
|
|
55
|
|
|
$this->dbquery->useErrorObject($this->error); |
|
56
|
|
|
|
|
57
|
|
|
return $this->dbquery; |
|
58
|
|
|
} |
|
59
|
|
|
|
|
60
|
|
|
/** |
|
61
|
|
|
* Bruges til at lave en menu p� kontakten eller produktet |
|
62
|
|
|
* |
|
63
|
|
|
* @param string $type contact eller product |
|
|
|
|
|
|
64
|
|
|
* @param integer $type_id id p� contact eller product. |
|
|
|
|
|
|
65
|
|
|
* |
|
66
|
|
|
* @return integer |
|
67
|
|
|
*/ |
|
68
|
|
|
public function findCountByContactId($contact_id) |
|
69
|
|
|
{ |
|
70
|
|
|
$sql = "SELECT id |
|
71
|
|
|
FROM debtor |
|
72
|
|
|
WHERE intranet_id = " . $this->kernel->intranet->get("id") . " |
|
73
|
|
|
AND contact_id = ".(int)$contact_id." |
|
74
|
|
|
AND type='".$this->type_key."' |
|
|
|
|
|
|
75
|
|
|
AND active = 1"; |
|
76
|
|
|
|
|
77
|
|
|
$db = new DB_Sql; |
|
78
|
|
|
$db->query($sql); |
|
79
|
|
|
return $db->numRows(); |
|
80
|
|
|
} |
|
81
|
|
|
|
|
82
|
|
|
function setNewContactId($old_contact_id, $new_contact_id) |
|
83
|
|
|
{ |
|
84
|
|
|
$db = new DB_Sql; |
|
85
|
|
|
$db->query('UPDATE debtor SET contact_id = ' . $new_contact_id . ' WHERE contact_id = ' . $old_contact_id); |
|
86
|
|
|
return true; |
|
87
|
|
|
} |
|
88
|
|
|
|
|
89
|
|
View Code Duplication |
function anyNew() |
|
|
|
|
|
|
90
|
|
|
{ |
|
91
|
|
|
$db = new DB_Sql; |
|
92
|
|
|
$db->query('SELECT * FROM debtor WHERE date_created >= |
|
93
|
|
|
DATE_SUB(NOW(),INTERVAL 1 DAY) |
|
94
|
|
|
AND type = ' .$this->type_key . ' AND intranet_id = ' .$this->kernel->intranet->get('id')); |
|
95
|
|
|
return $db->numRows(); |
|
96
|
|
|
} |
|
97
|
|
|
|
|
98
|
|
|
function findById($id) |
|
99
|
|
|
{ |
|
100
|
|
|
if (is_int($id) && $id != 0) { |
|
101
|
|
|
$types = self::getDebtorTypes(); |
|
102
|
|
|
|
|
103
|
|
|
$db = new DB_Sql; |
|
104
|
|
|
$db->query("SELECT type FROM debtor WHERE intranet_id = ".$this->kernel->intranet->get('id')." AND id = ".$id); |
|
105
|
|
|
if ($db->nextRecord()) { |
|
106
|
|
|
$type = $types[$db->f("type")]; |
|
107
|
|
|
} else { |
|
108
|
|
|
throw new Exception("Invalid id for debtor in Debtor::factory"); |
|
109
|
|
|
} |
|
110
|
|
|
} elseif (is_string($id) && $id != '') { |
|
111
|
|
|
$types = self::getDebtorTypes(); |
|
112
|
|
|
|
|
113
|
|
|
$db = new DB_Sql; |
|
114
|
|
|
$db->query("SELECT type, id FROM debtor WHERE intranet_id = ".$this->kernel->intranet->get('id')." AND identifier_key = \"".$id."\""); |
|
115
|
|
|
if ($db->nextRecord()) { |
|
116
|
|
|
$type = $types[$db->f("type")]; |
|
117
|
|
|
$id = $db->f("id"); |
|
118
|
|
|
} else { |
|
119
|
|
|
throw new Exception("Invalid identifier_key for debtor in Debtor::factory"); |
|
120
|
|
|
} |
|
121
|
|
|
} |
|
122
|
|
|
|
|
123
|
|
|
switch ($type) { |
|
|
|
|
|
|
124
|
|
View Code Duplication |
case "quotation": |
|
125
|
|
|
$this->kernel->useModule("quotation"); |
|
126
|
|
|
$object = new Quotation($this->kernel, intval($id)); |
|
127
|
|
|
return $object; |
|
128
|
|
|
break; |
|
|
|
|
|
|
129
|
|
|
|
|
130
|
|
View Code Duplication |
case "order": |
|
131
|
|
|
$this->kernel->useModule("order"); |
|
132
|
|
|
$object = new Order($this->kernel, intval($id)); |
|
133
|
|
|
break; |
|
134
|
|
|
|
|
135
|
|
View Code Duplication |
case "invoice": |
|
136
|
|
|
$this->kernel->useModule("invoice"); |
|
137
|
|
|
$object = new Invoice($this->kernel, intval($id)); |
|
138
|
|
|
break; |
|
139
|
|
|
|
|
140
|
|
|
case "credit_note": |
|
141
|
|
|
$this->kernel->useModule("invoice"); |
|
142
|
|
|
$object = new CreditNote($this->kernel, intval($id)); |
|
143
|
|
|
break; |
|
144
|
|
|
|
|
145
|
|
|
default: |
|
146
|
|
|
throw new Exception("Ugyldig type: '".$type."'"); |
|
147
|
|
|
break; |
|
|
|
|
|
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
return $object; |
|
151
|
|
|
} |
|
152
|
|
|
|
|
153
|
|
View Code Duplication |
function findByIdentifier($identifier) |
|
|
|
|
|
|
154
|
|
|
{ |
|
155
|
|
|
if (is_int($id) && $id != 0) { |
|
|
|
|
|
|
156
|
|
|
$types = self::getDebtorTypes(); |
|
157
|
|
|
|
|
158
|
|
|
$db = new DB_Sql; |
|
159
|
|
|
$db->query("SELECT type FROM debtor WHERE intranet_id = ".$kernel->intranet->get('id')." AND id = ".$id); |
|
|
|
|
|
|
160
|
|
|
if ($db->nextRecord()) { |
|
161
|
|
|
$type = $types[$db->f("type")]; |
|
162
|
|
|
} else { |
|
163
|
|
|
throw new Exception("Invalid id for debtor in Debtor::factory"); |
|
164
|
|
|
} |
|
165
|
|
|
} elseif (is_string($id) && $id != '') { |
|
|
|
|
|
|
166
|
|
|
$types = self::getDebtorTypes(); |
|
167
|
|
|
|
|
168
|
|
|
$db = new DB_Sql; |
|
169
|
|
|
$db->query("SELECT type, id FROM debtor WHERE intranet_id = ".$kernel->intranet->get('id')." AND identifier_key = \"".$id."\""); |
|
|
|
|
|
|
170
|
|
|
if ($db->nextRecord()) { |
|
171
|
|
|
$type = $types[$db->f("type")]; |
|
172
|
|
|
$id = $db->f("id"); |
|
173
|
|
|
} else { |
|
174
|
|
|
throw new Exception("Invalid identifier_key for debtor in Debtor::factory"); |
|
175
|
|
|
} |
|
176
|
|
|
} |
|
177
|
|
|
|
|
178
|
|
|
switch ($type) { |
|
|
|
|
|
|
179
|
|
|
case "quotation": |
|
180
|
|
|
$kernel->useModule("quotation"); |
|
181
|
|
|
$object = new Quotation($kernel, intval($id)); |
|
|
|
|
|
|
182
|
|
|
return $object; |
|
183
|
|
|
break; |
|
|
|
|
|
|
184
|
|
|
|
|
185
|
|
|
case "order": |
|
186
|
|
|
$kernel->useModule("order"); |
|
187
|
|
|
$object = new Order($kernel, intval($id)); |
|
188
|
|
|
break; |
|
189
|
|
|
|
|
190
|
|
|
case "invoice": |
|
191
|
|
|
$kernel->useModule("invoice"); |
|
192
|
|
|
$object = new Invoice($kernel, intval($id)); |
|
193
|
|
|
break; |
|
194
|
|
|
|
|
195
|
|
|
case "credit_note": |
|
196
|
|
|
$kernel->useModule("invoice"); |
|
197
|
|
|
$object = new CreditNote($kernel, intval($id)); |
|
198
|
|
|
break; |
|
199
|
|
|
|
|
200
|
|
|
default: |
|
201
|
|
|
throw new Exception("Ugyldig type: '".$type."'"); |
|
202
|
|
|
break; |
|
|
|
|
|
|
203
|
|
|
} |
|
204
|
|
|
|
|
205
|
|
|
return $object; |
|
206
|
|
|
} |
|
207
|
|
|
|
|
208
|
|
|
/** |
|
209
|
|
|
* Gets a list with debtors |
|
210
|
|
|
* |
|
211
|
|
|
* @return array |
|
212
|
|
|
*/ |
|
213
|
|
View Code Duplication |
public function findAll() |
|
|
|
|
|
|
214
|
|
|
{ |
|
215
|
|
|
$db = new DB_Sql; |
|
|
|
|
|
|
216
|
|
|
|
|
217
|
|
|
$this->dbquery = $this->getDBQuery(); |
|
218
|
|
|
|
|
219
|
|
|
$this->dbquery->setCondition("debtor.type = ".$this->getTypeKey()); |
|
220
|
|
|
|
|
221
|
|
|
if ($this->dbquery->checkFilter("contact_id")) { |
|
222
|
|
|
$this->dbquery->setCondition("debtor.contact_id = ".intval($this->dbquery->getFilter("contact_id"))); |
|
223
|
|
|
} |
|
224
|
|
|
|
|
225
|
|
|
if ($this->dbquery->checkFilter("text")) { |
|
226
|
|
|
$this->dbquery->setCondition("(debtor.description LIKE \"%".$this->dbquery->getFilter("text")."%\" OR debtor.girocode = \"".$this->dbquery->getFilter("text")."\" OR debtor.number = \"".$this->dbquery->getFilter("text")."\" OR address.name LIKE \"%".$this->dbquery->getFilter("text")."%\")"); |
|
227
|
|
|
} |
|
228
|
|
|
|
|
229
|
|
|
if ($this->dbquery->checkFilter("product_id")) { |
|
230
|
|
|
$this->dbquery->setCondition("debtor_item.product_id = ".$this->dbquery->getFilter('product_id')); |
|
231
|
|
|
if ($this->dbquery->checkFilter("product_variation_id")) { |
|
232
|
|
|
$this->dbquery->setCondition("debtor_item.product_variation_id = ".$this->dbquery->getFilter('product_variation_id')); |
|
233
|
|
|
} else { |
|
234
|
|
|
$this->dbquery->setCondition("debtor_item.product_variation_id = 0"); |
|
235
|
|
|
} |
|
236
|
|
|
} |
|
237
|
|
|
|
|
238
|
|
|
if ($this->dbquery->checkFilter("date_field")) { |
|
239
|
|
|
if (in_array($this->dbquery->getFilter("date_field"), array('this_date', 'date_created', 'date_sent', 'date_executed', 'data_cancelled'))) { |
|
240
|
|
|
$date_field = $this->dbquery->getFilter("date_field"); |
|
241
|
|
|
} else { |
|
242
|
|
|
$this->error->set("Ugyldigt datointerval felt"); |
|
243
|
|
|
} |
|
244
|
|
|
} else { |
|
245
|
|
|
$date_field = 'this_date'; |
|
246
|
|
|
} |
|
247
|
|
|
|
|
248
|
|
|
if ($this->dbquery->checkFilter("from_date")) { |
|
249
|
|
|
$date = new Intraface_Date($this->dbquery->getFilter("from_date")); |
|
250
|
|
|
if ($date->convert2db()) { |
|
251
|
|
|
$this->dbquery->setCondition("debtor.".$date_field." >= \"".$date->get()."\""); |
|
|
|
|
|
|
252
|
|
|
} else { |
|
253
|
|
|
$this->error->set("Fra dato er ikke gyldig"); |
|
254
|
|
|
} |
|
255
|
|
|
} |
|
256
|
|
|
|
|
257
|
|
|
// Poster med fakturadato f�r slutdato. |
|
258
|
|
|
if ($this->dbquery->checkFilter("to_date")) { |
|
259
|
|
|
$date = new Intraface_Date($this->dbquery->getFilter("to_date")); |
|
260
|
|
|
if ($date->convert2db()) { |
|
261
|
|
|
$this->dbquery->setCondition("debtor.".$date_field." <= \"".$date->get()."\""); |
|
262
|
|
|
} else { |
|
263
|
|
|
$this->error->set("Til dato er ikke gyldig"); |
|
264
|
|
|
} |
|
265
|
|
|
} |
|
266
|
|
|
// alle ikke bogf�rte skal findes |
|
267
|
|
|
if ($this->dbquery->checkFilter("not_stated")) { |
|
268
|
|
|
$this->dbquery->setCondition("voucher_id = 0"); |
|
269
|
|
|
} |
|
270
|
|
|
|
|
271
|
|
|
if ($this->dbquery->checkFilter("status")) { |
|
272
|
|
|
if ($this->dbquery->getFilter("status") == "-1") { |
|
|
|
|
|
|
273
|
|
|
// Beh�ves ikke, den tager alle. |
|
274
|
|
|
// $this->dbquery->setCondition("status >= 0"); |
|
|
|
|
|
|
275
|
|
|
} elseif ($this->dbquery->getFilter("status") == "-2") { |
|
276
|
|
|
// Not executed = �bne |
|
277
|
|
|
if ($this->dbquery->checkFilter("to_date")) { |
|
278
|
|
|
$date = new Intraface_Date($this->dbquery->getFilter("to_date")); |
|
279
|
|
|
if ($date->convert2db()) { |
|
280
|
|
|
// Poster der er executed eller cancelled efter dato, og sikring at executed stadig er det, da faktura kan s�ttes tilbage. |
|
281
|
|
|
$this->dbquery->setCondition("(debtor.date_executed >= \"".$date->get()."\" AND debtor.status = 2) OR (debtor.date_cancelled >= \"".$date->get()."\") OR debtor.status < 2"); |
|
282
|
|
|
} |
|
283
|
|
|
} else { |
|
284
|
|
|
// Hvis der ikke er nogen dato s� tager vi alle dem som p� nuv�rende tidspunkt har status under |
|
285
|
|
|
$this->dbquery->setCondition("debtor.status < 2"); |
|
286
|
|
|
} |
|
287
|
|
|
} elseif ($this->dbquery->getFilter("status") == "-3") { |
|
288
|
|
|
// Afskrevne. Vi tager f�rst alle sendte og executed. |
|
289
|
|
|
|
|
290
|
|
|
if ($this->get("type") != "invoice") { |
|
|
|
|
|
|
291
|
|
|
throw new Exception("Afskrevne kan kun benyttes ved faktura"); |
|
292
|
|
|
} |
|
293
|
|
|
|
|
294
|
|
|
$this->dbquery->setJoin("INNER", "invoice_payment", "invoice_payment.payment_for_id = debtor.id", "invoice_payment.intranet_id = ".$this->kernel->intranet->get("id")." AND invoice_payment.payment_for = 1"); |
|
295
|
|
|
$this->dbquery->setCondition("invoice_payment.type = -1"); |
|
296
|
|
|
|
|
297
|
|
|
if ($this->dbquery->checkFilter("to_date")) { |
|
298
|
|
|
$date = new Intraface_Date($this->dbquery->getFilter("to_date")); |
|
299
|
|
|
if ($date->convert2db()) { |
|
300
|
|
|
// alle som er sendte p� datoen og som ikke er cancelled |
|
301
|
|
|
$this->dbquery->setCondition("debtor.date_sent <= '".$date->get()."' AND debtor.status != 3"); |
|
302
|
|
|
$this->dbquery->setCondition("invoice_payment.payment_date <= '".$date->get()."'"); |
|
303
|
|
|
} |
|
304
|
|
|
} else { |
|
305
|
|
|
// Hvis der ikke er nogen dato s� tager vi alle dem som p� nuv�rende tidspunkt har status under |
|
306
|
|
|
$this->dbquery->setCondition("status = 1 OR status = 2"); |
|
307
|
|
|
} |
|
308
|
|
|
} else { |
|
309
|
|
|
$this->dbquery->setCondition("debtor.status = ".intval($this->dbquery->getFilter("status"))); |
|
310
|
|
|
|
|
311
|
|
|
/* |
|
|
|
|
|
|
312
|
|
|
// New date_field handles this instead |
|
313
|
|
|
switch ($this->dbquery->getFilter("status")) { |
|
314
|
|
|
case "0": |
|
315
|
|
|
$to_date_field = "date_created"; |
|
316
|
|
|
break; |
|
317
|
|
|
|
|
318
|
|
|
case "1": |
|
319
|
|
|
$to_date_field = "date_sent"; |
|
320
|
|
|
break; |
|
321
|
|
|
|
|
322
|
|
|
case "2": |
|
323
|
|
|
$to_date_field = "date_executed"; |
|
324
|
|
|
break; |
|
325
|
|
|
|
|
326
|
|
|
case "3": |
|
327
|
|
|
$to_date_field = "data_cancelled"; |
|
328
|
|
|
break; |
|
329
|
|
|
} |
|
330
|
|
|
|
|
331
|
|
|
if ($this->dbquery->checkFilter("to_date")) { |
|
332
|
|
|
$date = new Intraface_Date($this->dbquery->getFilter("to_date")); |
|
333
|
|
|
if ($date->convert2db()) { |
|
334
|
|
|
// This gives a problem: We have an invoice created 20/4 and is executed 5/5 |
|
335
|
|
|
// If we make a search: executed 1/4-30/4 the above invoice will not be calculated in with date search below. |
|
336
|
|
|
// And if we make a search : executed 1/5-30/5 it will not even be included in that search. |
|
337
|
|
|
// Why was this made in that way? |
|
338
|
|
|
// $this->dbquery->setCondition("debtor.".$to_date_field." <= \"".$date->get()."\""); |
|
339
|
|
|
// So instead we add this normal status search: Changed 12/7 2009 /Sune |
|
340
|
|
|
$this->dbquery->setCondition("debtor.status = ".intval($this->dbquery->getFilter("status"))); |
|
341
|
|
|
} |
|
342
|
|
|
} else { |
|
343
|
|
|
// tager dem som p� nuv�rende tidspunkt har den angivet status |
|
344
|
|
|
$this->dbquery->setCondition("debtor.status = ".intval($this->dbquery->getFilter("status"))); |
|
345
|
|
|
} |
|
346
|
|
|
*/ |
|
347
|
|
|
} |
|
348
|
|
|
} |
|
349
|
|
|
|
|
350
|
|
|
switch ($this->dbquery->getFilter("sorting")) { |
|
351
|
|
|
case 1: |
|
352
|
|
|
$this->dbquery->setSorting("debtor.number ASC"); |
|
353
|
|
|
break; |
|
354
|
|
|
case 2: |
|
355
|
|
|
$this->dbquery->setSorting("contact.number ASC"); |
|
356
|
|
|
break; |
|
357
|
|
|
case 3: |
|
358
|
|
|
$this->dbquery->setSorting("address.name ASC"); |
|
359
|
|
|
break; |
|
360
|
|
|
default: |
|
361
|
|
|
$this->dbquery->setSorting("debtor.number DESC"); |
|
362
|
|
|
} |
|
363
|
|
|
|
|
364
|
|
|
$db = $this->dbquery->getRecordset("DISTINCT(debtor.id)", "", false); |
|
365
|
|
|
$i = 0; |
|
366
|
|
|
$list = array(); |
|
367
|
|
|
|
|
368
|
|
|
while ($db->nextRecord()) { |
|
369
|
|
|
$debtor = $this->findById((int)$db->f("id")); |
|
370
|
|
|
$list[$i] = $debtor->get(); |
|
371
|
|
|
|
|
372
|
|
|
// $contact = new Contact($this->kernel, $db->f('contact_id')); |
|
|
|
|
|
|
373
|
|
|
if (is_object($debtor->contact->address)) { |
|
374
|
|
|
$list[$i]['contact'] = $debtor->contact->get(); |
|
375
|
|
|
$list[$i]['contact']['address'] = $debtor->contact->address->get(); |
|
376
|
|
|
|
|
377
|
|
|
// f�lgende skal v�k |
|
378
|
|
|
$list[$i]['contact_id'] = $debtor->contact->get('id'); |
|
379
|
|
|
$list[$i]['name'] = $debtor->contact->address->get('name'); |
|
380
|
|
|
$list[$i]['address'] = $debtor->contact->address->get('address'); |
|
381
|
|
|
$list[$i]['postalcode'] = $debtor->contact->address->get('postcode'); |
|
382
|
|
|
$list[$i]['city'] = $debtor->contact->address->get('city'); |
|
383
|
|
|
} |
|
384
|
|
|
$debtor->destruct(); |
|
385
|
|
|
unset($debtor); |
|
386
|
|
|
$i++; |
|
387
|
|
|
} |
|
388
|
|
|
unset($db); |
|
389
|
|
|
return $list; |
|
390
|
|
|
} |
|
391
|
|
|
|
|
392
|
|
|
/** |
|
393
|
|
|
* Funktion til at finde ud af, om der er oprettet nogen poster af den aktuelle bruger |
|
394
|
|
|
* |
|
395
|
|
|
* @return integer |
|
396
|
|
|
*/ |
|
397
|
|
|
public function isFilledIn() |
|
398
|
|
|
{ |
|
399
|
|
|
$db = new DB_Sql; |
|
400
|
|
|
$db->query("SELECT id FROM debtor WHERE type = " . $this->getTypeKey() . " AND intranet_id = " . $this->kernel->intranet->get('id')); |
|
401
|
|
|
return $db->numRows(); |
|
402
|
|
|
} |
|
403
|
|
|
} |
|
404
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.