Passed
Push — master ( 77e590...ab8fe6 )
by Andreas
64:34
created

org_openpsa_invoices_scheduler::_notify_owner()   B

Complexity

Conditions 5
Paths 9

Size

Total Lines 58
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 34
CRAP Score 5.0291

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 39
c 1
b 0
f 0
nc 9
nop 3
dl 0
loc 58
ccs 34
cts 38
cp 0.8947
crap 5.0291
rs 8.9848

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @package org.openpsa.invoices
4
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
6
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
7
 */
8
9
/**
10
 * Helper class to process subscription invoicing
11
 *
12
 * @package org.openpsa.invoices
13
 */
14
class org_openpsa_invoices_scheduler extends midcom_baseclasses_components_purecode
15
{
16
    /**
17
     * The deliverable we're processing
18
     *
19
     * @var org_openpsa_sales_salesproject_deliverable_dba
20
     */
21
    private $_deliverable;
22
23
    /**
24
     * The day of month on which subscriptions are invoiced (if none is set, they are invoiced continuously)
25
     *
26
     * @var int
27
     */
28
    private $subscription_day;
29
30 32
    public function __construct(org_openpsa_sales_salesproject_deliverable_dba $deliverable)
31
    {
32 32
        parent::__construct();
33 32
        $this->_deliverable = $deliverable;
34 32
        $this->subscription_day = midcom_baseclasses_components_configuration::get('org.openpsa.sales', 'config')->get('subscription_invoice_day_of_month');
35 32
    }
36
37
    /**
38
     * Initiates a new subscription cycle and registers a midcom.services.at call for the next cycle.
39
     *
40
     * The subscription cycles rely on midcom.services.at. I'm not sure if it is wise to rely on it for such
41
     * a totally mission critical part of OpenPSA. Some safeguards might be wise to add.
42
     */
43 8
    public function run_cycle($cycle_number, bool $send_invoice = true) : bool
44
    {
45 8
        if (time() < $this->_deliverable->start) {
46 1
            debug_add('Subscription hasn\'t started yet, register the start-up event to $start');
47 1
            return $this->_create_at_entry($cycle_number, $this->_deliverable->start);
48
        }
49
50 7
        debug_add('Running cycle ' . $cycle_number . ' for deliverable "' . $this->_deliverable->title . '"');
51
52 7
        $this_cycle_start = $this->get_cycle_start($cycle_number, time());
53 7
        if ($this->subscription_day && $cycle_number == 1) {
54
            // If there's a fixed day for invoicing, get_cycle_start already picked a future date for cycle 1
55
            $next_cycle_start = $this_cycle_start + 2; // +2 so we don't get overlaps in task
56
        } else {
57 7
            $next_cycle_start = $this->calculate_cycle_next($this_cycle_start);
58
        }
59 7
        $product = org_openpsa_products_product_dba::get_cached($this->_deliverable->product);
60
61 7
        if ($this->_deliverable->state < org_openpsa_sales_salesproject_deliverable_dba::STATE_STARTED) {
62 2
            $this->_deliverable->state = org_openpsa_sales_salesproject_deliverable_dba::STATE_STARTED;
63 2
            $this->_deliverable->update();
64
        }
65
66 7
        if ($send_invoice) {
67 7
            $calculator = new org_openpsa_invoices_calculator();
68 7
            $this_cycle_amount = $calculator->process_deliverable($this->_deliverable, $cycle_number);
69
        }
70
71 7
        $tasks_completed = [];
72 7
        $tasks_not_completed = [];
73 7
        $new_task = null;
74
75 7
        if ($product->orgOpenpsaObtype == org_openpsa_products_product_dba::TYPE_SERVICE) {
0 ignored issues
show
Bug Best Practice introduced by
The property orgOpenpsaObtype does not exist on midcom_core_dbaobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
76
            // Close previous task(s)
77 2
            $last_task = null;
78
79 2
            $qb = org_openpsa_projects_task_dba::new_query_builder();
80 2
            $qb->add_constraint('agreement', '=', $this->_deliverable->id);
81 2
            $qb->add_constraint('status', '<', org_openpsa_projects_task_status_dba::COMPLETED);
82
83 2
            foreach ($qb->execute() as $task) {
84 2
                if (org_openpsa_projects_workflow::complete($task, sprintf($this->_i18n->get_string('completed by subscription %s', 'org.openpsa.sales'), $cycle_number))) {
85 2
                    $tasks_completed[] = $task;
86
                } else {
87
                    $tasks_not_completed[] = $task;
88
                }
89 2
                $last_task = $task;
90
            }
91
92
            // Create task for the duration of this cycle
93 2
            $task_title = sprintf('%s %s', $this->_deliverable->title, $this->get_cycle_identifier($this_cycle_start));
0 ignored issues
show
Bug introduced by
It seems like $this->get_cycle_identifier($this_cycle_start) can also be of type false; however, parameter $values of sprintf() does only seem to accept double|integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

93
            $task_title = sprintf('%s %s', $this->_deliverable->title, /** @scrutinizer ignore-type */ $this->get_cycle_identifier($this_cycle_start));
Loading history...
94 2
            $new_task = $this->create_task($this_cycle_start, $next_cycle_start - 1, $task_title, $last_task);
95
        }
96
97
        // TODO: Warehouse management: create new order
98 7
        if (   $this->_deliverable->end < $next_cycle_start
99 7
            && $this->_deliverable->end != 0) {
100 1
            debug_add('Do not register next cycle, the contract ends before');
101 1
            return $this->_deliverable->end_subscription();
102
        }
103
104 7
        if (!$this->_create_at_entry($cycle_number + 1, $next_cycle_start)) {
0 ignored issues
show
Bug introduced by
It seems like $next_cycle_start can also be of type false; however, parameter $start of org_openpsa_invoices_scheduler::_create_at_entry() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

104
        if (!$this->_create_at_entry($cycle_number + 1, /** @scrutinizer ignore-type */ $next_cycle_start)) {
Loading history...
105
            return false;
106
        }
107 7
        if ($send_invoice) {
108
            $data = [
109 7
                'cycle_number' => $cycle_number,
110 7
                'next_run' => $next_cycle_start,
111 7
                'invoiced_sum' => $this_cycle_amount,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $this_cycle_amount does not seem to be defined for all execution paths leading up to this point.
Loading history...
112 7
                'tasks_completed' => $tasks_completed,
113 7
                'tasks_not_completed' => $tasks_not_completed
114
            ];
115 7
            $this->_notify_owner($calculator, $new_task, $data);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $calculator does not seem to be defined for all execution paths leading up to this point.
Loading history...
116
        }
117 7
        return true;
118
    }
119
120 8
    private function _create_at_entry($cycle_number, int $start) : bool
121
    {
122
        $args = [
123 8
            'deliverable' => $this->_deliverable->guid,
124 8
            'cycle'       => $cycle_number,
125
        ];
126 8
        $at_entry = new midcom_services_at_entry_dba();
127 8
        $at_entry->start = $start;
128 8
        $at_entry->component = 'org.openpsa.sales';
129 8
        $at_entry->method = 'new_subscription_cycle';
130 8
        $at_entry->arguments = $args;
131
132 8
        if (!$at_entry->create()) {
133
            debug_add('AT registration failed, last midgard error was: ' . midcom_connection::get_error_string(), MIDCOM_LOG_WARN);
134
            return false;
135
        }
136 8
        debug_add('AT entry for cycle ' . $cycle_number . ' created');
137 8
        org_openpsa_relatedto_plugin::create($at_entry, 'midcom.services.at', $this->_deliverable, 'org.openpsa.sales');
138 8
        return true;
139
    }
140
141 7
    private function _notify_owner(org_openpsa_invoices_calculator $calculator, ?org_openpsa_projects_task_dba $new_task, array $data)
142
    {
143 7
        $siteconfig = org_openpsa_core_siteconfig::get_instance();
144 7
        $message = [];
145 7
        $salesproject = org_openpsa_sales_salesproject_dba::get_cached($this->_deliverable->salesproject);
146
        try {
147 7
            $owner = midcom_db_person::get_cached($salesproject->owner);
0 ignored issues
show
Bug Best Practice introduced by
The property owner does not exist on midcom_core_dbaobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
148
        } catch (midcom_error $e) {
149
            $e->log();
150
            return;
151
        }
152 7
        $customer = $salesproject->get_customer();
0 ignored issues
show
Bug introduced by
The method get_customer() does not exist on midcom_core_dbaobject. It seems like you code against a sub-type of midcom_core_dbaobject such as org_openpsa_invoices_invoice_dba or org_openpsa_sales_salesproject_dba. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

152
        /** @scrutinizer ignore-call */ 
153
        $customer = $salesproject->get_customer();
Loading history...
153 7
        $l10n = $this->_i18n->get_l10n('org.openpsa.sales');
154 7
        if ($data['next_run'] === null) {
155
            $next_run_label = $l10n->get('no more cycles');
156
        } else {
157 7
            $next_run_label = $l10n->get_formatter()->date($data['next_run']);
158
        }
159
160
        // Title for long notifications
161 7
        $message['title'] = sprintf($l10n->get('subscription cycle %d closed for agreement %s (%s)'), $data['cycle_number'], $this->_deliverable->title, $customer->get_label());
162
163
        // Content for long notifications
164 7
        $message['content'] = "{$message['title']}\n\n";
165 7
        $message['content'] .= $l10n->get('invoiced') . ': ' . $l10n->get_formatter()->number($data['invoiced_sum']) . "\n\n";
166
167 7
        if ($data['invoiced_sum'] > 0) {
168 4
            $invoice = $calculator->get_invoice();
169 4
            $message['content'] .= $this->_l10n->get('invoice') . " {$invoice->number}:\n";
170 4
            $url = $siteconfig->get_node_full_url('org.openpsa.invoices');
171 4
            $message['content'] .= $url . 'invoice/' . $invoice->guid . "/\n\n";
0 ignored issues
show
Bug introduced by
Are you sure $url of type false|mixed|null can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

171
            $message['content'] .= /** @scrutinizer ignore-type */ $url . 'invoice/' . $invoice->guid . "/\n\n";
Loading history...
172
        }
173
174 7
        $message['content'] .= $this->render_task_info($l10n->get('tasks completed'), $data['tasks_completed']);
175 7
        $message['content'] .= $this->render_task_info($l10n->get('tasks not completed'), $data['tasks_not_completed']);
176
177 7
        if ($new_task) {
178 2
            $message['content'] .= "\n" . $l10n->get('created new task') . ":\n";
179 2
            $message['content'] .= "{$new_task->title}\n";
180
        }
181
182 7
        $message['content'] .= "\n" . $l10n->get('next run') . ": {$next_run_label}\n\n";
183 7
        $message['content'] .= $this->_i18n->get_string('agreement', 'org.openpsa.projects') . ":\n";
184
185 7
        $url = $siteconfig->get_node_full_url('org.openpsa.sales');
186 7
        $message['content'] .= $url . 'deliverable/' . $this->_deliverable->guid . '/';
187
188
        // Content for short notifications
189 7
        $message['abstract'] = sprintf(
190 7
            $l10n->get('%s: closed subscription cycle %d for agreement %s. invoiced %d. next cycle %s'),
191 7
            $customer->get_label(),
192 7
            $data['cycle_number'],
193 7
            $this->_deliverable->title,
194 7
            $data['invoiced_sum'],
195 7
            $next_run_label);
196
197
        // Send the message out
198 7
        org_openpsa_notifications::notify('org.openpsa.sales:new_subscription_cycle', $owner->guid, $message);
199 7
    }
200
201 7
    private function render_task_info(string $label, array $tasks) : string
202
    {
203 7
        $content = '';
204 7
        if (!empty($tasks)) {
205 2
            $content .= "\n{$label}:\n";
206
207 2
            foreach ($tasks as $task) {
208 2
                $content .= "{$task->title}: {$task->reportedHours}h\n";
209
            }
210
        }
211 7
        return $content;
212
    }
213
214
    /**
215
     * @todo Check if we already have an open task for this delivery?
216
     */
217 3
    public function create_task(int $start, int $end, string $title, org_openpsa_projects_task_dba $source_task = null) : org_openpsa_projects_task_dba
218
    {
219
        // Create the task
220 3
        $task = new org_openpsa_projects_task_dba();
221 3
        $task->title = $title;
222 3
        $task->start = $start;
223 3
        $task->end = $end;
224
        // TODO: Figure out if we really want to keep this
225 3
        $task->hoursInvoiceableDefault = true;
226
227 3
        $task->agreement = $this->_deliverable->id;
228 3
        $task->description = $this->_deliverable->description;
229 3
        $task->plannedHours = $this->_deliverable->plannedUnits;
230
231 3
        $salesproject = org_openpsa_sales_salesproject_dba::get_cached($this->_deliverable->salesproject);
232 3
        $task->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...
233 3
        $task->manager = $salesproject->owner;
0 ignored issues
show
Bug Best Practice introduced by
The property owner does not exist on midcom_core_dbaobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
234
235 3
        $project = $salesproject->get_project();
0 ignored issues
show
Bug introduced by
The method get_project() does not exist on midcom_core_dbaobject. Did you maybe mean get_parent()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

235
        /** @scrutinizer ignore-call */ 
236
        $project = $salesproject->get_project();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
236 3
        $task->project = $project->id;
237 3
        $task->orgOpenpsaAccesstype = $project->orgOpenpsaAccesstype;
238 3
        $task->orgOpenpsaOwnerWg = $project->orgOpenpsaOwnerWg;
239
240 3
        if (!empty($source_task)) {
241 3
            $task->priority = $source_task->priority;
242 3
            $task->manager = $source_task->manager;
243 3
            $task->hoursInvoiceableDefault = $source_task->hoursInvoiceableDefault;
244
        }
245
246 3
        if (!$task->create()) {
247
            throw new midcom_error("The task for this cycle could not be created. Last Midgard error was: " . midcom_connection::get_error_string());
248
        }
249 3
        $task->add_members('contacts', array_keys($salesproject->contacts));
0 ignored issues
show
Bug Best Practice introduced by
The property contacts does not exist on midcom_core_dbaobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
250 3
        if (!empty($source_task)) {
251 3
            $source_task->get_members();
252 3
            $task->add_members('resources', array_keys($source_task->resources));
253
        }
254
255
        // Copy tags from deliverable so we can seek resources
256 3
        $tagger = new net_nemein_tag_handler();
257 3
        $tagger->copy_tags($this->_deliverable, $task);
258
259 3
        midcom::get()->uimessages->add($this->_i18n->get_string('org.openpsa.sales', 'org.openpsa.sales'), sprintf($this->_i18n->get_string('created task "%s"', 'org.openpsa.sales'), $task->title));
260 3
        return $task;
261
    }
262
263
    /**
264
     * Calculcate remaining cycles until salesproject's end or the specified number of months passes
265
     *
266
     * @param integer $months The maximum number of months to look forward
267
     * @param integer $start The timestamp from which to begin
268
     */
269 13
    public function calculate_cycles(int $months = null, int $start = null) : int
270
    {
271 13
        if ($start === null) {
272 9
            $start = time();
273
        }
274 13
        $cycles = 0;
275 13
        $cycle_time = $this->_deliverable->start;
276 13
        $end_time = $this->_deliverable->end;
277
278
        // This takes care of invalid/unsupported unit configs
279 13
        if ($this->calculate_cycle_next($cycle_time) === false) {
280
            return $cycles;
281
        }
282
283 13
        while ($cycle_time < $start) {
284 8
            $cycle_time = $this->calculate_cycle_next($cycle_time);
285
        }
286
287 13
        if ($months !== null) {
288 9
            $end_time = mktime(date('H', $cycle_time), date('m', $cycle_time), date('i', $cycle_time), date('m', $cycle_time) + $months, date('d', $cycle_time), date('Y', $cycle_time));
0 ignored issues
show
Bug introduced by
date('m', $cycle_time) of type string is incompatible with the type integer expected by parameter $minute of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

288
            $end_time = mktime(date('H', $cycle_time), /** @scrutinizer ignore-type */ date('m', $cycle_time), date('i', $cycle_time), date('m', $cycle_time) + $months, date('d', $cycle_time), date('Y', $cycle_time));
Loading history...
Bug introduced by
date('d', $cycle_time) of type string is incompatible with the type integer expected by parameter $day of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

288
            $end_time = mktime(date('H', $cycle_time), date('m', $cycle_time), date('i', $cycle_time), date('m', $cycle_time) + $months, /** @scrutinizer ignore-type */ date('d', $cycle_time), date('Y', $cycle_time));
Loading history...
Bug introduced by
date('i', $cycle_time) of type string is incompatible with the type integer expected by parameter $second of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

288
            $end_time = mktime(date('H', $cycle_time), date('m', $cycle_time), /** @scrutinizer ignore-type */ date('i', $cycle_time), date('m', $cycle_time) + $months, date('d', $cycle_time), date('Y', $cycle_time));
Loading history...
Bug introduced by
date('H', $cycle_time) of type string is incompatible with the type integer expected by parameter $hour of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

288
            $end_time = mktime(/** @scrutinizer ignore-type */ date('H', $cycle_time), date('m', $cycle_time), date('i', $cycle_time), date('m', $cycle_time) + $months, date('d', $cycle_time), date('Y', $cycle_time));
Loading history...
Bug introduced by
date('Y', $cycle_time) of type string is incompatible with the type integer expected by parameter $year of mktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

288
            $end_time = mktime(date('H', $cycle_time), date('m', $cycle_time), date('i', $cycle_time), date('m', $cycle_time) + $months, date('d', $cycle_time), /** @scrutinizer ignore-type */ date('Y', $cycle_time));
Loading history...
Bug introduced by
It seems like $cycle_time can also be of type false; however, parameter $timestamp of date() does only seem to accept integer|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

288
            $end_time = mktime(date('H', /** @scrutinizer ignore-type */ $cycle_time), date('m', $cycle_time), date('i', $cycle_time), date('m', $cycle_time) + $months, date('d', $cycle_time), date('Y', $cycle_time));
Loading history...
289
        }
290
291 13
        $cycles = 0;
292 13
        while ($cycle_time < $end_time) {
293 13
            $cycle_time = $this->calculate_cycle_next($cycle_time);
0 ignored issues
show
Bug introduced by
It seems like $cycle_time can also be of type false; however, parameter $time of org_openpsa_invoices_sch...:calculate_cycle_next() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

293
            $cycle_time = $this->calculate_cycle_next(/** @scrutinizer ignore-type */ $cycle_time);
Loading history...
294 13
            if ($cycle_time <= $end_time) {
295 13
                $cycles++;
296
            }
297
        }
298 13
        return $cycles;
299
    }
300
301 21
    public function calculate_cycle_next(int $time)
302
    {
303 21
        switch ($this->_deliverable->unit) {
304 21
            case 'm':
305
                // Monthly recurring subscription
306 14
                $new_date = $this->_add_month($time, 1);
307 14
                break;
308 7
            case 'q':
309
                // Quarterly recurring subscription
310 3
                $new_date = $this->_add_month($time, 3);
311 3
                break;
312 4
            case 'hy':
313
                // Half-yearly recurring subscription
314 1
                $new_date = $this->_add_month($time, 6);
315 1
                break;
316 3
            case 'y':
317
                // Yearly recurring subscription
318 2
                $new_date = new DateTime('+1 year ' . gmdate('Y-m-d', $time), new DateTimeZone('GMT'));
319 2
                break;
320
            default:
321 1
                debug_add('Unrecognized unit value "' . $this->_deliverable->unit . '" for deliverable ' . $this->_deliverable->guid . ", returning false", MIDCOM_LOG_WARN);
322 1
                return false;
323
        }
324
325
        //If previous cycle was run at the end of the month, the new one should be at the end of the month as well
326 20
        $date = new DateTime(gmdate('Y-m-d', $time), new DateTimeZone('GMT'));
327 20
        if (   $date->format('t') == $date->format('j')
328 20
            && $new_date->format('t') != $new_date->format('j')) {
329 3
            $new_date->setDate((int) $new_date->format('Y'), (int) $new_date->format('m'), (int) $new_date->format('t'));
330
        }
331 20
        return (int) $new_date->format('U');
332
    }
333
334
    /**
335
     * Workaround for odd PHP DateTime behavior where for example
336
     * 2012-10-31 + 1 month would return 2012-12-01. This function makes
337
     * sure the new date is always in the expected month (so in the example above
338
     * it would return 2012-11-30)
339
     *
340
     * @param integer $time Original timestamp
341
     * @param integer $offset number of months to add
342
     */
343 18
    private function _add_month(int $time, int $offset) : DateTime
344
    {
345 18
        $orig = new DateTime(gmdate('Y-m-d', $time), new DateTimeZone('GMT'));
346 18
        $new_date = clone $orig;
347 18
        $new_date->modify('+' . $offset . ' months');
348 18
        $control = clone $new_date;
349 18
        $control->modify('-' . $offset . ' months');
350
351 18
        while ($orig->format('m') !== $control->format('m')) {
352 5
            $new_date->modify('-1 day');
353 5
            $control = clone $new_date;
354 5
            $control->modify('-' . $offset . ' months');
355
        }
356
357 18
        return $new_date;
358
    }
359
360 10
    public function get_cycle_start($cycle_number, int $time)
361
    {
362 10
        if ($cycle_number == 1) {
363 7
            if ($this->subscription_day) {
364 3
                return gmmktime(0, 0, 0, date('n', $time) + 1, $this->subscription_day, date('Y', $time));
0 ignored issues
show
Bug introduced by
date('Y', $time) of type string is incompatible with the type integer expected by parameter $year of gmmktime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

364
                return gmmktime(0, 0, 0, date('n', $time) + 1, $this->subscription_day, /** @scrutinizer ignore-type */ date('Y', $time));
Loading history...
365
            }
366
367
            // no explicit day of month set for invoicing, use the deliverable start date
368 4
            return $this->_deliverable->start;
369
        }
370
371
        // cycle number > 1
372 4
        return $time;
373
    }
374
375 6
    public function get_cycle_identifier(int $time)
376
    {
377 6
        $date = new DateTime(gmdate('Y-m-d', $time), new DateTimeZone('GMT'));
378
379 6
        switch ($this->_deliverable->unit) {
380 6
            case 'm':
381
                // Monthly recurring subscription
382 3
                $identifier = $date->format('Y-m');
383 3
                break;
384 3
            case 'q':
385
                // Quarterly recurring subscription
386 1
                $identifier = ceil(((int)$date->format('n')) / 4) . 'Q' . $date->format('y');
387 1
                break;
388 2
            case 'hy':
389
                // Half-yearly recurring subscription
390 1
                $identifier = ceil(((int)$date->format('n')) / 6) . '/' . $date->format('Y');
391 1
                break;
392 1
            case 'y':
393
                // Yearly recurring subscription
394 1
                $identifier = $date->format('Y');
395 1
                break;
396
            default:
397
                debug_add('Unrecognized unit value "' . $this->_deliverable->unit . '" for deliverable ' . $this->_deliverable->guid . ", returning false", MIDCOM_LOG_WARN);
398
                return false;
399
        }
400
401 6
        return $identifier;
402
    }
403
}
404