Passed
Push — master ( 4060b7...c5bdc7 )
by Andreas
24:04
created

org_openpsa_projects_task_dba   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 264
Duplicated Lines 0 %

Test Coverage

Coverage 87.5%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 127
c 4
b 0
f 0
dl 0
loc 264
ccs 112
cts 128
cp 0.875
rs 8.48
wmc 49

16 Methods

Rating   Name   Duplication   Size   Complexity  
A get_class_magic_default_privileges() 0 5 1
A _on_updating() 0 3 1
A _on_loaded() 0 9 3
A _on_creating() 0 6 2
A refresh() 0 6 1
A _on_updated() 0 21 5
A __get() 0 12 4
A add_members() 0 17 5
A get_icon() 0 3 1
A get_label() 0 10 2
A get_members() 0 11 4
A _update_parent() 0 10 3
B _prepare_save() 0 36 8
A refresh_status() 0 24 3
A get_agreement() 0 9 3
A get_status() 0 15 3

How to fix   Complexity   

Complex Class

Complex classes like org_openpsa_projects_task_dba often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use org_openpsa_projects_task_dba, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package org.openpsa.projects
4
 * @author Nemein Oy http://www.nemein.com/
5
 * @copyright Nemein Oy http://www.nemein.com/
6
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
7
 */
8
9
/**
10
 * MidCOM wrapped access to the MgdSchema class, keep logic here
11
 *
12
 * @property integer $up
13
 * @property integer $project
14
 * @property integer $start
15
 * @property integer $end
16
 * @property string $title
17
 * @property string $description
18
 * @property float $plannedHours
19
 * @property integer $status cache of last status
20
 * @property integer $agreement
21
 * @property integer $customer
22
 * @property integer $manager
23
 * @property float $reportedHours
24
 * @property float $invoicedHours
25
 * @property float $invoiceableHours
26
 * @property boolean $hoursInvoiceableDefault Are hours invoiceable by default ?
27
 * @property integer $priority
28
 * @property integer $orgOpenpsaAccesstype Shortcut for various ACL scenarios
29
 * @property string $orgOpenpsaOwnerWg The "owner" workgroup of this object
30
 * @package org.openpsa.projects
31
 */
