Upgrader.php 37.3 KB
Newer Older
1
<?php
mattwire's avatar
mattwire committed
2
3
4
5
6
7
8
9
10
11
/*
 +--------------------------------------------------------------------+
 | Copyright CiviCRM LLC. All rights reserved.                        |
 |                                                                    |
 | This work is published under the GNU AGPLv3 license with some      |
 | permitted exceptions and without any warranty. For full license    |
 | and copyright information, see https://civicrm.org/licensing       |
 +--------------------------------------------------------------------+
 */

12
13
use CRM_Civigiftaid_ExtensionUtil as E;

14
/**
mattwire's avatar
mattwire committed
15
 * Collection of upgrade steps.
16
17
18
 */
class CRM_Civigiftaid_Upgrader extends CRM_Civigiftaid_Upgrader_Base {

19
20
21
  const REPORT_CLASS = 'CRM_Civigiftaid_Report_Form_Contribute_GiftAid';
  const REPORT_URL = 'civicrm/contribute/report/uk-giftaid';

22
23
24
  // By convention, functions that look like "function upgrade_NNNN()" are
  // upgrade tasks. They are executed in order (like Drupal's hook_update_N).

25
26
27
28
29
30
31
32
33
34
35
36
37
  /** @var int */
  public $declarationCustomGroupID;

  /** @var int */
  public $contributionGiftaidCustomGroupId;


  /** @var array */
  public $optionGroupNameToId = [];

