Skip to content

Commit 2eb1a1e

Browse files
committed
enhancement: allow to set ticket as periodic and create new events by crontab script
1 parent 75a9725 commit 2eb1a1e

17 files changed

Lines changed: 362 additions & 11 deletions

bin/lms-timetable-scheduler.php

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
/*
5+
* LMS version 28.x
6+
*
7+
* (C) Copyright 2001-2025 LMS Developers
8+
*
9+
* Please, see the doc/AUTHORS for more information about authors!
10+
*
11+
* This program is free software; you can redistribute it and/or modify
12+
* it under the terms of the GNU General Public License Version 2 as
13+
* published by the Free Software Foundation.
14+
*
15+
* This program is distributed in the hope that it will be useful,
16+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
* GNU General Public License for more details.
19+
*
20+
* You should have received a copy of the GNU General Public License
21+
* along with this program; if not, write to the Free Software
22+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
23+
* USA.
24+
*
25+
* $Id$
26+
*/
27+
28+
$script_parameters = array(
29+
'schedule-tickets' => 'c',
30+
'debug' => 'd',
31+
);
32+
33+
$script_help = <<<EOF
34+
-c, --schedule-tickets create events for tickets with periodicity set;
35+
-d, --debug print all possible output;
36+
EOF;
37+
38+
require_once('script-options.php');
39+
40+
$debug = isset($options['debug']);
41+
42+
// Initialize Session, Auth and LMS classes
43+
$SYSLOG = null;
44+
$AUTH = null;
45+
$LMS = new LMS($DB, $AUTH, $SYSLOG);
46+
Localisation::setUiLanguage('pl');
47+
48+
$plugin_manager = LMSPluginManager::getInstance();
49+
$LMS->setPluginManager($plugin_manager);
50+
51+
$rt_schedule_planing_forward_events = ConfigHelper::getConfig('rt.schedule_planing_forward_events', 3);
52+
$tickets_to_plan = $LMS->GetQueueContents(
53+
[
54+
'periodicity' => -1,
55+
'deleted' => 0,
56+
'closed' => 0,
57+
'short' => true,
58+
]
59+
);
60+
61+
function CalculateNextOccurrenceInFuture($start, $periodicity)
62+
{
63+
global $EVENT_PERIODICITY;
64+
$timestamp = (new DateTime())->setTimestamp($start);
65+
$interval = $EVENT_PERIODICITY[$periodicity]['map'];
66+
$now = time();
67+
68+
do {
69+
$timestamp->modify($interval);
70+
} while ($timestamp->getTimestamp() <= $now);
71+
72+
return $timestamp->getTimestamp();
73+
}
74+
75+
if (empty($tickets_to_plan)) {
76+
die();
77+
}
78+
79+
foreach ($tickets_to_plan as $idx => $t) {
80+
$t = $LMS->GetTicketContents($t['id']);
81+
82+
for ($a = $t['openeventcount'] ?? 0; $a < $rt_schedule_planing_forward_events; $a++) {
83+
$ticket_events = $LMS->GetEventsByTicketId($t['ticketid']);
84+
$last_ticket_event = (is_array($ticket_events) && !empty($ticket_events)) ? end($ticket_events) : null;
85+
$timestamp = $last_ticket_event['date'] ?? $t['createtime'];
86+
$next_occurrence = CalculateNextOccurrenceInFuture($timestamp, $t['periodicity']);
87+
$params = [
88+
'date' => $next_occurrence,
89+
'begintime' => $last_ticket_event['begintime'] ?? 0,
90+
'endtime' => $last_ticket_event['endtime'] ?? 0,
91+
'enddate' => $last_ticket_event['enddate'] ?? 0,
92+
'title' => $t['messages'][0]['subject'] ?? null,
93+
'description' => $t['messages'][0]['body'] ?? null,
94+
'userlist' => array($t['owner']) ?? null,
95+
'custid' => $t['customerid'] ?? null,
96+
'address_id' => $t['address_id'] ?? null,
97+
'ticketid' => $t['ticketid'],
98+
'nodeid' => $t['nodeid'] ?? null,
99+
'private' => 0,
100+
'type' => EVENT_OTHER,
101+
];
102+
if ($debug) {
103+
print_r($params) . PHP_EOL;
104+
}
105+
$LMS->EventAdd($params);
106+
}
107+
}

