From 649d0e9cc23f8a8b9f49d1bc62589337c6b0a5d7 Mon Sep 17 00:00:00 2001 From: asem-compuco Date: Fri, 10 Mar 2023 14:50:50 +0200 Subject: [PATCH] MAE-928: Apply discount when payment plan selected --- .../API/PaymentSchedule/Base.php | 4 +- .../AbstractProcessor.php | 6 +-- .../Contribution.php | 5 ++- .../LineItem.php | 4 +- .../Page/InstalmentSchedule.php | 1 + .../Service/MembershipInstalmentsSchedule.php | 12 +++-- .../FixedPeriodTypeCalculator.php | 25 ++++++++++- .../RollingPeriodTypeCalculator.php | 20 +++++++-- .../PaymentSchedule/Getbymembershiptype.php | 7 +++ js/paymentPlanToggler.js | 45 ++++++++++++------- .../PaymentSchedule/MembershipTypeTest.php | 1 + 11 files changed, 99 insertions(+), 31 deletions(-) diff --git a/CRM/MembershipExtras/API/PaymentSchedule/Base.php b/CRM/MembershipExtras/API/PaymentSchedule/Base.php index e0a54c77..889e0ff7 100644 --- a/CRM/MembershipExtras/API/PaymentSchedule/Base.php +++ b/CRM/MembershipExtras/API/PaymentSchedule/Base.php @@ -39,6 +39,7 @@ protected function getInstalments(array $membershipTypes, array $nonMembershipPr $joinDate = !empty($this->params['join_date']) ? new DateTime($this->params['join_date']) : NULL; $startDate = !empty($this->params['start_date']) ? new DateTime($this->params['start_date']) : NULL; $endDate = !empty($this->params['end_date']) ? new DateTime($this->params['end_date']) : NULL; + $totalAmount = !empty($this->params['total_amount']) ? $this->params['total_amount'] : NULL; $membershipInstalmentsSchedule = new CRM_MembershipExtras_Service_MembershipInstalmentsSchedule( $membershipTypes, $this->params['schedule'] @@ -63,7 +64,8 @@ protected function getInstalments(array $membershipTypes, array $nonMembershipPr $paymentMethod, $membershipTypeDates['start_date'], $membershipTypeDates['end_date'], - $membershipTypeDates['join_date'] + $membershipTypeDates['join_date'], + $totalAmount ); } diff --git a/CRM/MembershipExtras/Hook/Pre/MembershipPaymentPlanProcessor/AbstractProcessor.php b/CRM/MembershipExtras/Hook/Pre/MembershipPaymentPlanProcessor/AbstractProcessor.php index fc1b5132..855c4328 100644 --- a/CRM/MembershipExtras/Hook/Pre/MembershipPaymentPlanProcessor/AbstractProcessor.php +++ b/CRM/MembershipExtras/Hook/Pre/MembershipPaymentPlanProcessor/AbstractProcessor.php @@ -85,15 +85,15 @@ protected function assignInstalmentDetails() { $this->instalmentsFrequencyUnit = $instalmentDetails['instalments_frequency_unit']; } - protected function getInstalmentAmountCalculator(array $membershipTypes, $periodType = 'rolling') { + protected function getInstalmentAmountCalculator(array $membershipTypes, $periodType = 'rolling', $totalAmount = NULL, $proRatedCalculated = TRUE) { if ($periodType == 'fixed') { - $calculator = new FixedPeriodTypeCalculator($membershipTypes); + $calculator = new FixedPeriodTypeCalculator($membershipTypes, $totalAmount, $proRatedCalculated); $calculator->setStartDate(new DateTime($this->getMembership()['start_date'])); $calculator->setEndDate(new DateTime($this->getMembership()['end_date'])); $calculator->setJoinDate(new DateTime($this->getMembership()['join_date'])); } else { - $calculator = new RollingPeriodTypeCalculator($membershipTypes); + $calculator = new RollingPeriodTypeCalculator($membershipTypes, $totalAmount, $proRatedCalculated); } $instalmentAmountCalculator = new InstalmentAmountCalculator($calculator); diff --git a/CRM/MembershipExtras/Hook/Pre/MembershipPaymentPlanProcessor/Contribution.php b/CRM/MembershipExtras/Hook/Pre/MembershipPaymentPlanProcessor/Contribution.php index 61f5af98..52d54b9e 100644 --- a/CRM/MembershipExtras/Hook/Pre/MembershipPaymentPlanProcessor/Contribution.php +++ b/CRM/MembershipExtras/Hook/Pre/MembershipPaymentPlanProcessor/Contribution.php @@ -49,7 +49,9 @@ public function __construct(&$params) { $this->setMembershipId(); $this->assignInstalmentDetails(); $this->handleContributionLineItems(); - $this->instalmentAmountCalculator = $this->getInstalmentAmountCalculator($this->membershipTypeLineItems, $this->periodType); + + $proRatedCalculated = TRUE; + $this->instalmentAmountCalculator = $this->getInstalmentAmountCalculator($this->membershipTypeLineItems, $this->periodType, $this->params['total_amount'], $proRatedCalculated); } /** @@ -290,7 +292,6 @@ private function adjustMembershipFee(CRM_Member_DAO_MembershipType $membershipTy if (!$this->isUsingPriceSet()) { return; } - $membershipType->minimum_fee = $lineTotal; } diff --git a/CRM/MembershipExtras/Hook/Pre/MembershipPaymentPlanProcessor/LineItem.php b/CRM/MembershipExtras/Hook/Pre/MembershipPaymentPlanProcessor/LineItem.php index e91a6818..1f27baeb 100644 --- a/CRM/MembershipExtras/Hook/Pre/MembershipPaymentPlanProcessor/LineItem.php +++ b/CRM/MembershipExtras/Hook/Pre/MembershipPaymentPlanProcessor/LineItem.php @@ -33,12 +33,14 @@ public function alterLineItemParameters() { */ private function handleMembershipTypeLineItem() { $lineItemMembershipType = CRM_Member_BAO_MembershipType::findById($this->params['membership_type_id']); + $totalAmount = $this->params['line_total'] + $this->params['tax_amount']; if ($this->isUsingPriceSet()) { //Since line item amount can be different from membership type amount //Make sure we are using line item total amount when using PriceSet $lineItemMembershipType->minimum_fee = $this->params['line_total']; + $totalAmount = NULL; } - $instalmentAmountCalculator = $this->getInstalmentAmountCalculator([$lineItemMembershipType], $lineItemMembershipType->period_type); + $instalmentAmountCalculator = $this->getInstalmentAmountCalculator([$lineItemMembershipType], $lineItemMembershipType->period_type, $totalAmount, TRUE); $instalmentAmount = $instalmentAmountCalculator->calculateInstalmentAmount($this->getLineItemInstalmentCount($lineItemMembershipType)); $this->params['line_total'] = $instalmentAmount->getAmount(); $this->params['unit_price'] = $instalmentAmount->getAmount(); diff --git a/CRM/MembershipExtras/Page/InstalmentSchedule.php b/CRM/MembershipExtras/Page/InstalmentSchedule.php index 0389630c..f90e3139 100644 --- a/CRM/MembershipExtras/Page/InstalmentSchedule.php +++ b/CRM/MembershipExtras/Page/InstalmentSchedule.php @@ -28,6 +28,7 @@ private function assignInstalments() { $params['payment_method'] = CRM_Utils_Request::retrieve('payment_method', 'Int'); $params['start_date'] = CRM_Utils_Request::retrieve('start_date', 'String'); $params['join_date'] = CRM_Utils_Request::retrieve('join_date', 'String'); + $params['total_amount'] = CRM_Utils_Request::retrieve('total_amount', 'Float'); try { $result = civicrm_api3('PaymentSchedule', $action, $params); diff --git a/CRM/MembershipExtras/Service/MembershipInstalmentsSchedule.php b/CRM/MembershipExtras/Service/MembershipInstalmentsSchedule.php index 2a37f0c4..c25179e2 100644 --- a/CRM/MembershipExtras/Service/MembershipInstalmentsSchedule.php +++ b/CRM/MembershipExtras/Service/MembershipInstalmentsSchedule.php @@ -57,6 +57,10 @@ class CRM_MembershipExtras_Service_MembershipInstalmentsSchedule { * @var DateTime|null */ private $joinDate; + /** + * @var float + */ + private $totalAmount; /** * @var \CRM_MembershipExtras_Service_MembershipInstalmentAmountCalculator */ @@ -94,17 +98,19 @@ public function __construct(array $membershipTypes, string $schedule) { * @param DateTime|null $startDate * @param DateTime|null $endDate * @param DateTime|null $joinDate + * @param float|null $totalAmount * * @return mixed * @throws Exception */ - public function generate($paymentMethod, DateTime $startDate = NULL, DateTime $endDate = NULL, DateTime $joinDate = NULL) { + public function generate($paymentMethod, DateTime $startDate = NULL, DateTime $endDate = NULL, DateTime $joinDate = NULL, $totalAmount = NULL) { if (empty($startDate)) { $startDate = new DateTime($this->getMembershipStartDate($this->membershipTypes[0]->id, $startDate, $endDate, $joinDate)); } $this->startDate = $startDate; $this->endDate = $endDate; $this->joinDate = $joinDate; + $this->totalAmount = $totalAmount; $this->instalmentCount = $this->getInstalmentsNumber( $this->membershipTypes[0], $this->schedule, $this->startDate, $this->endDate, $this->joinDate ); @@ -209,14 +215,14 @@ private function calculateInstalmentAmount() { */ private function getInstalmentAmountCalculator() { if ($this->membershipTypes[0]->period_type == 'fixed') { - $fixedPeriodTypCalculator = new FixedPeriodTypeCalculator($this->membershipTypes); + $fixedPeriodTypCalculator = new FixedPeriodTypeCalculator($this->membershipTypes, $this->totalAmount); $fixedPeriodTypCalculator->setStartDate($this->startDate); $fixedPeriodTypCalculator->setEndDate($this->endDate); $fixedPeriodTypCalculator->setJoinDate($this->joinDate); $this->instalmentCalculator = new InstalmentAmountCalculator($fixedPeriodTypCalculator); } else { - $this->instalmentCalculator = new InstalmentAmountCalculator(new RollingPeriodCalculator($this->membershipTypes)); + $this->instalmentCalculator = new InstalmentAmountCalculator(new RollingPeriodCalculator($this->membershipTypes, $this->totalAmount)); } } diff --git a/CRM/MembershipExtras/Service/MembershipPeriodType/FixedPeriodTypeCalculator.php b/CRM/MembershipExtras/Service/MembershipPeriodType/FixedPeriodTypeCalculator.php index 70ca96d6..066d064c 100644 --- a/CRM/MembershipExtras/Service/MembershipPeriodType/FixedPeriodTypeCalculator.php +++ b/CRM/MembershipExtras/Service/MembershipPeriodType/FixedPeriodTypeCalculator.php @@ -44,10 +44,20 @@ class CRM_MembershipExtras_Service_MembershipPeriodType_FixedPeriodTypeCalculato * @var array */ private $membershipTypes; + /** + * @var float|null + */ + private $totalAmount; + /** + * @var bool + */ + private $proRatedCalculated; - public function __construct(array $membershipTypes) { + public function __construct(array $membershipTypes, $totalAmount = NULL, $proRatedCalculated = FALSE) { $this->instalmentTaxAmountCalculator = new MembershipInstalmentTaxAmountCalculator(); $this->membershipTypes = $membershipTypes; + $this->totalAmount = $totalAmount; + $this->proRatedCalculated = $proRatedCalculated; } /** @@ -68,6 +78,7 @@ private function calculateProRatedAmount($amount, $duration, $diff) { */ public function calculate() { foreach ($this->membershipTypes as $membershipType) { + $discount = 1; $membershipTypeDurationCalculator = new MembershipTypeDurationCalculator($membershipType, new MembershipTypeDatesCalculator()); $settings = CRM_MembershipExtras_SettingsManager::getMembershipTypeSettings($membershipType->id); $annualProRataCalculation = $settings[SettingField::ANNUAL_PRORATA_CALCULATION_ELEMENT]; @@ -91,9 +102,21 @@ public function calculate() { $this->proRatedNumber = $membershipTypeDurationCalculator->calculateDaysBasedOnDates($this->startDate, $this->endDate, $this->joinDate); } } + + if (!empty($this->totalAmount) && !$this->proRatedCalculated) { + $discount = $this->totalAmount / ($membershipAmount + $taxAmount); + } + $amount = $this->calculateProRatedAmount($membershipAmount, $duration, $this->proRatedNumber); $taxAmount = $this->calculateProRatedAmount($taxAmount, $duration, $this->proRatedNumber); + if (!empty($this->totalAmount) && $this->proRatedCalculated) { + $discount = $this->totalAmount / ($amount + $taxAmount); + } + + $amount = $amount * $discount; + $taxAmount = $taxAmount * $discount; + $this->amount += $amount; $this->taxAmount += $taxAmount; diff --git a/CRM/MembershipExtras/Service/MembershipPeriodType/RollingPeriodTypeCalculator.php b/CRM/MembershipExtras/Service/MembershipPeriodType/RollingPeriodTypeCalculator.php index fc03224a..deb7b572 100644 --- a/CRM/MembershipExtras/Service/MembershipPeriodType/RollingPeriodTypeCalculator.php +++ b/CRM/MembershipExtras/Service/MembershipPeriodType/RollingPeriodTypeCalculator.php @@ -10,10 +10,15 @@ class CRM_MembershipExtras_Service_MembershipPeriodType_RollingPeriodTypeCalcula * @var array */ private $membershipTypes; + /** + * @var float|null + */ + private $totalAmountAfterDiscount; - public function __construct(array $membershipTypes) { + public function __construct(array $membershipTypes, $totalAmountAfterDiscount = NULL) { $this->instalmentTaxAmountCalculator = new MembershipInstalmentTaxAmountCalculator(); $this->membershipTypes = $membershipTypes; + $this->totalAmountAfterDiscount = $totalAmountAfterDiscount; } /** @@ -23,8 +28,17 @@ public function __construct(array $membershipTypes) { */ public function calculate() { foreach ($this->membershipTypes as $membershipType) { + $discount = 1; $amount = $membershipType->minimum_fee; - $taxAmount = $this->instalmentTaxAmountCalculator->calculateByMembershipType($membershipType, $membershipType->minimum_fee); + $taxAmount = $this->instalmentTaxAmountCalculator->calculateByMembershipType($membershipType, $amount); + + // calculate the discount amount + if (!empty($this->totalAmountAfterDiscount)) { + $discount = $this->totalAmountAfterDiscount / ($amount + $taxAmount); + } + + $amount = $amount * $discount; + $taxAmount = $taxAmount * $discount; $this->amount += $amount; $this->taxAmount += $taxAmount; @@ -32,5 +46,5 @@ public function calculate() { $this->generateLineItem($membershipType->financial_type_id, $amount, $taxAmount); } } - + } diff --git a/api/v3/PaymentSchedule/Getbymembershiptype.php b/api/v3/PaymentSchedule/Getbymembershiptype.php index f29ca4ef..f95d813a 100644 --- a/api/v3/PaymentSchedule/Getbymembershiptype.php +++ b/api/v3/PaymentSchedule/Getbymembershiptype.php @@ -45,6 +45,13 @@ function _civicrm_api3_payment_schedule_getbymembershiptype_spec(&$spec) { 'type' => CRM_Utils_Type::T_DATE, 'api.required' => 0, ]; + + $spec['total_amount'] = [ + 'name' => 'total_amount', + 'title' => 'Total Amount', + 'type' => CRM_Utils_Type::T_FLOAT, + 'api.required' => 0, + ]; } /** diff --git a/js/paymentPlanToggler.js b/js/paymentPlanToggler.js index c7a7fabd..167f0599 100644 --- a/js/paymentPlanToggler.js +++ b/js/paymentPlanToggler.js @@ -70,11 +70,12 @@ function paymentPlanToggler(togglerValue, currencySymbol) { * Price set, or Payment Plan Schedule */ function setScheduleEvents() { + let totalAmount; $('#total_amount, #membership_type_id_1, #record_contribution').change(() => { if (!isPaymentPlanTabActive()) { return; } - + totalAmount = parseFloat($('#total_amount').val().replace(",", "")); let isPriceSet = isPriceSetSelected(); if (isPriceSet) { let selectedPriceFieldValues = getSelectedPriceFieldValues(); @@ -86,7 +87,13 @@ function paymentPlanToggler(togglerValue, currencySymbol) { CRM.api3('PaymentSchedule', 'getscheduleoptionsbypricefieldvalues', params).then(function (result) { if (result.is_error === 0) { setPaymentPlanScheduleOption(result.values); - generateInstalmentSchedule(isPriceSet); + CRM.api3('MembershipType', 'get', { + "sequential": 1, + "return": ["minimum_fee"], + "id": parseInt($('#membership_type_id_1').val()), + }).then(function() { + generateInstalmentSchedule(isPriceSet); + }); } else { CRM.alert(result.error_message, 'Error', 'error'); } @@ -97,7 +104,13 @@ function paymentPlanToggler(togglerValue, currencySymbol) { }).then(function (result) { if (result.is_error === 0) { setPaymentPlanScheduleOption(result.values); - generateInstalmentSchedule(isPriceSet); + CRM.api3('MembershipType', 'get', { + "sequential": 1, + "return": ["minimum_fee"], + "id": parseInt($('#membership_type_id_1').val()), + }).then(function(anotherRes) { + generateInstalmentSchedule(isPriceSet, null, totalAmount); + }); } else { CRM.alert(result.error_message, 'Error', 'error'); } @@ -110,7 +123,7 @@ function paymentPlanToggler(togglerValue, currencySymbol) { if (!isPaymentPlanTabActive()) { return; } - generateInstalmentSchedule(isPriceSetSelected(), $('#start_date').val()); + generateInstalmentSchedule(isPriceSetSelected(), $('#start_date').val(), totalAmount); assignFirstContributionReceiveDate(); }); @@ -118,7 +131,7 @@ function paymentPlanToggler(togglerValue, currencySymbol) { if (!isPaymentPlanTabActive()) { return; } - generateInstalmentSchedule(isPriceSetSelected(), $('#renewal_date').val()); + generateInstalmentSchedule(isPriceSetSelected(), $('#renewal_date').val(), totalAmount); assignFirstContributionReceiveDate(); }); } @@ -168,7 +181,7 @@ function paymentPlanToggler(togglerValue, currencySymbol) { * @param {boolean} isPriceSet * @param startDate */ - function generateInstalmentSchedule(isPriceSet, startDate) { + function generateInstalmentSchedule(isPriceSet, startDate, totalAmount) { let schedule = $('#payment_plan_schedule').val(); let params = { schedule: schedule, @@ -185,17 +198,15 @@ function paymentPlanToggler(togglerValue, currencySymbol) { } else { params.membership_type_id = parseInt($('#membership_type_id_1').val()); } - let url = CRM.url('civicrm/member/instalment-schedule', params, 'back'); - CRM.loadPage(url, { - target: '#instalment_schedule_table', - dialog: false, - }).on('crmLoad', function (event, data) { - if (data.hasOwnProperty('is_error') && data.is_error == true) { - CRM.alert(data.error_message, 'Error', 'error'); - } else { - updateTotalAmount($('#instalment-total-amount').html(), isPriceSet); - setMembershipDates($('#instalment-membership-start-date').html(), $('#instalment-membership-end-date').html()); - } + params.snippet = "json"; + + $.post(CRM.url('civicrm/member/instalment-schedule', params, 'back'), {total_amount: totalAmount}, function(result) { + let instalmentScheduleTableContent = result['content']; + $("#instalment_schedule_table").html(instalmentScheduleTableContent); + updateTotalAmount($('#instalment-total-amount').html(), isPriceSet); + setMembershipDates($('#instalment-membership-start-date').html(), $('#instalment-membership-end-date').html()); + }).fail(function(response) { + CRM.alert(response.responseText, 'Error', 'error'); }); } diff --git a/tests/phpunit/CRM/MembershipExtras/API/PaymentSchedule/MembershipTypeTest.php b/tests/phpunit/CRM/MembershipExtras/API/PaymentSchedule/MembershipTypeTest.php index 8b543389..3d403a98 100644 --- a/tests/phpunit/CRM/MembershipExtras/API/PaymentSchedule/MembershipTypeTest.php +++ b/tests/phpunit/CRM/MembershipExtras/API/PaymentSchedule/MembershipTypeTest.php @@ -32,6 +32,7 @@ public function testInvalidScheduleWillThrowAnException() { $params['schedule'] = 'xyz'; $params['membership_type_id'] = $membershipType['id']; $params['payment_method'] = $this->getPaymentMethodValue(); + $params['total_amount'] = $membershipType['minimum_fee']; $schedule = new CRM_MembershipExtras_API_PaymentSchedule_MembershipType($params); $schedule->getPaymentSchedule(); }