  /** @var array */
  public $customFieldNamesToIds = [];

38
  /**
39
   */
40
  public function install() {
41
    $this->ensureDataStructures();
42
43
44
45
46
47
48
49
50
51

    // Nb. this is kept to preserve previous behaviour, it should not be needed.
    // Import existing batches.
    self::importBatches();

    $this->ensurePastYearSubmissionJob();

    // In case this extension had been installed and uninstalled before:
    $this->removeLegacyRegisteredReport();

52
53
54
55
56
57
58
59
60
61
62
63
64
65
  }
  /**
   * Safely repeatable function to ensure all the data structures we need
   * exist.
   */
  public function ensureDataStructures() {
    // Note most of these depend on the others having been called during
    // runtime, so the order is important.
    $this->setOptionGroups();
    $this->enableOptionGroups(1);
    $this->ensureCustomGroups();
    $this->ensureCustomFields();
    $this->setDefaultSettings();
    $this->ensureProfiles();
66
67
68
  }
  /**
   * Ensure we have the custom groups defined.
69
70
   *
   *
71
72
   */
  protected function ensureCustomGroups() {
73
    $this->declarationCustomGroupID = $this->findOrCreate('CustomGroup',
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
      ['name' => 'Gift_Aid_Declaration'],
      [
        'is_active' => 1,
        'extends'   => 'Individual',
      ],
      [
        'title'                => 'Gift Aid Declaration',
        'table_name'           => 'civicrm_value_gift_aid_declaration',
        'is_multiple'          => 1,
        'style'                => 'Tab',
        'collapse_display'     => 0,
        'collapse_adv_display' => 0,
        'weight'               => 1,
      ]
    )['id'];

    $this->contributionGiftaidCustomGroupId = $this->findOrCreate('CustomGroup',
      ['name' => 'Gift_Aid'],
      [
        'is_active' => 1,
        'extends'   => 'Contribution',
      ],
      [
97
        'title'                => 'Gift Aid',
98
99
        'style'                => 'Inline',
        'collapse_display'     => 0,
100
        'help_pre'             => 'Stores the Gift Aid values for contributions',
101
102
103
104
105
106
107
108
109
        'table_name'           => 'civicrm_value_gift_aid_submission',
        'is_multiple'          => 0,
        'collapse_adv_display' => 0,
        'weight'               => 2,
      ]
    )['id'];
  }
  /**
   * Ensure we have the custom fields defined for declaration
110
111
   *
   * Note: requires ensureCustomGroups() to have run.
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
   */
  protected function ensureCustomFields() {
    foreach ($this->getCustomFields() as $name => $details) {

      $findParams =  array_intersect_key($details, array_flip([
        'name', 'custom_group_id']));

      // Which param are required, i.e. if they are not these params, the
      // existing data will be re-set to them.
      if (!empty($details['_requiredParams'])) {
        // This field has specified its own.
        $requiredParams = $details['_requiredParams'];
      }
      else {
        // Typical case, insist on these:
        $requiredParams = array_intersect_key($details, array_flip([
          'default_value', 'is_active', 'is_searchable', 'weight', 'help_pre',
          'help_post', 'is_search_range'
        ] ));
      }
      unset($details['_requiredParams']);

      $additionalCreateParams = $details;

      // Ensure field exists.
137
138
139
140
141
      // Note: we store its ID so we can look it up easily later.
      // We cannot simply store the name though because sadly we have used
      // Eligible_for_Gift_Aid as a custom field name twice in two different
      // custom groups. Doh.
      $this->customFieldNamesToIds["$details[custom_group_id]--$details[name]"] = $this->findOrCreate('CustomField', $findParams, $requiredParams, $additionalCreateParams)['id'];
142
143
    }
  }
144

145
146
147
148
149
150
  /**
   */
  protected function ensureProfiles() {
    $profileId = $this->findOrCreate('UFGroup',
      ['name' => 'Gift_Aid'],
      [
151
152
        'is_active' => 1,
        'group_type' => 'Individual,Contact',
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
      ],
      [
        'title' => 'Gift Aid',
        'add_captcha' => 0,
        'is_map' => 0,
        'is_edit_link' => 0,
        'is_uf_link' => 0,
        'is_update_dupe' => 2,
        'is_proximity_search' => 0,
      ]
    )['id'];

    $this->findOrCreate('UFField',
      [
        'uf_group_id' => $profileId,
168
        'field_name' => 'custom_' . $this->customFieldNamesToIds["{$this->declarationCustomGroupID}--Eligible_for_Gift_Aid"],
169
170
171
172
173
      ],
      [
        'is_active' => 1,
        'is_view' => 0,
        'is_required' => 1,
174
175
176
177
178
179
180
181
        'help_post' => '<p>By selecting \'Yes\' above you are confirming that
<ol>
<li>You are a UK taxpayer</li>
<li>The amount of income and/or capital gains tax you pay is at least as much as we will reclaim on your donations in this tax year</li>
</ol>
<p><b>About Gift Aid</b><p>
<p>Gift Aid increases the value of membership contributions and donations to charities by allowing them to reclaim basic rate tax on your gift. It allows us to claim 25% on top of your donation, for example, on a £50 membership or donation, we can claim back an additional £12.50.</p>
<p>We would like to reclaim gift aid on your behalf. We can only reclaim Gift Aid if you are a UK taxpayer. Please confirm that you are eligible for Gift Aid. <a href="http://www.hmrc.gov.uk/individuals/giving/gift-aid.htm">More about Gift Aid</a>.</p>',
182
183
184
185
186
187
188
        'visibility' => 'User and User Admin Only',
      ],
      [
        'weight' => 1,
        'in_selector' => '0',
        'is_searchable' => '0',
        'label' => 'Can we reclaim gift aid on your donation?',
189
        'field_type' => 'Individual',
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
      ]);

    $this->findOrCreate('UFField',
      [
        'field_name' => 'first_name',
        'uf_group_id' => $profileId,
      ],
      [
        'is_active' => '1',
        'is_view' => '0',
        'is_required' => '1',
        'visibility' => 'User and User Admin Only',
      ],
      [
        'weight' => '2',
        'help_post' => '',
        'in_selector' => '0',
        'is_searchable' => '0',
        'label' => 'First Name',
        'field_type' => 'Individual',
      ]);

    $this->findOrCreate('UFField',
      [
        'field_name' => 'last_name',
        'uf_group_id' => $profileId,
      ],
      [
        'is_active' => '1',
        'is_view' => '0',
        'is_required' => '1',
        'visibility' => 'User and User Admin Only',
      ],
      [
        'weight' => '3',
        'help_post' => '',
        'in_selector' => '0',
        'is_searchable' => '0',
        'label' => 'Last Name',
        'field_type' => 'Individual',
      ]
    );

    $this->findOrCreate('UFField',
      [
        'field_name' => 'street_address',
        'uf_group_id' => $profileId,
      ],
      [
        'is_active' => '1',
        'is_view' => '0',
        'location_type_id' => '1',
        'is_required' => '1',
        'visibility' => 'User and User Admin Only',
      ],
      [
        'weight' => '4',
        'help_post' => '',
        'in_selector' => '0',
        'is_searchable' => '0',
        'label' => 'Street Address',
        'field_type' => 'Contact',
      ]);


    $this->findOrCreate('UFField',
      [
        'field_name' => 'supplemental_address_1',
        'uf_group_id' => $profileId,
      ],
      [
        'is_active' => '1',
        'is_view' => '0',
        'location_type_id' => '1',
        'is_required' => '0',
        'visibility' => 'User and User Admin Only',
      ],
      [
        'weight' => '5',
        'help_post' => '',
        'in_selector' => '0',
        'is_searchable' => '0',
        'label' => 'Supplemental Address 1',
        'field_type' => 'Contact',
      ]);

    $this->findOrCreate('UFField',
      [
        'uf_group_id' => $profileId,
        'field_name' => 'supplemental_address_2',
      ],
      [
        'is_active' => '1',
        'is_view' => '0',
        'location_type_id' => '1',
        'is_required' => '0',
        'visibility' => 'User and User Admin Only',
      ],
      [
        'weight' => '6',
        'help_post' => '',
        'in_selector' => '0',
        'is_searchable' => '0',
        'label' => 'Supplemental Address 2',
        'field_type' => 'Contact',
      ]);

    $this->findOrCreate('UFField',
      [
        'uf_group_id' => $profileId,
        'field_name' => 'city',
      ],
      [
        'is_active' => '1',
        'is_view' => '0',
        'location_type_id' => '1',
        'is_required' => '0',
        'visibility' => 'User and User Admin Only',
      ],
      [
        'weight' => '7',
        'help_post' => '',
        'in_selector' => '0',
        'is_searchable' => '0',
        'label' => 'City',
        'field_type' => 'Contact',
      ]);

    $this->findOrCreate('UFField',
      [
        'uf_group_id' => $profileId,
        'field_name' => 'state_province',
      ],
      [
        'is_active' => '1',
        'is_view' => '0',
        'location_type_id' => '1',
        'is_required' => '0',
        'visibility' => 'User and User Admin Only',
      ],
      [
        'weight' => '8',
        'help_post' => '',
        'in_selector' => '0',
        'is_searchable' => '0',
        'label' => 'County',
        'field_type' => 'Contact',
      ]);

    $this->findOrCreate('UFField',
      [
        'uf_group_id' => $profileId,
        'field_name' => 'postal_code',
      ],
      [
        'is_active' => '1',
        'is_view' => '0',
        'location_type_id' => '1',
        'is_required' => '1',
        'visibility' => 'User and User Admin Only',
      ],
      [
        'weight' => '9',
        'help_post' => '',
        'in_selector' => '0',
        'is_searchable' => '0',
        'label' => 'Post code',
        'field_type' => 'Contact',
      ]);

  }
  /**
   * Set up Past Year Submissions Job
   */
  public function ensurePastYearSubmissionJob() {
    $existing = civicrm_api3('Job', 'get', [
      'api_entity' => "gift_aid",
      'api_action' => "makepastyearsubmissions",
    ]);

    if (empty($existing['count'])) {
      $jobParams = [
        'domain_id' => CRM_Core_Config::domainID(),
        'run_frequency' => 'Daily',
        'name' => 'Make Past Year Submissions',
        'description' => 'Make Past Year Submissions',
        'api_entity' => 'gift_aid',
        'api_action' => 'makepastyearsubmissions',
        'is_active' => 0,
      ];
      civicrm_api3('Job', 'create', $jobParams);
    }
  }

