Merge pull request #19460 from colemanw/afformFix
[civicrm-core.git] / CRM / Member / BAO / MembershipStatus.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17 class CRM_Member_BAO_MembershipStatus extends CRM_Member_DAO_MembershipStatus {
18
19 /**
20 * Fetch object based on array of properties.
21 *
22 * @param array $params
23 * (reference ) an assoc array of name/value pairs.
24 * @param array $defaults
25 * (reference ) an assoc array to hold the flattened values.
26 *
27 * @return CRM_Member_BAO_MembershipStatus
28 */
29 public static function retrieve(&$params, &$defaults) {
30 $membershipStatus = new CRM_Member_DAO_MembershipStatus();
31 $membershipStatus->copyValues($params);
32 if ($membershipStatus->find(TRUE)) {
33 CRM_Core_DAO::storeValues($membershipStatus, $defaults);
34 return $membershipStatus;
35 }
36 return NULL;
37 }
38
39 /**
40 * Update the is_active flag in the db.
41 *
42 * @param int $id
43 * Id of the database record.
44 * @param bool $is_active
45 * Value we want to set the is_active field.
46 *
47 * @return bool
48 * true if we found and updated the object, else false
49 */
50 public static function setIsActive($id, $is_active) {
51 return CRM_Core_DAO::setFieldValue('CRM_Member_DAO_MembershipStatus', $id, 'is_active', $is_active);
52 }
53
54 /**
55 * Takes an associative array and creates a membership Status object.
56 *
57 * @param array $params
58 * Array of name/value pairs.
59 *
60 * @throws CRM_Core_Exception
61 * @return CRM_Member_DAO_MembershipStatus
62 */
63 public static function create($params) {
64 if (empty($params['id'])) {
65 //don't allow duplicate names - if id not set
66 $status = new CRM_Member_DAO_MembershipStatus();
67 $status->name = $params['name'];
68 if ($status->find(TRUE)) {
69 throw new CRM_Core_Exception('A membership status with this name already exists.');
70 }
71 }
72 return self::add($params);
73 }
74
75 /**
76 * Add the membership types.
77 *
78 * @param array $params
79 * Reference array contains the values submitted by the form.
80 * @param array $ids
81 * Array contains the id - this param is deprecated.
82 *
83 * @return CRM_Member_DAO_MembershipStatus
84 */
85 public static function add(&$params, $ids = []) {
86 if (!empty($ids)) {
87 CRM_Core_Error::deprecatedFunctionWarning('ids is a deprecated parameter');
88 }
89 $id = $params['id'] ?? $ids['membershipStatus'] ?? NULL;
90 if (!$id) {
91 CRM_Core_DAO::setCreateDefaults($params, self::getDefaults());
92 //copy name to label when not passed.
93 if (empty($params['label']) && !empty($params['name'])) {
94 $params['label'] = $params['name'];
95 }
96
97 if (empty($params['name']) && !empty($params['label'])) {
98 $params['name'] = $params['label'];
99 }
100 }
101
102 // set all other defaults to false.
103 if (!empty($params['is_default'])) {
104 $query = "UPDATE civicrm_membership_status SET is_default = 0";
105 CRM_Core_DAO::executeQuery($query);
106 }
107
108 // action is taken depending upon the mode
109 $membershipStatus = new CRM_Member_DAO_MembershipStatus();
110 $membershipStatus->copyValues($params);
111
112 $membershipStatus->id = $id;
113
114 $membershipStatus->save();
115 CRM_Member_PseudoConstant::flush('membershipStatus');
116 return $membershipStatus;
117 }
118
119 /**
120 * Get defaults for new entity.
121 * @return array
122 */
123 public static function getDefaults() {
124 return [
125 'is_active' => FALSE,
126 'is_current_member' => FALSE,
127 'is_admin' => FALSE,
128 'is_default' => FALSE,
129 ];
130 }
131
132 /**
133 * Get membership status.
134 *
135 * @param int $membershipStatusId
136 *
137 * @return array
138 */
139 public static function getMembershipStatus($membershipStatusId) {
140 $statusDetails = [];
141 $membershipStatus = new CRM_Member_DAO_MembershipStatus();
142 $membershipStatus->id = $membershipStatusId;
143 if ($membershipStatus->find(TRUE)) {
144 CRM_Core_DAO::storeValues($membershipStatus, $statusDetails);
145 }
146 return $statusDetails;
147 }
148
149 /**
150 * Delete membership Types.
151 *
152 * @param int $membershipStatusId
153 *
154 * @throws CRM_Core_Exception
155 */
156 public static function del($membershipStatusId) {
157 //check dependencies
158 //checking if membership status is present in some other table
159 $check = FALSE;
160
161 $dependency = ['Membership', 'MembershipLog'];
162 foreach ($dependency as $name) {
163 $baoString = 'CRM_Member_BAO_' . $name;
164 $dao = new $baoString();
165 $dao->status_id = $membershipStatusId;
166 if ($dao->find(TRUE)) {
167 throw new CRM_Core_Exception(ts('This membership status cannot be deleted as memberships exist with this status'));
168 }
169 }
170 CRM_Utils_Weight::delWeight('CRM_Member_DAO_MembershipStatus', $membershipStatusId);
171 //delete from membership Type table
172 $membershipStatus = new CRM_Member_DAO_MembershipStatus();
173 $membershipStatus->id = $membershipStatusId;
174 if (!$membershipStatus->find()) {
175 throw new CRM_Core_Exception(ts('Cannot delete membership status ' . $membershipStatusId));
176 }
177 $membershipStatus->delete();
178 CRM_Member_PseudoConstant::flush('membershipStatus');
179 }
180
181 /**
182 * Find the membership status based on start date, end date, join date & status date.
183 *
184 * Loop through all the membership status definitions, ordered by their
185 * weight. For each, we loop through all possible variations of the given
186 * start, end, and join dates and adjust the starts and ends based on that
187 * membership status's rules, where the last computed set of adjusted start
188 * and end becomes a candidate. Then we compare that candidate to either
189 * "today" or some other given date, and if it falls between the adjusted
190 * start and end we have a match and we stop looping through status
191 * definitions. Then we call a hook in case that wasn't enough loops.
192 *
193 * @param string $startDate
194 * Start date of the member whose membership status is to be calculated.
195 * @param string $endDate
196 * End date of the member whose membership status is to be calculated.
197 * @param string $joinDate
198 * Join date of the member whose membership status is to be calculated.
199 * @param string $statusDate
200 * Either the string "today" or a date against which we compare the adjusted start and end based on the status rules.
201 * @param bool $excludeIsAdmin
202 * Exclude the statuses having is_admin = 1.
203 * @param int $membershipTypeID
204 * Not used directly but gets passed to the hook.
205 * @param array $membership
206 * Membership params as available to calling function - not used directly but passed to the hook.
207 *
208 * @return array
209 */
210 public static function getMembershipStatusByDate(
211 $startDate, $endDate, $joinDate,
212 $statusDate = 'now', $excludeIsAdmin = FALSE, $membershipTypeID = NULL, $membership = []
213 ) {
214 $membershipDetails = [];
215
216 if (!$statusDate || $statusDate === 'today') {
217 $statusDate = 'now';
218 CRM_Core_Error::deprecatedFunctionWarning('pass now rather than today in');
219 }
220
221 $statusDate = date('Ymd', CRM_Utils_Time::strtotime($statusDate));
222
223 //fix for CRM-3570, if we have statuses with is_admin=1,
224 //exclude these statuses from calculatation during import.
225 $where = "is_active = 1";
226 if ($excludeIsAdmin) {
227 $where .= " AND is_admin != 1";
228 }
229
230 $query = "
231 SELECT *
232 FROM civicrm_membership_status
233 WHERE {$where}
234 ORDER BY weight ASC";
235
236 $membershipStatus = CRM_Core_DAO::executeQuery($query);
237
238 $dates = [
239 'start' => ($startDate && $startDate !== 'null') ? date('Ymd', CRM_Utils_Time::strtotime($startDate)) : '',
240 'end' => ($endDate && $endDate !== 'null') ? date('Ymd', CRM_Utils_Time::strtotime($endDate)) : '',
241 'join' => ($joinDate && $joinDate !== 'null') ? date('Ymd', CRM_Utils_Time::strtotime($joinDate)) : '',
242 ];
243
244 while ($membershipStatus->fetch()) {
245 $startEvent = NULL;
246 $endEvent = NULL;
247 foreach (['start', 'end'] as $eve) {
248 foreach ($dates as $dat => $date) {
249 // calculate start-event/date and end-event/date
250 if (($membershipStatus->{$eve . '_event'} === $dat . '_date') &&
251 $date
252 ) {
253 if ($membershipStatus->{$eve . '_event_adjust_unit'} &&
254 $membershipStatus->{$eve . '_event_adjust_interval'}
255 ) {
256 $month = date('m', CRM_Utils_Time::strtotime($date));
257 $day = date('d', CRM_Utils_Time::strtotime($date));
258 $year = date('Y', CRM_Utils_Time::strtotime($date));
259 // add in months
260 if ($membershipStatus->{$eve . '_event_adjust_unit'} === 'month') {
261 ${$eve . 'Event'} = date('Ymd', mktime(0, 0, 0,
262 $month + $membershipStatus->{$eve . '_event_adjust_interval'},
263 $day,
264 $year
265 ));
266 }
267 // add in days
268 if ($membershipStatus->{$eve . '_event_adjust_unit'} === 'day') {
269 ${$eve . 'Event'} = date('Ymd', mktime(0, 0, 0,
270 $month,
271 $day + $membershipStatus->{$eve . '_event_adjust_interval'},
272 $year
273 ));
274 }
275 // add in years
276 if ($membershipStatus->{$eve . '_event_adjust_unit'} === 'year') {
277 ${$eve . 'Event'} = date('Ymd', mktime(0, 0, 0,
278 $month,
279 $day,
280 $year + $membershipStatus->{$eve . '_event_adjust_interval'}
281 ));
282 }
283 // if no interval and unit, present
284 }
285 else {
286 ${$eve . 'Event'} = $date;
287 }
288 }
289 }
290 }
291
292 // check if statusDate is in the range of start & end events.
293 if ($startEvent && $endEvent) {
294 if (($statusDate >= $startEvent) && ($statusDate <= $endEvent)) {
295 $membershipDetails['id'] = $membershipStatus->id;
296 $membershipDetails['name'] = $membershipStatus->name;
297 }
298 }
299 elseif ($startEvent) {
300 if ($statusDate >= $startEvent) {
301 $membershipDetails['id'] = $membershipStatus->id;
302 $membershipDetails['name'] = $membershipStatus->name;
303 }
304 }
305 elseif ($endEvent) {
306 if ($statusDate <= $endEvent) {
307 $membershipDetails['id'] = $membershipStatus->id;
308 $membershipDetails['name'] = $membershipStatus->name;
309 }
310 }
311
312 // returns FIRST status record for which status_date is in range.
313 if ($membershipDetails) {
314 break;
315 }
316 }
317 //end fetch
318
319 //we bundle the arguments into an array as we can't pass 8 variables to the hook otherwise
320 // the membership array might contain the pre-altered settings so we don't want to merge this
321 $arguments = [
322 'start_date' => $startDate,
323 'end_date' => $endDate,
324 'join_date' => $joinDate,
325 'status_date' => $statusDate,
326 'exclude_is_admin' => $endDate,
327 'membership_type_id' => $membershipTypeID,
328 'start_event' => $startEvent,
329 'end_event' => $endEvent,
330 ];
331 CRM_Utils_Hook::alterCalculatedMembershipStatus($membershipDetails, $arguments, $membership);
332 return $membershipDetails;
333 }
334
335 /**
336 * Function that return the status ids whose is_current_member is set.
337 *
338 * @return array
339 */
340 public static function getMembershipStatusCurrent() {
341 $statusIds = [];
342 $membershipStatus = new CRM_Member_DAO_MembershipStatus();
343 $membershipStatus->is_current_member = 1;
344 $membershipStatus->find();
345 $membershipStatus->selectAdd();
346 $membershipStatus->selectAdd('id');
347 while ($membershipStatus->fetch()) {
348 $statusIds[] = $membershipStatus->id;
349 }
350 return $statusIds;
351 }
352
353 }