Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BASW-131: Membership end date rules #6

Open
wants to merge 3 commits into
base: 7.x-4.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions includes/utils.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1235,7 +1235,7 @@ function wf_crm_get_fields($var = 'fields') {
'type' => 'select',
'expose_list' => TRUE,
'value' => 0,
'exposed_empty_option' => t('Automatic'),
'exposed_empty_option' => '- ' . t('Automatic') . ' -',
);
$fields['membership_start_date_memberships'] = array (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe membership_start_date_rules_memberships is more clear, same for end_date_rules

'name' => t('Select Membership Types'),
Expand All @@ -1248,18 +1248,14 @@ function wf_crm_get_fields($var = 'fields') {
'type' => 'select',
'expose_list' => TRUE,
'value' => 0,
'exposed_empty_option' => t('Automatic'),
'exposed_empty_option' => '- ' . t('Automatic') . ' -',
);
$fields['membership_end_date_memberships'] = array (
'name' => t('Select Membership Types'),
'type' => 'select',
'expose_list' => TRUE,
'extra' => array('multiple' => 1),
);
$fields['membership_pro_rate_membership'] = array(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you removed this field on purpose ? or is it by mistake ?

'name' => t('Pro-rate Price of Membership'),
'type' => 'checkbox',
);
}
// CiviGrant fields
if (isset($sets['grant'])) {
Expand Down
36 changes: 35 additions & 1 deletion includes/wf_crm_admin_form.inc
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,37 @@ class wf_crm_admin_form {
}
}
}

// Pro rate price checkbox
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would it be easy to move this logic to a new private method with a clear name ?

$fieldName = 'pro_rate_memberships';
$fidProRate = "civicrm_{$c}_membership_{$n}_membership_{$fieldName}";
$this->form['membership'][$c]['membership'][$fs][$fidProRate] = array(
'#type' => 'checkbox',
'#title' => t('Pro Rate price of Membership').'&nbsp;<span class="crm-container"><a class="helpicon" href="#membership_pro_rate_membership">&nbsp;</a></span>',
'#default_value' => !empty($this->settings['data']['membership'][$c]['membership'][$n][$fieldName]),
);
// Handle dependent fields for membership date rules.
$dependentFields = array (
array (
'field' => "civicrm_{$c}_membership_{$n}_membership_start_date_memberships",
'depends_on' => "civicrm_{$c}_membership_{$n}_membership_start_date_rules",
),
array (
'field' => "civicrm_{$c}_membership_{$n}_membership_end_date_memberships",
'depends_on' => "civicrm_{$c}_membership_{$n}_membership_end_date_rules",
),
array (
'field' => "civicrm_{$c}_membership_{$n}_membership_pro_rate_memberships",
'depends_on' => "civicrm_{$c}_membership_{$n}_membership_end_date_rules",
),
);
foreach ($dependentFields as $dependentField) {
$this->form['membership'][$c]['membership'][$fs][$dependentField['field']]['#states'] = array (
'visible' => array (
":input[name='{$dependentField['depends_on']}']" => array ('value' => 2),
),
);
}
}
}
}
Expand Down Expand Up @@ -1311,7 +1342,7 @@ class wf_crm_admin_form {
}
// Remove 'create_civicrm_webform_element' option for membership date rules
if (strpos($field['form_key'], '_start_date_rules') !== FALSE
||
||
strpos($field['form_key'], '_start_date_memberships') !== FALSE
||
strpos($field['form_key'], '_end_date_rules') !== FALSE
Expand Down Expand Up @@ -1548,6 +1579,9 @@ class wf_crm_admin_form {
$this->data[$ent][$c][$table][$n][$name] = $val;
}
}
if ($name == 'pro_rate_memberships') {
$this->data[$ent][$c][$table][$n][$name] = $val;
}
}
}
}
Expand Down
20 changes: 16 additions & 4 deletions includes/wf_crm_admin_help.inc
Original file line number Diff line number Diff line change
Expand Up @@ -308,28 +308,40 @@ class wf_crm_admin_help {
'</p>';
}