  /**
   * Check we have the thing we need.
   *
   * When creating an entity $findParams + $requiredParams + $additionalCreateParams is used
   *
   * @param string $entity
   * @param array $findParams
   *   API params used to find if the thing we want exists. e.g. ['name' => 'my_name']
   * @param array $requiredParams
   *   If these values are incorrect in a found entity, they will be corrected.
   * @param array $additionalCreateParams
   *   These values will only be used if creating an entity.
   *
   * @return array
   *   The entity, as returned by a 'get' action (which can sometimes differ from the result of a create action.)
   */
  protected function findOrCreate($entity, $findParams, $requiredParams = [], $additionalCreateParams = []) {
    try {
      $result = civicrm_api3($entity, 'get', $findParams);
      $found = $result['count'];
      if ($found == 0) {
        // Not found, create now.
        $result = civicrm_api3($entity, 'create', $findParams + $requiredParams + $additionalCreateParams);
        return civicrm_api3($entity, 'getsingle', ['id' => $result['id']]);
      }
      elseif ($found == 1) {
        // Take the first (only) item, but check for requiredParams.
        $thing = current($result['values']);
        $corrections = [];
        foreach ($requiredParams as $k => $v) {
          if (($thing[$k] ?? NULL) != $v) {
            $corrections[$k] = $v;
          }
        }
        if ($corrections) {
          // Need to correct it/update it.
          $corrections['id'] = $thing['id'];
          civicrm_api3($entity, 'create', $corrections);
422
423
424
425
426
427
          // Reload
          return civicrm_api3($entity, 'getsingle', ['id' => $thing['id']]);
        }
        else {
          // Wanted one, found it, no corrections.
          return $thing;
428
429
430
431
432
433
434
435
436
437
438
439
        }
      }
      else {
        // Huh, we found more than one?
        throw new \Exception("Cannot install '$entity', expected 0 or 1 matching "
        . json_encode($findParams) . " but found $found");
      }
    }
    catch (\Exception $e) {
      Civi::log()->error("FAILED ON: " .json_encode([$entity, $findParams, $requiredParams, $additionalCreateParams]));
      throw $e;
    }
440
  }
441
442
  /**
   * Example: Run an external SQL script when the module is uninstalled
443
   */
444
  public function uninstall() {
445
    $this->unsetSettings();
446
447
448
449
  }

  /**
   * Example: Run a simple query when a module is enabled
450
   */
451
  public function enable() {
452
453
    $this->setOptionGroups();
    $this->enableOptionGroups(1);
454
    $this->ensureCustomGroups();
455
    $this->ensureCustomFields();
456
457
458
459
460
  }

  /**
   * Example: Run a simple query when a module is disabled
   *
461
   */
462
  public function disable() {
463
    $this->enableOptionGroups(0);
464
465
466
  }

  /**
467
   * Perform upgrade to version 2.1
468
469
470
   *
   * @return TRUE on success
   * @throws Exception
471
472
   */
  public function upgrade_2100() {
473
    $this->log('Applying update 2100');
474
    self::removeLegacyRegisteredReport();
475
    return TRUE;
476
477
  }

478
  /**
479
480
   * Perform upgrade to version 3.0
   *
481
482
483
   * @return bool
   */
  public function upgrade_3000() {
484
    $this->log('Applying update 3000');
485

486
487
    // Set default settings.
    $this->setDefaultSettings();
488

489
490
    // Create database schema.
    $this->executeSqlFile('sql/upgrade_3000.sql');
491

492
    // Import existing batches.
493
    self::importBatches();
494

495
496
497
    return TRUE;
  }

498
  /**
nishantB's avatar
nishantB committed
499
500
   * Set up Past Year Submissions Job
   */
mattwire's avatar
mattwire committed
501
  public function upgrade_3101() {
502
    $this->log('Applying update 3101 - Add past year submissions job');
503
    $this->ensurePastYearSubmissionJob();
nishantB's avatar
nishantB committed
504
505
506
    return TRUE;
  }

507
  public function upgrade_3102() {
nishantB's avatar
nishantB committed
508
    $this->log('Applying update 3102');
nishantB's avatar
nishantB committed
509
510
511
512
513
514
515
516

    // Alter existing eligible_for_gift_aid columns
    CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_value_gift_aid_declaration MODIFY COLUMN eligible_for_gift_aid int");
    CRM_Core_DAO::executeQuery("ALTER TABLE civicrm_value_gift_aid_submission MODIFY COLUMN eligible_for_gift_aid int");

    // Update custom field type from String to Int
    CRM_Core_DAO::executeQuery("UPDATE civicrm_custom_field SET data_type = 'Int' WHERE name = 'Eligible_for_Gift_Aid'");

517
518
    return TRUE;
  }
nishantB's avatar
nishantB committed
519

520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
  public function upgrade_3103() {
    $this->log('Applying update 3103 - delete old report templates');
    $this->removeLegacyRegisteredReport();
    return TRUE;
  }

