Merge pull request #14249 from yashodha/959_dev
[civicrm-core.git] / CRM / Member / ActionMapping.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28
29 /**
30 * Class CRM_Member_ActionMapping
31 *
32 * This defines the scheduled-reminder functionality for CiviMember
33 * memberships. It allows one to target reminders based on join date
34 * or end date, with additional filtering based on membership-type.
35 */
36 class CRM_Member_ActionMapping extends \Civi\ActionSchedule\Mapping {
37
38 /**
39 * The value for civicrm_action_schedule.mapping_id which identifies the
40 * "Membership Type" mapping.
41 *
42 * Note: This value is chosen to match legacy DB IDs.
43 */
44 const MEMBERSHIP_TYPE_MAPPING_ID = 4;
45
46 /**
47 * Register CiviMember-related action mappings.
48 *
49 * @param \Civi\ActionSchedule\Event\MappingRegisterEvent $registrations
50 */
51 public static function onRegisterActionMappings(\Civi\ActionSchedule\Event\MappingRegisterEvent $registrations) {
52 $registrations->register(CRM_Member_ActionMapping::create([
53 'id' => CRM_Member_ActionMapping::MEMBERSHIP_TYPE_MAPPING_ID,
54 'entity' => 'civicrm_membership',
55 'entity_label' => ts('Membership'),
56 'entity_value' => 'civicrm_membership_type',
57 'entity_value_label' => ts('Membership Type'),
58 'entity_status' => 'auto_renew_options',
59 'entity_status_label' => ts('Auto Renew Options'),
60 ]));
61 }
62
63 /**
64 * Get a list of available date fields.
65 *
66 * @return array
67 * Array(string $fieldName => string $fieldLabel).
68 */
69 public function getDateFields() {
70 return [
71 'join_date' => ts('Membership Join Date'),
72 'start_date' => ts('Membership Start Date'),
73 'end_date' => ts('Membership End Date'),
74 ];
75 }
76
77 /**
78 * Generate a query to locate recipients who match the given
79 * schedule.
80 *
81 * @param \CRM_Core_DAO_ActionSchedule $schedule
82 * The schedule as configured by the administrator.
83 * @param string $phase
84 * See, e.g., RecipientBuilder::PHASE_RELATION_FIRST.
85 * @param array $defaultParams
86 *
87 * @return \CRM_Utils_SQL_Select
88 * @see RecipientBuilder
89 */
90 public function createQuery($schedule, $phase, $defaultParams) {
91 $selectedValues = (array) \CRM_Utils_Array::explodePadded($schedule->entity_value);
92 $selectedStatuses = (array) \CRM_Utils_Array::explodePadded($schedule->entity_status);
93
94 $query = \CRM_Utils_SQL_Select::from("{$this->entity} e")->param($defaultParams);
95 $query['casAddlCheckFrom'] = 'civicrm_membership e';
96 $query['casContactIdField'] = 'e.contact_id';
97 $query['casEntityIdField'] = 'e.id';
98 $query['casContactTableAlias'] = NULL;
99
100 // Leaving this in case of legacy databases
101 $query['casDateField'] = str_replace('membership_', 'e.', $schedule->start_action_date);
102
103 // Options currently are just 'join_date', 'start_date', and 'end_date':
104 // they need an alias
105 if (strpos($query['casDateField'], 'e.') !== 0) {
106 $query['casDateField'] = 'e.' . $query['casDateField'];
107 }
108
109 // FIXME: Numbers should be constants.
110 if (in_array(2, $selectedStatuses)) {
111 //auto-renew memberships
112 $query->where("e.contribution_recur_id IS NOT NULL");
113 }
114 elseif (in_array(1, $selectedStatuses)) {
115 $query->where("e.contribution_recur_id IS NULL");
116 }
117
118 if (!empty($selectedValues)) {
119 $query->where("e.membership_type_id IN (@memberTypeValues)")
120 ->param('memberTypeValues', $selectedValues);
121 }
122 else {
123 // FIXME: The membership type is never null, so nobody will ever get a
124 // reminder if no membership types are selected. Either this should be a
125 // validation on the reminder form or all types should get a reminder if
126 // no types are selected.
127 $query->where("e.membership_type_id IS NULL");
128 }
129
130 // FIXME: This makes a lot of sense for renewal reminders, but a user
131 // scheduling another kind of reminder might not expect members to be
132 // excluded if they have status overrides. Ideally there would be some kind
133 // of setting per reminder.
134 $query->where("( e.is_override IS NULL OR e.is_override = 0 )");
135
136 // FIXME: Similarly to overrides, excluding contacts who can't edit the
137 // primary member makes sense in the context of renewals (see CRM-11342) but
138 // would be a surprise for other use cases.
139 $query->merge($this->prepareMembershipPermissionsFilter());
140
141 // FIXME: A lot of undocumented stuff happens with regard to
142 // `is_current_member`, and this is no exception. Ideally there would be an
143 // opportunity to pick statuses when setting up the scheduled reminder
144 // rather than making the assumptions here.
145 $query->where("e.status_id IN (#memberStatus)")
146 ->param('memberStatus', \CRM_Member_PseudoConstant::membershipStatus(NULL, "(is_current_member = 1 OR name = 'Expired')", 'id'));
147
148 // Why is this only for civicrm_membership?
149 if ($schedule->start_action_date && $schedule->is_repeat == FALSE) {
150 $query['casUseReferenceDate'] = TRUE;
151 }
152
153 return $query;
154 }
155
156 /**
157 * Filter out the memberships that are inherited from a contact that the
158 * recipient cannot edit.
159 *
160 * @return CRM_Utils_SQL_Select
161 */
162 protected function prepareMembershipPermissionsFilter() {
163 $joins = [
164 'cm' => 'LEFT JOIN civicrm_membership cm ON cm.id = e.owner_membership_id',
165 'rela' => 'LEFT JOIN civicrm_relationship rela ON rela.contact_id_a = e.contact_id AND rela.contact_id_b = cm.contact_id AND rela.is_permission_a_b = #editPerm',
166 'relb' => 'LEFT JOIN civicrm_relationship relb ON relb.contact_id_a = cm.contact_id AND relb.contact_id_b = e.contact_id AND relb.is_permission_b_a = #editPerm',
167 ];
168
169 return \CRM_Utils_SQL_Select::fragment()
170 ->join(NULL, $joins)
171 ->param('#editPerm', CRM_Contact_BAO_Relationship::EDIT)
172 ->where('!( e.owner_membership_id IS NOT NULL AND rela.id IS NULL and relb.id IS NULL )');
173 }
174
175 }