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