  public function upgrade_3104() {
    $this->log('Applying update 3104 - change profile(s) to use Individual declaration eligibility field instead of contribution eligibility field');
    $contributionGiftAidField = CRM_Civigiftaid_Utils::getCustomByName('Eligible_For_Gift_Aid', 'Gift_Aid');
    $contactGiftAidField = CRM_Civigiftaid_Utils::getCustomByName('Eligible_For_Gift_Aid', 'Gift_Aid_Declaration');
    $helpPost = '<p>By selecting &#039;Yes&#039; above you are confirming that you are a UK taxpayer and the amount of income and/or capital gains tax you pay is at least as much as we will reclaim on your donations in this tax year.</p>
<p><b>About Gift Aid</b></p>
<p>Gift Aid increases the value of donations to charities by allowing them to reclaim basic rate tax on your gift.  We would like to reclaim gift aid on your behalf.  We can only reclaim Gift Aid if you are a UK taxpayer.  Please confirm that you are a eligible for gift aid above.  <a href="http://www.hmrc.gov.uk/individuals/giving/gift-aid.htm">More about Gift Aid</a>.</p>';

    $query = "UPDATE civicrm_uf_field SET field_name='{$contactGiftAidField}', field_type='Individual', help_post='{$helpPost}' WHERE field_name='{$contributionGiftAidField}'";
    CRM_Core_DAO::executeQuery($query);

    $this->log('Applying update 3104 - checking optiongroups');
    $this->setOptionGroups();

    return TRUE;
  }

543
  public function upgrade_3107() {
544
    $this->log('Updating custom fields');
545
546
    $this->ensureCustomGroups();
    $this->ensureCustomFields();
547
548
549
    return TRUE;
  }

550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
  /**
   * There's the chance of ambiguity on the default value of batch_name,
   * which should be NULL. This was causing tests to fail on new installs
   * and historically a lot has changed around this so better to make sure
   * the default is applied here too.
   *
   * @see https://github.com/mattwire/uk.co.compucorp.civicrm.giftaid/pull/18
   * @see comment in install()
   */
  public function upgrade_3108() {
    $this->log('Updating default batch_name');
    $this->executeSqlFile('sql/reset_batch_name_default.sql');
    return TRUE;
  }

565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
  public function upgrade_3109() {
    $this->log('Disable "Yes, in the past 4 years" for contributions if it exists');
    try {
      $optionValueParams = [
        'option_group_id' => 'uk_taxpayer_options',
        'name' => 'uk_taxpayer_past_four_years',
      ];
      $optionValue = civicrm_api3('OptionValue', 'getsingle', $optionValueParams);
      $optionValueParams['id'] = $optionValue['id'];
      if (empty($optionValue['is_active'])) {
        $optionValueParams['is_active'] = 0;
        civicrm_api3('OptionValue', 'create', $optionValueParams);
      }
    }
    catch(Exception $e) {
      // Ok, it doesn't exist. Good.
    }
    return TRUE;
  }

Rich's avatar
Rich committed
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
  /**
   * Add given date.
   *
   */
  public function upgrade_3110() {
    $this->log('Add given date field.');
    $this->ensureCustomGroups();
    $this->ensureCustomFields();
    // Update any records missing a given date.
    $sql = "UPDATE civicrm_value_gift_aid_declaration
      SET given_date = start_date
      WHERE given_date IS NULL";

    CRM_Core_DAO::executeQuery($sql);

    return TRUE;
  }

603
604
  public function upgrade_3112() {
    $this->log('Check we have batch_type = "Gift Aid" and fix profile setup');
605
    $this->ensureDataStructures();
606
607
608
609
     \Civi\Api4\System::flush()
       ->setCheckPermissions(FALSE)
       ->setSession(TRUE)
       ->setTriggers(TRUE);
610
611
612
    return TRUE;
  }

613
614
615
  /**
   * @return array
   */
616
  private function getCustomFields() {
617
618
    if (empty($this->declarationCustomGroupID)) {
      throw new \RuntimeException("CRM_Civigiftaid_Upgrader::getCustomFields called before declarationCustomGroupID defined.");
619
620
621
622
    }
    if (empty($this->contributionGiftaidCustomGroupId)) {
      throw new \RuntimeException("CRM_Civigiftaid_Upgrader::getCustomFields called before contributionGiftaidCustomGroupId defined.");
    }
623

624
    $customFields = [
625
626
      // For contacts' declarations...
      [
627
        'custom_group_id' => $this->declarationCustomGroupID,
628
        'name' => 'Eligible_for_Gift_Aid',
629
        'label' => 'UK Tax Payer?',
630
631
        'data_type' => 'Int',
        'html_type' => 'Radio',
632
        'is_required' => '1',
633
634
635
636
        'is_searchable' => '1',
        'is_search_range' => '0',
        'weight' => '1',
        'is_active' => '1',
637
638
639
640
        'is_view' => '0',
        'text_length' => '255',
        'note_columns' => '60',
        'note_rows' => '4',
641
        'column_name' => 'eligible_for_gift_aid',
642
643
        'option_group_id' => $this->optionGroupNameToId['eligibility_declaration_options'],
        'in_selector' => '0',
644
645
      ],
      [
646
        'custom_group_id' => $this->declarationCustomGroupID,
647
648
649
650
651
652
653
654
        'name' => 'Address',
        'label' => 'Address',
        'data_type' => 'Memo',
        'html_type' => 'TextArea',
        'is_required' => '0',
        'is_searchable' => '1',
        'is_search_range' => '0',
        'weight' => '2',
655
        'help_pre' => 'The address and post code are automatically copied from the contact\'s "Home" address and formatted for submission to HMRC. You don\'t normally need to make any changes here.',
656
657
658
659
660
661
662
663
664
665
        'attributes' => 'rows=4, cols=60',
        'is_active' => '1',
        'is_view' => '0',
        'text_length' => '255',
        'note_columns' => '60',
        'note_rows' => '4',
        'column_name' => 'address',
        'in_selector' => '0',
      ],
      [
666
        'custom_group_id' => $this->declarationCustomGroupID,
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
        'name' => 'Post_Code',
        'label' => 'Post Code',
        'data_type' => 'String',
        'html_type' => 'Text',
        'is_required' => '0',
        'is_searchable' => '1',
        'is_search_range' => '0',
        'weight' => '3',
        'is_active' => '1',
        'is_view' => '0',
        'text_length' => '16',
        'note_columns' => '60',
        'note_rows' => '4',
        'column_name' => 'post_code',
        'in_selector' => '0',
      ],
Rich's avatar
Rich committed
683
      [
684
        'custom_group_id' => $this->declarationCustomGroupID,
Rich's avatar
Rich committed
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
        'name' => 'given_date',
        'label' => 'Date declaration given',
        'data_type' => 'Date',
        'html_type' => 'Select Date',
        'is_required' => '0',
        'is_searchable' => '0',
        'is_search_range' => '0',
        'weight' => '4',
        'is_active' => '1',
        'is_view' => '0',
        'text_length' => '255',
        'date_format' => 'dd-mm-yy',
        'note_columns' => '60',
        'note_rows' => '4',
        'column_name' => 'given_date',
        'in_selector' => '0',
      ],
702
      [
703
        'custom_group_id' => $this->declarationCustomGroupID,
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
        'name' => 'Start_Date',
        'label' => 'Start Date',
        'data_type' => 'Date',
        'html_type' => 'Select Date',
        'is_required' => '1',
        'is_searchable' => '0',
        'is_search_range' => '0',
        'weight' => '4',
        'is_active' => '1',
        'is_view' => '0',
        'text_length' => '255',
        'date_format' => 'dd-mm-yy',
        'note_columns' => '60',
        'note_rows' => '4',
        'column_name' => 'start_date',
        'in_selector' => '0',
      ],
      [
722
        'custom_group_id' => $this->declarationCustomGroupID,
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
        'name' => 'End_Date',
        'label' => 'End Date',
        'data_type' => 'Date',
        'html_type' => 'Select Date',
        'is_required' => '0',
        'is_searchable' => '0',
        'is_search_range' => '0',
        'weight' => '5',
        'is_active' => '1',
        'is_view' => '0',
        'text_length' => '255',
        'date_format' => 'dd-mm-yy',
        'note_columns' => '60',
        'note_rows' => '4',
        'column_name' => 'end_date',
        'in_selector' => '0',
      ],
      [
741
        'custom_group_id' => $this->declarationCustomGroupID,
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
        'name' => 'Reason_Ended',
        'label' => 'Reason Ended',
        'data_type' => 'String',
        'html_type' => 'Radio',
        'is_required' => '0',
        'is_searchable' => '0',
        'is_search_range' => '0',
        'weight' => '6',
        'is_active' => '1',
        'is_view' => '0',
        'text_length' => '32',
        'note_columns' => '60',
        'note_rows' => '4',
        'column_name' => 'reason_ended',
        'option_group_id' => $this->optionGroupNameToId['reason_ended'],
        'in_selector' => '0',
      ],
      [
760
        'custom_group_id' => $this->declarationCustomGroupID,
761
762
763
764
765
766
767
768
769
770
        'name' => 'Source',
        'label' => 'Source',
        'data_type' => 'String',
        'html_type' => 'Text',
        'is_required' => '0',
        'is_searchable' => '0',
        'is_search_range' => '0',
        'weight' => '7',
        'is_active' => '1',
        'is_view' => '0',
771
        'text_length' => '255',
772
773
774
775
776
777
        'note_columns' => '60',
        'note_rows' => '4',
        'column_name' => 'source',
        'in_selector' => '0',
      ],
      [
778
        'custom_group_id' => $this->declarationCustomGroupID,
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
        'name' => 'Notes',
        'label' => 'Notes',
        'data_type' => 'Memo',
        'html_type' => 'TextArea',
        'is_required' => '0',
        'is_searchable' => '0',
        'is_search_range' => '0',
        'weight' => '8',
        'attributes' => 'rows=4, cols=60',
        'is_active' => '1',
        'is_view' => '0',
        'text_length' => '255',
        'note_columns' => '60',
        'note_rows' => '4',
        'column_name' => 'notes',
        'in_selector' => '0',
      ],
      [
797
        'custom_group_id' => $this->declarationCustomGroupID,
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
        'name' => 'Scanned_Declaration',
        'label' => 'Scanned Declaration',
        'data_type' => 'File',
        'html_type' => 'File',
        'is_required' => '0',
        'is_searchable' => '0',
        'is_search_range' => '0',
        'weight' => '9',
        'is_active' => '1',
        'is_view' => '0',
        'text_length' => '255',
        'note_columns' => '60',
        'note_rows' => '4',
        'column_name' => 'scanned_declaration',
        'in_selector' => '0',
      ],

      // For Contributions' status
      [
        'custom_group_id' => $this->contributionGiftaidCustomGroupId,
        'name' => 'Eligible_for_Gift_Aid',
        'label' => 'Eligible for Gift Aid?',
        'data_type' => 'Int',
        'html_type' => 'Radio',
        'default_value' => '1',
        'is_required' => '1',
        'is_searchable' => '1',
        'is_search_range' => '0',
        'weight' => '1',
        'help_pre' => '\'Eligible for Gift Aid\' will be set automatically based on the financial type of the contribution if you do not select Yes or No',
        'is_active' => '1',
        'is_view' => '0',
        'text_length' => '255',
        'note_columns' => '60',
        'note_rows' => '4',
        'column_name' => 'eligible_for_gift_aid',
        'option_group_id' => $this->optionGroupNameToId['uk_taxpayer_options'],
        'in_selector' => '0',
      ],
      [
        'custom_group_id' => $this->contributionGiftaidCustomGroupId,
        'name' => 'Amount',
        'label' => 'Eligible Amount',
        'data_type' => 'Money',
        'html_type' => 'Text',
        'is_required' => '0',
        'is_searchable' => '1',
        'is_search_range' => '1',
        'weight' => '2',
        'is_active' => '1',
        'is_view' => '1',
        'text_length' => '255',
        'note_columns' => '60',
        'note_rows' => '4',
        'column_name' => 'amount',
        'in_selector' => '0',
      ],
      [
        'custom_group_id' => $this->contributionGiftaidCustomGroupId,
        'name' => 'Gift_Aid_Amount',
        'label' => 'Gift Aid Amount',
        'data_type' => 'Money',
        'html_type' => 'Text',
        'is_required' => '0',
        'is_searchable' => '1',
        'is_search_range' => '1',
        'weight' => '3',
        'is_active' => '1',
        'is_view' => '1',
        'text_length' => '255',
        'note_columns' => '60',
        'note_rows' => '4',
        'column_name' => 'gift_aid_amount',
        'in_selector' => '0',
      ],
      [
        'custom_group_id' => $this->contributionGiftaidCustomGroupId,
        'name' => 'Batch_Name',
        'label' => 'Batch Name',
        'data_type' => 'String',
        // 'html_type' => 'Select',
        'html_type' => 'Text',
        'default_value' => '',
        'is_required' => '0',
        'is_searchable' => '1',
        'is_search_range' => '0',
        'weight' => '4',
        'is_active' => '1',
        'is_view' => '1',
        'text_length' => '255',
        'note_columns' => '60',
        'note_rows' => '4',
        'column_name' => 'batch_name',
        'in_selector' => '0',
      ],
893
    ];
894

895
    return $customFields;
896
897
  }

898
  private function getOptionGroups() {
899
    // eligibility_declaration_options is for the Gift Aid Declaration, i.e. on the contact (3 options)
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
    // uk_taxpayer_options is for the Gift Aid Contribution (2 options)
    $optionGroups = [
      'eligibility_declaration_options' => [
        'name' => 'eligibility_declaration_options',
        'title' => 'GiftAid eligibility declaration options',
        'is_active' => 1,
        'is_reserved' => 1,
      ],
      'uk_taxpayer_options' => [
        'name' => 'uk_taxpayer_options',
        'title' => 'GiftAid contribution eligibility',
        'is_active' => 1,
        'is_reserved' => 1
      ],
      'giftaid_basic_rate_tax' => [
        'name' => 'giftaid_basic_rate_tax',
        'title' => 'GiftAid basic rate of tax',
        'is_active' => 1,
        'is_reserved' => 1
      ],
      'giftaid_batch_name' => [
        'name' => 'giftaid_batch_name',
        'title' => 'GiftAid batch name',
        'is_active' => 1,
        'is_reserved' => 1
      ],
      'reason_ended' => [
        'name' => 'reason_ended',
        'title' => 'GiftAid reason ended',
        'is_active' => 1,
        'is_reserved' => 1
      ],
932
    ];
933
934
935
    $optionGroups['batch_type'] = civicrm_api3('OptionGroup', 'getsingle', [
      'name' => 'batch_type',
    ]);
936
937
    return $optionGroups;
  }
nishantB's avatar
nishantB committed
938

939
  private function getOptionValues($optionGroups) {
mattwire's avatar
mattwire committed
940
    $optionValues = [
941
942
      // eligibility_declaration_options: these apply to contacts and record details
      // about their declarations.
mattwire's avatar
mattwire committed
943
      [
944
        'option_group_id' => $this->optionGroupNameToId['eligibility_declaration_options'],
945
        'label' => 'Yes, today and in the future',
nishantB's avatar
nishantB committed
946
947
        'value' => 1,
        'name' => 'eligible_for_giftaid',
948
949
        'is_default' => 0,
        'weight' => 2,
950
        'is_reserved' => 1,
mattwire's avatar
mattwire committed
951
952
      ],
      [
953
        'option_group_id' => $this->optionGroupNameToId['eligibility_declaration_options'],
nishantB's avatar
nishantB committed
954
955
956
        'label' => 'No',
        'value' => 0,
        'name' => 'not_eligible_for_giftaid',
957
958
        'is_default' => 0,
        'weight' => 3,
959
        'is_reserved' => 1,
mattwire's avatar
mattwire committed
960
961
      ],
      [
962
        'option_group_id' => $this->optionGroupNameToId['eligibility_declaration_options'],
963
        'label' => 'Yes, and for donations made in the past 4 years',
nishantB's avatar
nishantB committed
964
965
        'value' => 3,
        'name' => 'past_four_years',
966
967
        'is_default' => 0,
        'weight' => 1,
968
        'is_reserved' => 1,
mattwire's avatar
mattwire committed
969
      ],
970
      // uk_taxpayer_options: these apply to profiles
mattwire's avatar
mattwire committed
971
      [
972
        'option_group_id' => $this->optionGroupNameToId['uk_taxpayer_options'],
nishantB's avatar
nishantB committed
973
974
975
        'label' => 'Yes',
        'value' => 1,
        'name' => 'yes_uk_taxpayer',
976
        'is_default' => 0,
977
        'is_reserved' => 1,
mattwire's avatar
mattwire committed
978
979
      ],
      [
980
        'option_group_id' => $this->optionGroupNameToId['uk_taxpayer_options'],
nishantB's avatar
nishantB committed
981
982
983
        'label' => 'No',
        'value' => 0,
        'name' => 'not_uk_taxpayer',
984
        'is_default' => 0,
985
        'is_reserved' => 1,
mattwire's avatar
mattwire committed
986
      ],
987

988
      // These apply to why a declaration has an end date.
989
      [
990
        'option_group_id' => $this->optionGroupNameToId['reason_ended'],
991
992
993
994
995
996
997
        'label' => 'Contact Declined',
        'value' => 'Contact Declined',
        'name' => 'Contact_Declined',
        'is_default' => 0,
        'weight' => 2,
      ],
      [
998
        'option_group_id' => $this->optionGroupNameToId['reason_ended'],
999
1000
1001
1002
1003
1004
1005
        'label' => 'HMRC Declined',
        'value' => 'HMRC Declined',
        'name' => 'HMRC_Declined',
        'is_default' => 0,
        'weight' => 1,
      ],

1006
      // Tax rates
1007
      [
1008
        'option_group_id' => $this->optionGroupNameToId['giftaid_basic_rate_tax'],
1009
1010
1011
1012
1013
1014
1015
        'label' => 'The basic rate tax',
        'value' => 20,
        'name' => 'basic_rate_tax',
        'is_default' => 0,
        'weight' => 1,
        'is_reserved' => 1,
        'description' => 'The GiftAid basic tax rate (%)'
mattwire's avatar
mattwire committed
1016
      ],
1017
1018
1019
1020
1021
1022
1023
      [
        'option_group_id' => $this->optionGroupNameToId['batch_type'],
        'name' => "Gift Aid",
        'is_active' => 1,
        'is_reserved' => 1,
        'is_default' => 0,
      ]
mattwire's avatar
mattwire committed
1024
    ];
nishantB's avatar
nishantB committed
1025

1026
1027
1028
1029
1030
    return $optionValues;
  }

