Skip to content

Commit 6c46c0c

Browse files
committed
MM-213: Fix membership fee token value for multistep webform
1 parent d739ea7 commit 6c46c0c

File tree

2 files changed

+217
-34
lines changed

2 files changed

+217
-34
lines changed

js/webform_civicrm_forms.js

Lines changed: 167 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -438,44 +438,177 @@ var wfCivi = (function ($, D) {
438438
*/
439439
D.behaviors.membershipTypeFeesUpdate = {
440440
attach: function (context, settings) {
441-
const membershipSelector = 'select[id*="membership-membership-type-id"]';
442-
// Should apply for multiple membership type.
441+
const membershipSelector = '[id*="membership-membership-type-id"]';
442+
443+
// Function to get membership value from Drupal settings.
444+
function getMembershipValue(fieldsetName) {
445+
// Solution 1: Check Drupal settings.
446+
if (typeof Drupal.settings.webform !== 'undefined' &&
447+
typeof Drupal.settings.webform.membershipValues !== 'undefined') {
448+
return Drupal.settings.webform.membershipValues[fieldsetName] || '';
449+
}
450+
return '';
451+
}
452+
453+
// Function to update fee field with AJAX call.
454+
function updateFeeField(membershipId, feeFieldInput) {
455+
if (!membershipId || feeFieldInput.length === 0) {
456+
return;
457+
}
458+
459+
$.ajax({
460+
url: '/webform-civicrm/js/getMembershipFees/' + membershipId,
461+
dataType: 'json',
462+
success: function (response) {
463+
if (response.fees !== undefined) {
464+
$(feeFieldInput).val(response.fees);
465+
}
466+
},
467+
error: function () {
468+
console.log('Failed to fetch fee.');
469+
}
470+
});
471+
}
472+
473+
// Function to extract core membership pattern from field name.
474+
function extractMembershipPattern(elementName) {
475+
// Extract civicrm_X_membership_Y_membership from both structures:
476+
// 1. submitted[civicrm_1_membership_1_membership_membership_type_id]
477+
// 2. submitted[...][civicrm_1_membership_1_membership_membership_type_id]
478+
const match = elementName.match(/(civicrm_\d+_membership_\d+_membership)_membership_type_id/);
479+
return match ? match[1] : null;
480+
}
481+
482+
// Function to find corresponding fee field.
483+
function findCorrespondingFeeField(membershipPattern, context) {
484+
// Look for fee field with the same pattern.
485+
const feeFields = $('input[id*="membership-fee-amount-from-membership"]', context);
486+
return feeFields.filter(function() {
487+
const feeFieldName = $(this).attr('name');
488+
if (!feeFieldName) {
489+
return false;
490+
}
491+
492+
// Check if this fee field contains the same membership pattern.
493+
const feePattern = membershipPattern + '_fee_amount_from_membership';
494+
return feeFieldName.includes(feePattern);
495+
});
496+
}
497+
498+
// Function to get current membership type value from the form.
499+
function getCurrentMembershipValue(membershipPattern, context) {
500+
const membershipFields = $(membershipSelector, context).filter(function() {
501+
const fieldName = $(this).attr('name');
502+
if (!fieldName) {
503+
return false;
504+
}
505+
506+
const fieldPattern = extractMembershipPattern(fieldName);
507+
return fieldPattern === membershipPattern;
508+
});
509+
510+
if (membershipFields.length === 0) {
511+
return '';
512+
}
513+
514+
const field = membershipFields.first();
515+
if (field.is('input[type="radio"]')) {
516+
const radioName = field.attr('name');
517+
const checkedRadio = $('input[type="radio"][name="' + radioName + '"]:checked', context);
518+
return checkedRadio.length > 0 ? checkedRadio.val() : '';
519+
} else {
520+
return field.val() || '';
521+
}
522+
}
523+
524+
// Handle each membership field individually.
443525
$(membershipSelector, context).each(function() {
444-
// On change membership type field.
445-
$(membershipSelector, context).once('feeAjax').change(function () {
446-
const membershipSelect = $(this);
447-
// Get name attribute of membership type.
448-
var membershipSelectname = membershipSelect.attr('name');
449-
// Pregmatch to fetch fieldset name.
450-
// Capture fieldset name inside the first pair of square brackets.
451-
var membershipSelectMatch = membershipSelectname.match(/\[([^\[\]]+)\]/);
452-
// This is fieldset for each contact.
453-
var civiContactFieldset = membershipSelectMatch[1];
454-
455-
const feeField = 'input[id*="membership-fee-amount-from-membership"]';
456-
// Filter the fees input which has same fieldset as name.
457-
const feeFieldInput = $(feeField).filter(function() {
458-
// Safely checks if the name exists and includes the target.
459-
return $(this).attr('name')?.includes(civiContactFieldset);
460-
});
526+
const $element = $(this);
527+
const isRadio = $element.is('input[type="radio"]');
528+
const isSelect = $element.is('select');
461529

462-
// Make ajax request when fees field present.
463-
if (feeFieldInput) {
464-
const membershipId = membershipSelect.val();
465-
$.ajax({
466-
url: '/webform-civicrm/js/getMembershipFees/' + membershipId,
467-
dataType: 'json',
468-
success: function (response) {
469-
if (response.fees !== undefined) {
470-
$(feeFieldInput).val(response.fees);
471-
}
472-
},
473-
error: function () {
474-
console.log('Failed to fetch fee.');
475-
}
476-
});
530+
if (!isRadio && !isSelect) {
531+
return;
532+
}
533+
534+
const elementName = $element.attr('name');
535+
const membershipPattern = extractMembershipPattern(elementName);
536+
537+
if (!membershipPattern) {
538+
return;
539+
}
540+
541+
// Set up change handler for this specific membership instance.
542+
$element.once('feeAjax-' + membershipPattern).on('change', function () {
543+
let selectedValue;
544+
545+
// Get selected value based on element type.
546+
if (isRadio) {
547+
const radioName = $(this).attr('name');
548+
const checkedRadio = $('input[type="radio"][name="' + radioName + '"]:checked', context);
549+
if (checkedRadio.length === 0) {
550+
return;
551+
}
552+
selectedValue = checkedRadio.val();
553+
} else {
554+
selectedValue = $(this).val();
555+
}
556+
557+
// Find the corresponding fee field.
558+
const feeFieldInput = findCorrespondingFeeField(membershipPattern, context);
559+
// Update fee field.
560+
updateFeeField(selectedValue, feeFieldInput);
561+
});
562+
563+
// Check for stored values on page load for this membership instance.
564+
const storedValue = getMembershipValue(membershipPattern);
565+
if (storedValue) {
566+
// Find the corresponding fee field.
567+
const feeFieldInput = findCorrespondingFeeField(membershipPattern, context);
568+
// If fee field exists and is empty, update it.
569+
if (feeFieldInput.length > 0) {
570+
updateFeeField(storedValue, feeFieldInput);
477571
}
572+
}
573+
});
574+
575+
// Also check for fee fields that might exist without corresponding membership fields on current step.
576+
const feeFields = $('input[id*="membership-fee-amount-from-membership"]', context);
577+
feeFields.each(function() {
578+
const feeInput = $(this);
579+
const feeInputName = feeInput.attr('name');
580+
581+
if (!feeInputName) {
582+
return;
583+
}
584+
585+
// Extract membership pattern from fee field name.
586+
const match = feeInputName.match(/(civicrm_\d+_membership_\d+_membership)_fee_amount_from_membership/);
587+
const membershipPattern = match ? match[1] : null;
588+
589+
if (!membershipPattern) {
590+
return;
591+
}
592+
593+
// Check if corresponding membership field exists on current step.
594+
const correspondingMembershipField = $(membershipSelector).filter(function() {
595+
const membershipFieldName = $(this).attr('name');
596+
if (!membershipFieldName) {
597+
return false;
598+
}
599+
const fieldPattern = extractMembershipPattern(membershipFieldName);
600+
return fieldPattern === membershipPattern;
478601
});
602+
603+
// Only process if no corresponding membership field exists on current step.
604+
if (correspondingMembershipField.length === 0) {
605+
// Check stored value from Drupal settings.
606+
const membershipValue = getMembershipValue(membershipPattern);
607+
// If we have a stored value and fee field is empty, update it.
608+
if (membershipValue) {
609+
updateFeeField(membershipValue, feeInput);
610+
}
611+
}
479612
});
480613
}
481614
};

webform_civicrm.module

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ function webform_civicrm_form_alter(&$form, &$form_state, $form_id) {
9595
wf_crm_admin_component::checkBillingPagination($form['#node']);
9696
}
9797
}
98+
99+
// Store membership value on every steps and pass
100+
_webform_civicrm_form_alter_helper($form, $form_state, $form_id);
98101
}
99102

100103
/**
@@ -1093,3 +1096,50 @@ function webform_civicrm_civicrm_alterPaymentProcessorParams($paymentObj, $rawPa
10931096
$cookedParams['cancel_return'] = $rawParams['webform_redirect_cancel'];
10941097
}
10951098
}
1099+
1100+
/**
1101+
* Implements hook_form_alter().
1102+
*/
1103+
function _webform_civicrm_form_alter_helper(&$form, &$form_state, $form_id) {
1104+
// Early return if not a webform client form.
1105+
if (strpos($form_id, 'webform_client_form_') !== 0) {
1106+
return;
1107+
}
1108+
1109+
// Early return if no stored values from previous steps.
1110+
if (!isset($form_state['storage']['submitted'])) {
1111+
return;
1112+
}
1113+
1114+
$membership_values = [];
1115+
1116+
// Find membership type values in stored submissions.
1117+
foreach ($form_state['storage']['submitted'] as $cid => $value) {
1118+
$component = $form['#node']->webform['components'][$cid];
1119+
1120+
// Check if this is a membership type field.
1121+
if (strpos($component['form_key'], 'membership_membership_type_id') === FALSE) {
1122+
continue;
1123+
}
1124+
1125+
// Extract membership pattern from form_key.
1126+
// Form key: 'civicrm_X_membership_Y_membership_membership_type_id'.
1127+
// We want: 'civicrm_X_membership_Y_membership'.
1128+
if (preg_match('/(civicrm_\d+_membership_\d+_membership)_membership_type_id$/', $component['form_key'], $matches)) {
1129+
$membership_pattern = $matches[1];
1130+
$membership_values[$membership_pattern] = $value;
1131+
}
1132+
}
1133+
1134+
// Early return if no membership values found.
1135+
if (empty($membership_values)) {
1136+
return;
1137+
}
1138+
1139+
// Pass the values to JavaScript.
1140+
drupal_add_js([
1141+
'webform' => [
1142+
'membershipValues' => $membership_values,
1143+
],
1144+
], 'setting');
1145+
}

0 commit comments

Comments
 (0)