32
class org_openpsa_projects_task_dba extends midcom_core_dbaobject
33
{
34
    public $__midcom_class_name__ = __CLASS__;
35
    public $__mgdschema_class_name__ = 'org_openpsa_task';
36
37
    public $autodelete_dependents = [
38
        org_openpsa_projects_task_status_dba::class => 'task',
39
        org_openpsa_projects_task_resource_dba::class => 'task',
40
    ];
41
42
    public $contacts = []; //Shorthand access for contact members
43
    public $resources = []; // --''--
44
    public $_skip_acl_refresh = false;
45
    public $_skip_parent_refresh = false;
46
    private $_status;
47
48
    /**
49
     * Deny midgard:read by default
50
     */
51 1
    public function get_class_magic_default_privileges()
52
    {
53 1
        $privileges = parent::get_class_magic_default_privileges();
54 1
        $privileges['EVERYONE']['midgard:read'] = MIDCOM_PRIVILEGE_DENY;
55 1
        return $privileges;
56
    }
57
58 20
    public function _on_creating() : bool
59
    {
60 20
        if (!$this->manager) {
61 19
            $this->manager = midcom_connection::get_user();
62
        }
63 20
        return $this->_prepare_save();
64
    }
65
66 41
    public function _on_loaded()
67
    {
68 41
        if ($this->title == "") {
69 19
            $this->title = "Task #{$this->id}";
70
        }
71
72 41
        if (!$this->status) {
73
            //Default to proposed if no status is set
74 33
            $this->status = org_openpsa_projects_task_status_dba::PROPOSED;
75
        }
76 41
    }
77
78 30
    public function refresh() : bool
79
    {
80 30
        $this->contacts = [];
81 30
        $this->resources = [];
82 30
        $this->_status = null;
83 30
        return parent::refresh();
84
    }
85
86 49
    public function __get($property)
87
    {
88 49
        if ($property == 'status_type') {
89 32
            return org_openpsa_projects_workflow::get_status_type($this->status);
90
        }
91 49
        if (in_array($property, ['status_comment', 'status_time'])) {
92 6
            if ($this->_status === null) {
93 6
                $this->refresh_status();
94
            }
95 6
            return $this->_status[$property];
96
        }
97 49
        return parent::__get($property);
98
    }
99
100 32
    public function _on_updating() : bool
101
    {
102 32
        return $this->_prepare_save();
103
    }
104
105 32
    public function _on_updated()
106
    {
107
        // Sync the object's ACL properties into MidCOM ACL system
108 32
        if (   !$this->_skip_acl_refresh) {
109 13
            if ($this->orgOpenpsaAccesstype && $this->orgOpenpsaOwnerWg) {
110
                debug_add("Synchronizing task ACLs to MidCOM");
111
                $sync = new org_openpsa_core_acl_synchronizer();
112
                $sync->write_acls($this, $this->orgOpenpsaOwnerWg, $this->orgOpenpsaAccesstype);
113
            }
114
115
            //Ensure manager can do stuff
116 13
            if ($this->manager) {
117 10
                $manager_person = midcom::get()->auth->get_user($this->manager);
118 10
                $this->set_privilege('midgard:read', $manager_person->id, MIDCOM_PRIVILEGE_ALLOW);
119 10
                $this->set_privilege('midgard:create', $manager_person->id, MIDCOM_PRIVILEGE_ALLOW);
120 10
                $this->set_privilege('midgard:delete', $manager_person->id, MIDCOM_PRIVILEGE_ALLOW);
121 10
                $this->set_privilege('midgard:update', $manager_person->id, MIDCOM_PRIVILEGE_ALLOW);
122
            }
123
        }
124
125 32
        $this->_update_parent();
126 32
    }
127
128
    /**
129
     * Generate a user-readable label for the task using the task/project hierarchy
130
     */
131 3
    public function get_label() : string
132
    {
133 3
        $label_elements = [];
134 3
        $task = $this;
135
        do {
136 3
            $label_elements[] = $task->title;
137 3
        } while ($task = $task->get_parent());
138
139 3
        $label = implode(' / ', array_reverse($label_elements));
140 3
        return trim($label);
141
    }
142
143
    public function get_icon() : string
144
    {
145
        return 'calendar-check-o';
146
    }
147
148
    /**
149
     * Populates contacts as resources lists
150
     */
151 19
    public function get_members()
152
    {
153 19
        if ($this->id) {
154 19
            $mc = org_openpsa_projects_task_resource_dba::new_collector('task', $this->id);
155 19
            $ret = $mc->get_rows(['orgOpenpsaObtype', 'person']);
156
157 19
            foreach ($ret as $data) {
158 3
                if ($data['orgOpenpsaObtype'] == org_openpsa_projects_task_resource_dba::CONTACT) {
159 1
                    $this->contacts[$data['person']] = true;
160
                } else {
161 3
                    $this->resources[$data['person']] = true;
162
                }
163
            }
164
        }
165 19
    }
166
167
    /**
168
     * Adds new contacts or resources
169
     */
170 17
    public function add_members(string $property, array $ids)
171
    {
172 17
        if ($property === 'contacts') {
173 4
            $type = org_openpsa_projects_task_resource_dba::CONTACT;
174 17
        } elseif ($property === 'resources') {
175 17
            $type = org_openpsa_projects_task_resource_dba::RESOURCE;
176
        } else {
177
            return;
178
        }
179
180 17
        foreach ($ids as $id) {
181 17
            $resource = new org_openpsa_projects_task_resource_dba();
182 17
            $resource->orgOpenpsaObtype = $type;
183 17
            $resource->task = $this->id;
184 17
            $resource->person = (int) $id;
185 17
            if ($resource->create()) {
186 17
                $this->{$property}[$id] = true;
187
            }
188
        }
189 17
    }
190
191 33
    private function _prepare_save() : bool
192
    {
193
        //Make sure we have end
194 33
        if (!$this->end || $this->end == -1) {
195 19
            $this->end = time();
196
        }
197
        //Make sure we have start
198 33
        if (!$this->start) {
199 19
            $this->start = min(time(), $this->end - 1);
200
        }
201
202
        // Reset start and end to start/end of day
203 33
        $this->start = strtotime('today', $this->start);
204 33
        $this->end = strtotime('tomorrow', $this->end) - 1;
205
206 33
        if ($this->start > $this->end) {
207
            debug_add("start ({$this->start}) is greater than end ({$this->end}), aborting", MIDCOM_LOG_ERROR);
208
            return false;
209
        }
210
211 33
        if ($agreement = $this->get_agreement()) {
212
            // Get customer company into cache from agreement's sales project
213 12
            if (!$this->customer) {
214
                try {
215 11
                    $agreement = org_openpsa_sales_salesproject_deliverable_dba::get_cached($agreement);
216 11
                    $salesproject = org_openpsa_sales_salesproject_dba::get_cached($agreement->salesproject);
0 ignored issues
show
Bug Best Practice introduced by
The property salesproject does not exist on midcom_core_dbaobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
217 11
                    $this->customer = $salesproject->customer;
0 ignored issues
show
Bug Best Practice introduced by
The property customer does not exist on midcom_core_dbaobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
218
                }
219 12
                catch (midcom_error $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
220
            }
221
        } else {
222
            // No agreement, we can't be invoiceable
223 23
            $this->hoursInvoiceableDefault = false;
224
        }
225
226 33
        return true;
227
    }
228
229 34
    public function get_agreement() : int
230
    {
231 34
        if ($this->up) {
232
            do {
233
                $parent = $this->get_parent();
234
            } while ($parent->up);
0 ignored issues
show
Bug Best Practice introduced by
The property up does not exist on midcom_core_dbaobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
235
            return $parent->agreement;
0 ignored issues
show
Bug Best Practice introduced by
The property agreement does not exist on midcom_core_dbaobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
236
        }
237 34
        return $this->agreement;
238
    }
239
240 32
    private function _update_parent()
241
    {
242 32
        if (!$this->_skip_parent_refresh) {
243 28
            $project = new org_openpsa_projects_project($this->project);
244 28
            $project->refresh_from_tasks();
245
        }
246
        try {
247 32
            $agreement = new org_openpsa_sales_salesproject_deliverable_dba($this->get_agreement());
248 12
            $agreement->update_units();
249 29
        } catch (midcom_error $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
250 32
    }
251
252
    /**
253
     * Queries status objects
254
     */
255 7
    public function get_status() : array
256
    {
257
        //Simplistic approach
258 7
        $mc = org_openpsa_projects_task_status_dba::new_collector('task', $this->id);
259 7
        if ($this->status > org_openpsa_projects_task_status_dba::PROPOSED) {
260
            //Only get proposed status objects here if are not over that phase
261 5
            $mc->add_constraint('type', '<>', org_openpsa_projects_task_status_dba::PROPOSED);
262
        }
263 7
        if (!empty($this->resources)) {
264
            //Do not ever set status to declined if we still have resources left
265
            $mc->add_constraint('type', '<>', org_openpsa_projects_task_status_dba::DECLINED);
266
        }
267 7
        $mc->add_order('id', 'DESC');
268 7
        $mc->set_limit(1);
269 7
        return $mc->get_rows(['type', 'comment', 'metadata_created']);
270
    }
271
272 6
    public function refresh_status()
273
    {
274 6
        $this->_status = [
275
            'status_comment' => '',
276
            'status_time' => false,
277
        ];
278
279 6
        $ret = $this->get_status();
280 6
        if (empty($ret)) {
281
            //Failure to get status object
282
            debug_add('Could not find any status objects, defaulting to previous status');
283
            return;
284
        }
285 6
        $status = current($ret);
286
287
        //Update the status cache if necessary
288 6
        if ($this->status != $status['type']) {
289
            $this->status = $status['type'];
290
            $this->update();
291
        }
292
293
        //TODO: Check various combinations of accept/decline etc etc
294 6
        $this->_status['status_comment'] = $status['comment'];
295 6
        $this->_status['status_time'] = (int) $status['created']->format('U');
296 6
    }
297
}
298