public static function membership_start_date_rules() {
print '<p>'.
t('\'Automatic\' indicates that the start date will follow normal CiviCRM rules. i.e. New memberships will start immediately from date of sign-up and renewals will extend memberships.').
'</p><br /><p>'.
t('User select will create a new webform date component that the administrator can configure in many ways including adding a fixed or relative start or end date.').'</p>'.
t('\'User select\' will create a new webform date component that the administrator can configure in many ways including adding a fixed or relative start or end date.').'</p>'.
'</p><br /><p>'.
t('\'Relative to active membership end date\' will modify the start date of the new membership as follows:
<p>It will first check whether the user has an existing membership of this type. If so it will follow the automatic membership rules.</p>
<p>If the user does not have a membership of this type, it will create a new membership and then check if the user has any active memberships of the relevant types specified. If not then the system will follow automatic membership rules.</p>
<p>If so it will set the start date of the new membership to be the day following the end date of the existing active membership.</p>
<p>If the user currently has multiple relevant membership types the start date will be one day following the end date of the membership with the latest end date.</p>');
}

public static function membership_end_date_rules() {
print '<p>'.
t('"Automatic" indicates that new membership end date will be calculated according to the start date and the membership duration').
'</p>';
}

public static function membership_pro_rate_membership() {
print '<p>'.
t('If this box is ticket, the membership price will be calculated based on the number of days left until the end date compared to the number of days in a regular membership period').
t('If this box is ticked, the membership price will be calculated based on the number of days left until the end date compared to the number of days in a regular membership period').
'</p>';
}
public static function membership_start_date_memberships() {
print '<p>'.
t('If you do not select a membership type, new membership will start immediately once the current active membership with the latest end date has expired.').
'</p>';
}

public static function membership_end_date_memberships() {
print '<p>'.
t('If you do not select a membership type, new membership will end when the current active membership with the latest end date expires.').
'</p>';
}

Expand Down
123 changes: 113 additions & 10 deletions includes/wf_crm_webform_postprocess.inc
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ class wf_crm_webform_postprocess extends wf_crm_webform_base {
$this->loadMultiPageData();
$this->validateParticipants();
}

// Validate end dates as per membership date rules.
if(!empty($this->data['membership'])) {
$this->validateMembershipDateRules();
}

// Process live contribution. If the transaction is unsuccessful it will trigger a form validation error.
if ($this->contribution_page) {
Expand All @@ -103,6 +108,35 @@ class wf_crm_webform_postprocess extends wf_crm_webform_base {
$form_state['civicrm']['ent'] = $this->ent;
$form_state['civicrm']['line_items'] = $this->line_items;
}

/**
* Validate function for membership date rules.
*/
public function validateMembershipDateRules() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for this to be public method

if (!empty($this->data['membership'])) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or :

if (empty($this->data['membership'])) {
return;
}
// rest of the code

foreach ($this->ent['contact'] as $c => $contact) {
if ($contact['id'] && isset($this->all_sets['membership']) && !empty($this->data['membership'][$c]['number_of_membership'])) {
$isError = FALSE;
$membershipFieldName = '';
foreach (wf_crm_aval($this->data, "membership:$c:membership", array()) as $n => $params) {
$params = $this->processMembershipDateRules($contact['id'], $params, $c, $n);
$membershipFieldName = "civicrm_{$c}_membership_{$n}_membership_membership_type_id";
if (!empty($params['end_date_rules']) && $params['end_date_rules'] == 2 && empty($params['end_date'])) {
$isError = TRUE;
break;
}
elseif (!empty($params['end_date']) && (strtotime($params['end_date']) < time())) {
$isError = TRUE;
break;
}
}
if ($isError) {
form_set_error($membershipFieldName, 'Unfortuntately you cannot purchase this item as your membership has expired.');
}
}
}
}
}