doc/ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,6 +1069,7 @@ version 28-git (????-??-??):
10691069
- improvement: 'rt.from_header_email_is_customer_contact_check' configuration variable with default 'true'
10701070
value allows to disable checking if 'from' header e-mail address is customer contact during ticket message
10711071
reply form startup [chilan]
1072+
- enhancement: allow to set ticket as periodic and create new events by crontab script [interduo]
10721073

10731074
version 27.0 (2021-08-20):
10741075

doc/lms.mysql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2244,6 +2244,7 @@ CREATE TABLE rttickets (
22442244
modtime int(16) NOT NULL DEFAULT 0,
22452245
source tinyint(4) NOT NULL DEFAULT '0',
22462246
priority tinyint(4) DEFAULT NULL,
2247+
periodicity smallint DEFAULT 0,
22472248
deleted tinyint(1) NOT NULL DEFAULT '0',
22482249
deltime int(16) NOT NULL DEFAULT 0,
22492250
deluserid int(11) DEFAULT NULL,

doc/lms.pgsql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2157,6 +2157,7 @@ CREATE TABLE rttickets (
21572157
modtime bigint NOT NULL DEFAULT 0,
21582158
source smallint DEFAULT 0 NOT NULL,
21592159
priority smallint DEFAULT NULL,
2160+
periodicity smallint DEFAULT 0,
21602161
deleted smallint DEFAULT 0 NOT NULL,
21612162
deltime bigint DEFAULT 0 NOT NULL,
21622163
deluserid integer DEFAULT NULL
@@ -4525,6 +4526,6 @@ INSERT INTO netdevicemodels (name, alternative_name, netdeviceproducerid) VALUES
45254526
('XR7', 'XR7 MINI PCI PCBA', 2),
45264527
('XR9', 'MINI PCI 600MW 900MHZ', 2);
45274528

4528-
INSERT INTO dbinfo (keytype, keyvalue) VALUES ('dbversion', '2025081300');
4529+
INSERT INTO dbinfo (keytype, keyvalue) VALUES ('dbversion', '2025073000');
45294530

45304531
COMMIT;

lib/LMSManagers/LMSHelpdeskManager.php

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public function GetQueueContents(array $params)
138138
{
139139
$userid = Auth::GetCurrentUser();
140140
extract($params);
141-
foreach (array('ids', 'state', 'priority', 'source', 'owner', 'catids', 'removed', 'netdevids', 'netnodeids', 'deadline',
141+
foreach (array('ids', 'state', 'priority', 'periodicity', 'source', 'owner', 'catids', 'removed', 'netdevids', 'netnodeids', 'deadline',
142142
'serviceids', 'typeids', 'unread', 'parentids', 'verifierids', 'rights', 'projectids', 'cid', 'subject', 'fromdate', 'todate', 'short', 'watching') as $var) {
143143
if (!isset(${$var})) {
144144
${$var} = null;
@@ -185,6 +185,9 @@ public function GetQueueContents(array $params)
185185
case 'priority':
186186
$sqlord = ' ORDER BY t.priority';
187187
break;
188+
case 'periodicity':
189+
$sqlord = ' ORDER BY t.periodicity';
190+
break;
188191
case 'deadline':
189192
$sqlord = ' ORDER BY t.deadline';
190193
break;
@@ -223,6 +226,16 @@ public function GetQueueContents(array $params)
223226
$priorityfilter = ' AND t.priority = '.$priority;
224227
}
225228

229+
if (empty($periodicity)) {
230+
$periodicityfilter = '';
231+
} elseif (is_array($periodicity)) {
232+
$periodicityfilter = ' AND t.periodicity IN (' . implode(',', $periodicity) . ')';
233+
} elseif ($periodicity == -1) {
234+
$periodicityfilter = ' AND t.periodicity IS NOT NULL';
235+
} else {
236+
$periodicityfilter = ' AND t.periodicity = ' . $periodicity;
237+
}
238+
226239
if (empty($source) || intval($source) == -1) {
227240
$sourcefilter = '';
228241
} else {
@@ -485,6 +498,7 @@ public function GetQueueContents(array $params)
485498
. $parentfilter
486499
. $statefilter
487500
. $priorityfilter
501+
. $periodicityfilter
488502
. $sourcefilter
489503
. $ownerfilter
490504
. $removedfilter
@@ -504,7 +518,7 @@ public function GetQueueContents(array $params)
504518

505519
if ($result = $this->db->GetAll(
506520
'SELECT DISTINCT t.id, t.customerid, t.address_id, va.name AS vaname, va.city AS vacity, va.street, va.house, va.flat, c.address, c.city, vusers.name AS ownername,
507-
t.subject, t.state, owner AS ownerid, t.requestor AS req, t.source, t.priority, rtqueues.name,'
521+
t.subject, t.state, owner AS ownerid, t.requestor AS req, t.source, t.priority, t.periodicity, rtqueues.name,'
508522
. $this->db->Concat('c.lastname', "' '", 'c.name') . ' AS customername,
509523
t.requestor_phone, t.requestor_mail, t.deadline, t.requestor_userid, rq.name AS requestor_name,
510524
t.createtime AS createtime, u.name AS creatorname, t.deleted, t.deltime, t.deluserid,
@@ -594,6 +608,7 @@ public function GetQueueContents(array $params)
594608
. $parentfilter
595609
. $statefilter
596610
. $priorityfilter
611+
. $periodicityfilter
597612
. $sourcefilter
598613
. $ownerfilter
599614
. $removedfilter
@@ -651,6 +666,7 @@ public function GetQueueContents(array $params)
651666
$result['owner'] = $owner;
652667
$result['removed'] = $removed;
653668
$result['priority'] = $priority;
669+
$result['periodicity'] = $periodicity;
654670
$result['source'] = $source;
655671
$result['deadline'] = $deadline;
656672
$result['service'] = $serviceids;
@@ -1150,9 +1166,9 @@ public function TicketAdd($ticket, $files = null)
11501166

11511167
$this->db->Execute(
11521168
'INSERT INTO rttickets (queueid, customerid, requestor, requestor_mail, requestor_phone,
1153-
requestor_userid, subject, state, owner, createtime, modtime, cause, creatorid, source, priority, address_id, nodeid,
1169+
requestor_userid, subject, state, owner, createtime, modtime, cause, creatorid, source, priority, periodicity, address_id, nodeid,
11541170
netnodeid, netdevid, verifierid, deadline, service, type, invprojectid, parentid, customcreatetime, customresolvetime)
1155-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
1171+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
11561172
array(
11571173
$ticket['queue'],
11581174
empty($ticket['customerid']) ? null : $ticket['customerid'],
@@ -1169,6 +1185,7 @@ public function TicketAdd($ticket, $files = null)
11691185
$ticket['userid'] ?? Auth::GetCurrentUser(),
11701186
$ticket['source'] ?? 0,
11711187
isset($ticket['priority']) && strlen($ticket['priority']) ? $ticket['priority'] : null,
1188+
empty($ticket['periodicity']) ? null : $ticket['periodicity'],
11721189
!empty($ticket['address_id']) ? $ticket['address_id'] : null,
11731190
!empty($ticket['nodeid']) ? $ticket['nodeid'] : null,
11741191
!empty($ticket['netnodeid']) ? $ticket['netnodeid'] : null,
@@ -1296,7 +1313,7 @@ public function GetTicketContents($id, $short = false)
12961313

12971314
$ticket = $this->db->GetRow('SELECT t.id AS ticketid, t.queueid, rtqueues.name AS queuename, t.requestor, t.requestor_phone, t.requestor_mail,
12981315
t.requestor_userid, d.name AS requestor_username, t.state, t.owner, t.customerid, t.cause, t.creatorid, c.name AS creator,
1299-
t.source, t.priority, i.id AS invprojectid, i.name AS invproject_name, t.verifier_rtime, '
1316+
t.source, t.priority, t.periodicity, i.id AS invprojectid, i.name AS invproject_name, t.verifier_rtime, '
13001317
. $this->db->Concat('customers.lastname', "' '", 'customers.name') . ' AS customername,
13011318
o.name AS ownername, t.createtime, t.resolvetime,
13021319
t.customcreatetime,
@@ -1554,7 +1571,7 @@ protected function updateTicketParentID($ticketid, $parentid = null)
15541571

15551572
public function TicketChange($ticketid, array $props)
15561573
{
1557-
global $LMS, $RT_STATES, $RT_CAUSE, $RT_SOURCES, $RT_PRIORITIES, $SERVICETYPES, $RT_TYPES;
1574+
global $LMS, $RT_STATES, $RT_CAUSE, $RT_SOURCES, $RT_PRIORITIES, $SERVICETYPES, $RT_TYPES, $EVENT_PERIODICITY;
15581575

15591576
$allow_empty_categories = ConfigHelper::checkConfig('rt.allow_empty_categories', ConfigHelper::checkConfig('phpui.helpdesk_allow_empty_categories'));
15601577

@@ -1612,6 +1629,15 @@ public function TicketChange($ticketid, array $props)
16121629
$props['priority'] = $ticket['priority'];
16131630
}
16141631

1632+
if (array_key_exists('periodicity', $props) && $ticket['periodicity'] != $props['periodicity']) {
1633+
$a = isset($ticket['periodicity']) ? $EVENT_PERIODICITY[$ticket['periodicity']]['label'] : trans('none');
1634+
$b = isset($props['periodicity']) ? $EVENT_PERIODICITY[$props['periodicity']]['label'] : trans('none');
1635+
$notes[] = trans('Ticket\'s scheduling periodicity has been changed from $a to $b.', $a, $b);
1636+
$type = $type | RTMESSAGE_PERIODICITY_CHANGE;
1637+
} else {
1638+
$props['periodicity'] = $ticket['periodicity'];
1639+
}
1640+
16151641
if (isset($props['state']) && $ticket['state'] != $props['state']) {
16161642
$notes[] = trans('Ticket\'s state has been changed from $a to $b.', $RT_STATES[$ticket['state']]['label'], $RT_STATES[$props['state']]['label']);
16171643
$type = $type | RTMESSAGE_STATE_CHANGE;
@@ -1992,7 +2018,7 @@ public function TicketChange($ticketid, array $props)
19922018
$userid : $props['owner'];
19932019
$this->db->Execute(
19942020
'UPDATE rttickets SET queueid = ?, owner = ?, cause = ?, state = ?, modtime = ?, resolvetime=?, subject = ?,
1995-
customerid = ?, source = ?, priority = ?, address_id = ?, nodeid = ?, netnodeid = ?, netdevid = ?,
2021+
customerid = ?, source = ?, priority = ?, periodicity = ?, address_id = ?, nodeid = ?, netnodeid = ?, netdevid = ?,
19962022
verifierid = ?, verifier_rtime = ?, deadline = ?, service = ?, type = ?, invprojectid = ?,
19972023
requestor_userid = ?, requestor = ?, requestor_mail = ?, requestor_phone = ?, parentid = ?,
19982024
customcreatetime = ?, customresolvetime = ?
@@ -2008,6 +2034,7 @@ public function TicketChange($ticketid, array $props)
20082034
$props['customerid'],
20092035
$props['source'],
20102036
$props['priority'],
2037+
$props['periodicity'],
20112038
$props['address_id'],
20122039
$props['nodeid'],
20132040
$props['netnodeid'],

lib/SYSLOG.class.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class SYSLOG
9696
public const RES_TARIFF_PRICE_VARIANT = 68;
9797
public const RES_TICKET_MESSAGE = 69;
9898
public const RES_QUEUE = 70;
99+
public const RES_PERIODICITY = 71;
99100

100101
public const OPER_ADD = 1;
101102
public const OPER_DELETE = 2;
@@ -167,6 +168,7 @@ class SYSLOG
167168
self::RES_TARIFFASSIGN => 'tariff assignment<!syslog>',
168169
self::RES_EVENT => 'event<!syslog>',
169170
self::RES_EVENTASSIGN => 'event assignment<!syslog>',
171+
self::RES_PERIODICITY => 'scheduling periodicity<!syslog>',
170172
self::RES_ADDRESS => 'address<!syslog>',
171173
self::RES_TICKET => 'ticket<!syslog>',
172174
self::RES_DOCATTACH => 'document attachment<!syslog>',
@@ -239,6 +241,7 @@ class SYSLOG
239241
self::RES_TARIFFASSIGN => 'tariffassignmentid',
240242
self::RES_EVENT => 'eventid',
241243
self::RES_EVENTASSIGN => 'eventassignmentid',
244+
self::RES_PERIODICITY => 'periodid',
242245
self::RES_ADDRESS => 'address_id',
243246
self::RES_TICKET => 'ticketid',
244247
self::RES_DOCATTACH => 'documentattachmentid',

lib/definitions.php

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,8 @@
642642
RTMESSAGE_PARENT_CHANGE = 1048576,
643643
RTMESSAGE_ASSIGNED_EVENT_ADD = 2097152,
644644
RTMESSAGE_ASSIGNED_EVENT_CHANGE = 4194304,
645-
RTMESSAGE_ASSIGNED_EVENT_DELETE = 8388608;
645+
RTMESSAGE_ASSIGNED_EVENT_DELETE = 8388608,
646+
RTMESSAGE_PERIODICITY_CHANGE = 16777216;
646647

647648
const NETWORK_NODE_FLAG_BSA = 1,
648649
NETWORK_NODE_FLAG_INTERFACE_COUNT_INCREASE_POSSIBILITY = 2,
@@ -1069,7 +1070,10 @@
10691070
QUARTERLY = 4,
10701071
YEARLY = 5,
10711072
CONTINUOUS = 6,
1072-
HALFYEARLY = 7;
1073+
HALFYEARLY = 7,
1074+
EVERY_OTHER_DAY = 8,
1075+
EVERY_OTHER_WEEK = 9,
1076+
EVERY_OTHER_MONTH = 10;
10731077

10741078
// Accounting periods
10751079
$PERIODS = array(
@@ -1092,6 +1096,59 @@
10921096
YEARLY => ConfigHelper::getConfig('assignments.billing_period_yearly', trans('yearly')),
10931097
);
10941098

1099+
$EVENT_PERIODICITY = array(
1100+
DISPOSABLE => array(
1101+
'label' => trans('<!event>disposable'),
1102+
'map' => 0,
1103+
'name' => 'EVENT_PERIODICITY_DISPOSABLE',
1104+
),
1105+
DAILY => array(
1106+
'label' => trans('<!event>daily'),
1107+
'map' => '+1 day',
1108+
'name' => 'EVENT_PERIODICITY_DAILY',
1109+
),
1110+
EVERY_OTHER_DAY => array(
1111+
'label' => trans('<!event>every other day'),
1112+
'map' => '+2 days',
1113+
'name' => 'EVENT_PERIODICITY_EVERY_OTHER_DAY',
1114+
),
1115+
WEEKLY => array(
1116+
'label' => trans('<!event>weekly'),
1117+
'map' => '+1 week',
1118+
'name' => 'EVENT_PERIODICITY_WEEKLY',
1119+
),
1120+
EVERY_OTHER_WEEK => array(
1121+
'label' => trans('<!event>every other week'),
1122+
'map' => '+2 week',
1123+
'name' => 'EVENT_PERIODICITY_EVERY_OTHER_WEEK',
1124+
),
1125+
MONTHLY => array(
1126+
'label' => trans('<!event>monthly'),
1127+
'map' => '+1 month',
1128+
'name' => 'EVENT_PERIODICITY_MONTHLY',
1129+
),
1130+
EVERY_OTHER_MONTH => array(
1131+
'label' => trans('<!event>every other month'),
1132+
'map' => '+2 months',
1133+
'name' => 'EVENT_PERIODICITY_EVERY_OTHER_MONTH',
1134+
),
1135+
QUARTERLY => array(
1136+
'label' => trans('<!event>quarterly'),
1137+
'map' => '+3 months',
1138+
'name' => 'EVENT_PERIODICITY_QUARTERLY',
1139+
),
1140+
HALFYEARLY => array(
1141+
'label' => trans('<!event>halfyearly'),
1142+
'map' => '+6 months',
1143+
'name' => 'EVENT_PERIODICITY_HALFYEARLY',
1144+
),
1145+
YEARLY => array(
1146+
'label' => trans('<!event>yearly'),
1147+
'map' => '+1 year',
1148+
'name' => 'EVENT_PERIODICITY_YEARLY',
1149+
),
1150+
);
1151+
10951152
// Numbering periods
10961153
$NUM_PERIODS = array(
10971154
CONTINUOUS => trans('continuously'),
@@ -2069,6 +2126,7 @@ function ($link_technology) {
20692126
'_RT_PRIORITIES' => $RT_PRIORITIES,
20702127
'_RT_PRIORITY_STYLES' => $RT_PRIORITY_STYLES,
20712128
'_RT_TYPES' => $RT_TYPES,
2129+
'_EVENT_PERIODICITY' => $EVENT_PERIODICITY,
20722130
'_CONFIG_TYPES' => $CONFIG_TYPES,
20732131
'_TARIFF_FLAGS' => $TARIFF_FLAGS,
20742132
'_SERVICETYPES' => $SERVICETYPES,

0 commit comments

Comments
 (0)