1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Service contracts utils file. |
4
|
|
|
* |
5
|
|
|
* @package App |
6
|
|
|
* |
7
|
|
|
* @copyright YetiForce S.A. |
8
|
|
|
* @license YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com) |
9
|
|
|
* @author Mariusz Krzaczkowski <[email protected]> |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace App\Utils; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Service contracts utils class. |
16
|
|
|
*/ |
17
|
|
|
class ServiceContracts |
18
|
|
|
{ |
19
|
|
|
/** |
20
|
|
|
* Fields map. |
21
|
|
|
* |
22
|
|
|
* @var string[] |
23
|
|
|
*/ |
24
|
|
|
private static $fieldsMap = [ |
25
|
|
|
'reaction_time' => 'response', |
26
|
|
|
'resolve_time' => 'solution', |
27
|
|
|
'idle_time' => 'idle', |
28
|
|
|
]; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Get the amount of business time between two dates in minutes. |
32
|
|
|
* |
33
|
|
|
* @param string $start |
34
|
|
|
* @param string $end |
35
|
|
|
* @param array $days |
36
|
|
|
* @param string $startHour |
37
|
|
|
* @param string $endHour |
38
|
|
|
* @param bool $holidays |
39
|
|
|
* |
40
|
|
|
* @return int |
41
|
|
|
*/ |
42
|
|
|
public static function businessTime(string $start, string $end, string $startHour, string $endHour, array $days, bool $holidays): int |
43
|
|
|
{ |
44
|
|
|
$start = new \DateTime($start); |
45
|
|
|
$end = new \DateTime($end); |
46
|
|
|
$holidaysDates = $dates = []; |
47
|
|
|
$date = clone $start; |
48
|
|
|
$days = array_flip($days); |
49
|
|
|
if ($holidays) { |
50
|
|
|
$holidaysDates = array_flip(array_keys(\App\Fields\Date::getHolidays($start->format('Y-m-d'), $end->format('Y-m-d')))); |
51
|
|
|
} |
52
|
|
|
while ($date <= $end) { |
53
|
|
|
$datesEnd = (clone $date)->setTime(23, 59, 59); |
54
|
|
|
if (isset($days[$date->format('N')]) && (!$holidays || ($holidays && !isset($holidaysDates[$date->format('Y-m-d')])))) { |
55
|
|
|
$dates[] = [ |
56
|
|
|
'start' => clone $date, |
57
|
|
|
'end' => clone ($end < $datesEnd ? $end : $datesEnd), |
58
|
|
|
]; |
59
|
|
|
} |
60
|
|
|
$date->modify('+1 day')->setTime(0, 0, 0); |
61
|
|
|
} |
62
|
|
|
[$sh,$sm,$ss] = explode(':', $startHour); |
63
|
|
|
[$eh,$em,$es] = explode(':', $endHour); |
64
|
|
|
return array_reduce($dates, function ($carry, $item) use ($sh, $sm, $ss, $eh, $em, $es) { |
65
|
|
|
$businessStart = (clone $item['start'])->setTime($sh, $sm, $ss); |
66
|
|
|
$businessEnd = (clone $item['end'])->setTime($eh, $em, $es); |
67
|
|
|
$start = ($item['start'] < $businessStart) ? $businessStart : $item['start']; |
68
|
|
|
$end = ($item['end'] > $businessEnd) ? $businessEnd : $item['end']; |
69
|
|
|
return $carry += max(0, $end->getTimestamp() - $start->getTimestamp()); |
70
|
|
|
}, 0) / 60; |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Get default business hours. |
75
|
|
|
* |
76
|
|
|
* @return array |
77
|
|
|
*/ |
78
|
1 |
|
public static function getDefaultBusinessHours(): array |
79
|
|
|
{ |
80
|
1 |
|
if (\App\Cache::has('UtilsServiceContracts::getDefaultBusinessHours', '')) { |
81
|
1 |
|
return \App\Cache::get('UtilsServiceContracts::getDefaultBusinessHours', ''); |
82
|
|
|
} |
83
|
1 |
|
$rows = (new \App\Db\Query())->from('s_#__business_hours')->where(['default' => 1])->all(\App\Db::getInstance('admin')); |
84
|
1 |
|
\App\Cache::save('UtilsServiceContracts::getDefaultBusinessHours', '', $rows); |
85
|
1 |
|
return $rows; |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Get all business hours. |
90
|
|
|
* |
91
|
|
|
* @return array |
92
|
|
|
*/ |
93
|
|
|
public static function getAllBusinessHours(): array |
94
|
|
|
{ |
95
|
|
|
if (\App\Cache::has('UtilsServiceContracts::getAllBusinessHours', '')) { |
96
|
|
|
return \App\Cache::get('UtilsServiceContracts::getAllBusinessHours', ''); |
97
|
|
|
} |
98
|
|
|
$rows = (new \App\Db\Query())->from('s_#__business_hours')->all(\App\Db::getInstance('admin')); |
99
|
|
|
\App\Cache::save('UtilsServiceContracts::getAllBusinessHours', '', $rows); |
100
|
|
|
return $rows; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Get business hours by ids . |
105
|
|
|
* |
106
|
|
|
* @param string $ids ex. '1,2' |
107
|
|
|
* |
108
|
|
|
* @return array |
109
|
|
|
*/ |
110
|
|
|
public static function getBusinessHoursByIds(array $ids): array |
111
|
|
|
{ |
112
|
|
|
$cacheKey = implode(',', $ids); |
113
|
|
|
if (\App\Cache::has('UtilsServiceContracts::getBusinessHoursById', $cacheKey)) { |
114
|
|
|
return \App\Cache::get('UtilsServiceContracts::getBusinessHoursById', $cacheKey); |
115
|
|
|
} |
116
|
|
|
$rows = (new \App\Db\Query())->from('s_#__business_hours')->where(['id' => $ids])->all(\App\Db::getInstance('admin')); |
117
|
|
|
\App\Cache::save('UtilsServiceContracts::getBusinessHoursById', $cacheKey, $rows); |
118
|
|
|
return $rows; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Get sla policy by id. |
123
|
|
|
* |
124
|
|
|
* @param int $id |
125
|
|
|
* |
126
|
|
|
* @return array |
127
|
|
|
*/ |
128
|
|
|
private static function getSlaPolicyById(int $id): array |
129
|
|
|
{ |
130
|
|
|
if (\App\Cache::has('UtilsServiceContracts::getSlaPolicyById', $id)) { |
131
|
|
|
return \App\Cache::get('UtilsServiceContracts::getSlaPolicyById', $id); |
132
|
|
|
} |
133
|
|
|
$row = (new \App\Db\Query())->from('s_#__sla_policy')->where(['id' => $id])->one(\App\Db::getInstance('admin')); |
134
|
|
|
\App\Cache::save('UtilsServiceContracts::getSlaPolicyById', $id, $row); |
135
|
|
|
return $row; |
|
|
|
|
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* Get sla policy by module id. |
140
|
|
|
* |
141
|
|
|
* @param int $moduleId |
142
|
|
|
* |
143
|
|
|
* @return array |
144
|
|
|
*/ |
145
|
|
|
public static function getSlaPolicyByModule(int $moduleId): array |
146
|
|
|
{ |
147
|
|
|
if (\App\Cache::has('UtilsServiceContracts::getSlaPolicyByModule', $moduleId)) { |
148
|
|
|
return \App\Cache::get('UtilsServiceContracts::getSlaPolicyByModule', $moduleId); |
149
|
|
|
} |
150
|
|
|
$rows = (new \App\Db\Query())->from('s_#__sla_policy')->where(['tabid' => $moduleId])->all(\App\Db::getInstance('admin')); |
151
|
|
|
\App\Cache::save('UtilsServiceContracts::getSlaPolicyByModule', $moduleId, $rows); |
152
|
|
|
return $rows; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Get sla policy from service contracts by crm id. |
157
|
|
|
* |
158
|
|
|
* @param int $serviceContractId Service contracts id |
159
|
|
|
* @param int|null $sourceModuleId |
160
|
|
|
* |
161
|
|
|
* @return array |
162
|
|
|
*/ |
163
|
1 |
|
public static function getSlaPolicyForServiceContracts(int $serviceContractId, ?int $sourceModuleId = null): array |
164
|
|
|
{ |
165
|
1 |
|
if (\App\Cache::has('UtilsServiceContracts::getSlaPolicyForServiceContracts', $serviceContractId)) { |
166
|
1 |
|
$rows = \App\Cache::get('UtilsServiceContracts::getSlaPolicyForServiceContracts', $serviceContractId); |
167
|
|
|
} else { |
168
|
1 |
|
$rows = (new \App\Db\Query())->from('u_#__servicecontracts_sla_policy')->where(['crmid' => $serviceContractId])->all(); |
169
|
1 |
|
\App\Cache::save('UtilsServiceContracts::getSlaPolicyForServiceContracts', $serviceContractId, $rows); |
170
|
|
|
} |
171
|
1 |
|
if ($sourceModuleId) { |
|
|
|
|
172
|
1 |
|
foreach ($rows as $key => $value) { |
173
|
|
|
if ($sourceModuleId !== $value['tabid']) { |
174
|
|
|
unset($rows[$key]); |
175
|
|
|
} |
176
|
|
|
} |
177
|
|
|
} |
178
|
1 |
|
return $rows; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Delete sla policy for service contracts. |
183
|
|
|
* |
184
|
|
|
* @param int $crmId |
185
|
|
|
* @param int $sourceModuleId |
186
|
|
|
* @param int|null $rowId |
187
|
|
|
* |
188
|
|
|
* @return void |
189
|
|
|
*/ |
190
|
|
|
public static function deleteSlaPolicy(int $crmId, int $sourceModuleId, ?int $rowId = null) |
191
|
|
|
{ |
192
|
|
|
$where = ['crmid' => $crmId, 'tabid' => $sourceModuleId]; |
193
|
|
|
if ($rowId) { |
|
|
|
|
194
|
|
|
$where['id'] = $rowId; |
195
|
|
|
} |
196
|
|
|
\App\Db::getInstance()->createCommand() |
197
|
|
|
->delete('u_#__servicecontracts_sla_policy', $where)->execute(); |
198
|
|
|
\App\Cache::delete('UtilsServiceContracts::getSlaPolicyForServiceContracts', $crmId); |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Save sla policy for service contracts. |
203
|
|
|
* |
204
|
|
|
* @param array $data |
205
|
|
|
* @param bool $delete |
206
|
|
|
* |
207
|
|
|
* @return void |
208
|
|
|
*/ |
209
|
|
|
public static function saveSlaPolicy(array $data, bool $delete = true) |
210
|
|
|
{ |
211
|
|
|
$db = \App\Db::getInstance(); |
212
|
|
|
if ($delete) { |
213
|
|
|
self::deleteSlaPolicy($data['crmid'], $data['tabid']); |
214
|
|
|
} |
215
|
|
|
if ($data['policy_type']) { |
216
|
|
|
$db->createCommand()->insert('u_#__servicecontracts_sla_policy', $data)->execute(); |
217
|
|
|
return $db->getLastInsertID(); |
|
|
|
|
218
|
|
|
} |
219
|
|
|
return 0; |
|
|
|
|
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* Get rules for service contracts. |
224
|
|
|
* |
225
|
1 |
|
* @param int $serviceContractId Service contracts id |
226
|
|
|
* @param \Vtiger_Record_Model $recordModel Record the model that will be updated |
227
|
1 |
|
* |
228
|
1 |
|
* @return array |
229
|
|
|
*/ |
230
|
|
|
public static function getRulesForServiceContracts(int $serviceContractId, \Vtiger_Record_Model $recordModel): array |
231
|
|
|
{ |
232
|
|
|
$times = $businessHours = []; |
233
|
|
|
foreach (self::getSlaPolicyForServiceContracts($serviceContractId, $recordModel->getModule()->getId()) as $row) { |
234
|
|
|
switch ($row['policy_type']) { |
235
|
|
|
case 1: |
236
|
|
|
$slaPolicy = self::getSlaPolicyById($row['sla_policy_id']); |
237
|
|
|
$conditions = \App\Json::decode($slaPolicy['conditions']); |
238
|
|
|
if ($conditions && \App\Condition::checkConditions($conditions, $recordModel)) { |
239
|
|
|
if (empty($slaPolicy['operational_hours'])) { |
240
|
|
|
return $slaPolicy; |
241
|
|
|
} |
242
|
|
|
if ($slaPolicy['business_hours']) { |
243
|
|
|
return self::optimizeBusinessHours(explode(',', $slaPolicy['business_hours'])); |
244
|
|
|
} |
245
|
|
|
} |
246
|
|
|
break; |
247
|
|
|
case 2: |
248
|
|
|
$conditions = \App\Json::decode($row['conditions']); |
249
|
|
|
if ($conditions && $row['business_hours'] && \App\Condition::checkConditions($conditions, $recordModel)) { |
250
|
|
|
$businessHours = \array_merge($businessHours, explode(',', $row['business_hours'])); |
251
|
|
|
if ((isset($times['reaction_time']) && \App\Fields\TimePeriod::convertToMinutes($row['reaction_time']) < \App\Fields\TimePeriod::convertToMinutes($times['reaction_time'])) |
252
|
|
|
|| !isset($times['reaction_time'])) { |
253
|
|
|
$times = [ |
254
|
|
|
'reaction_time' => $row['reaction_time'], |
255
|
|
|
'idle_time' => $row['idle_time'], |
256
|
|
|
'resolve_time' => $row['resolve_time'], |
257
|
|
|
]; |
258
|
1 |
|
} |
259
|
|
|
} |
260
|
|
|
break; |
261
|
|
|
} |
262
|
|
|
} |
263
|
|
|
if ($businessHours) { |
264
|
|
|
$result = []; |
265
|
1 |
|
foreach (self::optimizeBusinessHours(\array_unique($businessHours)) as $value) { |
266
|
|
|
$result[] = array_merge($value, $times); |
267
|
|
|
} |
268
|
|
|
return $result; |
269
|
|
|
} |
270
|
|
|
return []; |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
/** |
274
|
|
|
* Get rules for record model which will be updated. |
275
|
|
|
* |
276
|
|
|
* @param \Vtiger_Record_Model $recordModel Record the model that will be updated |
277
|
|
|
* |
278
|
|
|
* @return array |
279
|
|
|
*/ |
280
|
|
|
public static function getSlaPolicyRulesForModule(\Vtiger_Record_Model $recordModel): array |
281
|
|
|
{ |
282
|
|
|
$times = $businessHours = []; |
283
|
|
|
foreach (self::getSlaPolicyForModule($recordModel->getModule()->getId()) as $row) { |
284
|
|
|
$conditions = \App\Json::decode($row['conditions']); |
285
|
|
|
if ($conditions && $row['business_hours'] && \App\Condition::checkConditions($conditions, $recordModel)) { |
286
|
|
|
$businessHours = \array_merge($businessHours, explode(',', $row['business_hours'])); |
287
|
|
|
if ((isset($times['reaction_time']) && \App\Fields\TimePeriod::convertToMinutes($row['reaction_time']) < \App\Fields\TimePeriod::convertToMinutes($times['reaction_time'])) |
288
|
|
|
|| !isset($times['reaction_time'])) { |
289
|
|
|
$times = [ |
290
|
|
|
'reaction_time' => $row['reaction_time'], |
291
|
|
|
'idle_time' => $row['idle_time'], |
292
|
|
|
'resolve_time' => $row['resolve_time'], |
293
|
|
|
]; |
294
|
|
|
} |
295
|
|
|
break; |
296
|
|
|
} |
297
|
|
|
} |
298
|
|
|
if ($businessHours) { |
299
|
|
|
$result = []; |
300
|
|
|
foreach (self::optimizeBusinessHours(\array_unique($businessHours)) as $value) { |
301
|
|
|
$result[] = array_merge($value, $times); |
302
|
|
|
} |
303
|
|
|
return $result; |
304
|
|
|
} |
305
|
|
|
return []; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Get sla policy by crm id. |
310
|
|
|
* |
311
|
|
|
* @param int $moduleId |
312
|
|
|
* |
313
|
|
|
* @return array |
314
|
|
|
*/ |
315
|
|
|
public static function getSlaPolicyForModule(int $moduleId): array |
316
|
|
|
{ |
317
|
|
|
if (\App\Cache::has('UtilsServiceContracts::getSlaPolicyForModule', $moduleId)) { |
318
|
|
|
$rows = \App\Cache::get('UtilsServiceContracts::getSlaPolicyForModule', $moduleId); |
319
|
|
|
} else { |
320
|
|
|
$rows = (new \App\Db\Query())->from('s_#__sla_policy')->where(['tabid' => $moduleId, 'available_for_record_time_count' => 1])->all(\App\Db::getInstance('admin')); |
321
|
|
|
\App\Cache::save('UtilsServiceContracts::getSlaPolicyForModule', $moduleId, $rows); |
322
|
|
|
} |
323
|
|
|
return $rows; |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* Parse business hours to days. |
328
|
|
|
* |
329
|
|
|
* @param array $rows |
330
|
|
|
* |
331
|
|
|
* @return array |
332
|
|
|
*/ |
333
|
|
|
private static function parseBusinessHoursToDays(array $rows): array |
334
|
|
|
{ |
335
|
|
|
$days = $holidays = []; |
336
|
|
|
foreach ($rows as $row) { |
337
|
|
|
foreach (explode(',', $row['working_days']) as $day) { |
338
|
|
|
if ((isset($days[$day]['working_hours_from']) && (int) $row['working_hours_from'] < (int) $days[$day]['working_hours_from']) |
339
|
|
|
|| empty($days[$day]['working_hours_from'])) { |
340
|
|
|
$days[$day] = [ |
341
|
|
|
'working_hours_from' => $row['working_hours_from'], |
342
|
|
|
'working_hours_to' => $row['working_hours_to'], |
343
|
|
|
'reaction_time' => $row['reaction_time'], |
344
|
|
|
'idle_time' => $row['idle_time'], |
345
|
|
|
'resolve_time' => $row['resolve_time'], |
346
|
|
|
]; |
347
|
|
|
} |
348
|
|
|
} |
349
|
|
|
if (!empty($row['holidays']) && ((isset($holidays['working_hours_from']) && (int) $row['working_hours_from'] < (int) $holidays['working_hours_from']) |
350
|
|
|
|| empty($holidays['working_hours_from']))) { |
351
|
|
|
$holidays = [ |
352
|
|
|
'working_hours_from' => $row['working_hours_from'], |
353
|
|
|
'working_hours_to' => $row['working_hours_to'], |
354
|
|
|
'reaction_time' => $row['reaction_time'], |
355
|
|
|
'idle_time' => $row['idle_time'], |
356
|
|
|
'resolve_time' => $row['resolve_time'], |
357
|
|
|
]; |
358
|
|
|
} |
359
|
|
|
} |
360
|
|
|
return ['days' => $days, 'holidays' => $holidays]; |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
/** |
364
|
|
|
* Undocumented function. |
365
|
|
|
* |
366
|
|
|
* @param array $businessHours |
367
|
|
|
* |
368
|
|
|
* @return array |
369
|
|
|
*/ |
370
|
|
|
private static function optimizeBusinessHours(array $businessHours): array |
371
|
|
|
{ |
372
|
|
|
$result = []; |
373
|
|
|
['days' => $days, 'holidays' => $holidays] = self::parseBusinessHoursToDays(self::getBusinessHoursByIds($businessHours)); |
374
|
|
|
foreach ($days as $day => $value) { |
375
|
|
|
$key = "{$value['working_hours_from']}|{$value['working_hours_to']}"; |
376
|
|
|
if (isset($result[$key])) { |
377
|
|
|
$result[$key] = ['working_days' => $result[$key]['working_days'] . ',' . $day] + $value; |
378
|
|
|
} else { |
379
|
|
|
$result[$key] = ['working_days' => $day] + $value; |
380
|
|
|
} |
381
|
|
|
} |
382
|
|
|
if ($holidays) { |
383
|
|
|
$key = "{$holidays['working_hours_from']}|{$holidays['working_hours_to']}"; |
384
|
|
|
$result[$key] = $result[$key] + ['holidays' => 1]; |
385
|
|
|
} |
386
|
|
|
return $result; |
387
|
|
|
} |
388
|
|
|
|
389
|
|
|
/** |
390
|
|
|
* Function returning difference in format between date times. |
391
|
|
|
* |
392
|
|
|
* @param string $start |
393
|
|
|
* @param string $end |
394
|
|
|
* @param \Vtiger_Record_Model $recordModel Record the model that will be updated |
395
|
|
|
* |
396
|
|
|
* @return int |
397
|
|
|
*/ |
398
|
|
|
public static function getDiff(string $start, \Vtiger_Record_Model $recordModel, string $end = ''): int |
399
|
|
|
{ |
400
|
|
|
if (!$end) { |
401
|
|
|
$end = date('Y-m-d H:i:s'); |
402
|
|
|
} |
403
|
|
|
$fieldModel = current($recordModel->getModule()->getReferenceFieldsForModule('ServiceContracts')); |
404
|
|
|
if ($fieldModel && ($value = $recordModel->get($fieldModel->getName()))) { |
405
|
|
|
return self::getDiffFromServiceContracts($start, $end, $value, $recordModel); |
406
|
|
|
} |
407
|
|
|
if (\is_int($diff = self::getDiffFromSlaPolicy($start, $end, $recordModel))) { |
408
|
|
|
return $diff; |
409
|
|
|
} |
410
|
|
|
if (!($diff = self::getDiffFromDefaultBusinessHours($start, $end))) { |
411
|
|
|
$diff = \App\Fields\DateTime::getDiff($start, $end, 'minutes'); |
412
|
|
|
} |
413
|
|
|
return $diff; |
414
|
1 |
|
} |
415
|
|
|
|
416
|
1 |
|
/** |
417
|
1 |
|
* Get the amount of business time between the two dates in minutes based on the service contracts. |
418
|
1 |
|
* |
419
|
|
|
* @param string $start |
420
|
|
|
* @param string $end |
421
|
1 |
|
* @param int $serviceContractId Service contracts id |
422
|
|
|
* @param \Vtiger_Record_Model $recordModel Record the model that will be updated |
423
|
|
|
* |
424
|
|
|
* @return int |
425
|
|
|
*/ |
426
|
|
|
public static function getDiffFromServiceContracts(string $start, string $end, int $serviceContractId, \Vtiger_Record_Model $recordModel): int |
427
|
|
|
{ |
428
|
|
|
if ($rules = self::getRulesForServiceContracts($serviceContractId, $recordModel)) { |
429
|
|
|
if (isset($rules['id'])) { |
430
|
|
|
return round(\App\Fields\DateTime::getDiff($start, $end, 'minutes')); |
|
|
|
|
431
|
|
|
} |
432
|
1 |
|
$time = 0; |
433
|
|
|
foreach ($rules as $row) { |
434
|
1 |
|
$time += self::businessTime($start, $end, $row['working_hours_from'], $row['working_hours_to'], explode(',', $row['working_days']), !empty($row['holidays'])); |
435
|
1 |
|
} |
436
|
1 |
|
return $time; |
437
|
|
|
} |
438
|
|
|
if (!($diff = self::getDiffFromDefaultBusinessHours($start, $end))) { |
439
|
|
|
$diff = round(\App\Fields\DateTime::getDiff($start, $end, 'minutes')); |
440
|
|
|
} |
441
|
|
|
return $diff; |
|
|
|
|
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
/** |
445
|
|
|
* Get the amount of business time between the two dates in minutes based on the global sla policy. |
446
|
|
|
* |
447
|
1 |
|
* @param string $start |
448
|
|
|
* @param string $end |
449
|
|
|
* @param \Vtiger_Record_Model $recordModel Record the model that will be updated |
450
|
1 |
|
* |
451
|
|
|
* @return int|null |
452
|
|
|
*/ |
453
|
|
|
public static function getDiffFromSlaPolicy(string $start, string $end, \Vtiger_Record_Model $recordModel): ?int |
454
|
|
|
{ |
455
|
|
|
if ($rules = self::getSlaPolicyRulesForModule($recordModel)) { |
456
|
|
|
$time = 0; |
457
|
|
|
foreach ($rules as $row) { |
458
|
|
|
$time += self::businessTime($start, $end, $row['working_hours_from'], $row['working_hours_to'], explode(',', $row['working_days']), !empty($row['holidays'])); |
459
|
|
|
} |
460
|
|
|
return $time; |
461
|
|
|
} |
462
|
|
|
return null; |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
/** |
466
|
|
|
* Get the amount of default business time between two dates in minutes. |
467
|
|
|
* |
468
|
|
|
* @param string $start |
469
|
|
|
* @param string $end |
470
|
|
|
* |
471
|
|
|
* @return int |
472
|
|
|
*/ |
473
|
|
|
public static function getDiffFromDefaultBusinessHours(string $start, string $end): int |
474
|
|
|
{ |
475
|
|
|
$businessHours = self::getDefaultBusinessHours(); |
476
|
|
|
if (!$businessHours) { |
477
|
|
|
return false; |
|
|
|
|
478
|
|
|
} |
479
|
|
|
$time = 0; |
480
|
|
|
foreach ($businessHours as $row) { |
481
|
|
|
if ($row['working_days']) { |
482
|
|
|
$time += self::businessTime($start, $end, $row['working_hours_from'], $row['working_hours_to'], explode(',', $row['working_days']), (bool) $row['holidays']); |
483
|
|
|
} |
484
|
|
|
} |
485
|
|
|
return $time; |
486
|
|
|
} |
487
|
|
|
|
488
|
|
|
/** |
489
|
|
|
* Update expected times. |
490
|
|
|
* |
491
|
|
|
* @param \Vtiger_Record_Model $recordModel |
492
|
|
|
* @param array $type |
493
|
|
|
* |
494
|
|
|
* @return void |
495
|
|
|
*/ |
496
|
|
|
public static function updateExpectedTimes(\Vtiger_Record_Model $recordModel, array $type) |
497
|
|
|
{ |
498
|
|
|
$field = \App\Field::getRelatedFieldForModule($recordModel->getModuleName(), 'ServiceContracts'); |
499
|
|
|
$serviceContract = ($field && !empty($recordModel->get($field['fieldname']))) ? $recordModel->get($field['fieldname']) : 0; |
|
|
|
|
500
|
|
|
foreach (self::getExpectedTimes($serviceContract, $recordModel, $type) as $key => $time) { |
501
|
|
|
$recordModel->set($key . '_expected', $time); |
502
|
|
|
} |
503
|
|
|
} |
504
|
|
|
|
505
|
|
|
/** |
506
|
|
|
* Get expected times from ServiceContracts. |
507
|
|
|
* |
508
|
|
|
* @param int $id Service contract id |
509
|
|
|
* @param \Vtiger_Record_Model $recordModel |
510
|
|
|
* @param array $type |
511
|
|
|
* |
512
|
|
|
* @return array |
513
|
|
|
*/ |
514
|
|
|
private static function getExpectedTimes(int $id, \Vtiger_Record_Model $recordModel, array $type): array |
515
|
|
|
{ |
516
|
|
|
$return = []; |
517
|
|
|
$date = new \DateTime(); |
518
|
|
|
if ($id && ($rules = self::getRulesForServiceContracts($id, $recordModel)) || ($rules = self::getSlaPolicyRulesForModule($recordModel))) { |
|
|
|
|
519
|
|
|
if (isset($rules['id'])) { |
520
|
|
|
foreach (self::$fieldsMap as $key => $fieldKey) { |
521
|
|
|
if (\in_array($fieldKey, $type)) { |
522
|
|
|
$minutes = \App\Fields\TimePeriod::convertToMinutes($rules[$key]); |
523
|
|
|
$return[$fieldKey] = (clone $date)->modify("+$minutes minute")->format('Y-m-d H:i:s'); |
524
|
|
|
} |
525
|
|
|
} |
526
|
|
|
return $return; |
527
|
|
|
} |
528
|
|
|
$days = self::parseBusinessHoursToDays($rules); |
529
|
|
|
} elseif ($businessHours = self::getDefaultBusinessHours()) { |
530
|
|
|
$days = self::parseBusinessHoursToDays($businessHours); |
531
|
|
|
} else { |
532
|
|
|
return []; |
533
|
|
|
} |
534
|
|
|
$day = $date->format('N'); |
535
|
|
|
$daySetting = null; |
536
|
|
|
if (\App\Fields\Date::getHolidays($date->format('Y-m-d'), $date->format('Y-m-d'))) { |
537
|
|
|
$daySetting = $days['holidays']; |
538
|
|
|
} elseif (empty($days['days'][$day])) { |
539
|
|
|
$daySetting = self::getNextBusinessDay($date, $days); |
540
|
|
|
} else { |
541
|
|
|
$daySetting = $days['days'][$day]; |
542
|
|
|
} |
543
|
|
|
if ($daySetting) { |
544
|
|
|
$interval = \App\Fields\DateTime::getDiff($date->format('Y-m-d') . ' ' . $daySetting['working_hours_to'], $date->format('Y-m-d H:i:s'), 'minutes'); |
545
|
|
|
foreach (self::$fieldsMap as $key => $fieldKey) { |
546
|
|
|
if (\in_array($fieldKey, $type)) { |
547
|
|
|
$minutes = \App\Fields\TimePeriod::convertToMinutes($daySetting[$key]); |
548
|
|
|
if ($minutes < $interval) { |
549
|
|
|
$return[$fieldKey] = (clone $date)->modify("+$minutes minute")->format('Y-m-d H:i:s'); |
550
|
|
|
} else { |
551
|
|
|
$tmpDate = clone $date; |
552
|
|
|
$tmpInterval = $interval; |
553
|
|
|
while ($minutes > $tmpInterval) { |
554
|
|
|
$minutes -= $tmpInterval; |
555
|
|
|
$tmpDaySetting = self::getNextBusinessDay($tmpDate, $days); |
556
|
|
|
$tmpInterval = \App\Fields\DateTime::getDiff($tmpDate->format('Y-m-d') . ' ' . $tmpDaySetting['working_hours_to'], $tmpDate->format('Y-m-d H:i:s'), 'minutes'); |
557
|
|
|
if ($minutes < $tmpInterval) { |
558
|
|
|
$return[$fieldKey] = (clone $tmpDate)->modify("+$minutes minute")->format('Y-m-d H:i:s'); |
559
|
|
|
} |
560
|
|
|
} |
561
|
|
|
} |
562
|
|
|
} |
563
|
|
|
} |
564
|
|
|
return $return; |
565
|
|
|
} |
566
|
|
|
return []; |
567
|
|
|
} |
568
|
|
|
|
569
|
|
|
/** |
570
|
|
|
* Get next business day. |
571
|
|
|
* |
572
|
|
|
* @param \DateTime $date |
573
|
|
|
* @param array $days |
574
|
|
|
* |
575
|
|
|
* @return array|null |
576
|
|
|
*/ |
577
|
|
|
private static function getNextBusinessDay(\DateTime &$date, array $days): ?array |
578
|
|
|
{ |
579
|
|
|
$tempDay = (int) $date->format('N') + 1; |
580
|
|
|
$counter = 1; |
581
|
|
|
$result = null; |
582
|
|
|
while ($counter < 14) { |
583
|
|
|
$date->modify('+1 day'); |
584
|
|
|
if (\App\Fields\Date::getHolidays($date->format('Y-m-d'), $date->format('Y-m-d'))) { |
585
|
|
|
$result = $days['holidays']; |
586
|
|
|
break; |
587
|
|
|
} |
588
|
|
|
if (isset($days['days'][$tempDay])) { |
589
|
|
|
$result = $days['days'][$tempDay]; |
590
|
|
|
break; |
591
|
|
|
} |
592
|
|
|
++$tempDay; |
593
|
|
|
if (8 === $tempDay) { |
594
|
|
|
$tempDay = 1; |
595
|
|
|
} |
596
|
|
|
++$counter; |
597
|
|
|
} |
598
|
|
|
if ($result) { |
599
|
|
|
\call_user_func_array([$date, 'setTime'], explode(':', $result['working_hours_from'])); |
600
|
|
|
} |
601
|
|
|
return $result; |
602
|
|
|
} |
603
|
|
|
|
604
|
|
|
/** |
605
|
|
|
* Get modules name related to ServiceContracts. |
606
|
|
|
* |
607
|
|
|
* @return string[] |
608
|
|
|
*/ |
609
|
|
|
public static function getModules(): array |
610
|
|
|
{ |
611
|
|
|
$modules = []; |
612
|
|
|
foreach (\App\Field::getRelatedFieldForModule(false, 'ServiceContracts') as $moduleName => $value) { |
613
|
|
|
if (\App\RecordStatus::getFieldName($moduleName)) { |
614
|
|
|
$modules[] = $moduleName; |
615
|
|
|
} |
616
|
|
|
} |
617
|
|
|
return $modules; |
618
|
|
|
} |
619
|
|
|
} |
620
|
|
|
|