/**
* Process webform submission when it is about to be saved. Called by the following hook:
Expand Down Expand Up @@ -1186,10 +1220,10 @@ class wf_crm_webform_postprocess extends wf_crm_webform_base {
$params['contact_id'] = $cid;
// The api won't let us manually set status without this weird param
$params['skipStatusCal'] = !empty($params['status_id']);

// Process date rules only for new memberships
// Process date rules only for new memberships.
if (!isset($params['id'])) {
$params = $this->processMembershipDateRules($cid, $params);
$params = $this->processMembershipDateRules($cid, $params, $c, $n);
}

$result = wf_civicrm_api('membership', 'create', $params);
Expand Down Expand Up @@ -1219,36 +1253,59 @@ class wf_crm_webform_postprocess extends wf_crm_webform_base {
}
}

/**
/**
* Process membership date rules
*/
private function processMembershipDateRules($cid, $params) {
private function processMembershipDateRules($cid, $params, $c, $n) {
$membershipParams = array (
'sequential' => 1,
'active_only' => 1,
'contact_id' => $cid,
'options' => array ('sort' => "end_date DESC", 'limit' => 1),
);

$rawValues = _webform_client_form_submit_flatten($this->node, wf_crm_aval($this->form_state, 'values:submitted'));

// Process start date rules.
if (!empty($params['start_date_rules']) && $params['start_date_rules'] == 2){
if (!empty($params['start_date_rules']) && $params['start_date_rules'] == 1) {
$fieldName = 'rule_start_date';
$fieldKey = "civicrm_{$c}_membership_{$n}_membership_{$fieldName}";
$componentId = $this->getComponent($fieldKey)['cid'];
if (!empty($params['rule_start_date'])) {
$params['start_date'] = $params['rule_start_date'];
}
elseif (!empty($rawValues[$componentId])) {
$params['start_date'] = $rawValues[$componentId]['year'] . '-' . $rawValues[$componentId]['month'] . '-' . $rawValues[$componentId]['day'];
}
}
elseif (!empty($params['start_date_rules']) && $params['start_date_rules'] == 2){
// Handle case when memberships are selected for start date rules.
if (!empty($params['start_date_memberships'])) {
$membershipParams['membership_type_id'] = array (
'IN' => $params['start_date_memberships']
);
}

$activeMemberships = civicrm_api3('Membership', 'get', $membershipParams);
if ($activeMemberships['count']) {
$activeMembershipsEndDate = new DateTime($activeMemberships['values'][0]['end_date']);
$start_date = $activeMembershipsEndDate->modify('+1 day');
$params['start_date'] = $start_date->format('Ymd');
}
}

// Process end date rules.
if (!empty($params['end_date_rules']) && $params['end_date_rules'] == 2) {
if (!empty($params['end_date_rules']) && $params['end_date_rules'] == 1) {
$fieldName = 'rule_end_date';
$fieldKey = "civicrm_{$c}_membership_{$n}_membership_{$fieldName}";
$componentId = $this->getComponent($fieldKey)['cid'];
if (!empty($params['rule_end_date'])) {
$params['end_date'] = $params['rule_end_date'];
}
elseif (!empty($rawValues[$componentId])) {
$params['start_date'] = $rawValues[$componentId]['year'] . '-' . $rawValues[$componentId]['month'] . '-' . $rawValues[$componentId]['day'];
}
}
elseif (!empty($params['end_date_rules']) && $params['end_date_rules'] == 2) {
// Handle case when memberships are selected for end date rules.
if (!empty($params['end_date_memberships'])) {
$membershipParams['membership_type_id'] = array (
Expand All @@ -1258,6 +1315,9 @@ class wf_crm_webform_postprocess extends wf_crm_webform_base {
$activeMemberships = civicrm_api3('Membership', 'get', $membershipParams);
if ($activeMemberships['count']) {
$activeMembershipsEndDate = $activeMemberships['values'][0]['end_date'];
if (empty($params['start_date'])) {
$params['start_date'] = date('Y-m-d');
}
$params['end_date'] = $activeMembershipsEndDate;
}
}
Expand Down Expand Up @@ -1603,6 +1663,16 @@ class wf_crm_webform_postprocess extends wf_crm_webform_base {
$membership_financialtype = $item['financial_type_id'];
};

// Pro rate logic.
if (!empty($item['end_date_rules'])
&&
!empty($item['pro_rate_memberships'])
&&
$item['end_date_rules'] == 2
) {
$price = $this->pro_rated_membership_fee($type, $price, $c, $item);
}

if ($price) {
$this->line_items[] = array(
'qty' => $item['num_terms'],
Expand Down Expand Up @@ -1642,6 +1712,39 @@ class wf_crm_webform_postprocess extends wf_crm_webform_base {
return round($this->totalContribution, 2);
}

/**
* Calculate pro rated membership fee.
* @param Integer $type
* @param Float $price
* @param Integer $c
* @param Array $item
* @return Float $price
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the docblock params does not match the method params

*/
private function pro_rated_membership_fee($type, $price, $c, $n, $item) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or change the method name to : caculateMembershipProRateFee

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you replace $c and $n and $item with names that represent what they actually about.

$durationInterval = $this->getMembershipTypeField($type, 'duration_interval');
$durationUnit = $this->getMembershipTypeField($type, 'duration_unit');

// Calculate price/day.
$dateToday = date('Y-m-d');
$dateAfterGiveDuration = date('Y-m-d', strtotime("{$dateToday} +{$durationInterval} {$durationUnit}"));
$dateToday = date_create($dateToday);
$dateAfterGiveDuration = date_create($dateAfterGiveDuration);
$durationInDays = date_diff($dateToday, $dateAfterGiveDuration)->format('%a');

$pricePerDay = $price / $durationInDays;

// Find number of pro rate days.
$params = $this->processMembershipDateRules($this->existing_contacts[$c], $item, $c, $n);
$start_date = date_create($params['start_date']);
$end_date = date_create($params['end_date']);
$proRateDays = date_diff($start_date, $end_date)->format('%a');

// Calculate Pro Rated membership fee.
$price = $pricePerDay * $proRateDays;

return $price;
}

/**
* Are billing fields exposed to this webform page?
* @return bool
Expand Down
45 changes: 24 additions & 21 deletions js/webform_civicrm_admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,8 @@ var wfCiviAdmin = (function ($, D) {

// Membership constraints
$('select[name$=_membership_num_terms]', context).once('crm-mem-date').change(function(e, type) {
var $dateWrappers = $(this).parent().siblings('[class$="-date"]').not('[class$="-status-override-end-date"]');
var $dateWrappers = $(this).parent().siblings('[class$="-date"]').not('[class$="-status-override-end-date"]')
.not('[class$="-rule-start-date"]').not('[class$="-rule-end-date"]');
if ($(this).val() == '0') {
$dateWrappers.show();
if (type !== 'init') {
Expand All @@ -504,36 +505,38 @@ var wfCiviAdmin = (function ($, D) {
$dateWrappers.hide().find('input').prop('checked', false);
}
}).trigger('change', 'init');
$('select[name$=_membership_status_id]', context).once('crm-mem-date').change(function(e) {
$target = $(this).parent().siblings('[class$="membership-status-override-end-date"]');
if ($(this).val() == '0') {
$target.hide().find('input').prop('checked', false);
} else {
$target.show();
}
}).change();

$('select[name$=_membership_start_date_rules]', context).change(function(e, type) {
var $dateRuleMembershipField = $(this).parent().siblings('[class$="-start-date-memberships"]');
var $relativeToExistingMembership = 2;
if ($(this).val() == $relativeToExistingMembership) {
$dateRuleMembershipField.show();
var $dateWrappers = $(this).parent().siblings('[class$="membership-rule-start-date"]');
$dateWrappers.show();
if ($(this).val() == '1') {
if (type !== 'init') {
$('input', $dateWrappers).prop('checked', true);
}
}
else {
$dateRuleMembershipField.hide();
$dateWrappers.hide().find('input').prop('checked', false);
}
}).trigger('change', 'init');

$('select[name$=_membership_end_date_rules]', context).change(function(e, type) {
var $dateRuleMembershipField = $(this).parent().siblings('[class$="-end-date-memberships"]');
var $relativeToExistingMembership = 2;
if ($(this).val() == $relativeToExistingMembership) {
$dateRuleMembershipField.show();
var $dateWrappers = $(this).parent().siblings('[class$="membership-rule-end-date"]');
$dateWrappers.show();
if ($(this).val() == '1') {
if (type !== 'init') {
$('input', $dateWrappers).prop('checked', true);
}
}
else {
$dateRuleMembershipField.hide();
$dateWrappers.hide().find('input').prop('checked', false);
}
}).trigger('change', 'init');
$('select[name$=_membership_status_id]', context).once('crm-mem-date').change(function(e) {
$target = $(this).parent().siblings('[class$="membership-status-override-end-date"]');
if ($(this).val() == '0') {
$target.hide().find('input').prop('checked', false);
} else {
$target.show();
}
}).change();

function billingMessages() {
var $pageSelect = $('[name=civicrm_1_contribution_1_contribution_contribution_page_id]');
Expand Down