generated from compucorp/template-repo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CIWEMB-320: User can optionally record payment for new membership
- Loading branch information
1 parent
8e337af
commit 3a7ce22
Showing
5 changed files
with
308 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<?php | ||
|
||
namespace Civi\Financeextras\Hook\BuildForm; | ||
|
||
use CRM_Financeextras_ExtensionUtil as E; | ||
|
||
class MembershipCreate { | ||
|
||
/** | ||
* @param \CRM_Member_Form_Membership $form | ||
*/ | ||
public function __construct(private \CRM_Member_Form_Membership $form) { | ||
} | ||
|
||
public function handle() { | ||
$this->configureRecordContributionField(); | ||
} | ||
|
||
/** | ||
* CiviCRM by default always shows the payment fields for new contribution, | ||
* This method configures the display of these payment related fields by | ||
* grouping them together as a block and adds a 'Record Payment' checkbox | ||
* that determines visibility of this block. | ||
* | ||
* Also, adds a payment amount field, that allows users to specify the amount | ||
* they would like to record for the contribution. | ||
*/ | ||
private function configureRecordContributionField() { | ||
if (!$this->isEdit()) { | ||
$this->form->addElement('radio', 'fe_member_type', NULL, ts('Paid Membership'), 'paid_member'); | ||
$this->form->addElement('radio', 'fe_member_type', NULL, ts('Free Membership'), 'free_member'); | ||
$this->form->add('checkbox', 'fe_record_payment_check', ts('Record Payment'), NULL); | ||
$this->form->add('text', 'fe_record_payment_amount', ts('Amount'), ['readonly' => TRUE]); | ||
\Civi::resources()->add([ | ||
'scriptFile' => [E::LONG_NAME, 'js/modifyMemberForm.js'], | ||
'region' => 'page-header', | ||
]); | ||
\Civi::resources()->add([ | ||
'template' => 'CRM/Financeextras/Form/Member/AddPayment.tpl', | ||
'region' => 'page-body', | ||
]); | ||
\Civi::resources()->addVars('financeextras', ['currencySymbol' => $this->getCurrencySymbol()]); | ||
} | ||
} | ||
|
||
/** | ||
* @return string | ||
*/ | ||
private function getCurrencySymbol() { | ||
$config = \CRM_Core_Config::singleton(); | ||
return \CRM_Core_DAO::getFieldValue('CRM_Financial_DAO_Currency', $config->defaultCurrency, 'symbol', 'name'); | ||
} | ||
|
||
private function isEdit() { | ||
return !empty($this->form->_id); | ||
} | ||
|
||
/** | ||
* Checks if the hook should run. | ||
* | ||
* @param \CRM_Core_Form $form | ||
* @param string $formName | ||
* | ||
* @return bool | ||
*/ | ||
public static function shouldHandle($form, $formName) { | ||
return $formName === "CRM_Member_Form_Membership"; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
<?php | ||
|
||
namespace Civi\Financeextras\Hook\ValidateForm; | ||
|
||
use Civi\Financeextras\Utils\OptionValueUtils; | ||
|
||
class MembershipCreate { | ||
|
||
/** | ||
* @param \CRM_Member_Form_Membership $form | ||
* @param array $fields | ||
* @param array $errors | ||
*/ | ||
public function __construct(private \CRM_Member_Form_Membership $form, private array &$fields, private array &$errors) { | ||
} | ||
|
||
public function handle() { | ||
$this->validatePaymentForm(); | ||
} | ||
|
||
public function validatePaymentForm() { | ||
if (empty($this->fields['record_contribution'])) { | ||
// Don't do anything if user will not be recording contribution | ||
return; | ||
} | ||
|
||
if ($this->fields['contribution_type_toggle'] == 'payment_plan') { | ||
// Don't do anything if membership is paid for using the payment plan option | ||
return; | ||
} | ||
|
||
if (empty($this->fields['fe_record_payment_check'])) { | ||
$data = &$this->form->controller->container(); | ||
// Before the form is submitted, we ensure that if the "Record Payment" box is unchecked, | ||
// payment will not be recorded against the contribution. | ||
$data['values']['Membership']['contribution_status_id'] = OptionValueUtils::getValueForOptionValue('contribution_status', 'Pending'); | ||
} | ||
|
||
if (!empty($this->fields['fe_record_payment_check']) && empty($this->fields['fe_record_payment_amount'])) { | ||
$this->errors['fe_record_payment_amount'] = ts('Payment amount is required'); | ||
} | ||
} | ||
|
||
/** | ||
* Checks if the hook should run. | ||
* | ||
* @param CRM_Core_Form $form | ||
* @param string $formName | ||
* | ||
* @return bool | ||
*/ | ||
public static function shouldHandle($form, $formName) { | ||
return $formName === "CRM_Member_Form_Membership" && ($form->_action & \CRM_Core_Action::ADD); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
CRM.$(function ($) { | ||
|
||
(function() { | ||
setTotalAmount(); | ||
togglePaymentBlock(); | ||
toggleMembershipType(); | ||
toggleContributionBlock(); | ||
placePaymentFieldsTogether(); | ||
hidePaymentFieldsOnPaymentToggler(); | ||
})(); | ||
|
||
function setTotalAmount() { | ||
const recordPaymentAmount = document.querySelector("input[name=fe_record_payment_amount]"); | ||
observeElement('input[name=total_amount]', "value", function () { | ||
toggleMembershipType(); | ||
recordPaymentAmount.value = $('#total_amount').val(); | ||
}); | ||
} | ||
|
||
function togglePaymentBlock() { | ||
$('input[name=fe_record_payment_check]').prop("checked", true).trigger('change') | ||
|
||
$('input[name=fe_record_payment_check]').on('change', () => { | ||
const recordPayment = $('input[name=fe_record_payment_check]').is(':checked') | ||
$('tr.record_payment-block_row').toggle(recordPayment) | ||
}); | ||
} | ||
|
||
function toggleMembershipType() { | ||
if ($('input[name=record_contribution]').is(':checked')) { | ||
$('input[name=fe_member_type][value=paid_member]').prop("checked", true) | ||
} else { | ||
$('input[name=fe_member_type][value=free_member]').prop("checked", true) | ||
} | ||
} | ||
|
||
function toggleContributionBlock() { | ||
toggleMembershipType(); | ||
|
||
$('tr#contri').after( | ||
$('<tr>').addClass('fe-membership_type-row').append($('<td>').attr('colspan', 2).append( | ||
$('.fe-membership_type') | ||
)) | ||
); | ||
|
||
$('input:radio[name=fe_member_type]').on('change', () => { | ||
const isPaid = $('input:radio[name=fe_member_type][value=paid_member]').is(':checked'); | ||
$('input#record_contribution').prop("checked", !isPaid).trigger('click') | ||
}); | ||
|
||
$('tr#contri').hide(); | ||
} | ||
|
||
function placePaymentFieldsTogether() { | ||
$('tr.crm-membership-form-block-receive_date').after( | ||
$('<tr>').addClass('record_payment-block_row').append($('<td>').attr('colspan', 2).append( | ||
$('.record_payment-block') | ||
)) | ||
) | ||
|
||
waitForElement($, 'input[name=contribution_type_toggle]', | ||
() => { | ||
$('tr.crm-membership-form-block-receive_date').before($('tr.crm-membership-form-block-financial_type_id')); | ||
$('tr.crm-contribution-form-block-financeextras_record_payment_amount').after( | ||
$('tr.crm-membership-form-block-payment_instrument_id') | ||
); | ||
$('tr.crm-membership-form-block-payment_instrument_id').after($('tr.crm-membership-form-block-trxn_id')) | ||
$('tr.crm-membership-form-block-trxn_id').after($('tr.crm-membership-form-block-billing')) | ||
|
||
$('tr.crm-membership-form-block-contribution_status_id').hide() | ||
} | ||
); | ||
|
||
$('tr.record_payment-block_row').before( | ||
$('<tr>').append($('<td>').attr('colspan', 2).append( | ||
$('.record_payment-block_check') | ||
)) | ||
) | ||
|
||
const symbol = CRM.vars.financeextras.currencySymbol; | ||
$('.crm-membership-form-block-total_amount label').text('Contribution Total Amount') | ||
$('.crm-membership-form-block-financial_type_id label').text('Contribution Financial Type') | ||
$('#total_amount').before($('<span>').text(`${symbol} `)) | ||
$('.record_payment-block #currency-symbol').text(symbol) | ||
} | ||
|
||
function hidePaymentFieldsOnPaymentToggler() { | ||
$('li[data-selector="payment_plan"]').click( () => { | ||
$('tr.record_payment-block_row').hide() | ||
$('div.record_payment-block_check').hide(); | ||
}); | ||
|
||
$('li[data-selector="contribution"]').click( () => { | ||
$('div.record_payment-block_check').show() | ||
togglePaymentBlock(); | ||
}); | ||
} | ||
|
||
/** | ||
* Triggers callback when element attribute changes. | ||
* | ||
* @param {object} $ | ||
* @param {string} elementPath | ||
* @param {object} callBack | ||
*/ | ||
function waitForElement($, elementPath, callBack) { | ||
(new MutationObserver(function() { | ||
callBack($(elementPath)); | ||
})).observe(document.querySelector(elementPath), { | ||
attributes: true, | ||
}); | ||
} | ||
|
||
/** | ||
* Observes change in property value for an element. | ||
* | ||
* This method is used to listen for change in input fields | ||
* that doesn't emit a change event when their value changes. | ||
* | ||
* @param {string} elementPath | ||
* @param {string} property | ||
* @param {function} callback | ||
* @param {number} delay | ||
*/ | ||
function observeElement(elementPath, property, callback, delay = 0) { | ||
const element = document.querySelector(elementPath) | ||
const elementPrototype = Object.getPrototypeOf(element); | ||
if (Object.hasOwn(elementPrototype, property)) { | ||
const descriptor = Object.getOwnPropertyDescriptor(elementPrototype, property); | ||
Object.defineProperty(element, property, { | ||
get: function() { | ||
return descriptor.get.apply(this, arguments); | ||
}, | ||
set: function () { | ||
const oldValue = this[property]; | ||
descriptor.set.apply(this, arguments); | ||
const newValue = this[property]; | ||
if (typeof callback == "function") { | ||
setTimeout(callback.bind(this, oldValue, newValue), delay); | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<div class="fe-membership_type"> | ||
<table> | ||
<tbody> | ||
<tr> | ||
<td class="label">{$form.fe_member_type.paid_member.label}</td> | ||
<td>{$form.fe_member_type.paid_member.html}</td> | ||
</tr> | ||
<tr> | ||
<td class="label">{$form.fe_member_type.free_member.label}</td> | ||
<td>{$form.fe_member_type.free_member.html}</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</div> | ||
<div class="record_payment-block_check"> | ||
<table> | ||
<tbody> | ||
<tr class="crm-contribution-form-block-financeextras_record_payment_check"> | ||
<td class="label">{$form.fe_record_payment_check.html}</td> | ||
<td>{$form.fe_record_payment_check.label}</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</div> | ||
<div class="record_payment-block"> | ||
<table> | ||
<tbody> | ||
<tr class="crm-contribution-form-block-financeextras_record_payment_amount"> | ||
<td class="label" id="amount-label">{$form.fe_record_payment_amount.label} <span class="crm-marker" title="This field is required."> *</span></td> | ||
<td><span id="currency-symbol"></span> {$form.fe_record_payment_amount.html}</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</div> |