  private function setOptionGroups() {
    foreach ($this->getOptionGroups() as $groupName => $groupParams) {
1031
1032

      // Create the option groups.
1033
1034
1035
      $optionGroups[$groupName] = civicrm_api3('OptionGroup', 'get', [
        'name' => $groupName,
      ]);
Rich's avatar
Rich committed
1036
      if ($optionGroups[$groupName]['id'] ?? NULL) {
1037
1038
1039
1040
        $groupParams['id'] = $optionGroups[$groupName]['id'];
      }
      // Add new option groups and options
      $optionGroups[$groupName] = civicrm_api3('OptionGroup', 'create', $groupParams);
1041
1042
      // Save the look up of this group's ID.
      $this->optionGroupNameToId[$groupName] = $optionGroups[$groupName]['id'];
1043
1044
    }

1045
    // Create the values within the groups.
1046
    $optionValues = $this->getOptionValues($optionGroups);
nishantB's avatar
nishantB committed
1047
    foreach($optionValues as $params) {
1048
1049
      $optionValue = civicrm_api3('OptionValue', 'get', [
        'option_group_id' => $params['option_group_id'],
1050
        'value' => $params['value'],
1051
      ]);
1052
      if (CRM_Utils_Array::value('id', $optionValue)) {
1053
1054
1055
        $params['id'] = $optionValue['id'];
      }
      civicrm_api3('OptionValue', 'create', $params);
nishantB's avatar
nishantB committed
1056
    }
1057
1058
  }

1059
1060
1061
1062
1063
1064
1065
1066
  /**
   * Enable/Disable option groups
   * @param int $enable
   */
  private function enableOptionGroups($enable = 1) {
    CRM_Core_DAO::executeQuery("UPDATE civicrm_option_group SET is_active = {$enable} WHERE name = 'giftaid_batch_name'");
    CRM_Core_DAO::executeQuery("UPDATE civicrm_option_group SET is_active = {$enable} WHERE name = 'giftaid_basic_rate_tax'");
    CRM_Core_DAO::executeQuery("UPDATE civicrm_option_group SET is_active = {$enable} WHERE name = 'reason_ended'");
mattwire's avatar
mattwire committed
1067

1068
1069
1070
1071
1072
1073
1074
1075
1076
    try {
      civicrm_api3('CustomGroup', 'update', [
        'is_active' => $enable,
        'id' => CRM_Utils_Array::value('id', civicrm_api3('CustomGroup', 'getsingle', ['name' => 'Gift_Aid'])),
      ]);
    }
    catch (Exception $e) {
      // Couldn't find CustomGroup, maybe it was manually deleted
    }
mattwire's avatar
mattwire committed
1077
1078

    try {
1079
1080
1081
      civicrm_api3('CustomGroup', 'update', [
        'is_active' => $enable,
        'id' => CRM_Utils_Array::value('id', civicrm_api3('CustomGroup', 'getsingle', ['name' => 'Gift_Aid_Declaration'])),
mattwire's avatar
mattwire committed
1082
1083
1084
      ]);
    }
    catch (Exception $e) {
1085
      // Couldn't find CustomGroup, maybe it was manually deleted
mattwire's avatar
mattwire committed
1086
1087
    }

1088
1089
1090
1091
1092
1093
1094
1095
1096
    try {
      civicrm_api3('UFGroup', 'update', [
        'is_active' => $enable,
        'id' =>  CRM_Utils_Array::value('id',civicrm_api3('UFGroup', 'getsingle', ['name' => 'Gift_Aid'])),
      ]);
    }
    catch (Exception $e) {
      // Couldn't find CustomGroup, maybe it was manually deleted
    }
mattwire's avatar
mattwire committed
1097
1098
  }

1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
  /**
   * Remove report templates created by older versions
   */
  private static function removeLegacyRegisteredReport(){
    $report1 = civicrm_api3('OptionValue', 'get', [
      'option_group_id' => "report_template",
      'name' => 'GiftAid_Report_Form_Contribute_GiftAid',
    ]);
    $report2 = civicrm_api3('OptionValue', 'get', [
      'option_group_id' => "report_template",
      'value' => 'civicrm/contribute/uk-giftaid',
    ]);

    $reports = [];
    if (!empty($report1['count'])) {
      $reports[] = CRM_Utils_Array::first($report1['values']);
    }
    if (!empty($report2['count'])) {
      $reports[] = CRM_Utils_Array::first($report2['values']);
    }
    foreach ($reports as $report) {
      civicrm_api3('OptionValue', 'delete', ['id' => $report['id']]);
    }
1122
1123
  }

1124
  private static function migrateOneToTwo($ctx){
1125
1126
1127
1128
    $ctx->executeSqlFile('sql/upgrade_20.sql');
    $query = "SELECT DISTINCT batch_name
              FROM civicrm_value_gift_aid_submission
             ";
mattwire's avatar
mattwire committed
1129
    $batchNames = [];
1130
1131
1132
1133
    $dao = CRM_Core_DAO::executeQuery($query);
    while ($dao->fetch()) {
      array_push($batchNames, $dao->batch_name);
    }
1134
1135
    $gId = CRM_Utils_Array::value('id',civicrm_api3('OptionGroup', 'getsingle', ['name' => 'giftaid_batch_name']));
    if ($gId) {
1136
      foreach ($batchNames as $name) {
mattwire's avatar
mattwire committed
1137
        $params = [
1138
1139
          'option_group_id' => $gId,
          'name' => $name,
mattwire's avatar
mattwire committed
1140
        ];
1141
1142
1143
1144
1145
1146
1147
        $existing = civicrm_api3('OptionValue', 'get', $params);
        if (empty($existing['count'])) {
          $params['label'] = $name;
          $params['value'] = $name;
          $params['is_active'] = 1;
          civicrm_api3('OptionValue', 'create', $params);
        }
1148
1149
1150
      }
    }
  }
1151

1152
1153
1154
1155
  /**
   * Set the default admin settings for the extension.
   */
  private function setDefaultSettings() {
1156
1157
    Civi::settings()->set(E::SHORT_NAME . 'globally_enabled', 1);
    Civi::settings()->set(E::SHORT_NAME . 'financial_types_enabled', []);
1158
1159
  }

1160
1161
1162
1163
  /**
   * Remove the admin settings for the extension.
   */
  private function unsetSettings() {
1164
1165
    Civi::settings()->revert(E::SHORT_NAME . 'globally_enabled');
    Civi::settings()->revert(E::SHORT_NAME . 'financial_types_enabled');
1166
1167
  }

1168
1169
1170
1171
  /**
   * Create default settings for existing batches, for which settings don't already exist.
   */
  private static function importBatches() {
1172
    $sql = "
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
      SELECT id
      FROM civicrm_batch
      WHERE name LIKE 'GiftAid%'
    ";

    $dao = CRM_Core_DAO::executeQuery($sql);

    $basicRateTax = CRM_Civigiftaid_Utils_Contribution::getBasicRateTax();

    while ($dao->fetch()) {
      // Only add settings for batches for which settings don't exist already
      if (CRM_Civigiftaid_BAO_BatchSettings::findByBatchId($dao->id) === FALSE) {
        // Set globally enabled to TRUE by default, for existing batches
mattwire's avatar
mattwire committed
1186
        CRM_Civigiftaid_BAO_BatchSettings::create([
1187
          'batch_id' => (int) $dao->id,
mattwire's avatar
mattwire committed
1188
          'financial_types_enabled' => [],
1189
1190
          'globally_enabled' => TRUE,
          'basic_rate_tax' => $basicRateTax
mattwire's avatar
mattwire committed
1191
        ]);
1192
1193
1194
1195
1196
      }
    }
  }

  private function log($message) {
1197
    Civi::log()->info($message);
1198
  }
1199
}